It does seem like an early pass for "trivial function elimination" could potentially be beneficial at low optimization levels (even potentially for optimized builds, w.r.t. code size).
To spec it very roughly,
- Before any other inlining passes have run
- For every "small" function
- Where "small" could usefully be defined more aggressively than the standard inlining passes' definition as one of
- Below some (small) threshold of IR statements
- Below some (small) threshold of basic blocks
- One of the above, but also has no unwind edges
- (would require running after unwind edge elimination)
- Any combination of the above
- Where "small" could usefully be defined more aggressively than the standard inlining passes' definition as one of
- Replace calls to the function with the body of the function
Ideally this pass would run top-down to eliminate the most "trivial functions," which could later be dead code eliminated at higher opt levels. But if the threshold is tuned correctly (my initial idea was literally to cap it to a single basic block, or however many are required to just tail call a function with some argument shuffling) it may be equivalent either way.
Is this something that could be done by the MIR inliner? Doing this could even potentially e.g. remove the need to pass a bunch of monomorphizations of e.g. <&[T]>::into_iter
calls to LLVM. If it's not too out there, I'm open to making this my first compiler contribution (as opposed to libs) if someone can mentor it. If we don't already have a pass similar to this it's definitely worth experimenting with. If we do already have a general inlining pass, we could experiment with a low threshold (the "trivial function" threshold) configuration of it run earlier, and additionally in -O1.