2

I'm using tinygo's LLVM bindings for Go (don't know if this is relevant) and I'm trying to implement If/Then branching without the else block.

I've found this answer on stackoverflow: LLVM IRBuilder If-Then Codegen but it's using the C++ API and tinygo's bindings don't provide a way to "push_back" a block into a function.

I have this piece of code that I'm trying to compile:

func factorial(x: i8) -> i8 {

    if x == 0 {
        return 1;
    } 
    
    return x * factorial(x - 1);
}

I'm going to omit some stuff for simplicity, so here's the generated IR:

define i8 @factorial(i8 %x) {
entry:
  %RETURN_VALUE = alloca i8, align 1
  %0 = alloca i8, align 1
  store i8 %x, i8* %0, align 1
  %1 = load i8, i8* %0, align 1
  %2 = icmp eq i8 %1, 0
  br i1 %2, label %3, label %merge

3:                                                ; preds = %entry
  store i8 1, i8* %RETURN_VALUE, align 1
  br label %merge

merge:                                            ; preds = %3, %entry
  %4 = load i8, i8* %0, align 1
  %5 = load i8, i8* %0, align 1
  %6 = sub i8 %5, 1
  %7 = call i8 @factorial(i8 %6)
  %8 = mul i8 %4, %7
  store i8 %8, i8* %RETURN_VALUE, align 1
  %9 = load i8, i8* %RETURN_VALUE, align 1
  ret i8 %9
}

My approach is to have a variable of some sort and then if a return statement is encountered, just change it's value, so it can be returned once at the end of the function. This works perfectly fine with If/Then/Else branching, but I want my language to be imperative instead of functional and allow If/Then branching.

Also, I've tried compiling C code to LLVM IR (clang -S -emit-llvm x.c) and see what it looks like on the inside.

There are some dummy statements, because clang is always trying to make optimizations, but I want the IR to be as raw as possible.

int main() {

    int x = 8;
    if (x) {
        return 1;
    }

    x = 3;
    return 0;
}

this C code compiles to

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 8, i32* %2, align 4
  %3 = load i32, i32* %2, align 4
  %4 = icmp ne i32 %3, 0
  br i1 %4, label %5, label %6

5:                                                ; preds = %0
  store i32 1, i32* %1, align 4
  br label %7

6:                                                ; preds = %0
  store i32 3, i32* %2, align 4
  store i32 0, i32* %1, align 4
  br label %7

7:                                                ; preds = %6, %5
  %8 = load i32, i32* %1, align 4
  ret i32 %8
}

I honestly have no idea how to implement this type of branching.

kamkow1
  • 467
  • 2
  • 8
  • The first IR in your example, are you generating that with the go api? How did you make blocks "3" and "merge"? The C++ term "push_back" is just "add to end of list", a function *is* a list of basic blocks. – Nick Lewycky Sep 14 '22 at 05:27
  • @NickLewycky sorry for a late response, but I was busy. the problem is that the go api only gives me a AddBasicBlock() method but I cannot manually modify the block list as it was shown in the post that I've linked. In Go arrays don't have methods like C++ vectors and the way you append items is by using the append() function, which uses an existing array and an extra element to form a new array. You take that new array and use it to override the existing array. This approach would be okay, only if the go api exposed a field that can be overridden with a new value. – kamkow1 Sep 14 '22 at 13:17

0 Answers0