我开始在我的语言中添加闭包(lambdas),使用LLVM作为后端.我已经将它们实现为可以始终内联的简单情况,即闭包定义本身的代码不需要生成,因为它在内联使用时也是如此.
但是如果闭包并不总是内联的话,如何为闭包生成代码(例如,它被传递给另一个没有内联的函数).优选地,呼叫站点不应该关心它们是否被传递给常规功能或闭包并且将它们称为正常功能.
我可以使用合成名称生成一个函数,但它必须将引用环境作为额外参数,并且该函数不能仅传递给另一个不知道所需额外参数的函数.
我想到了一种可能的解决方案,使用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)
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们假设这不会被内联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* = …Run Code Online (Sandbox Code Playgroud) 我正在研究用LLVM编译的语言.只是为了好玩,我想做一些微基准测试.其中一个,我在一个循环中运行了一百万个sin/cos计算.在伪代码中,它看起来像这样:
var x: Double = 0.0
for (i <- 0 to 100 000 000)
x = sin(x)^2 + cos(x)^2
return x.toInteger
Run Code Online (Sandbox Code Playgroud)
如果我使用以下形式使用LLVM IR内联汇编来计算sin/cos:
%sc = call { double, double } asm "fsincos", "={st(1)},={st},1,~{dirflag},~{fpsr},~{flags}" (double %"res") nounwind
Run Code Online (Sandbox Code Playgroud)
这比分别使用fsin和fcos而不是fsincos更快.但是,它比我分别调用llvm.sin.f64and llvm.cos.f64intrinsics,编译调用C math lib函数要慢,至少使用我正在使用的目标设置(x86_64启用了SSE).
似乎LLVM在单/双精度FP之间插入一些转换 - 这可能是罪魁祸首.这是为什么?对不起,我是大会上的新手:
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0: # %loopEntry1
xorps %xmm0, %xmm0
movl $-1, %eax
jmp .LBB44_1
.align 16, 0x90
.LBB44_2: # %then4
# in Loop: Header=BB44_1 Depth=1 …Run Code Online (Sandbox Code Playgroud)