Compiler construction LLVM alloca在while语句上导致堆栈溢出
我正在为一种以LLVM-IR为目标的玩具语言实现前端编译器,在运行编译的Compiler construction LLVM alloca在while语句上导致堆栈溢出,compiler-construction,llvm,llvm-ir,Compiler Construction,Llvm,Llvm Ir,我正在为一种以LLVM-IR为目标的玩具语言实现前端编译器,在运行编译的while语句时遇到堆栈溢出: 例如,这段代码应该永远运行,但编译后的版本堆栈在一段时间后溢出 def run(): Void = { i = 0; while(true) { i = i + 1; } } 以下是Compled LLVM-IR: define i32 @run() nounwind ssp { ; i = 0 %i = alloca i32, ali
while
语句时遇到堆栈溢出:
例如,这段代码应该永远运行,但编译后的版本堆栈在一段时间后溢出
def run(): Void = {
i = 0;
while(true) {
i = i + 1;
}
}
以下是Compled 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
}
define i32@run(){
;i=0
%i=alloca i32,对齐4
%1=alloca i32,对齐4
存储i32 0,i32*%1,对齐4
%2=负载i32*%1,对齐4
存储i32%2,i32*%i,对齐4
br标签%3
; : %3
;虽然(正确)
;由compileExpression生成(条件)
%4=alloca i1,对齐4
存储i1为真,i1*%4,对齐4
%5=负载i1*%4,对齐4
br i1%5,标签%6,标签%11
; : %6
;i=i+1
;由compileExpression(正文)生成
%7=负载i32*%i,对齐4
%8=alloca i32,对齐4
存储i32 1,i32*%8,对齐4
%9=负载i32*%8,对齐4
%10=添加新南威尔士州i32%7,%9
存储i32%10,i32*%i,对齐4
br标签%3
; : %11
%12=负载i32*%i,对齐4
ret i32%12
}
我们认为我们的问题来自于没有发布的每个alloca
,因为我们仍然处于相同的功能中
:
“alloca”内存在函数返回时自动释放
我们应该如何编译while循环?我们可以避免这个问题吗?使用mem2reg传递来转换alloca以注册值。寄存器值在最后一次使用时被释放。您会生成错误的IR:具体来说,
alloca
在循环中是个坏主意,并且确实会导致堆栈溢出
我希望看到的是循环外部的alloca
,然后是循环内部的load
,add
和store
序列。稍后,您可以运行mem2reg过程,它将去除alloca
s,并将加载
和存储
转换为更有效的phi
对于while条件,您的alloca
也需要做同样的事情:您需要提前准备内存,并且只在循环内部存储它
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
}
当while语句有更大的条件/主体表达式时,这会起作用吗?是的,它在ir级别工作,因此需要更长的时间来处理更多的指令。我们尝试了opt-S-mem2reg test.ll-o test.ll
我们的主体中仍然有一些alloca
,因此仍然存在堆栈溢出。