Compiler construction LLVM alloca在while语句上导致堆栈溢出

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

我正在为一种以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, 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
,因此仍然存在堆栈溢出。