5

我正在为针对 LLVM-IR 的玩具语言实现前端编译器,在运行编译while语句时遇到堆栈溢出:

例如,这段代码应该永远运行,但一段时间后我们编译的版本堆栈溢出。

def run(): Void = {
    i = 0;
    while(true) {
        i = i + 1;
    }
}

这是编译后的 LLVM-IR:

define i32 @run() nounwind ssp {
    ; i = 0
    %i = alloca i32, align 4
    %1 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    %2 = load i32* %1, align 4
    store i32 %2, i32* %i, align 4
    br label %3

; <label>: %3
    ; while(true)
    ; Generated by compileExpression(condition)
    %4 = alloca i1, align 4
    store i1 true, i1* %4, align 4
    %5 = load i1* %4, align 4
    br i1 %5, label %6, label %11

; <label>: %6
    ; i = i + 1
    ; Generated by compileExpression(body)
    %7 = load i32* %i, align 4
    %8 = alloca i32, align 4
    store i32 1, i32* %8, align 4
    %9 = load i32* %8, align 4
    %10 = add nsw i32 %7, %9
    store i32 %10, i32* %i, align 4
    br label %3

; <label>: %11
    %12 = load i32* %i, align 4
    ret i32 %12
}

我们认为我们的问题来自所有alloca未发布的,因为我们仍然在同一个函数中。

LLVM 文档

当函数返回时,“分配”的内存会自动释放。

我们应该如何编译while循环?
我们能避免这个问题吗?

4

3 回答 3

7

您会生成错误的 IR:具体而言,alloca在循环中是一个坏主意,并且确实会导致堆栈溢出。

我希望看到的是alloca循环外,然后是循环内的load,addstore序列。稍后您可以运行 mem2reg 通行证,这将摆脱allocas 并将 and 转换loadstore更有效的phi.

对于你alloca的 while 条件也是一样的:你需要做同样的事情,提前准备内存并且只在循环内部store

于 2014-01-09T18:02:12.247 回答
0

使用 mem2reg 传递将 allocas 转换为寄存器值。寄存器值在最后一次使用时被释放。

于 2014-01-09T16:31:09.037 回答
0
define i32 @run() nounwind ssp {
    ; i = 0
    %i = alloca i32, align 4
    %1 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    %2 = load i32* %1, align 4
    store i32 %2, i32* %i, align 4
    %3 = alloca i1, align 4
    store i1 true, i1* %3, align 4
    %4 = alloca i32, align 4
    br label %whilecond

whilecond:
    ; while(true)
    ; Generated by compileExpression(condition)
    %5 = load i1* %3, align 4
    br i1 %5, label %whilebody, label %whileexit

whilebody:
    ; i = i + 1
    ; Generated by compileExpression(body)
    %6 = load i32* %i, align 4
    store i32 1, i32* %4, align 4
    %7 = load i32* %4, align 4
    %8 = add nsw i32 %6, %7
    store i32 %8, i32* %i, align 4
    br label %whilecond

whileexit:
    %9 = load i32* %i, align 4
    ret i32 %9
}

在 opt -mem2reg 结果为:

define i32 @run() #0 {
       br label %whilecond

whilecond:                                        ; preds = %whilebody, %0
       %i.0 = phi i32 [ 0, %0 ], [ %1, %whilebody ]
       br i1 true, label %whilebody, label %whileexit

whilebody:                                        ; preds = %whilecond
       %1 = add nsw i32 %i.0, 1
       br label %whilecond

whileexit:                                        ; preds = %whilecond
       ret i32 %i.0
}
于 2014-01-12T01:52:54.303 回答