IRBuilder doesn't properly keep track of its current location
- Dominant language
- Python
- Stars
- 2.3k
- Forks
- 372
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 10
Description
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:
``` c++
int main(int argc, char** argv) {
LLVMContext &Context = getGlobalContext();
std::unique_ptr Mod = make_unique("my module", Context);
std::vector 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:
``` python
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.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading IRBuilder's insertion-point handling around _anchor, position_at_start, and ret, then reproduce the two-builder Python example from the issue. Compare the numeric anchor behavior with the demonstrated LLVM IRBuilder behavior and define completion as keeping the original builder's insertion location valid when instructions are inserted before it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100