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

时间:2016-03-13 21:36:46

标签: pointers recursion assembly llvm

这是我的整个计划;我使用愚蠢的递归算法来熟悉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程序集不需要指针作为其参数。

2 个答案:

答案 0 :(得分:2)

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

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

您可以retcall,而不是同时执行* {。你在这里写的是试图返回函数的地址 - 当然不是i32,因此类型错误。相反,你需要做类似的事情:

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



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

答案 1 :(得分:1)

我甚至不知道llvm存在,但是将递归调用结果赋给变量工作。然后回来。即使在-O0,这也会优化。有关正确的术语,请参阅Oak的答案/评论。

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
}