In Unladen Swallow we (intend to) compile each function as we
determine it's hot. To "compile" a function means to translate it from
CPython bytecode to LLVM IR, optimize the IR using a
FunctionPassManager, and JIT the IR to machine code. We'd like to
include inlining among our optimizations. Currently the Inliner is a
CallGraphSCCPass, which can only be run by the (Module)PassManager and
which will try to inline calls into every function in the module,
including functions we've already optimized. This seems like it will
I think that, because we optimize every Python function as we generate
it, it makes sense to write a SingleFunctionInliner (is there a better
name?) that acts as a FunctionPass and add that to our normal
FunctionPassManager. Can you guys see anything that will go wrong if I
do that? Is there a better way, or an option that doesn't involve
writing new code?
Does this need to be an actual pass? You can just call the InlineFunction API to inline individual call sites as needed, check out llvm/Transforms/Utils/Cloning.h. CloneAndPruneFunctionInto is a particularly useful function if you're doing argument specialization, because it is very efficient at handling this (better than inlining a bunch of code and then deleting it as dead later).
It would be more convenient as a pass since the other optimizations
I'm running are arranged as passes, but it's not absolutely necessary.
It'd be relatively easy to wrap InlineFunction() into a pass, of
course. On the other hand, the functions I'm currently worried about
inlining are all known when we translate the bytecode into IR, so I
could replace the IRBuilder::CreateCall() calls with appropriate uses
of CloneAndPruneFunctionInto(). I also just discovered `llc
-cppgen=inline`, which does something similar.
Once I have the call or invoke instruction in a function, it looks
like InlineFunction() does everything CloneAndPruneFunctionInto does,
along with all the necessary bookkeeping around it. The only reason to
call CloneAndPrune directly would be to avoid creating the call
instruction in the first place, right?
It looks easiest to wrap InlineFunction() into a pass, but I'll keep
the other things in mind for when we add more interesting inlining.