在 LLVM 程序集中递归的正确方法是什么?

What's the proper way to recurse in LLVM assembly?

这是我的完整程序;我正在使用一种愚蠢的递归算法来熟悉 LLVM 程序集:

declare void @print_int(i32)

define i32 @rec_add(i32 %a, i32 %b) {
entry:
    %tmp1 = icmp eq i32 %a, 0
    br i1 %tmp1, label %done, label %recurse
    recurse:
        %tmp2 = sub i32 %a, 1
        %tmp3 = add i32 %b, 1
        ret i32 @rec_add(i32 %tmp2, i32 %tmp3)
    done:
        ret i32 %b
}

define i32 @main() {
    %tmp4 = i32 4;
    %tmp5 = i32 1;
    %cast = call i32 @rec_add(i32 %tmp4, i32 %tmp5)
    call void @print_int(i32 %cast)
}

当我用 $ llvm-as rec_add.ll 编译这个程序时,我收到这个错误信息:

llvm-as: rec_add.ll:10:11: error: global variable reference must have pointer type
                ret i32 @rec_add(i32 %tmp2, i32 %tmp3)
                        ^

我不明白这个错误信息是什么意思,因为我的程序没有全局变量。我知道 LLVM 程序集不需要指针作为其参数。

我什至不知道 llvm-as 的存在,但是将递归调用结果分配给一个变量是可行的。然后return那个。这甚至在 -O0 处也进行了优化。请参阅 Oak 的 answer/comments 了解正确的术语。

define i32 @rec_add(i32 %a, i32 %b) {
entry:
    %tmp1 = icmp eq i32 %a, 0
    br i1 %tmp1, label %done, label %recurse
    recurse:
        %tmp2 = sub i32 %a, 1
        %tmp3 = add i32 %b, 1
        %tmp4 = call i32 @rec_add(i32 %tmp2, i32 %tmp3)
        ret i32 %tmp4
    done:
        ret i32 %b
}

编译为 clang-3.8 -O0 -S -o- 的递归调用。

clang-3.8 -Wall -O3 rec-add.ll -S -masm=intel -o-,llvm看透递归:

rec_add:                                # @rec_add
# BB#0:                                 # %entry
        lea     eax, [rdi + rsi]
        ret

你的 main 没有编译:

rec-add.ll:17:13: error: expected instruction opcode
    %tmp4 = i32 4;

这样就可以了:

define i32 @main() {
    %cast = call i32 @rec_add(i32  4, i32 1)
    call void @print_int(i32 %cast)
    ret i32 0
}

与高级语言不同,LLVM 指令不能由复合表达式组成。所以这不是一个有效的指令:

ret i32 @rec_add(i32 %tmp2, i32 %tmp3)

您可以选择 retcall,但不能同时选择两者*。您实际上在这里写的是试图 return 函数的 地址 - 这当然不是 i32,因此是类型错误。相反,您需要执行以下操作:

%something = call i32 @rec_add(i32 %tmp2, i32 %tmp3)
ret i32 %something



* 好吧,如果这些是 constant expressions,你可以做复合的东西,但这里不是这种情况。