Just-In-Time Compilation and the Microsoft VM for Java |
|
|
 Previous |
 Virtual Machine |
 Next |
The JIT Compiler/VM Interface
The following diagram describes the relationship between the JIT compiler and the Microsoft VM for Java.
The JIT compiler and the VM provide interfaces that both use to enable effective JIT compilation of code. The "compilation unit" for the JIT compiler is a single method. The decision of which particular method is compiled is made solely by the VM. Tools vendors might want to provide facilities in their tools (for example, via language constructs or UI) to augment .class files to indicate what methods to JIT (the precise format of such .class file attributes is TBD).
These interfaces are designed to allow clean separation and independence between the VM and the JIT compiler. This will enable other vendors to replace one JIT compiler with another, as appropriate.
The interfaces are given in full in the vm2jit.h header file. They cover direct access between the VM and the JIT compiler, as well as compile and run-time helper functions. Limited debugging of JIT compiled code is also provided for, but feedback from tools vendors and other JIT compiler writers is welcome.
Independent of the interface given in the header filer, a JIT compiler (vendor) can make the following assumptions.
- Code and data blocks allocated via the respective VM helper functions will stay in place. Therefore, most references to methods and data members can be resolved at compilation time.
- The first element in every object contains the reference to the respective classes vtable (method-pointer table).
- For arrays (which are also Java objects) the offset for the element count as well as the offset for the first array element are always at the same fixed offset relative to the beginning of the object (array).
- Every loaded interface is assigned a unique handle, which allows quick access at run time.
- The vtable offsets of interface-methods are always the same across all implementations of the interface. Therefore, all vtable-offsets for methods can be resolved at compile time.
At the same time, the code and data generated by a JIT-compiler for the Microsoft VM for Java must meet the following criteria.
- All methods must have a local stack frame.
- For the x86 platform, the calling convention is basically "__pascal": parameters must be pushed from left to right ("this" is pushed before any user arguments), the callee pops arguments, EBX/ESI/EDI are the callee-saved registers, and the FP stack is always empty on entry to methods, return values in EAX (32-bit scalars), EDX:EAX (64-bit integers), or on the FP stack (floats and doubles).
- The JIT compiler must provide an API that enumerates all live references in a method while it is calling a method (stack crawling support for GC).
- Optionally, the JIT compiler may provide an API that enumerates all live references in a method at any time (this allows GC to occur at any time).
- Currently only "base" pointers can be live at points where GC may occur (for example, the code might not have a pointer into the interior of an object/array that is live across a method call).
- Code sharing across multiple namespaces is supported (optional).
It is up to the JIT compiler to decide whether it allows interruption by the garbage collector at any time or just while a method calls another method. The latter is certainly easier to implement and needs less information at run time, but in certain scenarios it could possibly disable the garbage collection process for an extended period of time (for example, a method executes a loop that does not contain a method call). In those cases the JIT compiler is required to insert code that (conditionally) invokes the garbage collector directly unless it can be determined that the code will execute method calls frequently enough. To give the JIT maximal flexibility, this decision can be made for every method individually.
Beyond just generating code for a method, the JIT must provide additional information to enable proper execution and debugging of Java applications. Depending on the type of information, that is done in two ways:
- Information defined by the VM: The JIT compiler provides this information at compile time to the VM, and the VM records and interprets it. (Examples: exception handler tables, line number information.)
- Private JIT information: The JIT compiler is free to store any private information along with each method so that it can provide the requested information to the VM at run time; the VM takes care of saving the information (to the VM it is just a single block of opaque data) and later passing it back to the JIT compiler at run time. (Examples: enumeration of live object references in a stack frame, decision if a method is "GC-safe" at the current offset, and so on.)