我通过生成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
的完整原始代码和优化代码的一些贴:original,optimized
在这种情况下,我可以选择不通过'添加'在环境中发挥作用,因为它是一个全球性的。但是,我仍然希望这个例子能够正确优化。
答案 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作为第一步。