How are dynamic languages ​​jitted?

In dynamic languages, how is JIT code compiled into machine code dynamically entered? More specifically: does the compiler infer types at some point? Or is it strictly interpreted in these cases?

For example, if I have something like the following pseudocode

def func(arg) if (arg) return 6 else return "Hi" 

How can the execution platform know before running the code what the return type of the function is?

+4
source share
1 answer

In general, this is not so. However, it can accept any type and optimize for this. Details depend on what kind of JIT it is.

The so-called tracing JIT compilers interpret and monitor the program, as well as record types, branches, etc. for one run (e.g. loop iteration). They record these observations, insert (fairly quickly) a check that these assumptions are still true when the code is executed, and then optimize the output from the following code based on these statements. For example, if your function is called in a loop with a constant true argument and adds it to it, the JIT compiler first writes such instructions (we will ignore frame management, memory allocation, variable indirectness, etc. Not because they are not important, but because that they take up a lot of code and are also optimized):

 ; 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) 

and then optimizes it as follows:

 ; 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 

Guards can also be moved to optimize earlier code, teaming up to have fewer guards if redundant, reinforced to provide more optimization, etc. If the guards too often fail, or some code is otherwise useless, it can be dropped or at least fixed to switch to another version if the protection fails.

Other JITs take a more static approach. For example, you can make a quick, inaccurate type of output to at least recognize several operations. Some JIT compilers work only in the area of ​​functions (for example, they are called compilers of the JIT JIT method), so they probably cannot make the most of your code fragment (one of the reasons JIT compiler traces are very popular). However, they exist - an example is the latest version of Mozilla's JavaScript engine, Ion Monkey , although this seems to inspire JIT tracking as well. You can also insert the addition of not always the correct optimizations (for example, a built-in function that can be changed later) and delete them when they become incorrect.

When everything else fails, you can do what interpreters do, box objects, use pointers for them, tag data, and select code based on the tag. But this is extremely inefficient, the whole purpose of JIT compilers gets rid of this overhead, so they will only do this when there is no reasonable alternative (or when they are still warming up).

+2
source

All Articles