我开始在使用LLVM作为后端的语言中添加闭包(lambdas)。我已经在简单的情况下实现了它们,它们总是可以内联的,即闭包定义本身的代码不需要生成,因为它在使用的地方是内联的。
但是如何在闭包不总是内联的情况下为闭包生成代码(例如,它被传递给另一个未内联的函数)。最好,调用站点不应该关心传递给它们的是常规函数还是闭包,并将它们作为常规函数进行调用。
我可以生成一个带有合成名称的函数,但它必须将引用环境作为额外的参数,并且该函数不能只是传递给另一个不知道所需额外参数的函数。
我已经想到了一种可能的解决方案,使用LLVM的trampoline内部函数,它从函数中“删除”一个参数,返回一个指向一个较少参数的trampoline函数的指针。在这种情况下,如果为闭包生成的函数将引用环境作为第一个参数,我可以删除它并返回一个函数,该函数接受与闭包实际声明的参数完全相同的参数。这听起来可行吗?高效?有没有更好的解决方案?
代码示例:
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
}
这看起来对吗?
发布于 2012-01-03 18:40:02
听起来是可行的和有效的。
另一种不需要弹床的方法是将闭包类型定义为一对函数指针和指向环境的指针,即堆栈指针。在C调用约定中,额外的参数被忽略,所以如果你提供环境作为最后一个参数,你甚至可以使用(function_ptr,null)作为常规函数的回调。
发布于 2012-01-10 00:27:58
一个愚蠢的想法是,对于每个闭包,您将生成一个线程本地结构来保存所需的数据(可以只是一个指向本地结构的指针,也可以是几个指针)。
闭包的创建者负责设置TLS变量并“保存”它们的状态(以允许递归调用)。
然后用户正常调用该函数,它被执行并使用环境。
调用之后,闭包的创建者将原始值“恢复”到TLS变量中。
https://stackoverflow.com/questions/8706998
复制相似问题