im working on a my own programming language. Im currently generating the code in LLVM IR. I got a question on nested If statement with phi. So lets say I have this in my language :
if n < 0 then
print("n < 0")
else
if 100 < n then
print("100")
else
print("\n")
I generated this in llvm ir :
; If
; ObjectIdentifier
%15 = load %struct.Main*, %struct.Main** %2
%16 = getelementptr inbounds %struct.Main, %struct.Main* %15, i32 0, i32 1
%17 = load i32, i32* %16
; VarValue
%18 = alloca i32
store i32 0, i32* %18
%19 = load i32, i32* %18
; Lower
%20 = icmp slt i32 %17, %19
br i1 %20, label %condIf1, label %condElse1
condIf1:
; Call Method
%21 = load %struct.Main*, %struct.Main** %2
%22 = getelementptr inbounds %struct.Main, %struct.Main* %21, i32 0, i32 0
; Arguments
; VarValue
%23 = alloca [6 x i8]
store [6 x i8] c"n < 0\00", [6 x i8]* %23
%24 = bitcast [6 x i8]* %23 to i8*
%25 = call %struct.IO* @IO_print(%struct.IO* %22, i8* %24)
br label %condEnd1
condElse1:
; If
; VarValue
%26 = alloca i32
store i32 100, i32* %26
%27 = load i32, i32* %26
; ObjectIdentifier
%28 = load %struct.Main*, %struct.Main** %2
%29 = getelementptr inbounds %struct.Main, %struct.Main* %28, i32 0, i32 1
%30 = load i32, i32* %29
; Lower
%31 = icmp slt i32 %27, %30
br i1 %31, label %condIf2, label %condElse2
condIf2:
; Call Method
%32 = load %struct.Main*, %struct.Main** %2
%33 = getelementptr inbounds %struct.Main, %struct.Main* %32, i32 0, i32 0
; Arguments
; VarValue
%34 = alloca [8 x i8]
store [8 x i8] c"n > 100\00", [8 x i8]* %34
%35 = bitcast [8 x i8]* %34 to i8*
%36 = call %struct.IO* @IO_print(%struct.IO* %33, i8* %35)
br label %condEnd2
condElse2:
; Call Method
%37 = load %struct.Main*, %struct.Main** %2
%38 = getelementptr inbounds %struct.Main, %struct.Main* %37, i32 0, i32 0
; Arguments
; VarValue
%39 = alloca [2 x i8]
store [2 x i8] c"\0a\00", [2 x i8]* %39
%40 = bitcast [2 x i8]* %39 to i8*
%41 = call %struct.IO* @IO_print(%struct.IO* %38, i8* %40)
br label %condEnd2
condEnd2:
%42 = phi %struct.IO* [%36, %condIf2], [%41, %condElse2]
br label %condEnd1
condEnd1:
%43 = phi %struct.IO* [%25, %condIf1], [%43, %condElse1]
Everything compiled, but i get those errors :
PHI node entries do not match predecessors!
%43 = phi %struct.IO* [ %25, %condIf1 ], [ %42, %condElse1 ]
label %condElse1
label %condEnd2
Instruction does not dominate all uses!
%42 = phi %struct.IO* [ %36, %condIf2 ], [ %41, %condElse2 ]
%43 = phi %struct.IO* [ %25, %condIf1 ], [ %42, %condElse1 ]
I can't figure out exactly what is the problem with the phi. Do you have any hint on how to solve this issue or use something else then phi ? Thank you !
The 'phi' instruction is used to implement the φ node in the SSA graph representing the function. Typically it is used to implement branching.
Summary. SSA is s property of IR(Intermediate representation) that requires each variable to only be assigned once and each variable to be declared before it is used. Functional languages such as Kaleidoscope makes it easy to generate a LLVM IR directly in SSA form.
The align 4 ensures that the address will be a multiple of 4 store i32 0, i32* %1. This sets the 32 bit integer pointed to by %1 to the 32 bit value 0. It's like saying *x = 1 in C++ ret i32 0. This returns from the function with a 32 bit return value of 0.
LLVM is designed around a language-independent intermediate representation (IR) that serves as a portable, high-level assembly language that can be optimized with a variety of transformations over multiple passes. LLVM.
condEnd1:
%43 = phi %struct.IO* [%25, %condIf1], [%test, %condElse1]
The predecessors of condEnd1
(i.e. the blocks that jump to condEnd1
) are condIf1
and condEnd2
, not condElse1
(that's what the error message is telling you). A phi node should list all of the block's predecessor and no other blocks because it's saying "if you jumped here from block foo
, use value bar
" and that only makes sense if foo
is a block that can actually jump to the block that the phi node is in.
Further %test
isn't available in the block %condElse1
(%condElse1
does not contain the definition of %test
, nor do all control flow paths to %condElse1
go through the block that does), so if %condElse1
were a predecessor, you wouldn't be allowed to take the value of %test
when jumping from there.
Both of these can be fixed by replacing %condElse1
with %condEnd2
in the phi. %condEnd2
is actually a predecessor of condEnd1
and it contains the definition of %test
.
As for alternatives to using phi nodes:
A common way to represent local variables is to allocate stack space for each variable using alloca
at the beginning of the function and then writing and reading from that memory when you need the value. This way you'd have one register per variable to store the variable's address, which would be defined in the first block of the function and thus dominate the entire function, and then, each time you work with a variable, temporary registers that don't outlive the current block. So there'd be no need for phi nodes.
In cases where the variables' addresses are never used, LLVM's mem2reg phase would rewrite this to the version using registers and phi nodes, so you'll get the same optimizability and performance, but don't have to transform everything to SSA form yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With