嵌套函数的LLVM IR优化

时间:2015-04-15 13:57:13

标签: optimization nested llvm llvm-ir

我通过生成LLVM IR代码编写玩具功能语言的编译器。但是,我在使用嵌套函数优化案例时遇到了一些问题。

函数和运算符是curry,因此y = 10 + 20执行为f = plus(10); y = f(20)。到目前为止,LLVM优化过程已经足够智能,可以将上述内容优化为ret i32 30

一旦我添加了对嵌套函数的支持(通过lambda-lifting和免费标识符的附加env参数),LLVM就无法再优化上面的简单示例。

以下是嵌套函数的工作原理:当调用嵌套函数时,它需要的每个自由变量都被写入指针数组(env)并传递给嵌套函数。嵌套函数加载env并将每个索引读入本地寄存器。我希望优化器能够内联调用,然后消除env的冗余存储和重新加载。优化程序会内联调用,但无法消除env

原始代码和优化代码的最小示例如下:

declare i8* @malloc(i32)

@x = private constant i32 42

define i32 @main() {
  %env_pass = insertvalue [1 x i32*] zeroinitializer, i32* @x, 0
  %env_pass_ptr = call i8* @malloc(i32 8)
  %env_pass_cast = bitcast i8* %env_pass_ptr to [1 x i32*]*
  store [1 x i32*] %env_pass, [1 x i32*]* %env_pass_cast
  %res = call i32 @nested_func(i8* %env_pass_ptr)
  ret i32 %res
}

define private i32 @nested_func(i8* %env) {
  %env_ptr = bitcast i8* %env to [1 x i32*]*
  %env_val = load [1 x i32*]* %env_ptr
  %my_x = extractvalue [1 x i32*] %env_val, 0
  %val = load i32* %my_x
  ret i32 %val
}

优化到:

@x = private constant i32 42

declare noalias i8* @malloc(i32) #0

define i32 @main() #0 {
  %env_pass_ptr = tail call i8* @malloc(i32 8)
  %env_pass_cast = bitcast i8* %env_pass_ptr to [1 x i32*]*
  store [1 x i32*] [i32* @x], [1 x i32*]* %env_pass_cast
  %1 = getelementptr inbounds [1 x i32*]* %env_pass_cast, i32 0, i32 0
  %2 = load i32** %1
  %val.i = load i32* %2
  ret i32 %val.i
}

attributes #0 = { nounwind }

这个例子应该减少到ret i32 42。我怀疑问题出在getelementptr,这是由优化器产生的。

以下是表达式10 + 20的完整原始代码和优化代码的一些贴:originaloptimized

在这种情况下,我可以选择不通过'添加'在环境中发挥作用,因为它是一个全球性的。但是,我仍然希望这个例子能够正确优化。

2 个答案:

答案 0 :(得分:0)

如果我用getelementptr直接写入索引而不是使用insertvalue,我发现问题已经减少了。这是在找到另一个提示LLVM isn't great at optimizing insertvalue的问题之后发生的。

使用getelementptr后,上面的示例缩小为ret i32 42,现在我语言中的基本算术将减少为常量。使用多个运算符的方法(例如(10+20)*5)仍然没有完全减少。

答案 1 :(得分:0)

您的示例缺少数据布局字符串。没有这个,许多优化器都无法产生不错的结果 - 它们无法知道指针的大小等等。

有关详细信息,请参阅http://llvm.org/docs/LangRef.html#data-layout。我建议抓取平台上clang生成的数据布局字符串,然后粘贴到.ll作为第一步。