我开始在使用LLVM作为后端的语言中添加闭包(lambda)。我已经为简单情况实现了它们,其中它们可以始终内联,即闭包定义本身的代码不需要生成,因为它会在使用时被内联。
但是,如果闭包不能始终内联(例如,它被传递给另一个不内联的函数),如何为闭包生成代码?最好的情况是,调用站点不应关心它们是否传递常规函数或闭包,并且应像调用普通函数一样调用它们。
我可以生成一个具有合成名称的函数,但它必须作为额外参数获取引用环境,并且该函数无法仅传递给不知道所需额外参数的另一个函数。
我想到了一种可能的解决方案,使用LLVM的跳板内置函数,从函数中“切除”一个参数,返回一个指向跳板函数的指针,该跳板函数少接收一个参数。在这种情况下,如果为闭包生成的函数将引用环境作为第一个参数,则我可以切除它并获得一个只接收与闭包声明完全相同数量的参数的函数。这个解决方案可行吗?高效吗?还有更好的解决方案吗?
代码示例:
但是,如果闭包不能始终内联(例如,它被传递给另一个不内联的函数),如何为闭包生成代码?最好的情况是,调用站点不应关心它们是否传递常规函数或闭包,并且应像调用普通函数一样调用它们。
我可以生成一个具有合成名称的函数,但它必须作为额外参数获取引用环境,并且该函数无法仅传递给不知道所需额外参数的另一个函数。
我想到了一种可能的解决方案,使用LLVM的跳板内置函数,从函数中“切除”一个参数,返回一个指向跳板函数的指针,该跳板函数少接收一个参数。在这种情况下,如果为闭包生成的函数将引用环境作为第一个参数,则我可以切除它并获得一个只接收与闭包声明完全相同数量的参数的函数。这个解决方案可行吗?高效吗?还有更好的解决方案吗?
代码示例:
def applyFunctionTo(value: Int, f: (Int) -> Int) = f(value)
def main() = {
val m := 4;
val n := 5;
val lambda := { (x: Int) => x + m + n };
applyFunctionTo(3, lambda)
}
现在,假设这个代码不会被内联到def main() = 3 + 4 + 5
中,而且applyFunctionTo
可能会单独编译,我们无法在调用站点进行更改。使用trampolining,我想生成的代码大概是这样的(以伪代码表示,*表示指针):
def main$lambda$1(env: {m: Int, n: Int}*, x: Int) = x + env.m + env.n
def main() = {
m = 4
n = 5
env* = allocate-space-for {Int, Int}
env = {m, n}
tramp* = create-trampoline-for(main$lambda$1*, env*)
return applyFunctionTo(3, tramp*)
// release memory for env and trampoline if the lambda didn't escape
}
这是否看起来正确?