llvmlite
llvmlite copied to clipboard
Split context manager for if-then into two new blocks
I am trying to use LLVM lite to implement a while loop construct. I know the IR for this, but I am having trouble implementing this in Python. I believe that the reason for this is because the current implementation of the if-then
context manager contains the following basic blocks:
entry:
instructions
entry.if:
instrs
entry.endif
instrs
I believe that in order to be able to use this context manager, unless there is another way to do it with the current implementation, it should have the entry block split in two, like so:
entry:
instructions
entry.cond:
instrs
entry.if:
instrs
entry.endif
instrs
This way, users would be able to jump specifically back into the conditional basic block and avoid reinitialising any variables defined outside of the if statement, allowing for code such as:
int main() {
int x = 10;
START: if(x > 0) {
printf("%d\n", x);
x -= 1;
goto START;
}
}
Could you implement this by adding a basic block to the current function with append_basic_block
, then using the if-then
context manager in that new block?
How would I tell LLVM to add the instructions for initializing the variables to the entry block and to add the condition to the conditional block? Because presumably that would change between programs? This would be the kind of thing I'm after:
entry:
%".2" = bitcast [6 x i8]* @"fstr" to i64*
%".3" = alloca double
store double 0x4024000000000000, double* %".3"
br label %"cond"
cond:
%".5" = load double, double* %".3"
%".6" = fcmp ogt double %".5", 0x0
%".7" = uitofp i1 %".6" to double
%".8" = fptosi double %".7" to i1
br i1 %".8", label %"entry.if", label %"entry.endif"
entry.if:
%".10" = load double, double* %".3"
%".11" = call i32 (i64*, ...) @"printf"(i64* %".2", double %".10")
%".12" = load double, double* %".3"
%".13" = fsub double %".12", 0x3ff0000000000000
store double %".13", double* %".3"
br label %"cond"
entry.endif:
ret void
}
The following:
from llvmlite import ir
# Create a module, function, basic block, and builder on the block
mod = ir.Module()
fnty = ir.FunctionType(ir.VoidType(), [])
fn = ir.Function(mod, fnty, 'fn')
block = fn.append_basic_block('init')
builder = ir.IRBuilder(block)
# Initialize a variable
z = ir.Constant(ir.IntType(1), 0)
a = builder.add(z, z, 'a')
# Create a new block with an if-then
it_block = block.function.append_basic_block('it')
it_builder = ir.IRBuilder(it_block)
with it_builder.if_then(a) as bbend:
it_builder.add(z, z, 'b')
it_builder.branch(it_block)
# The initialization block should branch to the if-then block
builder.branch(it_block)
print(fn)
produces:
define void @"fn"()
{
init:
%"a" = add i1 0, 0
br label %"it"
it:
br i1 %"a", label %"it.if", label %"it.endif"
it.if:
%"b" = add i1 0, 0
br label %"it"
it.endif:
}
The function itself is as bit nonsensical, but does this illustrate how to get toward the block structure / control flow you're aiming for?
I kind of have it, except that what should be in the entry
basic block isn't in a basic block and is instead stored inside self.builder.block
. My original plan was to loop over the basic block and add the instructions to the entry block, but I'm not sure how to do that dynamically without hardcoding the instructions:
# This is the result of printing out self.builder.block
%"entry" = entry:
%".2" = bitcast [6 x i8]* @"fstr" to i64*
%".3" = alloca double
store double 0x4024000000000000, double* %".3"
# This is the main function with the entry block in it
define i32 @"main_fn"()
{
entry:
br label %"entry.cond"
entry.cond:
%".3" = load double, double* %".3"
%".4" = fcmp ogt double %".3", 0x0
%".5" = uitofp i1 %".4" to double
%".6" = fptosi double %".5" to i1
br i1 %".6", label %"entry.if", label %"entry.endif"
entry.if:
%".8" = load double, double* %".3"
%".9" = call i32 (i64*, ...) @"printf"(i64* %".2", double %".8")
%".10" = load double, double* %".3"
%".11" = fsub double %".10", 0x3ff0000000000000
store double %".11", double* %".3"
br label %"entry.cond"
entry.endif:
ret void
}
Can you share the code you have that produced that? (or a small reproducer that produces something with a similar problem) - I'm finding it a bit hard to see what to suggest from just the output.
The code I wrote that produces the above is at this link:
https://pastebin.com/Bmynpy66
I am also having some trouble understanding and getting the getelementptr
instruction to work, as I'm not entirely sure what it needs to be given, but I think I should make a separate post for that.
Been having a similar issue, the following code reproduces it:
builder = ir.IRBuilder()
module = ir.Module(name=__file__)
integer = ir.IntType(64)
fcopy = ir.FunctionType(ir.PointerType(integer), [ir.PointerType(integer), ir.PointerType(integer), integer])
copy = ir.Function(module, fcopy, name="copy")
copy_entry = copy.append_basic_block("copy_entry")
builder.position_at_end(copy_entry)
src, dst, cur_list_len = copy.args
src = builder.bitcast(src, ir.PointerType(integer))
dst = builder.bitcast(dst, ir.PointerType(integer))
cnt = integer(0)
cnt_ptr = builder.alloca(integer)
builder.store(cnt, cnt_ptr)
copy_loop = builder.append_basic_block("copy_loop")
builder.position_at_end(copy_loop)
with builder.if_then(
builder.icmp_signed("<", builder.load(cnt_ptr), cur_list_len),
) as then:
cnt = builder.load(cnt_ptr)
builder.store( # Increment ptrs and copy from src to dst
builder.load(builder.inttoptr(builder.add(builder.ptrtoint(src, integer), cnt), ir.PointerType(integer))),
builder.inttoptr(builder.add(builder.ptrtoint(dst, integer), cnt), ir.PointerType(integer))
)
builder.store(builder.add(cnt, integer(1)), cnt_ptr)
builder.branch(copy_loop)
builder.ret(dst)