IRBuilder doesn't properly keep track of its current location
I'm running into a problem when using two IRBuilders on the same basic block. The C++ IRBuilder can keep track of its location even if instructions were inserted into the BB before its current location. Here's an example:
int main(int argc, char** argv) {
LLVMContext &Context = getGlobalContext();
std::unique_ptr<Module> Mod = make_unique<Module>("my module", Context);
std::vector<Type*> Args(3, Type::getDoubleTy(Context));
FunctionType *FT = FunctionType::get(Type::getDoubleTy(Context), Args, false);
Function *F =
Function::Create(FT, Function::ExternalLinkage, "foo", Mod.get());
Function::arg_iterator arg_iter = F->arg_begin();
Value *arg1 = arg_iter++; arg1->setName("arg1");
Value *arg2 = arg_iter++; arg2->setName("arg2");
Value *arg3 = arg_iter++; arg3->setName("arg3");
BasicBlock *BB = BasicBlock::Create(Context, "entry", F);
// Create an IRBuilder pointing to the beginning of the entry block, and add
// a couple of instructions.
IRBuilder<> Builder(Context);
Builder.SetInsertPoint(BB);
Value *fa = Builder.CreateFAdd(arg1, arg2);
Value *fb = Builder.CreateFAdd(fa, arg3);
// Now create a new builder and point it to the beginning of 'entry'. Add
// an instruction.
IRBuilder<> TmpBuilder(BB, BB->begin());
TmpBuilder.CreateFSub(arg3, arg2);
// Add another instruction via the original builder. Note that it goes into
// the end of BB, since Builder still points there.
Builder.CreateRet(fb);
Mod->dump();
return 0;
}
This correctly creates the code:
define double @foo(double %arg1, double %arg2, double %arg3) {
entry:
%0 = fsub double %arg3, %arg2
%1 = fadd double %arg1, %arg2
%2 = fadd double %1, %arg3
ret double %2
}
Even though TmpBuilder added instructions before the location of Builder. This is because Builder remembers its location by using an iterator of ilist, which is tolerant to such changes.
On the other hand with llvmlite:
import llvmlite.ir as ll
module = ll.Module()
func_ty = ll.FunctionType(ll.DoubleType(), 3 * [ll.DoubleType()])
func = ll.Function(module, func_ty, 'foo')
func.args[0].name = 'arg1'
func.args[1].name = 'arg2'
func.args[2].name = 'arg3'
bb = func.append_basic_block('entry')
builder = ll.IRBuilder(bb)
fa = builder.fadd(func.args[0], func.args[1])
fb = builder.fadd(fa, func.args[2])
tmp_builder = ll.IRBuilder()
tmp_builder.position_at_start(bb)
tmp_builder.fsub(func.args[2], func.args[1])
builder.ret(fb)
print(module)
Gives:
define double @"foo"(double %"arg1", double %"arg2", double %"arg3")
{
entry:
%".6" = fsub double %"arg3", %"arg2"
%".4" = fadd double %"arg1", %"arg2"
ret double %".5"
%".5" = fadd double %".4", %"arg3"
}
Note where the ret got inserted.
This is because in llvmlite, IRBuilder keeps track of its location using a simple numeric index (_anchor) and is oblivious to changes in the underlying BB.
Hmm, indeed. Is there a reason you want two builders on the same BB? (it should work fine on separate BBs, I think). Python lists are fast, it would be a pity to bake our own linked lists for this.
See for example this code: https://github.com/eliben/pykaleidoscope/blob/master/chapter7.py#L604
The idea is: at some point I want to add stuff to the beginning of the entry block (allocas are one common case). The caller doesn't know if it's the same block as the current builder is in. The most convenient approach in C++ would be to just create a new builder for the entry block. But if it happens so that the current builder is also still pointing to the entry BB, things go askew. Of course the entry BB is just an example, it can be any BB.
It can be easily worked around, of course, but the mismatch with the C++ API is inconvenient.
I see. Numba has a helper around that, we could fold it into llvmlite: https://github.com/numba/numba/blob/master/numba/cgutils.py#L162 (other helpers could probably be folded too, if desired)
Ah yes, indeed those look relevant & interesting.
I like the idea of pushing generally useful things out of cgutils into llvmlite and documenting them there. That will also help us when training someone on how to write LLVM implementations for operations in Numba.
I've just pushed a couple additional helpers in the IRBuilder class: see https://github.com/numba/llvmlite/blob/master/llvmlite/ir/builder.py#L98 (goto_block(), goto_entry_block(), if_then(), if_else())
Any thoughts on fixing the underlying issue? I ran into this and spent a long time trying to figure out what was going on.