X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=docs%2Fmini-doc.txt;h=ba19c1031f112256c83295ebe53f2ecf6dfb8680;hb=813c1308fe53d16b419d22777f4d1124f06e7c9e;hp=052421e391ae1145f1274b54516e2bdb8653bdc9;hpb=596a57ce07d82f2c1a152183412e8145942726fc;p=mono.git diff --git a/docs/mini-doc.txt b/docs/mini-doc.txt index 052421e391a..ba19c1031f1 100644 --- a/docs/mini-doc.txt +++ b/docs/mini-doc.txt @@ -2,8 +2,17 @@ A new JIT compiler for the Mono Project Miguel de Icaza (miguel@{ximian.com,gnome.org}), + Paolo Molaro (lupus@{ximian.com,debian.org}) + + This documents overall design of the Mono JIT up to version + 2.0. After Mono 2.0 the JIT engine was upgraded from + a tree-based intermediate representation to a linear + intermediate representation. + + The Linear IL is documented here: + + http://www.mono-project.com/Linear_IL - * Abstract Mini is a new compilation engine for the Mono runtime. The @@ -69,6 +78,24 @@ CIL code on the fly to marshal parameters, and then this code is in turned processed by the JIT engine. + Mono has now gone through three different JIT engines, these + are: + + * Original JIT engine: 2002, hard to port, hard to + implement optimizations. + + * Second JIT engine, used up until Mono 2.0, very + portable, many new optimizations. + + * Third JIT engine, replaced the code generation layer from + being based on a tree representation to be based on a linear + representation. + + For more information on the code generation changes, see our + web site for the details on the Linear IL: + + http://www.mono-project.com/Linear_IL + * Previous Experiences Mono has built a JIT engine, which has been used to bootstrap @@ -161,7 +188,7 @@ application domains, or generate code that will not be shared across application domains. -* Objectives of the new JIT engine. +* Second Generation JIT engine. We wanted to support a number of features that were missing: @@ -205,7 +232,7 @@ necessary that the JIT engine works in most of today's commercial hardware platforms. -* Features of the new JIT engine. +* Features of the Second JIT engine. The new JIT engine was architected by Dietmar Maurer and Paolo Molaro, based on the new objectives. @@ -374,13 +401,14 @@ variables to registers. The linear scan pass uses the information that was previously gathered by the loop nesting and loop structure computation to favor variables in inner - loops. + loops. This process updates the basic block `nesting' field + which is later used during liveness analysis. Stack space is then reserved for the local variables and any temporary variables generated during the various optimizations. -** Instruction selection +** Instruction selection: Only used up until Mono 2.0 At this point, the BURS instruction selector is invoked to transform the tree-based representation into a list of @@ -488,7 +516,9 @@ operations if possible. The rest of the code generation is fairly simple: a switch - statement is used to generate code for each of the MonoInsts + statement is used to generate code for each of the MonoInsts, + in the mono/mini/mini-ARCH.c files, the method is called + "mono_arch_output_basic_block". We always try to allocate code in sequence, instead of just using malloc. This way we increase spatial locality which gives a massive @@ -605,7 +635,7 @@ registers, fixed registers and clobbered registers by each operation. -* BURG Code Generator Generator +* BURG Code Generator Generator: Only used up to Mono 2.0 monoburg was written by Dietmar Maurer. It is based on the papers from Christopher W. Fraser, Robert R. Henry and Todd @@ -619,6 +649,120 @@ JIT. This simplifies the code because we can directly pass DAGs and don't need to convert them to trees. +* Adding IL opcodes: an excercise (from a post by Paolo Molaro) + + mini.c is the file that read the IL code stream and decides + how any single IL instruction is implemented + (mono_method_to_ir () func), so you always have to add an + entry to the big switch inside the function: there are plenty + of examples in that file. + + An IL opcode can be implemented in a number of ways, depending + on what it does and how it needs to do it. + + Some opcodes are implemented using a helper function: one of + the simpler examples is the CEE_STELEM_REF implementation. + + In this case the opcode implementation is written in a C + function. You will need to register the function with the jit + before you can use it (mono_register_jit_call) and you need to + emit the call to the helper using the mono_emit_jit_icall() + function. + + This is the simpler way to add a new opcode and it doesn't + require any arch-specific change (though it's limited to what + you can do in C code and the performance may be limited by the + function call). + + Other opcodes can be implemented with one or more of the already + implemented low-level instructions. + + An example is the OP_STRLEN opcode which implements + String.Length using a simple load from memory. In this case + you need to add a rule to the appropriate burg file, + describing what are the arguments of the opcode and what is, + if any, it's 'return' value. + + The OP_STRLEN case is: + + reg: OP_STRLEN (reg) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->reg1, + state->left->reg1, G_STRUCT_OFFSET (MonoString, length)); + } + + The above means: the OP_STRLEN takes a register as an argument + and returns its value in a register. And the implementation + of this is included in the braces. + + The opcode returns a value in an integer register + (state->reg1) by performing a int32 load of the length field + of the MonoString represented by the input register + (state->left->reg1): before the burg rules are applied, the + internal representation is based on trees, so you get the + left/right pointers (state->left and state->right + respectively, the result is stored in state->reg1). + + This instruction implementation doesn't require arch-specific + changes (it is using the MONO_EMIT_LOAD_MEMBASE_OP which is + available on all platforms), and usually the produced code is + fast. + + Next we have opcodes that must be implemented with new low-level + architecture specific instructions (either because of performance + considerations or because the functionality can't get implemented in + other ways). + + You also need a burg rule in this case, too. For example, + consider the OP_CHECK_THIS opcode (used to raise an exception + if the this pointer is null). The burg rule simply reads: + + stmt: OP_CHECK_THIS (reg) { + mono_bblock_add_inst (s->cbb, tree); + } + + Note that this opcode does not return a value (hence the + "stmt") and it takes a register as input. + + mono_bblock_add_inst (s->cbb, tree) just adds the instruction + (the tree variable) to the current basic block (s->cbb). In + mini this is the place where the internal representation + switches from the tree format to the low-level format (the + list of simple instructions). + + In this case the actual opcode implementation is delegated to + the arch-specific code. A low-level opcode needs an entry in + the machine description (the *.md files in mini/). This entry + describes what kind of registers are used if any by the + instruction, as well as other details such as constraints or + other hints to the low-level engine which are architecture + specific. + + cpu-pentium.md, for example has the following entry: + + checkthis: src1:b len:3 + + This means the instruction uses an integer register as a base + pointer (basically a load or store is done on it) and it takes + 3 bytes of native code to implement it. + + Now you just need to provide the low-level implementation for + the opcode in one of the mini-$arch.c files, in the + mono_arch_output_basic_block() function. There is a big switch + here too. The x86 implementation is: + + case OP_CHECK_THIS: + /* ensure ins->sreg1 is not NULL */ + x86_alu_membase_imm (code, X86_CMP, ins->sreg1, 0, 0); + break; + + If the $arch-codegen.h header file doesn't have the code to + emit the low-level native code, you'll need to write that as + well. + + Complex opcodes with register constraints may require other + changes to the local register allocator, but usually they are + not needed. + * Future Profile-based optimization is something that we are very @@ -650,4 +794,4 @@ processors, and some of the framework exists today in our register allocator and the instruction selector to cope with this, but has not been finished. The instruction selection - would happen at the same time as local register allocation. \ No newline at end of file + would happen at the same time as local register allocation. <