我正在为针对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
未发布的问题,因为我们仍处于相同的功能中。
当函数返回时,'alloca'd内存会自动释放。
我们应该如何编译while循环?
我们可以避免这个问题吗?
答案 0 :(得分:7)
你生成了错误的IR:具体来说,循环中的alloca
是一个坏主意,并且确实会导致堆栈溢出。
我希望看到的是循环外的alloca
,然后是循环内的load
,add
和store
序列。稍后您可以运行mem2reg传递,它将删除alloca
并将load
和store
转换为效率更高的phi
。
对于while条件,alloca
也是如此:你需要做同样的事情,提前准备内存并在循环内只准备store
。
答案 1 :(得分:0)
使用mem2reg pass将allocas转换为寄存器值。寄存器值在上次使用时会被释放。
答案 2 :(得分: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
}
在选择-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
}