在动态语言中,动态类型代码JIT如何编译成机器代码?更具体地说:编译器是否在某些时候推断出类型?或者在这些情况下是否严格解释?
例如,如果我有类似下面的pseuocode
def func(arg)
if (arg)
return 6
else
return "Hi"
在运行代码之前,执行平台如何知道函数的返回类型是什么?
答案 0 :(得分:3)
一般来说,它没有。但是,它可以采用任何一种类型,并对其进行优化。细节取决于它是什么类型的JIT。
所谓的跟踪JIT编译器解释并观察程序,并记录单次运行的类型,分支等(例如循环迭代)。他们记录这些观察结果,插入一个(非常快速)检查,在执行代码时这些假设仍然是真的,然后根据这些假设优化下面的代码。例如,如果你的函数在一个循环中被调用,并且有一个常量的参数并且向它添加一个,那么JIT编译器首先记录这样的指令(我们将忽略调用帧管理,内存分配,变量间接等等,不是因为那些并不重要,但因为它们需要大量的代码并且已经过优化了):
; calculate arg
guard_true(arg)
boxed_object o1 = box_int(6)
guard_is_boxed_int(o1)
int i1 = unbox_int(o1)
int i2 = 1
i3 = add_int(res2, res3)
然后像这样优化它:
; calculate arg
; may even be elided, arg may be constant without you realizing it
guard_true(arg)
; guard_is_boxed_int constant-folded away
; unbox_int constant-folded away
; add_int constant-folded away
int i3 = 7
也可以移动防护装置以允许优化早期代码,结合使用更少的防护装置,如果它们是冗余的则被省略,加强以允许更多优化,等等。 如果警卫过于频繁地失败,或者某些代码在其他方面变得无用,则可以将其丢弃,或者至少修补以在警卫失败时跳转到不同的版本。
其他JIT采取更静态的方法。例如,您可以进行快速,不准确的类型推断,以至少识别一些操作。一些JIT编译器仅在函数作用域上运行(因此它们被一些人称为JIT编译器),因此它们可能无法创建大量的代码片段(追踪JIT编译器非常受欢迎的一个原因)。尽管如此,它们仍然存在 - 一个例子是Mozilla的JavaScript引擎Ion Monkey的最新版本,尽管它显然也是从跟踪JIT中获得灵感的。您还可以插入添加不总是有效的优化(例如,内联可能在以后更改的函数),并在它们出错时将其删除。
当所有其他方法都失败时,您可以执行解释器执行的操作,框对象,使用指针,标记数据以及根据标记选择代码。但这是非常低效的,JIT编译器的整个目的是摆脱这种开销,所以只有在没有合理的替代方案时(或者当它们仍在升温时)它们才会这样做。