我正在为动态类型语言制作字节码解释器。例如这个表达式:
2 + 3
将创建以下类似程序集的输出(稍后编译为字节码):
iconst reg1, 2 ; Put integer 2 to register 1
iconst reg2, 3 ; Put integer 3 to register 2
iadd reg3, reg1, reg2 ; Add the 2 as integers from reg1 and 2 and put it into reg3
另一个例子是:
1 + 3.2
输出:iconst reg1, 1
itof reg2, reg1 ; Convert integer to float and put it into reg2
fconst reg3, reg2
fadd reg4, reg2, reg3
所以每个类型都有它自己的操作符(fadd, iadd,可能还有sadd…)这意味着我需要知道编译类型的类型。这不是一个问题,直到我有一个函数调用:
function foo(x, y):
return x + y
foo()可以用整数、浮点数甚至混合数来调用。所以我不能在编译时为它生成正确的字节码。最好怎么做,这样才不会影响速度。用所使用的参数类型来生成函数是一个好策略吗?如果这个被调用:
foo(2, 3.2)
它会生成类似foo@int,float()的东西。还是在运行时解决它更好?我能在不影响性能的情况下这样做吗?如何?lua是如何做到的呢?
对不起,如果这是一个重复,也许我需要工作我的谷歌搜索技能。
我可以想到几种方法来处理这个问题:
1 -记录foo(x, y)是如何被调用的,并记录不同的函数签名。这可能会变得复杂,因为foo(x, y)可能从bar(a, b)被调用,而bar(a, b)可能从baz(c, d)被调用,等等。
在您解析了整个程序,构建了AST,并遍历它以记录对foo(x, y)调用的所有唯一签名之后,为调用它的不同方式生成代码。
你可能有:
foo(int, int)
foo(float, int)
这告诉您需要两个唯一的foo(x, y)函数。
2 -将操作码设置为具有通用的add, sub, multi等操作码。然后,泛型操作码必须在运行时确定其操作数的类型并适当地执行。这将稍微慢一些,因为操作码现在必须检查操作数的类型,并在此基础上进行分支。
3 -正如您所推测的,在实际调用函数之前不要生成函数代码。这将稍微慢一些,但只是在第一次调用上述函数时。与(1)一样,您仍然需要支持多个唯一的函数签名,因此仍然需要一些簿记。
我希望这对你有帮助!