llvmlite icon indicating copy to clipboard operation
llvmlite copied to clipboard

IRBuilder doesn't properly keep track of its current location

Open eliben opened this issue 10 years ago • 7 comments

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.

eliben avatar Feb 01 '15 14:02 eliben

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.

pitrou avatar Feb 09 '15 18:02 pitrou

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.

eliben avatar Feb 09 '15 19:02 eliben

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)

pitrou avatar Feb 09 '15 19:02 pitrou

Ah yes, indeed those look relevant & interesting.

eliben avatar Feb 09 '15 19:02 eliben

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.

seibert avatar Feb 09 '15 22:02 seibert

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())

pitrou avatar Apr 28 '15 16:04 pitrou

Any thoughts on fixing the underlying issue? I ran into this and spent a long time trying to figure out what was going on.

thomaspinckney3 avatar Apr 04 '22 17:04 thomaspinckney3