Permalink
Browse files

documentation updates

svn path=/trunk/mono/; revision=3126
  • Loading branch information...
1 parent f1a186b commit 04bbd9582f186151f25730a31cda0341df276148 Dietmar Maurer committed Mar 15, 2002
Showing with 80 additions and 63 deletions.
  1. +18 −2 docs/exceptions
  2. +4 −61 docs/jit-thoughts
  3. +52 −0 docs/jit-trampolines
  4. +3 −0 docs/object-layout
  5. +3 −0 docs/unmanaged-calls
View
20 docs/exceptions
@@ -1,3 +1,6 @@
+Author: Dietmar Maurer (dietmar@ximian.com)
+(C) 2001 Ximian, Inc.
+
Exception implementation (jit):
===============================
@@ -8,8 +11,15 @@ We record the code address (start_address, size) of all methods. That way it is
possible to map an instruction pointer (IP) to the method information needed
for unwinding the stack:
+We also save a Last Managed Frame (LMF) structure at each call from managed to
+unmanaged code. That way we can recover from exceptions inside unmanaged code.
+
void handle_exception ((struct sigcontext *ctx, gpointer obj)
{
+ if (ctx->ip < mono_end_of_stack) {
+ /* unhandled exception */
+ abort ();
+ }
info = mono_jit_info_table_find (mono_jit_info_table, ctx->ip);
@@ -21,14 +31,20 @@ void handle_exception ((struct sigcontext *ctx, gpointer obj)
execute_all_finally_handler ();
// restore register, including IP and Frame pointer
- restore_caller_saved_registers (ji, ctx);
+ ctx = restore_caller_saved_registers_from_ctx (ji, ctx);
// continue unwinding
handle_exception (ctx, obj);
} else {
- // not implemented
+ lmf = get_last_managed_frame ();
+
+ // restore register, including IP and Frame pointer
+ ctx = restore_caller_saved_registers_from_lmf (ji, lmf);
+
+ // continue unwinding
+ handle_exception (ctx, obj);
}
}
View
65 docs/jit-thoughts
@@ -11,48 +11,6 @@ We are designing a JIT compiler, so we have to consider two things:
The current approach is to keep the JITer as simple as possible, and thus as
fast as possible. The generated code quality will suffer from that.
-Register allocation is first done inside the trees of the forest, and each
-tree can use the full set of registers. We simply split a tree if we get out of
-registers, for example the following tree:
-
-
- add(R0)
- / \
- / \
- a(R0) add(R1)
- / \
- / \
- b(R1) add(R2)
- / \
- / \
- c(R2) b(R3)
-
-can be transformed to:
-
-
- stloc(t1) add(R0)
- | / \
- | / \
- add(R0) a(R0) add(R1)
- / \ / \
- / \ / \
- c(R0) b(R1) b(R1) t1(R2)
-
-
-Please notice that the split trees use less registers than the original
-tree.
-
-Triggering JIT compilation:
-===========================
-
-The current approach is to call functions indirectly. The address to call is
-stored in the MonoMethod structure. For each method we create a trampoline
-function. When called, this function does the JIT compilation and replaces the
-trampoline with the compiled method address.
-
-We should consider using the CACAO approach, they do not use a trampoline at
-all.
-
Register Allocation:
====================
@@ -61,15 +19,6 @@ allocation. For example this is needed by call, which return the value always
in EAX on x86. The current implementation works without such system, due to
special forest generation.
-X86 Register Allocation:
-========================
-
-We can use 8bit or 16bit registers on the x86. If we use that feature we have
-more registers to allocate, which maybe prevents some register spills. We
-currently ignore that ability and always allocate 32 bit registers, because I
-think we would gain very little from that optimisation and it would complicate
-the code.
-
Different Register Sets:
========================
@@ -86,11 +35,10 @@ call class methods for ALU operations like add or sub. Sure, this method will
be be a bit inefficient.
The more performant solution is to allocate two 32bit registers for each 64bit
-value. We add a new non terminal to the monoburg grammar called long_reg. The
+value. We add a new non terminal to the monoburg grammar called "lreg". The
register allocation routines takes care of this non terminal and allocates two
32 bit registers for them.
-
Forest generation:
==================
@@ -157,14 +105,6 @@ STLOC(ADD (LDLOC, LDLOC))
This is what lcc is doing, if I understood 12.8, page 342, 343?
-Value Types:
-============
-
-The only CLI instructions which can handle value types are loads and stores,
-either to local variable, to the stack or to array elements. Value types with a
-size smaller than sizeof(int) are handled like any other basic type. For other
-value types we load the base address and emit block copies to store them.
-
Possible Optimisations:
=======================
@@ -180,3 +120,6 @@ else for (i = 0; i < N; i++) { check_range (a, i); a [i] = X; }
The "else" is only to keep original semantics (exception handling).
+We need loop detection logic in order to implement this (dominator tree).
+
+AFAIK CACAO also implements this.
View
52 docs/jit-trampolines
@@ -0,0 +1,52 @@
+Author: Dietmar Maurer (dietmar@ximian.com)
+(C) 2001 Ximian, Inc.
+
+Howto trigger JIT compilation
+=============================
+
+The JIT translates CIL code to native code on a per method basis. For example
+if you have this simple program:
+
+public class Test {
+ public static void Main () {
+ System.Console.WriteLine ("Hello");
+ }
+}
+
+the JIT first compiles the Main function. Unfortunately Main() contains another
+reference to System.Console.WriteLine(), so the JIT also needs the address for
+WriteLine() to generate a call instruction.
+
+The simplest solution would be to JIT compile System.Console.WriteLine()
+to generate that address. But that would mean that we JIT compile half of our
+class library at once, since WriteLine() uses many other classes and function,
+and we have to call the JIT for each of them. Even worse there is the
+possibility of cyclic references, and we would end up in an endless loop.
+
+Thus we need some kind of trampoline function for JIT compilation. Such a
+trampoline first calls the JIT compiler to create native code, and then jumps
+directly into that code. Whenever the JIT needs the address of a function (to
+emit a call instruction) it uses the address of those trampoline functions.
+
+One drawback of this approach is that it requires an additional indirection. We
+always call the trampoline. Inside the trampoline we need to check if the
+method is already compiled or not, and when not compiled we start JIT
+compilation. After that we call the code. This process is quite time consuming
+and shows very bad performance.
+
+The solution is to add some logic to the trampoline function to detect from
+where it is called. It is then possible for the JIT to patch the call
+instruction in the caller, so that it directly calls the JIT compiled code
+next time.
+
+Implementation for x86
+======================
+
+emit-x86.c (arch_create_jit_trampoline): return the JIT trampoline function
+
+emit-x86.c (x86_magic_trampoline): contains the code to detect the caller and
+patch the call instruction.
+
+emit-x86.c (arch_compile_method): JIT compile a method
+
+
View
3 docs/object-layout
@@ -1,3 +1,6 @@
+Author: Dietmar Maurer (dietmar@ximian.com)
+(C) 2001 Ximian, Inc.
+
Object and VTable layout
========================
View
3 docs/unmanaged-calls
@@ -1,3 +1,6 @@
+Author: Dietmar Maurer (dietmar@ximian.com)
+(C) 2001 Ximian, Inc.
+
More about PInvoke and Internal calls
=====================================

0 comments on commit 04bbd95

Please sign in to comment.