【问题标题】:LLVM alloca causes stack overflow on while statementLLVM alloca 导致 while 语句的堆栈溢出
【发布时间】:2014-01-09 16:00:29
【问题描述】:

我正在为针对 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 Documentation:

'分配的内存会在函数返回时自动释放。

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

【问题讨论】:

    标签: compiler-construction llvm llvm-ir


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 当 while 语句有更大的条件/主体表达式时,这会起作用吗?
      • 是的,它在 ir 级别上工作,因此需要更长的时间和更多的指令。
      • 我们尝试了opt -S -mem2reg test.ll -o test.ll,但在正文中仍然有一些alloca,所以我们仍然有堆栈溢出。
      【解决方案3】:
      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
      }
      

      【讨论】:

        猜你喜欢
        • 2015-05-21
        • 2014-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-11
        • 2015-12-21
        相关资源
        最近更新 更多