New compilation engine for Mono
authorMiguel de Icaza <miguel@gnome.org>
Sat, 5 Apr 2003 19:21:32 +0000 (19:21 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Sat, 5 Apr 2003 19:21:32 +0000 (19:21 -0000)
svn path=/trunk/mono/; revision=13205

68 files changed:
docs/aot-compiler.txt [new file with mode: 0644]
docs/local-regalloc.txt [new file with mode: 0644]
docs/mini-doc.txt [new file with mode: 0644]
docs/opcode-decomp.txt [new file with mode: 0644]
mono/mini/.cvsignore [new file with mode: 0644]
mono/mini/README [new file with mode: 0644]
mono/mini/TODO [new file with mode: 0644]
mono/mini/TestDriver.cs [new file with mode: 0644]
mono/mini/aot-compiler.txt [new file with mode: 0644]
mono/mini/aot.c [new file with mode: 0644]
mono/mini/arrays.cs [new file with mode: 0644]
mono/mini/basic-float.cs [new file with mode: 0644]
mono/mini/basic-long.cs [new file with mode: 0644]
mono/mini/basic.cs [new file with mode: 0644]
mono/mini/bench.cs [new file with mode: 0644]
mono/mini/cfold.c [new file with mode: 0644]
mono/mini/cprop.c [new file with mode: 0644]
mono/mini/cpu-g4.md [new file with mode: 0644]
mono/mini/cpu-pentium.md [new file with mode: 0644]
mono/mini/debug-dwarf2.c [new file with mode: 0644]
mono/mini/debug-mini.c [new file with mode: 0644]
mono/mini/debug-private.h [new file with mode: 0644]
mono/mini/debug-stabs.c [new file with mode: 0644]
mono/mini/debug.c [new file with mode: 0644]
mono/mini/debug.h [new file with mode: 0644]
mono/mini/debugger-main.c [new file with mode: 0644]
mono/mini/dominators.c [new file with mode: 0644]
mono/mini/driver.c [new file with mode: 0644]
mono/mini/emullong.brg [new file with mode: 0644]
mono/mini/exceptions-ppc.c [new file with mode: 0644]
mono/mini/exceptions-x86.c [new file with mode: 0644]
mono/mini/exceptions.cs [new file with mode: 0644]
mono/mini/genmdesc.c [new file with mode: 0644]
mono/mini/graph.c [new file with mode: 0644]
mono/mini/helpers.c [new file with mode: 0644]
mono/mini/iltests.il [new file with mode: 0644]
mono/mini/inssel-float.brg [new file with mode: 0644]
mono/mini/inssel-long.brg [new file with mode: 0644]
mono/mini/inssel-long32.brg [new file with mode: 0644]
mono/mini/inssel-x86.brg [new file with mode: 0644]
mono/mini/inssel.brg [new file with mode: 0644]
mono/mini/jit-icalls.c [new file with mode: 0644]
mono/mini/linear-scan.c [new file with mode: 0644]
mono/mini/liveness.c [new file with mode: 0644]
mono/mini/local-regalloc.txt [new file with mode: 0644]
mono/mini/main.c [new file with mode: 0644]
mono/mini/makefile [new file with mode: 0644]
mono/mini/mini-arch.h [new file with mode: 0644]
mono/mini/mini-doc.txt [new file with mode: 0644]
mono/mini/mini-ops.h [new file with mode: 0644]
mono/mini/mini-ppc.c [new file with mode: 0644]
mono/mini/mini-ppc.h [new file with mode: 0644]
mono/mini/mini-x86.c [new file with mode: 0644]
mono/mini/mini-x86.h [new file with mode: 0644]
mono/mini/mini.c [new file with mode: 0644]
mono/mini/mini.h [new file with mode: 0644]
mono/mini/mini.prj [new file with mode: 0644]
mono/mini/objects.cs [new file with mode: 0644]
mono/mini/opcode-decomp.txt [new file with mode: 0644]
mono/mini/regalloc.c [new file with mode: 0644]
mono/mini/regalloc.h [new file with mode: 0644]
mono/mini/regset.c [new file with mode: 0644]
mono/mini/regset.h [new file with mode: 0644]
mono/mini/ssa.c [new file with mode: 0644]
mono/mini/test.cs [new file with mode: 0644]
mono/mini/tramp-ppc.c [new file with mode: 0644]
mono/mini/tramp-x86.c [new file with mode: 0644]
mono/mini/viewstat.pl [new file with mode: 0644]

diff --git a/docs/aot-compiler.txt b/docs/aot-compiler.txt
new file mode 100644 (file)
index 0000000..ab1af90
--- /dev/null
@@ -0,0 +1,44 @@
+Mono Ahead Of Time Compiler
+===========================
+
+The new mono JIT has sophisticated optimization features. It uses SSA and has a
+pluggable architecture for further optimizations. This makes it possible and
+efficient to use the JIT also for AOT compilation.
+
+
+* file format: We use the native object format of the platform. That way it is
+  possible to reuse existing tools like objdump and the dynamic loader. All we
+  need is a working assembler, i.e. we write out a text file which is then
+  passed to gas (the gnu assembler) to generate the object file.
+
+* file names: we simply add ".so" to the generated file. For example:
+  basic.exe -> basic.exe.so
+  corlib.dll -> corlib.dll.so
+
+* staring the AOT compiler: mini --aot assembly_name
+
+The following things are saved in the object file:
+
+* version infos: 
+
+* native code: this is labeled with method_XXXXXXXX: where XXXXXXXX is the
+  hexadecimal token number of the method.
+
+* additional informations needed by the runtime: For example we need to store
+  the code length and the exception tables. We also need a way to patch
+  constants only available at runtime (for example vtable and class
+  addresses). This is stored i a binary blob labeled method_info_XXXXXXXX:
+
+PROBLEMS:
+
+- all precompiled methods must be domain independent, or we add patch infos to
+  patch the target doamin.
+
+- the main problem is how to patch runtime related addresses, for example:
+
+  - current application domain
+  - string objects loaded with LDSTR
+  - address of MonoClass data
+  - static field offsets 
+  - method addreses
+  - virtual function and interface slots
diff --git a/docs/local-regalloc.txt b/docs/local-regalloc.txt
new file mode 100644 (file)
index 0000000..a6e5235
--- /dev/null
@@ -0,0 +1,208 @@
+
+* Proposal for the local register allocator
+
+       The local register allocator deals with allocating registers
+       for temporaries inside a single basic block, while the global 
+       register allocator is concerned with method-wide allocation of 
+       variables.
+       The global register allocator uses callee-saved register for it's 
+       purpouse so that there is no need to save and restore these registers
+       at call sites.
+
+       There are a number of issues the local allocator needs to deal with:
+       *) some instructions expect operands in specific registers (for example
+               the shl instruction on x86, or the call instruction with thiscall
+               convention, or the equivalent call instructions on other architectures, 
+               such as the need to put output registers in %oX on sparc)
+       *) some instructions deliver results only in specific registers (for example
+               the div instruction on x86, or the call instructionson on almost all
+               the architectures).
+       *) it needs to know what registers may be clobbered by an instruction
+               (such as in a method call)
+       *) it should avoid excessive reloads or stores to improve performance
+       
+       While which specific instructions have limitations is architecture-dependent,
+       the problem shold be solved in an arch-independent way to reduce code duplication.
+       The register allocator will be 'driven' by the arch-dependent code, but it's 
+       implementation should be arch-independent.
+
+       To improve the current local register allocator, we need to
+       keep more state in it than the current setup that only keeps busy/free info.
+
+       Possible state information is:
+
+       free: the resgister is free to use and it doesn't contain useful info
+       freeable: the register contains data loaded from a local (there is 
+               also info about _which_ local it contains) as a result from previous
+               instructions (like, there was a store from the register to the local)
+       moveable: it contains live data that is needed in a following instruction, but
+               the contents may be moved to a different register
+       busy: the register contains live data and it is placed there because
+               the following instructions need it exactly in that register
+       allocated: the register is used by the global allocator
+
+       The local register allocator will have the following interfaces:
+
+       int get_register ();
+               Searches for a register in the free state. If it doesn't find it,
+               searches for a freeable register. Sets the status to moveable.
+               Looking for a 'free' register before a freeable one should allow for
+               removing a few redundant loads (though I'm still unsure if such
+               things should be delegated entirely to the peephole pass).
+       
+       int get_register_force (int reg);
+               Returns 'reg' if it is free or freeable. If it is moveable, it moves it 
+               to another free or freeable register.
+               Sets the status of 'reg' to busy.
+       
+       void set_register_freeable (int reg);
+               Sets the status of 'reg' to freeable.
+       
+       void set_register_free (int reg);
+               Sets the status of 'reg' to free.
+
+       void will_clobber (int reg);
+               Spills the register to the stack. Sets the status to freeable.
+               After the clobbering has occurred, set the status to free.
+
+       void register_unspill (int reg);
+               Un-spills register reg and sets the status to moveable.
+
+       FIXME: how is the 'local' information represented? Maybe a MonoInst* pointer.
+
+       Note: the register allocator will insert instructions in the basic block
+       during it's operation.
+
+* Examples
+
+       Given the tree (on x86 the right argument to shl needs to be in ecx):
+
+       store (local1, shl (local1, call (some_arg)))
+
+       At the start of the basic block, the registers are set to the free state.
+       The sequence of instructions may be:
+               instruction             register status -> [%eax %ecx %edx]
+               start                                       free free free
+               eax = load local1                           mov  free free
+               /* call clobbers eax, ecx, edx */
+               spill eax                                   free free free
+               call                                        mov  free free
+               /* now eax contains the right operand of the shl */
+               mov %eax -> %ecx                            free busy free
+               un-spill                                    mov  busy free
+               shl %cl, %eax                               mov  free free
+       
+       The resulting x86 code is:
+               mov $fffc(%ebp), %eax
+               mov %eax, $fff0(%ebp)
+               push some_arg
+               call func
+               mov %eax, %ecx
+               mov $fff0(%ebp), %eax
+               shl %cl, %eax
+               
+       Note that since shl could operate directly on memory, we could have:
+       
+               push some_arg
+               call func
+               mov %eax, %ecx
+               shl %cl, $fffc(%ebp)
+
+       The above example with loading the operand in a register is just to complicate
+       the example and show that the algorithm should be able to handle it.
+
+       Let's take another example with the this-call call convention (the first argument 
+       is passed in %ecx).
+       In this case, will_clobber() will be called only on %eax and %edx, while %ecx
+       will be allocated with get_register_force ().
+       Note: when a register is allocated with get_register_force(), it should be set
+       to a different state as soon as possible.
+
+       store (local1, shl (local1, this-call (local1)))
+
+               instruction             register status -> [%eax %ecx %edx]
+               start                                       free free free
+               eax = load local1                           mov  free free
+               /* force load in %ecx */
+               ecx = load local1                           mov  busy free
+               spill eax                                   free busy free
+               call                                        mov  free free
+               /* now eax contains the right operand of the shl */
+               mov %eax -> %ecx                            free busy free
+               un-spill                                    mov  busy free
+               shl %cl, %eax                               mov  free free
+
+       What happens when a register that we need to allocate with get_register_force ()
+       contains an operand for the next instruction?
+
+               instruction             register status -> [%eax %ecx %edx]
+               eax = load local0                           mov  free free
+               ecx = load local1                           mov  mov  free
+               get_register_force (ecx) here.
+               We have two options: 
+                       mov %ecx, %edx
+               or:
+                       spill %ecx
+               The first option is way better (and allows the peephole pass to
+               just load the value in %edx directly, instead of loading first to %ecx).
+               This doesn't work, though, if the instruction clobbers the %edx register
+               (like in a this-call). So, we first need to clobber the registers
+               (so the state of %ecx changes to freebale and there is no issue
+               with get_register_force ()).
+               What if an instruction both clobbers a register and requires it as 
+               an operand? Lets' take the x86 idiv instruction as an example: it
+               requires the dividend in edx:eax and returns the result in eax,
+               with the modulus in edx.
+       
+       store (local1, div (local1, local2))
+               
+               instruction             register status -> [%eax %ecx %edx]
+               eax = load local0                           mov  free free
+               will_clobber eax, edx                       free mov  free
+               force mov %ecx, %eax                        busy free free
+               set %edx                                    busy free busy
+               idiv                                        mov  free free
+       
+       Note: edx is set to free after idiv, because the modulus is not needed
+       (if it was a rem, eax would have been freed).
+       If we load the divisor before will_clobber(), we'll have to spill
+       eax and reload it later. If we load it just after the idiv, there is no issue.
+       In any case, the algorithm should give the correct results and allow the operation.
+               
+       Working recursively on the isntructions there shouldn't be huge issues
+       with this algorithm (though, of course, it's not optimal and it may
+       introduce excessive spills or register moves). The advantage over the current
+       local reg allocator is that:
+       1) the number of spills/moves would be smaller anyway
+       2) a separate peephole pass could be able to eliminate reg moves
+       3) we'll be able to remove the 'forced' spills we currently do with
+               the return value of method calls
+
+* Issues
+
+       How to best integrate such a reg allocator with the burg stuff.
+
+       Think about a call os sparc with two arguments: they got into %o0 and %o1
+       and each of them sets the register as busy. But what if the values to put there
+       are themselves the result of a call? %o0 is no problem, but for all the 
+       next argument n the above algorithm would spill all the 0...n-1 registers...
+
+* Papers
+
+       More complex solutions to the local register allocator problem:
+       http://dimacs.rutgers.edu/TechnicalReports/abstracts/1997/97-33.html
+
+       Combining register allocation and instruction scheduling:
+       http://citeseer.nj.nec.com/motwani95combining.html
+
+       More on LRA euristics:
+       http://citeseer.nj.nec.com/liberatore97hardness.html
+
+       Linear-time optimal code scheduling for delayedload architectures
+       http://www.cs.wisc.edu/~fischer/cs701.f01/inst.sched.ps.gz
+
+       Precise Register Allocation for Irregular Architectures
+       http://citeseer.nj.nec.com/kong98precise.html
+
+       Allocate registers first to subtrees that need more of them.
+       http://www.upb.de/cs/ag-kastens/compii/folien/comment401-409.2.pdf
diff --git a/docs/mini-doc.txt b/docs/mini-doc.txt
new file mode 100644 (file)
index 0000000..cfe1a91
--- /dev/null
@@ -0,0 +1,628 @@
+
+              A new JIT compiler for the Mono Project
+
+          Miguel de Icaza (miguel@{ximian.com,gnome.org}),
+
+  
+* Abstract
+
+       Mini is a new compilation engine for the Mono runtime.  The
+       new engine is designed to bring new code generation
+       optimizations, portability and precompilation. 
+
+       In this document we describe the design decisions and the
+       architecture of the new compilation engine. 
+
+* Introduction
+
+       First we discuss the overall architecture of the Mono runtime,
+       and how code generation fits into it; Then we discuss the
+       development and basic architecture of our first JIT compiler
+       for the ECMA CIL framework.  The next section covers the
+       objectives for the work on the new JIT compiler, then we
+       discuss the new features available in the new JIT compiler,
+       and finally a technical description of the new code generation
+       engine.
+
+* Architecture of the Mono Runtime
+
+       The Mono runtime is an implementation of the ECMA Common
+       Language Infrastructure (CLI), whose aim is to be a common
+       platform for executing code in multiple languages.
+
+       Languages that target the CLI generate images that contain
+       code in high-level intermediate representation called the
+       "Common Intermediate Language".  This intermediate language is
+       rich enough to allow for programs and pre-compiled libraries
+       to be reflected.  The execution environment allows for an
+       object oriented execution environment with single inheritance
+       and multiple interface implementations.
+
+       This runtime provides a number of services for programs that
+       are targeted to it: Just-in-Time compilation of CIL code into
+       native code, garbage collection, thread management, I/O
+       routines, single, double and decimal floating point,
+       asynchronous method invocation, application domains, and a
+       framework for building arbitrary RPC systems (remoting) and
+       integration with system libraries through the Platform Invoke
+       functionality.
+
+       The focus of this document is on the services provided by the
+       Mono runtime to transform CIL bytecodes into code that is
+       native to the underlying architecture.
+
+       The code generation interface is a set of macros that allow a
+       C programmer to generate code on the fly, this is done
+       through a set of macros found in the mono/jit/arch/ directory.
+       These macros are used by the JIT compiler to generate native
+       code. 
+
+       The platform invocation code is interesting, as it generates
+       CIL code on the fly to marshal parameters, and then this
+       code is in turned processed by the JIT engine.
+
+* Previous Experiences
+
+       Mono has built a JIT engine, which has been used to bootstrap
+       Mono since January, 2002.  This JIT engine has reasonable
+       performance, and uses an tree pattern matching instruction
+       selector based on the BURS technology.  This JIT compiler was
+       designed by Dietmar Maurer, Paolo Molaro and Miguel de Icaza.
+
+       The existing JIT compiler has three phases:
+
+               * Re-creation of the semantic tree from CIL
+                 byte-codes.
+
+               * Instruction selection, with a cost-driven
+                 engine. 
+
+               * Code generation and register allocation.
+
+       It is also hooked into the rest of the runtime to provide
+       services like marshaling, just-in-time compilation and
+       invocation of "internal calls". 
+
+       This engine constructed a collection of trees, which we
+       referred to as the "forest of trees", this forest is created by
+       "hydrating" the CIL instruction stream.
+
+       The first step was to identify the basic blocks on the method,
+       and computing the control flow graph (cfg) for it.  Once this
+       information was computed, a stack analysis on each basic block
+       was performed to create a forest of trees for each one of
+       them. 
+
+       So for example, the following statement:
+
+              int a, b;
+              ...
+              b = a + 1;
+
+       Which would be represented in CIL as:
+
+                       ldloc.0 
+                       ldc.i4.1 
+                       add 
+                       stloc.1 
+
+       After the stack analysis would create the following tree:
+
+               (STIND_I4 ADDR_L[EBX|2] (
+                        ADD (LDIND_I4 ADDR_L[ESI|1]) 
+                        CONST_I4[1]))
+
+        This tree contains information from the stack analysis: for
+        instance, notice that the operations explicitly encode the
+        data types they are operating on, there is no longer an
+        ambiguity on the types, because this information has been
+        inferred. 
+
+       At this point the JIT would pass the constructed forest of
+       trees to the architecture-dependant JIT compiler.  
+
+       The architecture dependent code then performed register
+       allocation (optionally using linear scan allocation for
+       variables, based on life analysis).  
+
+       Once variables had been assigned, a tree pattern matching with
+       dynamic programming is used (the tree pattern matcher is
+       custom build for each architecture, using a code
+       generator: monoburg). The instruction selector used cost
+       functions to select the best instruction patterns.  
+
+       The instruction selector is able to produce instructions that
+       take advantage of the x86 instruction indexing instructions
+       for example. 
+
+       One problem though is that the code emitter and the register
+       allocator did not have any visibility outside the current
+       tree, which meant that some redundant instructions were
+       generated.  A peephole optimizer with this architecture was
+       hard to write, given the tree-based representation that is
+       used.
+
+       This JIT was functional, but it did not provide a good
+       architecture to base future optimizations on.  Also the
+       line between architecture neutral and architecture
+       specific code and optimizations was hard to draw.
+
+       The JIT engine supported two code generation modes to support
+       the two optimization modes for applications that host multiple
+       application domains: generate code that will be shared across
+       application domains, or generate code that will not be shared
+       across application domains.
+
+* Objectives of the new JIT engine.
+
+       We wanted to support a number of features that were missing:
+
+          * Ahead-of-time compilation.  
+
+            The idea is to allow developers to pre-compile their code
+            to native code to reduce startup time, and the working
+            set that is used at runtime in the just-in-time compiler.
+
+            Although in Mono this has not been a visible problem, we
+            wanted to pro-actively address this problem.
+
+            When an assembly (a Mono/.NET executable) is installed in
+            the system, it would then be possible to pre-compile the
+            code, and have the JIT compiler tune the generated code
+            to the particular CPU on which the software is
+            installed. 
+
+            This is done in the Microsoft.NET world with a tool
+            called ngen.exe
+
+          * Have a good platform for doing code optimizations. 
+
+            The design called for a good architecture that would
+            enable various levels of optimizations: some
+            optimizations are better performed on high-level
+            intermediate representations, some on medium-level and
+            some at low-level representations.
+
+            Also it should be possible to conditionally turn these on
+            or off.  Some optimizations are too expensive to be used
+            in just-in-time compilation scenarios, but these
+            expensive optimizations can be turned on for
+            ahead-of-time compilations or when using profile-guided
+            optimizations on a subset of the executed methods.
+
+          * Reduce the effort required to port the Mono code
+             generator to new architectures.
+
+            For Mono to gain wide adoption in the Unix world, it is
+            necessary that the JIT engine works in most of today's
+            commercial hardware platforms. 
+
+* Features of the new JIT engine.
+
+       The new JIT engine was architected by Dietmar Maurer and Paolo
+       Molaro, based on the new objectives.
+
+       Mono provides a number of services to applications running
+       with the new JIT compiler:
+
+            * Just-in-Time compilation of CLI code into native code.
+
+            * Ahead-of-Time compilation of CLI code, to reduce
+               startup time of applications. 
+
+       A number of software development features are also available:
+
+            * Execution time profiling (--profile)
+
+              Generates a report of the times consumed by routines,
+              as well as the invocation times, as well as the
+              callers.
+
+            * Memory usage profiling (--profile)
+
+              Generates a report of the memory usage by a program
+              that is ran under the Mono JIT.
+
+            * Code coverage (--coverage)
+
+            * Execution tracing.
+
+        People who are interested in developing and improving the Mini
+        JIT compiler will also find a few useful routines:
+
+            * Compilation times
+
+              This is used to time the execution time for the JIT
+              when compiling a routine. 
+
+            * Control Flow Graph and Dominator Tree drawing.
+
+              These are visual aids for the JIT developer: they
+              render representations of the Control Flow graph, and
+              for the more advanced optimizations, they draw the
+              dominator tree graph. 
+
+              This requires Dot (from the graphwiz package) and Ghostview.
+
+            * Code generator regression tests.  
+
+              The engine contains support for running regression
+              tests on the virtual machine, which is very helpful to
+              developers interested in improving the engine.
+
+            * Optimization benchmark framework.
+
+              The JIT engine will generate graphs that compare
+              various benchmarks embedded in an assembly, and run the
+              various tests with different optimization flags.  
+
+              This requires Perl, GD::Graph.
+
+* Flexibility
+
+       This is probably the most important component of the new code
+       generation engine.  The internals are relatively easy to
+       replace and update, even large passes can be replaced and
+       implemented differently.
+
+* New code generator
+
+       Compiling a method begins with the `mini_method_to_ir' routine
+       that converts the CIL representation into a medium
+       intermediate representation.
+
+       The mini_method_to_ir routine performs a number of operations:
+
+           * Flow analysis and control flow graph computation.
+
+             Unlike the previous version, stack analysis and control
+             flow graphs are computed in a single pass in the
+             mini_method_to_ir function, this is done for performance
+             reasons: although the complexity increases, the benefit
+             for a JIT compiler is that there is more time available
+             for performing other optimizations.
+
+           * Basic block computation.
+
+             mini_method_to_ir populates the MonoCompile structure
+             with an array of basic blocks each of which contains
+             forest of trees made up of MonoInst structures.
+
+           * Inlining
+
+             Inlining is no longer restricted to methods containing
+             one single basic block, instead it is possible to inline
+             arbitrary complex methods.
+
+             The heuristics to choose what to inline are likely going
+             to be tuned in the future.
+
+           * Method to opcode conversion.
+
+             Some method call invocations like `call Math.Sin' are
+             transformed into an opcode: this transforms the call
+             into a semantically rich node, which is later inline
+             into an FPU instruction.
+
+             Various Array methods invocations are turned into
+             opcodes as well (The Get, Set and Address methods)
+
+           * Tail recursion elimination
+
+       Basic blocks ****
+
+       The MonoInst structure holds the actual decoded instruction,
+       with the semantic information from the stack analysis.
+       MonoInst is interesting because initially it is part of a tree
+       structure, here is a sample of the same tree with the new JIT
+       engine:
+
+                (stind.i4 regoffset[0xffffffd4(%ebp)] 
+                          (add (ldind.i4 regoffset[0xffffffd8(%ebp)])
+                                iconst[1]))
+
+       This is a medium-level intermediate representation (MIR). 
+
+       Some complex opcodes are decomposed at this stage into a
+       collection of simpler opcodes.  Not every complex opcode is
+       decomposed at this stage, as we need to preserve the semantic
+       information during various optimization phases.  
+
+       For example a NEWARR opcode carries the length and the type of
+       the array that could be used later to avoid type checking or
+       array bounds check.
+
+        There are a number of operations supported on this
+       representation:
+
+               * Branch optimizations.
+
+               * Variable liveness.
+
+               * Loop optimizations: the dominator trees are
+                 computed, loops are detected, and their nesting
+                 level computed.
+
+               * Conversion of the method into static single assignment
+                  form (SSA form).
+
+               * Dead code elimination.
+
+               * Constant propagation.
+
+               * Copy propagation.
+
+               * Constant folding.
+
+       Once the above optimizations are optionally performed, a
+       decomposition phase is used to turn some complex opcodes into
+       internal method calls.  In the initial version of the JIT
+       engine, various operations on longs are emulated instead of
+       being inlined.  Also the newarr invocation is turned into a
+       call to the runtime.
+
+       At this point, after computing variable liveness, it is
+       possible to use the linear scan algorithm for allocating
+       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. 
+
+       Stack space is then reserved for the local variables and any
+       temporary variables generated during the various
+       optimizations.
+
+** Instruction selection 
+
+       At this point, the BURS instruction selector is invoked to
+       transform the tree-based representation into a list of
+       instructions.  This is done using a tree pattern matcher that
+       is generated for the architecture using the `monoburg' tool. 
+
+       Monoburg takes as input a file that describes tree patterns,
+       which are matched against the trees that were produced by the
+       engine in the previous stages.
+
+       The pattern matching might have more than one match for a
+       particular tree.  In this case, the match selected is the one
+       whose cost is the smallest.  A cost can be attached to each
+       rule, and if no cost is provided, the implicit cost is one.
+       Smaller costs are selected over higher costs.
+
+       The cost function can be used to select particular blocks of
+       code for a given architecture, or by using a prohibitive high
+       number to avoid having the rule match.
+
+       The various rules that our JIT engine uses transform a tree of
+       MonoInsts into a list of monoinsts:
+
+       +-----------------------------------------------------------+
+       | Tree                                           List       |
+       | of           ===> Instruction selection ===>   of         |
+       | MonoInst                                       MonoInst.  |
+        +-----------------------------------------------------------+
+
+       During this process various "types" of MonoInst kinds 
+       disappear and turned into lower-level representations.  The
+       JIT compiler just happens to reuse the same structure (this is
+       done to reduce memory usage and improve memory locality).
+
+       The instruction selection rules are split in a number of
+       files, each one with a particular purpose:
+
+               inssel.brg
+                       Contains the generic instruction selection
+                       patterns.
+
+               inssel-x86.brg
+                       Contains x86 specific rules.
+
+               inssel-ppc.brg
+                       Contains PowerPC specific rules.
+
+               inssel-long32.brg
+                       burg file for 64bit instructions on 32bit architectures.
+
+               inssel-long.brg
+                       burg file for 64bit architectures.
+
+               inssel-float.brg
+                       burg file for floating point instructions
+               
+       For a given build, a set of those files would be included.
+       For example, for the build of Mono on the x86, the following
+       set is used:
+
+           inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg
+
+** Native method generation
+
+       The native method generation has a number of steps:
+
+               * Architecture specific register allocation.
+
+                 The information about loop nesting that was
+                 previously gathered is used here to hint the
+                 register allocator. 
+
+               * Generating the method prolog/epilog.
+
+               * Optionally generate code to introduce tracing facilities.
+
+               * Hooking into the debugger.
+
+               * Performing any pending fixups. 
+
+               * Code generation.
+
+*** Code Generation
+
+       The actual code generation is contained in the architecture
+       specific portion of the compiler.  The input to the code
+       generator is each one of the basic blocks with its list of
+       instructions that were produced in the instruction selection
+       phase.
+
+       During the instruction selection phase, virtual registers are
+       assigned.  Just before the peephole optimization is performed,
+       physical registers are assigned.
+
+       A simple peephole and algebraic optimizer is ran at this
+       stage.  
+
+       The peephole optimizer removes some redundant operations at
+       this point.  This is possible because the code generation at
+       this point has visibility into the basic block that spans the
+       original trees.  
+
+       The algebraic optimizer performs some simple algebraic
+       optimizations that replace expensive operations with cheaper
+       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
+
+       We always try to allocate code in sequence, instead of just using
+       malloc. This way we increase spatial locality which gives a massive
+       speedup on most architectures.
+
+*** Ahead of Time compilation
+
+       Ahead-of-Time compilation is a new feature of our new
+       compilation engine.  The compilation engine is shared by the
+       Just-in-Time (JIT) compiler and the Ahead-of-Time compiler
+       (AOT).
+
+       The difference is on the set of optimizations that are turned
+       on for each mode: Just-in-Time compilation should be as fast
+       as possible, while Ahead-of-Time compilation can take as long
+       as required, because this is not done at a time criticial
+       time. 
+
+       With AOT compilation, we can afford to turn all of the
+       computationally expensive optimizations on.
+
+       After the code generation phase is done, the code and any
+       required fixup information is saved into a file that is
+       readable by "as" (the native assembler available on all
+       systems). This assembly file is then passed to the native
+       assembler, which generates a loadable module.
+
+       At execution time, when an assembly is loaded from the disk,
+       the runtime engine will probe for the existance of a
+       pre-compiled image.  If the pre-compiled image exists, then it
+       is loaded, and the method invocations are resolved to the code
+       contained in the loaded module.
+
+       The code generated under the AOT scenario is slightly
+       different than the JIT scenario.  It generates code that is
+       application-domain relative and that can be shared among
+       multiple thread.
+
+       This is the same code generation that is used when the runtime
+       is instructed to maximize code sharing on a multi-application
+       domain scenario.
+
+* SSA-based optimizations
+
+       SSA form simplifies many optimization because each variable has exactly
+       one definition site. All uses of a variable are "dominated" by its
+       definition, which enables us to implement algorithm like:
+
+               * conditional constant propagation
+
+               * array bound check removal
+
+               * dead code elimination
+
+       And we can implement those algorithm in a efficient way using SSA. 
+
+
+* Register allocation.
+
+       Global register allocation is performed on the medium
+       intermediate representation just before instruction selection
+       is performed on the method.  Local register allocation is
+       later performed at the basic-block level on the 
+
+       Global register allocation uses the following input:
+
+        1) set of register-sized variables that can be allocated to a
+        register (this is an architecture specific setting, for x86
+        these registers are the callee saved register ESI, EDI and
+        EBX). 
+
+        2) liveness information for the variables
+
+        3) (optionally) loop info to favour variables that are used in
+        inner loops.
+
+       During instruction selection phase, symbolic registers are
+       assigned to temporary values in expressions.
+
+       Local register allocation assigns hard registers to the
+       symbolic registers, and it is performed just before the code
+       is actually emitted and is performed at the basic block level.
+       A CPU description file describes the input registers, output
+       registers, fixed registers and clobbered registers by each
+       operation.
+
+
+----------
+* Bootstrap 
+
+       The Mini bootstrap parses the arguments passed on the command
+       line, and initializes the JIT runtime. Each time the
+       mini_init() routine is invoked, a new Application Domain will
+       be returned.
+
+* Signal handlers
+
+       mono_runtime_install_handlers
+
+* BURG Code Generator Generator
+
+       monoburg was written by Dietmar Maurer. It is based on the
+       papers from Christopher W. Fraser, Robert R. Henry and Todd
+       A. Proebsting: "BURG - Fast Optimal Instruction Selection and
+       Tree Parsing" and "Engineering a Simple, Efficient Code
+       Generator Generator".
+
+       The original BURG implementation is unable to work on DAGs, instead only
+       trees are allowed. Our monoburg implementations is able to generate tree
+       matcher which works on DAGs, and we use this feature in the new
+       JIT. This simplifies the code because we can directly pass DAGs and
+       don't need to convert them to trees.
+
+* Future
+
+        Profile-based optimization is something that we are very
+        interested in supporting.  There are two possible usage
+        scenarios: 
+
+          * Based on the profile information gathered during
+             the execution of a program, hot methods can be compiled
+             with the highest level of optimizations, while bootstrap
+             code and cold methods can be compiled with the least set
+             of optimizations and placed in a discardable list.
+
+          * Code reordering: this profile-based optimization would
+             only make sense for pre-compiled code.  The profile
+             information is used to re-order the assembly code on disk
+             so that the code is placed on the disk in a way that
+             increments locality.  
+
+            This is the same principle under which SGI's cord program
+            works.  
+
+       The nature of the CIL allows the above optimizations to be
+       easy to implement and deploy.  Since we live and define our
+       universe for these things, there are no interactions with
+       system tools required, nor upgrades on the underlying
+       infrastructure required.
+
+       Instruction scheduling is important for certain kinds of
+       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
diff --git a/docs/opcode-decomp.txt b/docs/opcode-decomp.txt
new file mode 100644 (file)
index 0000000..48968d1
--- /dev/null
@@ -0,0 +1,113 @@
+
+* How to handle complex IL opcodes in an arch-independent way
+
+       Many IL opcodes are very simple: add, ldind etc.
+       Such opcodes can be implemented with a single cpu instruction
+       in most architectures (on some, a group of IL instructions
+       can be converted to a single cpu op).
+       There are many IL opcodes, though, that are more complex, but
+       can be expressed as a series of trees or a single tree of
+       simple operations. Such simple operations are architecture-independent.
+       It makes sense to decompose such complex IL instructions in their
+       simpler equivalent so that we gain in several ways:
+       *) porting effort is easier, because only the simple instructions 
+               need to be implemented in arch-specific code
+       *) we could apply BURG rules to the trees and do pattern matching
+               on them to optimize the expressions according to the host cpu
+       
+       The issue is: where do we do such conversion from coarse opcodes to 
+       simple expressions?
+
+* Doing the conversion in method_to_ir ()
+
+       Some of these conversions can certainly be done in method_to_ir (),
+       but it's not always easy to decide which are better done there and 
+       which in a different pass.
+       For example, let's take ldlen: in the mono implementation, ldlen
+       can be simply implemented with a load from a fixed position in the 
+       array object:
+
+               len = [reg + maxlen_offset]
+       
+       However, ldlen carries also semantics information: the result is the
+       length of the array, and since in the CLR arrays are of fixed size,
+       this information can be useful to later do bounds check removal.
+       If we convert this opcode in method_to_ir () we lost some useful
+       information for further optimizations.
+
+       In some other ways, decomposing an opcode in method_to_ir() may
+       allow for better optimizations later on (need to come up with an 
+       example here ...).
+
+* Doing the conversion in inssel.brg
+
+       Some conversion may be done inside the burg rules: this has the 
+       disadvantage that the instruction selector is not run again on
+       the resulting expression tree and we could miss some optimization
+       (this is what effectively happens with the coarse opcodes in the old 
+       jit). This may also interfere with an efficient local register allocator.
+       It may be possible to add an extension in monoburg that allows a rule 
+       such as:
+
+               recheck: LDLEN (reg) {
+                       create an expression tree representing LDLEN
+                       and return it
+               }
+       
+       When the monoburg label process gets back a recheck, it will run
+       the labeling again on the resulting expression tree.
+       If this is possible at all (and in an efficient way) is a 
+       question for dietmar:-)
+       It should be noted, though, that this may not always work, since
+       some complex IL opcodes may require a series of expression trees
+       and handling such cases in monoburg could become quite hairy.
+       For example, think of opcode that need to do multiple actions on the 
+       same object: this basically means a DUP...
+       On the other end, if a complex opcode needs a DUP, monoburg doesn't
+       actually need to create trees if it emits the instructions in
+       the correct sequence and maintains the right values in the registers
+       (usually the values that need a DUP are not changed...). How
+       this integrates with the current register allocator is not clear, since
+       that assigns registers based on the rule, but the instructions emitted 
+       by the rules may be different (this already happens with the current JIT
+       where a MULT is replaced with lea etc...).
+
+* Doing it in a separate pass.
+
+       Doing the conversion in a separate pass over the instructions
+       is another alternative. This can be done right after method_to_ir ()
+       or after the SSA pass (since the IR after the SSA pass should look
+       almost like the IR we get back from method_to_ir ()).
+
+       This has the following advantages:
+       *) monoburg will handle only the simple opcodes (makes porting easier)
+       *) the instruction selection will be run on all the additional trees
+       *) it's easier to support coarse opcodes that produce multiple expression 
+               trees (and apply the monoburg selector on all of them)
+       *) the SSA optimizer will see the original opcodes and will be able to use
+               the semantic info associated with them
+       
+       The disadvantage is that this is a separate pass on the code and
+       it takes time (how much has not been measured yet, though).
+
+       With this approach, we may also be able to have C implementations
+       of some of the opcodes: this pass would insert a function call to 
+       the C implementation (for example in the cases when first porting
+       to a new arch and implemenating some stuff may be too hard in asm).
+
+* Extended basic blocks
+
+       IL code needs a lot of checks, bounds checks, overflow checks,
+       type checks and so on. This potentially increases by a lot
+       the number of basic blocks in a control flow graph. However,
+       all such blocks end up with a throw opcode that gives control to the
+       exception handling mechanism.
+       After method_to_ir () a MonoBasicBlock can be considered a sort
+       of extended basic block where the additional exits don't point
+       to basic blocks in the same procedure (at least when the method
+       doesn't have exception tables).
+       We need to make sure the passes following method_to_ir () can cope
+       with such kinds of extended basic blocks (especially the passes
+       that we need to apply to all the methods: as a start, we could
+       skip SSA optimizations for methods with exception clauses...)
+
diff --git a/mono/mini/.cvsignore b/mono/mini/.cvsignore
new file mode 100644 (file)
index 0000000..6ba3086
--- /dev/null
@@ -0,0 +1,5 @@
+mini
+mono-debugger-mini-wrapper
+.libs
+*.o
+test.exe
diff --git a/mono/mini/README b/mono/mini/README
new file mode 100644 (file)
index 0000000..7a64e0a
--- /dev/null
@@ -0,0 +1 @@
+Mini is the new JIT compiler for Mono.
diff --git a/mono/mini/TODO b/mono/mini/TODO
new file mode 100644 (file)
index 0000000..a04a7ca
--- /dev/null
@@ -0,0 +1,13 @@
+* use a pool of MBState structures to speedup monoburg instead of using a
+  mempool.
+* the decode tables in the burg-generated could use short instead of int
+  (this should save about 1 KB)
+* track the use of ESP, so that we can avoid the x86_lea in the epilog
+
+
+Other Ideas:
+
+* the ORP people avoids optimizations inside catch handlers - just to save
+  memory (for example allocation of strings - instead they allocate strings when
+  the code is executed (like the --shared option)). But there are only a few
+  functions using catch handlers, so I consider this a minor issue.
\ No newline at end of file
diff --git a/mono/mini/TestDriver.cs b/mono/mini/TestDriver.cs
new file mode 100644 (file)
index 0000000..7854bcb
--- /dev/null
@@ -0,0 +1,79 @@
+using System;
+using System.Reflection;
+
+
+public class TestDriver {
+
+       static public int RunTests (Type type, string[] args) {
+               int failed = 0, ran = 0;
+               int result, expected, elen;
+               int i, j;
+               string name;
+               MethodInfo[] methods;
+               bool do_timings = false;
+               int tms = 0;
+               DateTime start, end = DateTime.Now;
+
+               if (args != null && args.Length > 0) {
+                       for (j = 0; j < args.Length; j++) {
+                               if (args [j] == "--time") {
+                                       do_timings = true;
+                                       string[] new_args = new string [args.Length - 1];
+                                       for (i = 0; i < j; ++i)
+                                               new_args [i] = args [i];
+                                       j++;
+                                       for (; j < args.Length; ++i, ++j)
+                                               new_args [i] = args [j];
+                                       args = new_args;
+                                       break;
+                               }
+                       }
+               }
+               methods = type.GetMethods (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);
+               for (i = 0; i < methods.Length; ++i) {
+                       name = methods [i].Name;
+                       if (!name.StartsWith ("test_"))
+                               continue;
+                       if (args != null && args.Length > 0) {
+                               bool found = false;
+                               for (j = 0; j < args.Length; j++) {
+                                       if (name.EndsWith (args [j])) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if (!found)
+                                       continue;
+                       }
+                       for (j = 5; j < name.Length; ++j)
+                               if (!Char.IsDigit (name [j]))
+                                       break;
+                       expected = Int32.Parse (name.Substring (5, j - 5));
+                       start = DateTime.Now;
+                       result = (int)methods [i].Invoke (null, null);
+                       if (do_timings) {
+                               end = DateTime.Now;
+                               long tdiff = end.Ticks - start.Ticks;
+                               int mdiff = (int)tdiff/10000;
+                               tms += mdiff;
+                               Console.WriteLine ("{0} took {1} ms", name, mdiff);
+                       }
+                       ran++;
+                       if (result != expected) {
+                               failed++;
+                               Console.WriteLine ("{0} failed: got {1}, expected {2}", name, result, expected);
+                       }
+               }
+               
+               if (do_timings) {
+                       Console.WriteLine ("Total ms: {0}", tms);
+               }
+               Console.WriteLine ("Regression tests: {0} ran, {1} failed in {2}", ran, failed, type);
+               //Console.WriteLine ("Regression tests: {0} ran, {1} failed in [{2}]{3}", ran, failed, type.Assembly.GetName().Name, type);
+               return failed;
+       }
+       static public int RunTests (Type type) {
+               return RunTests (type, null);
+       }
+}
+
diff --git a/mono/mini/aot-compiler.txt b/mono/mini/aot-compiler.txt
new file mode 100644 (file)
index 0000000..ab1af90
--- /dev/null
@@ -0,0 +1,44 @@
+Mono Ahead Of Time Compiler
+===========================
+
+The new mono JIT has sophisticated optimization features. It uses SSA and has a
+pluggable architecture for further optimizations. This makes it possible and
+efficient to use the JIT also for AOT compilation.
+
+
+* file format: We use the native object format of the platform. That way it is
+  possible to reuse existing tools like objdump and the dynamic loader. All we
+  need is a working assembler, i.e. we write out a text file which is then
+  passed to gas (the gnu assembler) to generate the object file.
+
+* file names: we simply add ".so" to the generated file. For example:
+  basic.exe -> basic.exe.so
+  corlib.dll -> corlib.dll.so
+
+* staring the AOT compiler: mini --aot assembly_name
+
+The following things are saved in the object file:
+
+* version infos: 
+
+* native code: this is labeled with method_XXXXXXXX: where XXXXXXXX is the
+  hexadecimal token number of the method.
+
+* additional informations needed by the runtime: For example we need to store
+  the code length and the exception tables. We also need a way to patch
+  constants only available at runtime (for example vtable and class
+  addresses). This is stored i a binary blob labeled method_info_XXXXXXXX:
+
+PROBLEMS:
+
+- all precompiled methods must be domain independent, or we add patch infos to
+  patch the target doamin.
+
+- the main problem is how to patch runtime related addresses, for example:
+
+  - current application domain
+  - string objects loaded with LDSTR
+  - address of MonoClass data
+  - static field offsets 
+  - method addreses
+  - virtual function and interface slots
diff --git a/mono/mini/aot.c b/mono/mini/aot.c
new file mode 100644 (file)
index 0000000..e965b94
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * aot.c: mono Ahead of Time compiler
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <limits.h>    /* for PAGESIZE */
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini.h"
+
+#define ENCODE_TYPE_POS(t,l) (((t) << 24) | (l))
+#define DECODE_TYPE(v) ((v) >> 24)
+#define DECODE_POS(v) ((v) & 0xffffff)
+
+gpointer
+mono_aot_get_method (MonoMethod *method)
+{
+       MonoClass *klass = method->klass;
+       MonoAssembly *ass = klass->image->assembly;
+       MonoJumpInfo *patch_info = NULL;
+       GModule *module = ass->aot_module;
+       char *method_label, *info_label;
+       guint8 *code = NULL;
+       gpointer *info;
+       guint code_len, used_int_regs;
+
+       if (!module)
+               return NULL;
+
+       if (!method->token)
+               return NULL;
+
+       g_assert (klass->inited);
+
+       method_label = g_strdup_printf ("method_%08X", method->token);
+
+       if (!g_module_symbol (module, method_label, (gpointer *)&code)) {
+               g_free (method_label);          
+               return NULL;
+       }
+
+       info_label = g_strdup_printf ("%s_patch_info", method_label);
+       if (!g_module_symbol (module, info_label, (gpointer *)&info)) {
+               g_free (method_label);          
+               g_free (info_label);
+               return NULL;
+       }
+
+       //printf ("FOUND AOT compiled code for %s %p %p\n", mono_method_full_name (method, TRUE), code, info);
+
+       code_len = GPOINTER_TO_UINT (*((gpointer **)info));
+       info++;
+       used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info));
+       info++;
+
+       if (info) {
+               MonoMemPool *mp = mono_mempool_new (); 
+               MonoImage *image;
+               guint8 *page_start;
+               gpointer *table;
+               int pages;
+               int i, err;
+
+               while (*info) {
+                       MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
+                       gpointer *data = *((gpointer **)info);
+                       info++;
+                       ji->type = DECODE_TYPE (GPOINTER_TO_UINT (*info));
+                       ji->ip.i = DECODE_POS (GPOINTER_TO_UINT (*info));
+
+                       switch (ji->type) {
+                       case MONO_PATCH_INFO_CLASS:
+                               image = mono_image_loaded_by_guid ((char *)data [1]);
+                               g_assert (image);
+                               ji->data.klass = mono_class_get (image, (guint32)data [0]);
+                               g_assert (ji->data.klass);
+                               mono_class_init (ji->data.klass);
+                               break;
+                       case MONO_PATCH_INFO_IMAGE:
+                               ji->data.image = mono_image_loaded_by_guid ((char *)data);
+                               g_assert (ji->data.image);
+                               break;
+                       case MONO_PATCH_INFO_METHOD:
+                       case MONO_PATCH_INFO_METHODCONST:
+                               image = mono_image_loaded_by_guid ((char *)data [1]);
+                               g_assert (image);
+                               ji->data.method = mono_get_method (image, (guint32)data [0], NULL);
+                               g_assert (ji->data.method);
+                               mono_class_init (ji->data.method->klass);
+                               break;
+                       case MONO_PATCH_INFO_FIELD:
+                               image = mono_image_loaded_by_guid ((char *)data [1]);
+                               g_assert (image);
+                               ji->data.field = mono_field_from_token (image, (guint32)data [0], NULL);
+                               mono_class_init (ji->data.field->parent);
+                               g_assert (ji->data.field);
+                               break;
+                       case MONO_PATCH_INFO_INTERNAL_METHOD:
+                               ji->data.name = (char *)data;
+                               g_assert (ji->data.name);
+                               break;
+                       case MONO_PATCH_INFO_SWITCH:
+                               ji->table_size = (int)data [0];
+                               table = g_new (gpointer, ji->table_size);
+                               ji->data.target = table;
+                               for (i = 0; i < ji->table_size; i++) {
+                                       table [i] = data [i + 1];
+                               }
+                               break;
+                       case MONO_PATCH_INFO_R4:
+                       case MONO_PATCH_INFO_R8:
+                               ji->data.target = data;
+                               break;
+                       default:
+                               g_warning ("unhandled type %d", ji->type);
+                               g_assert_not_reached ();
+                       }
+
+                       info++;
+                       ji->next = patch_info;
+                       patch_info = ji;
+               }
+
+               /* disable write protection */
+               page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
+               pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
+               err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
+               g_assert (err == 0);
+
+               mono_arch_patch_code (method, mono_root_domain, code, patch_info);
+               mono_mempool_destroy (mp);
+       }
+
+       g_free (info_label);
+       g_free (method_label);
+
+       {
+               MonoJitInfo *info;
+               info = mono_mempool_alloc0 (mono_root_domain->mp, sizeof (MonoJitInfo));
+               info->code_size = code_len;
+               info->used_regs = used_int_regs;
+               info->method = method;
+               info->code_start = code;
+               mono_jit_info_table_add (mono_root_domain, info);
+       }
+       mono_jit_stats.methods_aot++;
+
+       return code;
+}
+
+#if 0
+static void
+write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
+{
+       int i;
+
+       fprintf (fp, ".globl %s\n", name);
+       fprintf (fp, ".data\n\t.align %d\n", align);
+       fprintf (fp, "\t.type %s,@object\n", name);
+       fprintf (fp, "\t.size %s,%d\n", name, size);
+       fprintf (fp, "%s:\n", name);
+       for (i = 0; i < size; i++) { 
+               fprintf (fp, ".byte %d\n", buf [i]);
+       }
+       
+}
+#endif
+
+static void
+write_string_symbol (FILE *fp, const char *name, char *value)
+{
+       fprintf (fp, ".globl %s\n", name);
+       fprintf (fp, ".data\n");
+       fprintf (fp, "%s:\n", name);
+       fprintf (fp, "\t.string \"%s\"\n", value);
+}
+
+static guint32
+mono_get_field_token (MonoClassField *field) 
+{
+       MonoClass *klass = field->parent;
+       int i;
+
+       for (i = 0; i < klass->field.count; ++i) {
+               if (field == &klass->fields [i])
+                       return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
+       }
+
+       g_assert_not_reached ();
+       return 0;
+}
+
+static char *
+cond_emit_image_label (FILE *tmpfp, GHashTable *image_hash, MonoImage *image)
+{
+       char *label;
+       
+       if ((label = g_hash_table_lookup (image_hash, image))) 
+               return label;
+
+       label = g_strdup_printf ("image_patch_info_%p", image);
+       fprintf (tmpfp, "%s:\n", label);
+       fprintf (tmpfp, "\t.string \"%s\"\n", image->guid);
+
+       g_hash_table_insert (image_hash, image, label);
+
+       return label;
+}
+
+static char *
+cond_emit_icall_label (FILE *tmpfp, GHashTable *hash, const char *icall_name)
+{
+       char *label;
+
+       if ((label = g_hash_table_lookup (hash, icall_name))) 
+               return label;
+
+       label = g_strdup_printf ("icall_patch_info_%s", icall_name);
+       fprintf (tmpfp, "%s:\n", label);
+       fprintf (tmpfp, "\t.string \"%s\"\n", icall_name);
+
+       g_hash_table_insert (hash, (gpointer)icall_name, label);
+
+       return label;
+}
+
+static char *
+cond_emit_method_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info)
+{
+       MonoMethod *method = patch_info->data.method;
+       char *l1, *l2;
+
+       if ((l1 = g_hash_table_lookup (hash, method))) 
+               return l1;
+       
+       l2 = cond_emit_image_label (tmpfp, hash, method->klass->image);
+       fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
+       l1 = g_strdup_printf ("method_patch_info_%08x_%p", method->token, method);
+       fprintf (tmpfp, "%s:\n", l1);
+       fprintf (tmpfp, "\t.long 0x%08x\n", method->token);
+       g_assert (method->token);
+       fprintf (tmpfp, "\t.long %s\n", l2);
+               
+       g_hash_table_insert (hash, method, l1);
+
+       return l1;
+}
+
+static char *
+cond_emit_klass_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info)
+{
+       MonoClass *klass = patch_info->data.klass;
+       char *l1, *l2;
+
+       if ((l1 = g_hash_table_lookup (hash, klass))) 
+               return l1;
+       
+       l2 = cond_emit_image_label (tmpfp, hash, klass->image);
+       fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
+       l1 = g_strdup_printf ("klass_patch_info_%08x_%p", klass->type_token, klass);
+       fprintf (tmpfp, "%s:\n", l1);
+       fprintf (tmpfp, "\t.long 0x%08x\n", klass->type_token);
+       g_assert (klass->type_token);
+       fprintf (tmpfp, "\t.long %s\n", l2);
+               
+       g_hash_table_insert (hash, klass, l1);
+
+       return l1;
+}
+
+static char *
+cond_emit_field_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info)
+{
+       MonoClassField *field = patch_info->data.field;
+       char *l1, *l2;
+       guint token;
+
+       if ((l1 = g_hash_table_lookup (hash, field))) 
+               return l1;
+       
+       l2 = cond_emit_image_label (tmpfp, hash, field->parent->image);
+       fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
+       token = mono_get_field_token (field);
+       l1 = g_strdup_printf ("klass_patch_info_%08x_%p", token, field);
+       fprintf (tmpfp, "%s:\n", l1);
+       fprintf (tmpfp, "\t.long 0x%08x\n", token);
+       g_assert (token);
+       fprintf (tmpfp, "\t.long %s\n", l2);
+               
+       g_hash_table_insert (hash, field, l1);
+
+       return l1;
+}
+
+int
+mono_compile_assembly (MonoAssembly *ass, guint32 opts)
+{
+       MonoCompile *cfg;
+       MonoImage *image = ass->image;
+       MonoMethod *method;
+       char *com, *tmpfname;
+       FILE *tmpfp;
+       int i, j;
+       guint8 *code, *mname;
+       int ccount = 0, mcount = 0, lmfcount = 0, ecount = 0, abscount = 0, wrappercount = 0, ocount = 0;
+       GHashTable *ref_hash;
+       int func_alignment = 16;
+
+       printf ("Mono AOT compiler - compiling assembly %s\n", image->name);
+
+       tmpfname = g_strdup_printf ("%s/mono_aot_%05d",  P_tmpdir, getpid ());
+       tmpfp = fopen (tmpfname, "w+");
+       g_assert (tmpfp);
+
+       ref_hash = g_hash_table_new (NULL, NULL);
+
+       write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
+               
+       for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
+               MonoJumpInfo *patch_info;
+               gboolean skip;
+               guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
+                       method = mono_get_method (image, token, NULL);
+               
+               /* fixme: maybe we can also precompile wrapper methods */
+               if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+                   (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
+                   (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
+                   (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
+                       //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
+                       continue;
+               }
+
+               mcount++;
+
+               /* fixme: we need to patch the IP for the LMF in that case */
+               if (method->save_lmf) {
+                       //printf ("Skip (needs lmf):  %s\n", mono_method_full_name (method, TRUE));
+                       lmfcount++;
+                       continue;
+               }
+
+               /* fixme: add methods with exception tables */
+               if (((MonoMethodNormal *)method)->header->num_clauses) {
+                       //printf ("Skip (exceptions): %s\n", mono_method_full_name (method, TRUE));
+                       ecount++;
+                       continue;
+               }
+
+               //printf ("START:           %s\n", mono_method_full_name (method, TRUE));
+               //mono_compile_method (method);
+
+               cfg = mini_method_compile (method, opts, mono_root_domain, 0);
+               g_assert (cfg);
+
+               if (cfg->disable_aot) {
+                       ocount++;
+                       continue;
+               }
+
+               skip = FALSE;
+               for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+                       if (patch_info->type == MONO_PATCH_INFO_ABS) {
+                               /* unable to handle this */
+                               //printf ("Skip (abs addr):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
+                               skip = TRUE;    
+                               break;
+                       }
+               }
+
+               if (skip) {
+                       abscount++;
+                       continue;
+               }
+
+               skip = FALSE;
+               for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+                       if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
+                            patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
+                           patch_info->data.method->wrapper_type) {
+                               /* unable to handle this */
+                               //printf ("Skip (wrapper call):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
+                               skip = TRUE;    
+                               break;
+                       }
+               }
+
+               if (skip) {
+                       wrappercount++;
+                       continue;
+               }
+
+               //printf ("Compile:           %s\n", mono_method_full_name (method, TRUE));
+
+               code = cfg->native_code;
+
+               fprintf (tmpfp, ".text\n");
+               mname = g_strdup_printf ("method_%08X", token);
+               fprintf (tmpfp, "\t.align %d\n", func_alignment);
+               fprintf (tmpfp, ".globl %s\n", mname);
+               fprintf (tmpfp, "%s:\n", mname);
+
+               for (j = 0; j < cfg->code_len; j++) 
+                       fprintf (tmpfp, ".byte %d\n", (unsigned int) code [j]);
+
+               fprintf (tmpfp, ".data\n");
+
+               j = 0;
+               for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+                       switch (patch_info->type) {
+                       case MONO_PATCH_INFO_LABEL:
+                       case MONO_PATCH_INFO_BB:
+                               /* relative jumps are no problem, there is no need to handle then here */
+                               break;
+                       case MONO_PATCH_INFO_SWITCH: {
+                               gpointer *table = (gpointer *)patch_info->data.target;
+                               int k;
+
+                               fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
+                               fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
+                               fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
+
+                               for (k = 0; k < patch_info->table_size; k++) {
+                                       fprintf (tmpfp, "\t.long %d\n", (guint8 *)table [k] - code);
+                               }
+                               j++;
+                               break;
+                       }
+                       case MONO_PATCH_INFO_INTERNAL_METHOD:
+                               patch_info->data.name = cond_emit_icall_label (tmpfp, ref_hash, patch_info->data.name);
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_METHODCONST:
+                       case MONO_PATCH_INFO_METHOD:
+                               patch_info->data.name = cond_emit_method_label (tmpfp, ref_hash, patch_info);
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_FIELD:
+                               patch_info->data.name = cond_emit_field_label (tmpfp, ref_hash, patch_info);
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_CLASS:
+                               patch_info->data.name = cond_emit_klass_label (tmpfp, ref_hash, patch_info);
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_IMAGE:
+                               patch_info->data.name = cond_emit_image_label (tmpfp, ref_hash, patch_info->data.image);
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_R4:
+                               fprintf (tmpfp, "\t.align 8\n");
+                               fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
+                               fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
+                               
+                               j++;
+                               break;
+                       case MONO_PATCH_INFO_R8:
+                               fprintf (tmpfp, "\t.align 8\n");
+                               fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
+                               fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
+                               fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
+                               
+                               j++;
+                               break;
+                       default:
+                               g_warning ("unable to handle jump info %d", patch_info->type);
+                               g_assert_not_reached ();
+                       }
+               }
+               
+               fprintf (tmpfp, ".globl %s_patch_info\n", mname);
+               fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
+               fprintf (tmpfp, "%s_patch_info:\n", mname);
+               
+               fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
+               fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs);
+
+               if (j) {
+                       j = 0;
+                       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+                               switch (patch_info->type) {
+                               case MONO_PATCH_INFO_METHODCONST:
+                               case MONO_PATCH_INFO_METHOD:
+                               case MONO_PATCH_INFO_CLASS:
+                               case MONO_PATCH_INFO_FIELD:
+                               case MONO_PATCH_INFO_INTERNAL_METHOD:
+                               case MONO_PATCH_INFO_IMAGE:
+                                       fprintf (tmpfp, "\t.long %s\n", patch_info->data.name);
+                                       fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i));
+                                       j++;
+                                       break;
+                               case MONO_PATCH_INFO_SWITCH:
+                               case MONO_PATCH_INFO_R4:
+                               case MONO_PATCH_INFO_R8:
+                                       fprintf (tmpfp, "\t.long %s_patch_info_%d\n", mname, j);
+                                       fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i));
+                                       j++;
+                                       break;
+                               case MONO_PATCH_INFO_LABEL:
+                               case MONO_PATCH_INFO_BB:
+                                       break;
+                               default:
+                                       g_warning ("unable to handle jump info %d", patch_info->type);
+                                       g_assert_not_reached ();
+                               }
+
+                       }
+               }
+               /* NULL terminated array */
+               fprintf (tmpfp, "\t.long 0\n");
+
+               /* fixme: save the rest of the required infos */
+
+               g_free (mname);
+               mono_destroy_compile (cfg);
+
+               ccount++;
+       }
+
+       fclose (tmpfp);
+
+       com = g_strdup_printf ("as %s -o %s.o;ld -shared -o %s.so %s.o;rm %s.o;strip --strip-unneeded --discard-all %s.so", 
+                              tmpfname, tmpfname, image->name, tmpfname, tmpfname, image->name);
+
+       printf ("Executing the native assembler now:\n%s\n", com);
+       system (com); 
+
+       printf ("Compile %d out of %d methods (%d%%)\n", ccount, mcount, (ccount*100)/mcount);
+       printf ("%d methods contains exception tables (%d%%)\n", ecount, (ecount*100)/mcount);
+       printf ("%d methods contains absolute addresses (%d%%)\n", abscount, (abscount*100)/mcount);
+       printf ("%d methods contains wrapper references (%d%%)\n", wrappercount, (wrappercount*100)/mcount);
+       printf ("%d methods contains lmf pointers (%d%%)\n", lmfcount, (lmfcount*100)/mcount);
+       printf ("%d methods has other problems (%d%%)\n", ocount, (ocount*100)/mcount);
+       //unlink (tmpfname);
+
+       return 0;
+}
diff --git a/mono/mini/arrays.cs b/mono/mini/arrays.cs
new file mode 100644 (file)
index 0000000..201d994
--- /dev/null
@@ -0,0 +1,131 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+       
+       static int test_10_create () {
+               int[] a = new int [10];
+               return a.Length;
+       }
+
+       static int test_0_unset_value () {
+               int[] a = new int [10];
+               return a [5];
+       }
+
+       static int test_3_set_value () {
+               int[] a = new int [10];
+               a [5] = 3;
+               return a [5];
+       }
+
+       static int test_0_char_array_1 () {
+               int value = -30;
+               char[] tmp = new char [20];
+               char[] digitLowerTable = new char[16];
+               tmp[0] = digitLowerTable[-(value % 10)];
+               return 0;
+       }
+       
+       static int test_0_char_array_2 () {
+               int value = 5;
+               char[] tmp = new char [20];
+               char[] digitLowerTable = new char[16];
+               tmp[0] = digitLowerTable[value % 10];
+               return 0;
+       }
+
+       static int test_0_char_array_3 () {
+               int value = -1;
+               char[] tmp = new char [20];
+               char[] digitLowerTable = new char[16];
+               tmp [0] = digitLowerTable[value & 15];          
+               return 0;
+       }
+
+       unsafe static int test_0_byte_array () {
+               byte [] src = new byte [8];
+               double ret;
+               byte *dst = (byte *)&ret;
+               int start = 0;
+
+               dst[0] = src[4 + start];
+               
+               return 0;
+       }
+       
+       public static int test_0_set_after_shift () {
+               int [] n = new int [1];
+               int b = 16;
+                   
+               n [0] = 100 + (1 << (16 - b));
+
+               if (n [0] != 101)
+                       return 1;
+
+               return 0;
+       }
+
+       private Int32[] m_array = new int [10];
+       
+       void setBit (int bitIndex, bool value) {
+               int index = bitIndex/32;
+               int shift = bitIndex%32;
+
+               Int32 theBit = 1 << shift;
+               if (value)
+                       m_array[index] |= theBit;
+               else
+                       m_array[index] &= ~theBit;
+       }
+       
+       bool getBit (int bitIndex) {
+               int index = bitIndex/32;
+               int shift = bitIndex%32;
+
+               Int32 theBit = m_array[index] & (1 << shift);
+               return (theBit == 0) ? false : true;
+
+       }
+       
+       public static int test_1_bit_index () {
+               Tests t = new Tests ();
+               t.setBit (0, true);
+               t.setBit (3, true);
+               if (t.getBit (1))
+                       return 4;
+               if (!t.getBit (0))
+                       return 5;
+               if (!t.getBit (3))
+                       return 6;
+               return 1;
+       }
+
+}
+
diff --git a/mono/mini/basic-float.cs b/mono/mini/basic-float.cs
new file mode 100644 (file)
index 0000000..281175f
--- /dev/null
@@ -0,0 +1,426 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+       
+       static int test_0_beq () {
+               double a = 2.0;
+               if (a != 2.0)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_bne_un () {
+               double a = 2.0;
+               if (a == 1.0)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_conv_r8 () {
+               double a = 2;
+               if (a != 2.0)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_conv_i4 () {
+               double a = 2.0;
+               int b = (int)a;
+               if (b != 2)
+                       return 1;
+               return 0;
+       }
+
+       static int test_5_add () {
+               double a = 2.0;
+               double b = 3.0;         
+               return (int)(a + b);
+       }
+
+       static int test_5_sub () {
+               double a = 8.0;
+               double b = 3.0;         
+               return (int)(a - b);
+       }       
+
+       static int test_24_mul () {
+               double a = 8.0;
+               double b = 3.0;         
+               return (int)(a * b);
+       }       
+
+       static int test_4_div () {
+               double a = 8.0;
+               double b = 2.0;         
+               return (int)(a / b);
+       }       
+
+       static int test_2_rem () {
+               double a = 8.0;
+               double b = 3.0;         
+               return (int)(a % b);
+       }       
+
+       static int test_2_neg () {
+               double a = -2.0;                
+               return (int)(-a);
+       }
+       
+       static int test_46_float_spill () {
+               // we overflow the FP stack
+               double a = 1;
+               double b = 2;
+               double c = 3;
+               double d = 4;
+               double e = 5;
+               double f = 6;
+               double g = 7;
+               double h = 8;
+               double i = 9;
+
+               return (int)(1.0 + (a + (b + (c + (d + (e + (f + (g + (h + i)))))))));
+       }
+
+       static int test_4_long_cast () {
+               long a = 1000;
+               double d = (double)a;
+               long b = (long)d;
+               if (b != 1000)
+                       return 0;
+               return 4;
+       }
+
+
+       static int test_15_float_cmp () {
+               double a = 2.0;
+               double b = 1.0;
+               int result = 0;
+               bool val;
+               
+               val = a == a;
+               if (!val)
+                       return result;
+               result++;
+
+               val = a < a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a > a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a <= a;
+               if (!val)
+                       return result;
+               result++;
+
+               val = a >= a;
+               if (!val)
+                       return result;
+               result++;
+
+               val = b == a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b < a;
+               if (!val)
+                       return result;
+               result++;
+
+               val = b > a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b <= a;
+               if (!val)
+                       return result;
+               result++;
+
+               val = b >= a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a == b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a < b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a > b;
+               if (!val)
+                       return result;
+               result++;
+
+               val = a <= b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a >= b;
+               if (!val)
+                       return result;
+               result++;
+
+               return result;
+       }
+
+       static int test_15_float_cmp_un () {
+               double a = Double.NaN;
+               double b = 1.0;
+               int result = 0;
+               bool val;
+               
+               val = a == a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a < a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a > a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a <= a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a >= a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b == a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b < a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b > a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b <= a;
+               if (val)
+                       return result;
+               result++;
+
+               val = b >= a;
+               if (val)
+                       return result;
+               result++;
+
+               val = a == b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a < b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a > b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a <= b;
+               if (val)
+                       return result;
+               result++;
+
+               val = a >= b;
+               if (val)
+                       return result;
+               result++;
+
+               return result;
+       }
+
+       static int test_15_float_branch () {
+               double a = 2.0;
+               double b = 1.0;
+               int result = 0;
+               
+               if (!(a == a))
+                       return result;
+               result++;
+
+               if (a < a)
+                       return result;
+               result++;
+
+               if (a > a)
+                       return result;
+               result++;
+
+               if (!(a <= a))
+                       return result;
+               result++;
+
+               if (!(a >= a))
+                       return result;
+               result++;
+
+               if (b == a)
+                       return result;
+               result++;
+
+               if (!(b < a))
+                       return result;
+               result++;
+
+               if (b > a)
+                       return result;
+               result++;
+
+               if (!(b <= a))
+                       return result;
+               result++;
+
+               if (b >= a)
+                       return result;
+               result++;
+
+               if (a == b)
+                       return result;
+               result++;
+
+               if (a < b)
+                       return result;
+               result++;
+
+               if (!(a > b))
+                       return result;
+               result++;
+
+               if (a <= b)
+                       return result;
+               result++;
+
+               if (!(a >= b))
+                       return result;
+               result++;
+
+               return result;
+       }
+
+       static int test_15_float_branch_un () {
+               double a = Double.NaN;
+               double b = 1.0;
+               int result = 0;
+               
+               if (a == a)
+                       return result;
+               result++;
+
+               if (a < a)
+                       return result;
+               result++;
+
+               if (a > a)
+                       return result;
+               result++;
+
+               if (a <= a)
+                       return result;
+               result++;
+
+               if (a >= a)
+                       return result;
+               result++;
+
+               if (b == a)
+                       return result;
+               result++;
+
+               if (b < a)
+                       return result;
+               result++;
+
+               if (b > a)
+                       return result;
+               result++;
+
+               if (b <= a)
+                       return result;
+               result++;
+
+               if (b >= a)
+                       return result;
+               result++;
+
+               if (a == b)
+                       return result;
+               result++;
+
+               if (a < b)
+                       return result;
+               result++;
+
+               if (a > b)
+                       return result;
+               result++;
+
+               if (a <= b)
+                       return result;
+               result++;
+
+               if (a >= b)
+                       return result;
+               result++;
+
+               return result;
+       }
+
+}
+
diff --git a/mono/mini/basic-long.cs b/mono/mini/basic-long.cs
new file mode 100644 (file)
index 0000000..dcf3211
--- /dev/null
@@ -0,0 +1,595 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+       
+       static int test_0_beq () {
+               long a = 0xffffffffff;
+               if (a != 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_bne_un () {
+               long a = 0xffffffffff;
+               if (a == 0xfffffffffe)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_ble () {
+               long a = 0xffffffffff;
+               if (a > 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_ble_un () {
+               ulong a = 0xffffffffff;
+               if (a > 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_bge () {
+               long a = 0xffffffffff;
+               if (a < 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_bge_un () {
+               ulong a = 0xffffffffff;
+               if (a < 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_blt () {
+               long a = 0xfffffffffe;
+               if (a >= 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_blt_un () {
+               ulong a = 0xfffffffffe;
+               if (a >= 0xffffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_bgt () {
+               long a = 0xffffffffff;
+               if (a <= 0xfffffffffe)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_conv_to_i4 () {
+               long a = 0;
+
+               return (int)a;
+       }
+       static int test_0_conv_from_i4 () {
+               long a = 2;
+               if (a != 2)
+                       return 1;
+
+               int b = 2;
+
+               if (a != b)
+                   return 2;
+               return 0;
+       }
+
+       static int test_0_conv_from_i4_negative () {
+               long a = -2;
+               if (a != -2)
+                       return 1;
+
+               int b = -2;
+
+               if (a != b)
+                   return 2;
+               return 0;
+       }
+
+       /*
+       static int test_0_conv_from_r8 () {
+               double b = 2.0;
+               long a = (long)b;
+
+               if (a != 2)
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_conv_from_r4 () {
+               float b = 2.0F;
+               long a = (long)b;
+
+               if (a != 2)
+                       return 1;
+               return 0;
+       }
+       */
+       
+       static int test_8_and () {
+               long a = 0xffffffffff;
+               long b = 8;             
+               return (int)(a & b);
+       }
+
+       static int test_8_and_imm () {
+               long a = 0xffffffffff;
+               return (int)(a & 8);
+       }
+
+       static int test_10_or () {
+               long a = 8;
+               long b = 2;             
+               return (int)(a | b);
+       }
+
+       static int test_10_or_imm () {
+               long a = 8;
+               return (int)(a | 2);
+       }
+
+       static int test_5_xor () {
+               long a = 7;
+               long b = 2;             
+               return (int)(a ^ b);
+       }
+
+       static int test_5_xor_imm () {
+               long a = 7;
+               return (int)(a ^ 2);
+       }
+
+       static int test_5_add () {
+               long a = 2;
+               long b = 3;             
+               return (int)(a + b);
+       }
+
+       static int test_5_add_imm () {
+               long a = 2;
+               return (int)(a + 3);
+       }
+
+       static int test_0_add_imm_no_inc () {
+               // we can't blindly convert an add x, 1 to an inc x
+               long a = 0x1ffffffff;
+               long c;
+               c = a + 2;
+               if (c == ((a + 1) + 1))
+                       return 0;
+               return 1;
+       }
+
+       static int test_5_sub () {
+               long a = 8;
+               long b = 3;             
+               return (int)(a - b);
+       }
+
+       static int test_5_sub_imm () {
+               long a = 8;
+               return (int)(a - 3);
+       }
+
+       static int test_2_neg () {
+               long a = -2;            
+               return (int)(-a);
+       }       
+
+       static int test_0_shl () {
+               long a = 9;
+               int b = 1;
+               
+               if ((a >> b) != 4)
+                       return 1;
+
+
+               return 0;
+       }
+       
+       static int test_1_rshift ()
+       {
+               long a = 9;
+               int b = 1;
+               a = -9;
+               if ((a >> b) != -5)
+                       return 0;
+               return 1;
+       }
+
+       static int test_5_shift ()
+       {
+               long a = 9;
+               int b = 1;
+               int count = 0;
+               
+               if ((a >> b) != 4)
+                       return count;
+               count++;
+
+               if ((a >> 63) != 0)
+                       return count;
+               count++;
+
+               if ((a << 1) != 18)
+                       return count;
+               count++;
+
+               if ((a << b) != 18)
+                       return count;
+               count++;
+
+               a = -9;
+               if ((a >> b) != -5)
+                       return count;
+               count++;
+
+               return count;
+       }
+
+       static int test_1_simple_neg () {
+               long a = 9;
+               
+               if (-a != -9)
+                       return 0;
+               return 1;
+       }
+
+       static int test_2_compare () {
+               long a = 1;
+               long b = 1;
+               
+               if (a != b)
+                       return 0;
+               return 2;
+       }
+
+       static int test_9_alu ()
+       {
+               long a = 9, b = 6;
+               int count = 0;
+               
+               if ((a + b) != 15)
+                       return count;
+               count++;
+               
+               if ((a - b) != 3)
+                       return count;
+               count++;
+
+               if ((a & 8) != 8)
+                       return count;
+               count++;
+
+               if ((a | 2) != 11)
+                       return count;
+               count++;
+
+               if ((a * b) != 54)
+                       return count;
+               count++;
+               
+               if ((a / 4) != 2)
+                       return count;
+               count++;
+               
+               if ((a % 4) != 1)
+                       return count;
+               count++;
+
+               if (-a != -9)
+                       return count;
+               count++;
+
+               b = -1;
+               if (~b != 0)
+                       return count;
+               count++;
+
+               return count;
+       }
+       
+       static int test_24_mul () {
+               long a = 8;
+               long b = 3;             
+               return (int)(a * b);
+       }       
+       
+       static int test_24_mul_ovf () {
+               long a = 8;
+               long b = 3;
+               long res;
+               
+               checked {
+                       res = a * b;
+               }
+               return (int)res;
+       }       
+
+       static int test_24_mul_un () {
+               ulong a = 8;
+               ulong b = 3;            
+               return (int)(a * b);
+       }       
+       
+       static int test_24_mul_ovf_un () {
+               ulong a = 8;
+               ulong b = 3;
+               ulong res;
+               
+               checked {
+                       res = a * b;
+               }
+               return (int)res;
+       }       
+       
+       static int test_4_divun () {
+               uint b = 12;
+               int a = 3;
+               return (int)(b / a);
+       }
+
+       static int test_1431655764_bigdivun_imm () {
+               uint b = (uint)-2;
+               return (int)(b / 3);
+       }
+
+       static int test_1431655764_bigdivun () {
+               uint b = (uint)-2;
+               int a = 3;
+               return (int)(b / a);
+       }
+
+       static int test_1_remun () {
+               uint b = 13;
+               int a = 3;
+               return (int)(b % a);
+       }
+
+       static int test_2_bigremun () {
+               uint b = (uint)-2;
+               int a = 3;
+               return (int)(b % a);
+       }
+
+       static int test_0_ceq () {
+               long a = 2;
+               long b = 2;
+               long c = 3;
+               long d = 0xff00000002;
+               
+               bool val = (a == b); // this should produce a ceq
+               if (!val)
+                       return 1;
+               
+               val = (a == c); // this should produce a ceq
+               if (val)
+                       return 2;
+               
+               val = (a == d); // this should produce a ceq
+               if (val)
+                       return 3;
+               
+               return 0;
+       }
+       
+       static int test_0_clt () {
+               long a = 2;
+               long b = 2;
+               long c = 3;
+               long d = 0xff00000002L;
+               long e = -1;
+               
+               bool val = (a < b); // this should produce a clt
+               if (val)
+                       return 1;
+               
+               val = (a < c); // this should produce a clt
+               if (!val)
+                       return 2;
+               
+               val = (c < a); // this should produce a clt
+               if (val)
+                       return 3;
+               
+               val = (e < d); // this should produce a clt
+               if (!val)
+                       return 4;
+               
+               val = (d < e); // this should produce a clt
+               if (val)
+                       return 5;
+               
+               return 0;
+       }
+       
+       static int test_0_clt_un () {
+               ulong a = 2;
+               ulong b = 2;
+               ulong c = 3;
+               ulong d = 0xff00000002;
+               ulong e = 0xffffffffffffffff;
+               
+               bool val = (a < b); // this should produce a clt_un
+               if (val)
+                       return 1;
+               
+               val = (a < c); // this should produce a clt_un
+               if (!val)
+                       return 1;
+               
+               val = (d < e); // this should produce a clt_un
+               if (!val)
+                       return 1;
+               
+               val = (e < d); // this should produce a clt_un
+               if (val)
+                       return 1;
+               
+               return 0;
+       }
+
+       static int test_0_cgt () {
+               long a = 2;
+               long b = 2;
+               long c = 3;
+               long d = 0xff00000002L;
+               long e = -1;
+               
+               bool val = (a > b); // this should produce a cgt
+               if (val)
+                       return 1;
+               
+               val = (a > c); // this should produce a cgt
+               if (val)
+                       return 2;
+               
+               val = (c > a); // this should produce a cgt
+               if (!val)
+                       return 3;
+               
+               val = (e > d); // this should produce a cgt
+               if (val)
+                       return 4;
+               
+               val = (d > e); // this should produce a cgt
+               if (!val)
+                       return 5;
+               
+               return 0;
+       }
+
+       static int test_0_cgt_un () {
+               ulong a = 2;
+               ulong b = 2;
+               ulong c = 3;
+               ulong d = 0xff00000002;
+               ulong e = 0xffffffffffffffff;
+               
+               bool val = (a > b); // this should produce a cgt_un
+               if (val)
+                       return 1;
+               
+               val = (a > c); // this should produce a cgt_un
+               if (val)
+                       return 1;
+               
+               val = (d > e); // this should produce a cgt_un
+               if (val)
+                       return 1;
+               
+               val = (e > d); // this should produce a cgt_un
+               if (!val)
+                       return 1;
+               
+               return 0;
+       }
+
+       static long return_5low () {
+               return 5;
+       }
+       
+       static long return_5high () {
+               return 0x500000000;
+       }
+
+       static int test_3_long_ret () {
+               long val = return_5low ();
+               return (int) (val - 2);
+       }
+
+       static int test_1_long_ret2 () {
+               long val = return_5high ();
+               if (val > 0xffffffff)
+                       return 1;
+               return 0;
+       }
+
+       static int test_3_byte_cast () {
+               ulong val = 0xff00ff00f0f0f0f0;
+               byte b;
+               b = (byte) (val & 0xFF);
+               if (b != 0xf0)
+                       return 1;
+               return 3;
+       }
+       
+       static int test_4_ushort_cast () {
+               ulong val = 0xff00ff00f0f0f0f0;
+               ushort b;
+               b = (ushort) (val & 0xFFFF);
+               if (b != 0xf0f0)
+                       return 1;
+               return 4;
+       }
+
+       static int test_500_mul_div () {
+               long val = 1000;
+               long exp = 10;
+               long maxexp = 20;
+               long res = val * exp / maxexp;
+
+               return (int)res;
+       }
+
+       static long position = 0;
+
+       static int test_4_static_inc_long () {
+
+               int count = 4;
+
+               position = 0;
+
+               position += count;
+
+               return (int)position;
+       }
+       
+       static void doit (double value, out long m) {
+               m = (long) value;
+       }
+       
+       static int test_0_ftol_clobber () {
+               long m;
+               doit (1.3, out m);
+               if (m != 1)
+                       return 2;
+               return 0;
+       }
+}
+
diff --git a/mono/mini/basic.cs b/mono/mini/basic.cs
new file mode 100644 (file)
index 0000000..81d4b72
--- /dev/null
@@ -0,0 +1,622 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+       
+       static int test_0_return () {
+               return 0;
+       }
+
+       static int test_100000_return_large () {
+               return 100000;
+       }
+
+       static int test_1_load_bool () {
+               bool a = true;
+               return a? 1: 0;
+       }
+
+       static int test_0_load_bool_false () {
+               bool a = false;
+               return a? 1: 0;
+       }
+
+       static int test_200_load_byte () {
+               byte a = 200;
+               return a;
+       }
+
+       static int test_100_load_sbyte () {
+               sbyte a = 100;
+               return a;
+       }
+
+       static int test_200_load_short () {
+               short a = 200;
+               return a;
+       }
+
+       static int test_100_load_ushort () {
+               ushort a = 100;
+               return a;
+       }
+
+       static int test_3_add_simple () {
+               int a = 1; 
+               int b = 2;
+               return a + b;
+       }
+
+       static int test_3_add_imm () {
+               int a = 1; 
+               return a + 2;
+       }
+
+       static int test_13407573_add_largeimm () {
+               int a = 1; 
+               return a + 13407572;
+       }
+
+       static int test_1_sub_simple () {
+               int a = 1; 
+               int b = 2;
+               return b - a;
+       }
+
+       static int test_1_sub_simple_un () {
+               uint a = 1; 
+               uint b = 2;
+               return (int)(b - a);
+       }
+
+       static int test_1_sub_imm () {
+               int b = 2;
+               return b - 1;
+       }
+
+       static int test_0_sub_inv_imm () {
+               int b = 2;
+               return 2 - b;
+       }
+
+       static int test_2_and () {
+               int b = 2;
+               int a = 3;
+               return b & a;
+       }
+
+       static int test_0_and_imm () {
+               int b = 2;
+               return b & 0x10;
+       }
+
+       static int test_0_and_large_imm () {
+               int b = 2;
+               return b & 0x10000000;
+       }
+
+       static int test_2_div () {
+               int b = 6;
+               int a = 3;
+               return b / a;
+       }
+
+       static int test_4_div_imm () {
+               int b = 12;
+               return b / 3;
+       }
+
+       static int test_4_divun_imm () {
+               uint b = 12;
+               return (int)(b / 3);
+       }
+
+       static int test_0_div_fold () {
+               int b = -1;
+               return b / 2;
+       }
+
+       static int test_1_remun_imm () {
+               uint b = 13;
+               return (int)(b % 3);
+       }
+
+       static int test_2_bigremun_imm () {
+               uint b = (uint)-2;
+               return (int)(b % 3);
+       }
+
+       static int test_2_rem () {
+               int b = 5;
+               int a = 3;
+               return b % a;
+       }
+
+       static int test_4_rem_imm () {
+               int b = 12;
+               return b % 8;
+       }
+
+       static int test_9_mul () {
+               int b = 3;
+               int a = 3;
+               return b * a;
+       }
+
+       static int test_15_mul_imm () {
+               int b = 3;
+               return b * 5;
+       }
+
+       static int test_24_mul () {
+               int a = 3;
+               int b = 8;
+               int res;
+
+               res = a * b;
+               
+               return res;
+       }
+
+       static int test_24_mul_ovf () {
+               int a = 3;
+               int b = 8;
+               int res;
+
+               checked {
+                       res = a * b;
+               }
+               
+               return res;
+       }
+
+       static int test_24_mul_un () {
+               uint a = 3;
+               uint b = 8;
+               uint res;
+
+               res = a * b;
+               
+               return (int)res;
+       }
+
+       static int test_24_mul_ovf_un () {
+               uint a = 3;
+               uint b = 8;
+               uint res;
+
+               checked {
+                       res = a * b;
+               }
+               
+               return (int)res;
+       }
+
+       static int test_3_or () {
+               int b = 2;
+               int a = 3;
+               return b | a;
+       }
+
+       static int test_3_or_un () {
+               uint b = 2;
+               uint a = 3;
+               return (int)(b | a);
+       }
+
+       static int test_3_or_short_un () {
+               ushort b = 2;
+               ushort a = 3;
+               return (int)(b | a);
+       }
+
+       static int test_18_or_imm () {
+               int b = 2;
+               return b | 0x10;
+       }
+
+       static int test_268435458_or_large_imm () {
+               int b = 2;
+               return b | 0x10000000;
+       }
+
+       static int test_1_neg () {
+               int b = -2;
+               b++;
+               return -b;
+       }
+
+       static int test_2_not () {
+               int b = ~2;
+               b = ~b;
+               return b;
+       }
+
+       static int test_16_shift () {
+               int b = 2;
+               int a = 3;
+               return b << a;
+       }
+       
+       static int test_16_shift_add () {
+               int b = 2;
+               int a = 3;
+               int c = 0;
+               return b << (a + c);
+       }
+       
+       static int test_16_shift_add2 () {
+               int b = 2;
+               int a = 3;
+               int c = 0;
+               return (b + c) << a;
+       }
+       
+       static int test_16_shift_imm () {
+               int b = 2;
+               return b << 3;
+       }
+       
+       static int test_12_shift_imm_inv () {
+               int b = 2;
+               return 3 << 2;
+       }
+
+       static int test_12_shift_imm_inv_sbyte () {
+               sbyte b = 2;
+               return 3 << 2;
+       }
+
+       static int test_1_rshift_imm () {
+               int b = 8;
+               return b >> 3;
+       }
+       
+       static int test_2_unrshift_imm () {
+               uint b = 16;
+               return (int)(b >> 3);
+       }
+       
+       static int test_0_bigunrshift_imm () {
+               uint b = (uint)-1;
+               b = b >> 1;
+               if (b != 0x7fffffff)
+                       return 1;
+               return 0;
+       }
+       
+       static int test_0_bigrshift_imm () {
+               int b = -1;
+               b = b >> 1;
+               if (b != -1)
+                       return 1;
+               return 0;
+       }
+       
+       static int test_1_rshift () {
+               int b = 8;
+               int a = 3;
+               return b >> a;
+       }
+       
+       static int test_2_unrshift () {
+               uint b = 16;
+               int a = 3;
+               return (int)(b >> a);
+       }
+       
+       static int test_0_bigunrshift () {
+               uint b = (uint)-1;
+               int a = 1;
+               b = b >> a;
+               if (b != 0x7fffffff)
+                       return 1;
+               return 0;
+       }
+       
+       static int test_0_bigrshift () {
+               int b = -1;
+               int a = 1;
+               b = b >> a;
+               if (b != -1)
+                       return 1;
+               return 0;
+       }
+       
+       static int test_2_cond () {
+               int b = 2, a = 3, c;
+               if (a == b)
+                       return 0;
+               return 2;
+       }
+       
+       static int test_2_cond_short () {
+               short b = 2, a = 3, c;
+               if (a == b)
+                       return 0;
+               return 2;
+       }
+       
+       static int test_2_cond_sbyte () {
+               sbyte b = 2, a = 3, c;
+               if (a == b)
+                       return 0;
+               return 2;
+       }
+       
+       static int test_6_cascade_cond () {
+               int b = 2, a = 3, c;
+               if (a == b)
+                       return 0;
+               else if (b > a)
+                       return 1;
+               else if (b != b)
+                       return 2;
+               else {
+                       c = 1;
+               }
+               return a + b + c;
+       }
+       
+       static int test_6_cascade_short () {
+               short b = 2, a = 3, c;
+               if (a == b)
+                       return 0;
+               else if (b > a)
+                       return 1;
+               else if (b != b)
+                       return 2;
+               else {
+                       c = 1;
+               }
+               return a + b + c;
+       }
+       
+       static int test_15_for_loop () {
+               int i;
+               for (i = 0; i < 15; ++i) {
+               }
+               return i;
+       }
+       
+       static int test_11_nested_for_loop () {
+               int i, j = 0; /* mcs bug here if j not set */
+               for (i = 0; i < 15; ++i) {
+                       for (j = 200; j >= 5; --j) ;
+               }
+               return i - j;
+       }
+
+       static int test_11_several_nested_for_loops () {
+               int i, j = 0; /* mcs bug here if j not set */
+               for (i = 0; i < 15; ++i) {
+                       for (j = 200; j >= 5; --j) ;
+               }
+               i = j = 0;
+               for (i = 0; i < 15; ++i) {
+                       for (j = 200; j >= 5; --j) ;
+               }
+               return i - j;
+       }
+
+       static int test_0_conv_ovf_i1 () {
+               int c;
+
+               //for (int j = 0; j < 10000000; j++)
+               checked {
+                       c = 127;
+                       sbyte b = (sbyte)c;
+                       c = -128;
+                       b = (sbyte)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_i1_un () {
+               uint c;
+
+               checked {
+                       c = 127;
+                       sbyte b = (sbyte)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_i2 () {
+               int c;
+
+               checked {
+                       c = 32767;
+                       Int16 b = (Int16)c;
+                       c = -32768;
+                       b = (Int16)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_i2_un () {
+               uint c;
+
+               checked {
+                       c = 32767;
+                       Int16 b = (Int16)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_u2 () {
+               int c;
+
+               checked {
+                       c = 65535;
+                       UInt16 b = (UInt16)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_u2_un () {
+               uint c;
+
+               checked {
+                       c = 65535;
+                       UInt16 b = (UInt16)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_conv_ovf_u4 () {
+               int c;
+
+               checked {
+                       c = 0x7fffffff;
+                       uint b = (uint)c;
+               }
+               
+               return 0;
+       }
+       
+       static int test_0_bool () {
+               bool val = true;
+               if (val)
+                       return 0;
+               return 1;
+       }
+       
+       static int test_1_bool_inverted () {
+               bool val = true;
+               if (!val)
+                       return 0;
+               return 1;
+       }
+
+       static int test_1_bool_assign () {
+               bool val = true;
+               val = !val; // this should produce a ceq
+               if (val)
+                       return 0;
+               return 1;
+       }
+
+       static int test_1_bool_multi () {
+               bool val = true;
+               bool val2 = true;
+               val = !val;
+               if ((val && !val2) && (!val2 && val))
+                       return 0;
+               return 1;
+       }
+
+       static int test_16_spill () {
+               int a = 1;
+               int b = 2;
+               int c = 3;
+               int d = 4;
+               int e = 5;
+
+               return (1 + (a + (b + (c + (d + e)))));
+       }
+
+       static int test_1_switch () {
+               int n = 0;
+
+               switch (n) {
+               case 0: return 1;
+               case 1: return 2;
+               case -1: return 3;
+               default:
+                       return 4;
+               }
+               return 1;
+       }
+
+
+       static int test_0_while_loop_1 () {
+
+               int value = 255;
+               
+               do {
+                       value = value >> 4;
+               } while (value != 0);
+               
+               return 0;
+       }
+
+       static int test_0_while_loop_2 () {
+               int value = 255;
+               int position = 5;
+               
+               do {
+                       value = value >> 4;
+               } while (value != 0 && position > 1);
+       
+               return 0;
+       }
+
+       static int test_0_char_conv () {
+               int i = 1;
+               
+               char tc = (char) ('0' + i);
+
+               if (tc != '1')
+                       return 1;
+               
+               return 0;
+       }
+
+       static int test_3_shift_regalloc () {
+               int shift = 8;
+               int orig = 1;
+               byte value = 0xfe;
+
+               orig &= ~(0xff << shift);
+               orig |= value << shift;
+
+               if (orig == 0xfe01)
+                       return 3;
+               return 0;
+       }
+
+       enum E {A, B};
+       
+       static int test_2_optimize_branches () {
+               switch (E.A) {
+               case E.A:
+                       if (E.A == E.B) {
+                       }
+                       break;
+               }
+               return 2;
+       }
+}
diff --git a/mono/mini/bench.cs b/mono/mini/bench.cs
new file mode 100644 (file)
index 0000000..c6a2362
--- /dev/null
@@ -0,0 +1,244 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main (string[] args) {
+               return TestDriver.RunTests (typeof (Tests), args);
+       }
+       
+       static public int test_0_many_nested_loops () {
+               // we do the loop a few times otherwise it's too fast
+               for (int i = 0; i < 5; ++i) {
+               int n = 16;
+               int x = 0;
+               int a = n;
+               while (a-- != 0) {
+                   int b = n;
+                   while (b-- != 0) {
+                       int c = n;
+                       while (c-- != 0) {
+                           int d = n;
+                       while (d-- != 0) {
+                               int e = n;
+                               while (e-- != 0) {
+                                   int f = n;
+                                   while (f-- != 0) {
+                                       x++;
+                                   }
+                               }
+                       }
+                       }
+                   }
+               }
+               if (x != 16777216)
+                       return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_logic_run ()
+       {
+               // GPL: Copyright (C) 2001  Southern Storm Software, Pty Ltd.
+               int iter, i = 0;
+
+               while (i++ < 10) {
+               // Initialize.
+               bool flag1 = true;
+               bool flag2 = true;
+               bool flag3 = true;
+               bool flag4 = true;
+               bool flag5 = true;
+               bool flag6 = true;
+               bool flag7 = true;
+               bool flag8 = true;
+               bool flag9 = true;
+               bool flag10 = true;
+               bool flag11 = true;
+               bool flag12 = true;
+               bool flag13 = true;
+
+               // First set of tests.
+               for(iter = 0; iter < 2000000; ++iter) {
+                       if((flag1 || flag2) && (flag3 || flag4) &&
+                          (flag5 || flag6 || flag7))
+                               {
+                               flag8 = !flag8;
+                               flag9 = !flag9;
+                               flag10 = !flag10;
+                               flag11 = !flag11;
+                               flag12 = !flag12;
+                               flag13 = !flag13;
+                               flag1 = !flag1;
+                               flag2 = !flag2;
+                               flag3 = !flag3;
+                               flag4 = !flag4;
+                               flag5 = !flag5;
+                               flag6 = !flag6;
+                               flag1 = !flag1;
+                               flag2 = !flag2;
+                               flag3 = !flag3;
+                               flag4 = !flag4;
+                               flag5 = !flag5;
+                               flag6 = !flag6;
+                       }
+               }
+               }
+               return 0;
+       }
+       static public int test_1028_sieve () {
+           //int NUM = ((argc == 2) ? atoi(argv[1]) : 1);
+           int NUM = 2000;
+           byte[] flags = new byte[8192 + 1];
+           int i, k;
+           int count = 0;
+
+           while (NUM-- != 0) {
+               count = 0; 
+               for (i=2; i <= 8192; i++) {
+                   flags[i] = 1;
+               }
+               for (i=2; i <= 8192; i++) {
+                   if (flags[i] != 0) {
+                       // remove all multiples of prime: i
+                       for (k=i+i; k <= 8192; k+=i) {
+                           flags[k] = 0;
+                       }
+                       count++;
+                   }
+               }
+           }
+           //printf("Count: %d\n", count);
+           return(count);
+       }
+       
+       public static int fib (int n) {
+               if (n < 2)
+                       return 1;
+               return fib(n-2)+fib(n-1);
+       }
+
+       public static int test_3524578_fib () {
+               for (int i = 0; i < 10; i++)
+                       fib (32);
+               
+               return fib (32);
+       }
+
+        private static ulong numMoves;
+
+        static void movetower (int disc, int from, int to, int use) {
+               if (disc > 0) {         
+                       numMoves++;
+                       movetower (disc-1, from, use, to);
+                       movetower (disc-1, use, to, from);
+               }
+        }
+
+        public static int test_0_hanoi () {
+               int iterations = 5000;
+               int numdiscs = 12;
+               
+               numMoves = 0;
+               while (iterations > 0) {
+                       iterations--;
+                       movetower (numdiscs, 1, 3, 2);
+               }
+               if (numMoves != 20475000)
+                       return 1;
+               return 0;
+        }
+
+       public static int test_0_castclass () {
+               object a = "a";
+
+               for (int i = 0; i < 100000000; i++) {
+                       string b = (string)a;
+                       if ((object)a != (object)b)
+                               return 1;
+               }
+               return 0;
+       }
+       
+       public static int test_23005000_float () {
+               double a, b, c, d;
+               bool val;
+               int loops = 0;
+               a = 0.0;
+               b = 0.0001;
+               c = 2300.5;
+               d = 1000.0;
+
+               while (a < c) {
+                       if (a == d)
+                               b *= 2;
+                       a += b;
+                       val = b >= c;
+                       if (val) break;
+                       loops++;
+               }
+               return loops;
+       }
+
+       /*
+        /// Gaussian blur of a generated grayscale picture
+        private int test_0_blur(int size) {
+               const int num  = 5; // Number of time to blur
+               byte[,] arr1 = new byte[size, size];
+               byte[,] arr2 = new byte[size, size];
+
+               int iterations = 1;
+               
+               while(iterations-- > 0) {
+
+                       // Draw fake picture
+                       for(int i = 0; i < size; i++) {
+                               for(int j = 0; j < size; j++) {   
+                                       arr1[i, j] = (byte) (i%255); 
+                               }
+                       }
+
+                       for(int n = 0; n < num; n++) { // num rounds of blurring
+                               for(int i = 3; i < size-3; i++) // vertical blur arr1 -> arr2
+                                       for(int j = 0; j < size; j++)
+                                               arr2[i, j] = (byte)((arr1[i-3, j] + arr1[i+3, j]
+                                                                    + 6*(arr1[i-2, j]+arr1[i+2, j])
+                                                                    + 15*(arr1[i-1, j]+arr1[i+1, j])
+                                                                    + 20*arr1[i, j] + 32)>>6);
+
+                               for(int j = 3; j < size-3; j++) // horizontal blur arr1 -> arr2
+                                       for(int i = 0; i < size; i++)
+                                               arr1[i, j] = (byte)((arr2[i, j-3] + arr2[i, j+3]
+                                                                    + 6*(arr2[i, j-2]+arr2[i, j+2])
+                                                                    + 15*(arr2[i, j-1]+arr2[i, j+1])
+                                                                    + 20*arr2[i, j] + 32)>>6);
+                       }
+               }
+
+               return 0;
+        }
+       */
+}
+
diff --git a/mono/mini/cfold.c b/mono/mini/cfold.c
new file mode 100644 (file)
index 0000000..86471e8
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * cfold.c: Constant folding support
+ *
+ * Author:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.  http://www.ximian.com
+ */
+#include "mini.h"
+
+static int
+is_power_of_two (guint32 val)
+{
+       int i, j, k;
+
+       for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
+               if (val & j)
+                       break;
+       }
+       if (i == 32 || val & k)
+               return -1;
+       return i;
+}
+
+#define FOLD_BINOP(name,op)    \
+       case name:      \
+               if (inst->inst_i0->opcode != OP_ICONST) \
+                       return; \
+               if (inst->inst_i1->opcode == OP_ICONST) {       \
+                       inst->opcode = OP_ICONST;       \
+                       inst->inst_c0 = inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0;       \
+               } \
+                return;
+
+/*
+ * We try to put constants on the left side of a commutative operation
+ * because it reduces register pressure and it matches the usual cpu 
+ * instructions with immediates.
+ */
+#define FOLD_BINOPCOMM(name,op)        \
+       case name:      \
+               if (inst->inst_i0->opcode == OP_ICONST) {\
+                       if (inst->inst_i1->opcode == OP_ICONST) {       \
+                               inst->opcode = OP_ICONST;       \
+                               inst->inst_c0 = inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0;       \
+                                return; \
+                       } else { \
+                               MonoInst *tmp = inst->inst_i0;  \
+                               inst->inst_i0 = inst->inst_i1;  \
+                               inst->inst_i1 = tmp;    \
+                       } \
+               } \
+               if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_MUL) {    \
+                       int power2;     \
+                       if (inst->inst_i1->inst_c0 == 1) {      \
+                               *inst = *(inst->inst_i0);       \
+                               return; \
+                       } else if (inst->inst_i1->inst_c0 == -1) {      \
+                               inst->opcode = CEE_NEG; \
+                               return; \
+                       }       \
+                       power2 = is_power_of_two (inst->inst_i1->inst_c0);      \
+                       if (power2 < 0) return; \
+                       inst->opcode = CEE_SHL; \
+                       inst->inst_i1->inst_c0 = power2;        \
+               } \
+                return;
+
+#define FOLD_BINOPZ(name,op,cast)      \
+       case name:      \
+               if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_REM_UN && inst->inst_i1->inst_c0 == 2) {  \
+                       inst->opcode = CEE_AND; \
+                       inst->inst_i1->inst_c0 = 1;     \
+                       return; \
+               }       \
+               if (inst->inst_i1->opcode == OP_ICONST) {       \
+                       /* let the runtime throw the exception. */      \
+                       if (!inst->inst_i1->inst_c0) return;    \
+                       if (inst->inst_i0->opcode == OP_ICONST) {       \
+                               inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
+                               inst->opcode = OP_ICONST;       \
+                       } else {        \
+                               int power2 = is_power_of_two (inst->inst_i1->inst_c0);  \
+                               if (power2 < 0) return; \
+                               if (inst->opcode == CEE_REM_UN) {       \
+                                       inst->opcode = CEE_AND; \
+                                       inst->inst_i1->inst_c0 = (1 << power2) - 1;     \
+                               } else if (inst->opcode == CEE_DIV_UN) {        \
+                                       inst->opcode = CEE_SHR_UN;      \
+                                       inst->inst_i1->inst_c0 = power2;        \
+                               }       \
+                       }       \
+               } \
+                return;
+       
+#define FOLD_BINOPA(name,op,cast)      \
+       case name:      \
+               if (inst->inst_i0->opcode != OP_ICONST) \
+                       return; \
+               if (inst->inst_i1->opcode == OP_ICONST) {       \
+                       inst->opcode = OP_ICONST;       \
+                       inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
+               } \
+                return;
+       
+#define FOLD_CXX(name,op,cast) \
+       case name:      \
+               if (inst->inst_i0->opcode != OP_COMPARE)        \
+                       return; \
+               if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
+                       return; \
+               if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
+                       inst->opcode = OP_ICONST;       \
+                       inst->inst_c0 = (cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0; \
+               } \
+                return;
+
+#define FOLD_UNOP(name,op)     \
+       case name:      \
+               if (inst->inst_i0->opcode != OP_ICONST) \
+                       return; \
+               inst->opcode = OP_ICONST;       \
+               inst->inst_c0 = op inst->inst_i0->inst_c0; \
+                return;
+
+#define FOLD_BRBINOP(name,op,cast)     \
+       case name:      \
+               if (inst->inst_i0->opcode != OP_COMPARE)        \
+                       return; \
+               if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
+                       return; \
+               if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
+                       if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0)     \
+                               inst->opcode = CEE_BR;  \
+                       else    \
+                               inst->opcode = CEE_NOP; \
+               } \
+                return;
+
+/*
+ * Helper function to do constant expression evaluation.
+ * We do constant folding of integers only, FP stuff is much more tricky,
+ * int64 probably not worth it.
+ */
+void
+mono_constant_fold_inst (MonoInst *inst, gpointer data)
+{
+       switch (inst->opcode) {
+
+       /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */
+       /*FOLD_BRBINOP (CEE_BEQ,==,gint32)
+       FOLD_BRBINOP (CEE_BGE,>=,gint32)
+       FOLD_BRBINOP (CEE_BGT,>,gint32)
+       FOLD_BRBINOP (CEE_BLE,<=,gint32)
+       FOLD_BRBINOP (CEE_BLT,<,gint32)
+       FOLD_BRBINOP (CEE_BNE_UN,!=,guint32)
+       FOLD_BRBINOP (CEE_BGE_UN,>=,guint32)
+       FOLD_BRBINOP (CEE_BGT_UN,>,guint32)
+       FOLD_BRBINOP (CEE_BLE_UN,<=,guint32)
+       FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/
+
+       FOLD_BINOPCOMM (CEE_MUL,*)
+
+       FOLD_BINOPCOMM (CEE_ADD,+)
+       FOLD_BINOP (CEE_SUB,-)
+       FOLD_BINOPZ (CEE_DIV,/,gint32)
+       FOLD_BINOPZ (CEE_DIV_UN,/,guint32)
+       FOLD_BINOPZ (CEE_REM,%,gint32)
+       FOLD_BINOPZ (CEE_REM_UN,%,guint32)
+       FOLD_BINOPCOMM (CEE_AND,&)
+       FOLD_BINOPCOMM (CEE_OR,|)
+       FOLD_BINOPCOMM (CEE_XOR,^)
+       FOLD_BINOP (CEE_SHL,<<)
+       FOLD_BINOP (CEE_SHR,>>)
+       case CEE_SHR_UN:
+               if (inst->inst_i0->opcode != OP_ICONST)
+                       return;
+               if (inst->inst_i1->opcode == OP_ICONST) {
+                       inst->opcode = OP_ICONST;
+                       inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0;
+               }
+               return;
+       FOLD_UNOP (CEE_NEG,-)
+       FOLD_UNOP (CEE_NOT,~)
+       FOLD_CXX (OP_CEQ,==,gint32)
+       FOLD_CXX (OP_CGT,>,gint32)
+       FOLD_CXX (OP_CGT_UN,>,guint32)
+       FOLD_CXX (OP_CLT,<,gint32)
+       FOLD_CXX (OP_CLT_UN,<,guint32)
+       /* we should be able to handle isinst and castclass as well */
+       case CEE_ISINST:
+       case CEE_CASTCLASS:
+       /*
+        * TODO: 
+        *      conv.* opcodes.
+        *      *ovf* opcodes? I'ts slow and hard to do in C.
+        *      switch can be replaced by a simple jump 
+        */
+       default:
+               return;
+       }
+}
+
+void
+mono_constant_fold (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *ins;
+               for (ins = bb->code; ins; ins = ins->next)      
+                       mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
+       }
+}
+
diff --git a/mono/mini/cprop.c b/mono/mini/cprop.c
new file mode 100644 (file)
index 0000000..c24eb0f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * cprop.c: Constant propagation.
+ *
+ * Author:
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+
+/* dumb list-based implementation for now */
+
+typedef struct _MiniACP MiniACP;
+
+struct _MiniACP {
+       MiniACP *next;
+       short dreg;
+       short sreg;
+       int type;
+};
+
+static int
+copy_value (MiniACP *acp, int reg, int type)
+{
+       MiniACP *tmp = acp;
+
+       //g_print ("search reg %d '%c'\n", reg, type);
+       while (tmp) {
+       //      g_print ("considering dreg %d, sreg %d '%c'\n", tmp->dreg, tmp->sreg, tmp->type);
+               if (tmp->type == type && tmp->dreg == reg) {
+       //              g_print ("copy prop from %d to %d\n", tmp->sreg, tmp->dreg);
+                       return tmp->sreg;
+               }
+               tmp = tmp->next;
+       }
+       return reg;
+}
+
+static MiniACP*
+remove_acp (MiniACP *acp, int reg, int type)
+{
+       MiniACP *tmp = acp;
+       MiniACP *prev = NULL;
+
+       while (tmp) {
+               if (tmp->type == type && (tmp->dreg == reg || tmp->sreg == reg)) {
+                       if (prev)
+                               prev->next = tmp->next;
+                       else
+                               acp = tmp->next;
+               } else {
+                       prev = tmp;
+               }
+               tmp = tmp->next;
+       }
+       return acp;
+}
+
+static MiniACP*
+add_acp (MonoCompile *cfg, MiniACP *acp, int sreg, int dreg, int type)
+{
+       MiniACP *newacp = mono_mempool_alloc (cfg->mempool, sizeof (MiniACP));;
+       newacp->type = type;
+       newacp->sreg = sreg;
+       newacp->dreg = dreg;
+
+       newacp->next = acp;
+       return newacp;
+}
+
+static void
+local_copy_prop (MonoCompile *cfg, MonoInst *code)
+{
+       MiniACP *acp = NULL;
+       const char *spec;
+       MonoInst *ins = code;
+
+       //g_print ("starting BB\n");
+
+       while (ins) {
+               spec = ins_spec [ins->opcode];
+               //print_ins (0, ins);
+
+               if (spec [MONO_INST_CLOB] != 's' && spec [MONO_INST_CLOB] != '1' && spec [MONO_INST_CLOB] != 'd' && spec [MONO_INST_CLOB] != 'a' && spec [MONO_INST_CLOB] != 'c') {
+                       if (spec [MONO_INST_SRC1] == 'f') {
+                               ins->sreg1 = copy_value (acp, ins->sreg1, 'f');
+                       } else if (spec [MONO_INST_SRC1]) {
+                               ins->sreg1 = copy_value (acp, ins->sreg1, 'i');
+                       }
+               }
+
+               if (spec [MONO_INST_CLOB] != 's') {
+                       if (spec [MONO_INST_SRC2] == 'f') {
+                               ins->sreg2 = copy_value (acp, ins->sreg2, 'f');
+                       } else if (spec [MONO_INST_SRC2]) {
+                               ins->sreg2 = copy_value (acp, ins->sreg2, 'i');
+                       }
+               }
+
+               /* invalidate pairs */
+               if (spec [MONO_INST_DEST] == 'f') {
+                       acp = remove_acp (acp, ins->dreg, 'f');
+               } else if (spec [MONO_INST_DEST]) {
+                       acp = remove_acp (acp, ins->dreg, 'i');
+               }
+
+               /* insert pairs */
+               /*
+                * Later copy-propagate also immediate values and memory stores.
+                */
+               if (ins->opcode == OP_MOVE && ins->sreg1 != ins->dreg) {
+       //              g_print ("added acp of %d <- %d '%c'\n", ins->dreg, ins->sreg1, spec [MONO_INST_SRC1]);
+                       acp = add_acp (cfg, acp, ins->sreg1, ins->dreg, spec [MONO_INST_SRC1]);
+               }
+
+               if (spec [MONO_INST_CLOB] != 'c') {
+                       /* this is a call, invalidate all the pairs */
+                       acp = NULL;
+               } else if ((ins->opcode) == CEE_BR || (ins->opcode >= CEE_BEQ && ins->opcode <= CEE_BLT) ||
+                               (ins->opcode >= CEE_BNE_UN && ins->opcode <= CEE_BLT_UN)) {
+                       acp = NULL; /* invalidate all pairs */
+                       /* it's not enough to invalidate the pairs, because we don't always
+                        * generate extended basic blocks (the BRANCH_LABEL stuff in the burg rules)
+                        * This issue is going to reduce a lot the possibilities for optimization!
+                        */
+                       return;
+               }
+               ins = ins->next;
+       }
+}
+
diff --git a/mono/mini/cpu-g4.md b/mono/mini/cpu-g4.md
new file mode 100644 (file)
index 0000000..a40508d
--- /dev/null
@@ -0,0 +1,611 @@
+# g4-class cpu description file
+# this file is read by genmdesc to pruduce a table with all the relevant information
+# about the cpu instructions that may be used by the regsiter allocator, the scheduler
+# and other parts of the arch-dependent part of mini.
+#
+# An opcode name is followed by a colon and optional specifiers.
+# A specifier has a name, a colon and a value. Specifiers are separated by white space.
+# Here is a description of the specifiers valid for this file and their possible values.
+#
+# dest:register       describes the destination register of an instruction
+# src1:register       describes the first source register of an instruction
+# src2:register       describes the second source register of an instruction
+#
+# register may have the following values:
+#      i  integer register
+#      b  base register (used in address references)
+#      f  floating point register
+#
+# len:number         describe the maximun length in bytes of the instruction
+# number is a positive integer
+#
+# cost:number        describe how many cycles are needed to complete the instruction (unused)
+#
+# clob:spec          describe if the instruction clobbers registers or has special needs
+#
+# spec can be one of the following characters:
+#      c  clobbers caller-save registers
+#      r  'reserves' the destination register until a later instruction unreserves it
+#          used mostly to set output registers in function calls
+#
+# flags:spec        describe if the instruction uses or sets the flags (unused)
+#
+# spec can be one of the following chars:
+#      s  sets the flags
+#       u  uses the flags
+#       m  uses and modifies the flags
+#
+# res:spec          describe what units are used in the processor (unused)
+#
+# delay:            describe delay slots (unused)
+#
+# the required specifiers are: len, clob (if registers are clobbered), the registers
+# specifiers if the registers are actually used, flags (when scheduling is implemented).
+#
+# See the code in mini-x86.c for more details on how the specifiers are used.
+#
+nop: len:4
+break: len:4
+ldarg.0:
+ldarg.1:
+ldarg.2:
+ldarg.3:
+ldloc.0:
+ldloc.1:
+ldloc.2:
+ldloc.3:
+stloc.0:
+stloc.1:
+stloc.2:
+stloc.3:
+ldarg.s:
+ldarga.s:
+starg.s:
+ldloc.s:
+ldloca.s:
+stloc.s:
+ldnull:
+ldc.i4.m1:
+ldc.i4.0:
+ldc.i4.1:
+ldc.i4.2:
+ldc.i4.3:
+ldc.i4.4:
+ldc.i4.5:
+ldc.i4.6:
+ldc.i4.7:
+ldc.i4.8:
+ldc.i4.s:
+ldc.i4:
+ldc.i8:
+ldc.r4:
+ldc.r8:
+unused99:
+dup:
+pop:
+jmp:
+call: dest:a clob:c len:4
+calli:
+ret:
+br.s:
+brfalse.s:
+brtrue.s:
+beq.s:
+bge.s:
+bgt.s:
+ble.s:
+blt.s:
+bne.un.s:
+bge.un.s:
+bgt.un.s:
+ble.un.s:
+blt.un.s:
+br: len:4
+brfalse:
+brtrue:
+beq: len:8
+bge: len:8
+bgt: len:8
+ble: len:8
+blt: len:8
+bne.un: len:8
+bge.un: len:8
+bgt.un: len:8
+ble.un: len:8
+blt.un: len:8
+switch:
+ldind.i1: dest:i len:8
+ldind.u1: dest:i len:8
+ldind.i2: dest:i len:8
+ldind.u2: dest:i len:8
+ldind.i4: dest:i len:8
+ldind.u4: dest:i len:8
+ldind.i8:
+ldind.i: dest:i len:8
+ldind.r4:
+ldind.r8:
+ldind.ref: dest:i len:8
+stind.ref: src1:b src2:i
+stind.i1: src1:b src2:i
+stind.i2: src1:b src2:i
+stind.i4: src1:b src2:i
+stind.i8:
+stind.r4: src1:b src2:f
+stind.r8: src1:b src2:f
+add: dest:i src1:i src2:i len:4
+sub: dest:i src1:i src2:i len:4
+mul: dest:i src1:i src2:i len:4
+div: dest:a src1:i src2:i len:4
+div.un: dest:a src1:i src2:i len:4
+rem: dest:d src1:i src2:i len:12
+rem.un: dest:d src1:i src2:i len:12
+and: dest:i src1:i src2:i len:4
+or: dest:i src1:i src2:i len:4
+xor: dest:i src1:i src2:i len:4
+shl: dest:i src1:i src2:i len:4
+shr: dest:i src1:i src2:i len:4
+shr.un: dest:i src1:i src2:i len:4
+neg: dest:i src1:i len:4
+not: dest:i src1:i len:4
+conv.i1: dest:i src1:i len:4
+conv.i2: dest:i src1:i len:4
+conv.i4: dest:i src1:i len:4
+conv.i8:
+conv.r4: dest:f src1:i len:7
+conv.r8: dest:f src1:i len:7
+conv.u4: dest:i src1:i
+conv.u8:
+callvirt:
+cpobj:
+ldobj:
+ldstr:
+newobj:
+castclass:
+isinst:
+conv.r.un:
+unused58:
+unused1:
+unbox:
+throw: src1:i len:8
+ldfld:
+ldflda:
+stfld:
+ldsfld:
+ldsflda:
+stsfld:
+stobj:
+conv.ovf.i1.un:
+conv.ovf.i2.un:
+conv.ovf.i4.un:
+conv.ovf.i8.un:
+conv.ovf.u1.un:
+conv.ovf.u2.un:
+conv.ovf.u4.un:
+conv.ovf.u8.un:
+conv.ovf.i.un:
+conv.ovf.u.un:
+box:
+newarr:
+ldlen:
+ldelema:
+ldelem.i1:
+ldelem.u1:
+ldelem.i2:
+ldelem.u2:
+ldelem.i4:
+ldelem.u4:
+ldelem.i8:
+ldelem.i:
+ldelem.r4:
+ldelem.r8:
+ldelem.ref:
+stelem.i:
+stelem.i1:
+stelem.i2:
+stelem.i4:
+stelem.i8:
+stelem.r4:
+stelem.r8:
+stelem.ref:
+unused2:
+unused3:
+unused4:
+unused5:
+unused6:
+unused7:
+unused8:
+unused9:
+unused10:
+unused11:
+unused12:
+unused13:
+unused14:
+unused15:
+unused16:
+unused17:
+conv.ovf.i1:
+conv.ovf.u1:
+conv.ovf.i2:
+conv.ovf.u2:
+conv.ovf.i4:
+conv.ovf.u4:
+conv.ovf.i8:
+conv.ovf.u8:
+unused50:
+unused18:
+unused19:
+unused20:
+unused21:
+unused22:
+unused23:
+refanyval:
+ckfinite: dest:f src1:f len:22
+unused24:
+unused25:
+mkrefany:
+unused59:
+unused60:
+unused61:
+unused62:
+unused63:
+unused64:
+unused65:
+unused66:
+unused67:
+ldtoken:
+conv.u2: dest:i src1:i len:4
+conv.u1: dest:i src1:i len:4
+conv.i: dest:i src1:i len:4
+conv.ovf.i:
+conv.ovf.u:
+add.ovf:
+add.ovf.un:
+mul.ovf: dest:i src1:i src2:i len:8
+# this opcode is handled specially in the code generator
+mul.ovf.un: dest:i src1:i src2:i len:12
+sub.ovf:
+sub.ovf.un:
+endfinally: len:10
+leave:
+leave.s:
+stind.i:
+conv.u: dest:i src1:i len:4
+unused26:
+unused27:
+unused28:
+unused29:
+unused30:
+unused31:
+unused32:
+unused33:
+unused34:
+unused35:
+unused36:
+unused37:
+unused38:
+unused39:
+unused40:
+unused41:
+unused42:
+unused43:
+unused44:
+unused45:
+unused46:
+unused47:
+unused48:
+prefix7:
+prefix6:
+prefix5:
+prefix4:
+prefix3:
+prefix2:
+prefix1:
+prefixref:
+arglist:
+ceq: dest:i len:12
+cgt: dest:i len:12
+cgt.un: dest:i len:12
+clt: dest:i len:12
+clt.un: dest:i len:12
+ldftn:
+ldvirtftn:
+unused56:
+ldarg:
+ldarga:
+starg:
+ldloc:
+ldloca:
+stloc:
+localloc: dest:i src1:i len:30
+unused57:
+endfilter:
+unaligned.:
+volatile.:
+tail.:
+initobj:
+unused68:
+cpblk:
+initblk:
+unused69:
+rethrow:
+unused:
+sizeof:
+refanytype:
+unused52:
+unused53:
+unused54:
+unused55:
+unused70:
+illegal:
+endmac:
+mono_func1:
+mono_proc2:
+mono_proc3:
+mono_free:
+mono_objaddr:
+mono_ldptr:
+mono_vtaddr:
+mono_newobj:
+mono_retobj:
+load:
+ldaddr:
+store:
+phi:
+rename:
+compare: src1:i src2:i len:4
+compare_imm: src1:i len:12
+fcompare: src1:f src2:f len:12
+lcompare:
+local:
+arg:
+outarg: src1:i len:1
+outarg_imm: len:5
+retarg:
+setret: dest:a src1:i len:4
+setlret: dest:l src1:i src2:i len:8
+setreg: dest:i src1:i len:4 clob:r
+setregimm: dest:i len:8 clob:r
+setfreg: dest:f src1:f len:4 clob:r
+checkthis: src1:b len:4
+voidcall: len:8 clob:c
+voidcall_reg: src1:i len:8 clob:c
+voidcall_membase: src1:b len:8 clob:c
+fcall: dest:f len:8 clob:c
+fcall_reg: dest:f src1:i len:8 clob:c
+fcall_membase: dest:f src1:b len:8 clob:c
+lcall: dest:l len:8 clob:c
+lcall_reg: dest:l src1:i len:8 clob:c
+lcall_membase: dest:l src1:b len:8 clob:c
+vcall: len:8 clob:c
+vcall_reg: src1:i len:8 clob:c
+vcall_membase: src1:b len:8 clob:c
+call_reg: dest:i src1:i len:8 clob:c
+call_membase: dest:i src1:b len:8 clob:c
+trap:
+iconst: dest:i len:8
+i8const:
+r4const: dest:f len:8
+r8const: dest:f len:8
+regvar:
+reg:
+regoffset:
+label:
+store_membase_imm: dest:b len:12
+store_membase_reg: dest:b src1:i len:8
+storei1_membase_imm: dest:b len:12
+storei1_membase_reg: dest:b src1:i len:8
+storei2_membase_imm: dest:b len:12
+storei2_membase_reg: dest:b src1:i len:8
+storei4_membase_imm: dest:b len:12
+storei4_membase_reg: dest:b src1:i len:8
+storei8_membase_imm: dest:b 
+storei8_membase_reg: dest:b src1:i 
+storer4_membase_reg: dest:b src1:f len:8
+storer8_membase_reg: dest:b src1:f len:8
+load_membase: dest:i src1:b len:12
+loadi1_membase: dest:i src1:b len:12
+loadu1_membase: dest:i src1:b len:12
+loadi2_membase: dest:i src1:b len:12
+loadu2_membase: dest:i src1:b len:12
+loadi4_membase: dest:i src1:b len:12
+loadu4_membase: dest:i src1:b len:12
+loadi8_membase: dest:i src1:b
+loadr4_membase: dest:f src1:b len:8
+loadr8_membase: dest:f src1:b len:12
+loadu4_mem: dest:i len:8
+move: dest:i src1:i len:4
+add_imm: dest:i src1:i len:12
+sub_imm: dest:i src1:i len:12
+mul_imm: dest:i src1:i len:12
+# there is no actual support for division or reminder by immediate
+# we simulate them, though (but we need to change the burg rules 
+# to allocate a symbolic reg for src2)
+div_imm: dest:a src1:i src2:i len:12
+div_un_imm: dest:a src1:i src2:i len:12
+rem_imm: dest:d src1:i src2:i len:16
+rem_un_imm: dest:d src1:i src2:i len:16
+and_imm: dest:i src1:i len:8
+or_imm: dest:i src1:i len:8
+xor_imm: dest:i src1:i len:8
+shl_imm: dest:i src1:i len:8
+shr_imm: dest:i src1:i len:8
+shr_un_imm: dest:i src1:i len:8
+cond_exc_eq: len:8
+cond_exc_ne_un: len:8
+cond_exc_lt: len:8
+cond_exc_lt_un: len:8
+cond_exc_gt: len:8
+cond_exc_gt_un: len:8
+cond_exc_ge: len:8
+cond_exc_ge_un: len:8
+cond_exc_le: len:8
+cond_exc_le_un: len:8
+cond_exc_ov: len:8
+cond_exc_no: len:8
+cond_exc_c: len:8
+cond_exc_nc: len:8
+long_add:
+long_sub:
+long_mul:
+long_div:
+long_div_un:
+long_rem:
+long_rem_un:
+long_and:
+long_or:
+long_xor:
+long_shl:
+long_shr:
+long_shr_un:
+long_neg:
+long_not:
+long_conv_to_i1:
+long_conv_to_i2:
+long_conv_to_i4:
+long_conv_to_i8:
+long_conv_to_r4:
+long_conv_to_r8:
+long_conv_to_u4:
+long_conv_to_u8:
+long_conv_to_u2:
+long_conv_to_u1:
+long_conv_to_i:
+long_conv_to_ovf_i: dest:i src1:i src2:i len:30
+long_conv_to_ovf_u:
+long_add_ovf:
+long_add_ovf_un:
+long_mul_ovf: 
+long_mul_ovf_un:
+long_sub_ovf:
+long_sub_ovf_un:
+long_conv_to_ovf_i1_un:
+long_conv_to_ovf_i2_un:
+long_conv_to_ovf_i4_un:
+long_conv_to_ovf_i8_un:
+long_conv_to_ovf_u1_un:
+long_conv_to_ovf_u2_un:
+long_conv_to_ovf_u4_un:
+long_conv_to_ovf_u8_un:
+long_conv_to_ovf_i_un:
+long_conv_to_ovf_u_un:
+long_conv_to_ovf_i1:
+long_conv_to_ovf_u1:
+long_conv_to_ovf_i2:
+long_conv_to_ovf_u2:
+long_conv_to_ovf_i4:
+long_conv_to_ovf_u4:
+long_conv_to_ovf_i8:
+long_conv_to_ovf_u8:
+long_ceq:
+long_cgt:
+long_cgt_un:
+long_clt:
+long_clt_un:
+long_conv_to_r_un: dest:f src1:i src2:i len:37 
+long_conv_to_u:
+long_shr_imm:
+long_shr_un_imm:
+long_shl_imm:
+long_add_imm:
+long_sub_imm:
+long_beq:
+long_bne_un:
+long_blt:
+long_blt_un:
+long_bgt:
+long_btg_un:
+long_bge:
+long_bge_un:
+long_ble:
+long_ble_un:
+float_beq: len:8
+float_bne_un: len:8
+float_blt: len:8
+float_blt_un: len:8
+float_bgt: len:8
+float_btg_un: len:8
+float_bge: len:8
+float_bge_un: len:8
+float_ble: len:8
+float_ble_un: len:8
+float_add: len:4
+float_sub: len:4
+float_mul: len:4
+float_div: len:4
+float_div_un: len:4
+float_rem: len:16
+float_rem_un: len:16
+float_neg: dest:f src1:f len:4
+float_not: dest:f src1:f len:4
+float_conv_to_i1: dest:i src1:f len:40
+float_conv_to_i2: dest:i src1:f len:40
+float_conv_to_i4: dest:i src1:f len:40
+float_conv_to_i8: dest:l src1:f len:40
+float_conv_to_r4:
+float_conv_to_r8:
+float_conv_to_u4: dest:i src1:f len:40
+float_conv_to_u8: dest:l src1:f len:40
+float_conv_to_u2: dest:i src1:f len:40
+float_conv_to_u1: dest:i src1:f len:40
+float_conv_to_i: dest:i src1:f len:40
+float_conv_to_ovf_i:
+float_conv_to_ovd_u:
+float_add_ovf:
+float_add_ovf_un:
+float_mul_ovf:
+float_mul_ovf_un:
+float_sub_ovf:
+float_sub_ovf_un:
+float_conv_to_ovf_i1_un:
+float_conv_to_ovf_i2_un:
+float_conv_to_ovf_i4_un:
+float_conv_to_ovf_i8_un:
+float_conv_to_ovf_u1_un:
+float_conv_to_ovf_u2_un:
+float_conv_to_ovf_u4_un:
+float_conv_to_ovf_u8_un:
+float_conv_to_ovf_i_un:
+float_conv_to_ovf_u_un:
+float_conv_to_ovf_i1:
+float_conv_to_ovf_u1:
+float_conv_to_ovf_i2:
+float_conv_to_ovf_u2:
+float_conv_to_ovf_i4:
+float_conv_to_ovf_u4:
+float_conv_to_ovf_i8:
+float_conv_to_ovf_u8:
+float_ceq: dest:i src1:f src2:f len:12
+float_cgt: dest:i src1:f src2:f len:12
+float_cgt_un: dest:i src1:f src2:f len:12
+float_clt: dest:i src1:f src2:f len:12
+float_clt_un: dest:i src1:f src2:f len:12
+float_conv_to_u: dest:i src1:f len:36
+handler: len:12
+op_endfilter: src1:i len:12
+aot_const: dest:i len:8
+x86_test_null: src1:i len:4
+x86_compare_membase_reg: src1:b src2:i len:8
+x86_compare_membase_imm: src1:b len:8
+x86_compare_reg_membase: src1:i src2:b len:8
+x86_inc_reg: dest:i src1:i clob:1 len:1
+x86_inc_membase: src1:b len:6
+x86_dec_reg: dest:i src1:i clob:1 len:1
+x86_dec_membase: src1:b len:6
+x86_add_membase_imm: src1:b len:8
+x86_sub_membase_imm: src1:b len:8
+x86_push: src1:i len:1
+x86_push_imm: len:5
+x86_push_membase: src1:b len:6
+x86_push_obj: src1:b len:30
+x86_lea: dest:i src1:i src2:i len:7
+x86_xchg: src1:i src2:i clob:x len:1
+x86_fpop: src1:f len:2
+x86_fp_load_i8: dest:f src1:b len:7
+x86_fp_load_i4: dest:f src1:b len:7
+adc: dest:i src1:i src2:i len:4
+addcc: dest:i src1:i src2:i len:4
+subcc: dest:i src1:i src2:i len:4
+adc_imm: dest:i src1:i len:8
+sbb: dest:i src1:i src2:i len:4
+sbb_imm: dest:i src1:i len:8
+br_reg: src1:i len:8
diff --git a/mono/mini/cpu-pentium.md b/mono/mini/cpu-pentium.md
new file mode 100644 (file)
index 0000000..90e8526
--- /dev/null
@@ -0,0 +1,619 @@
+# x86-class cpu description file
+# this file is read by genmdesc to pruduce a table with all the relevant information
+# about the cpu instructions that may be used by the regsiter allocator, the scheduler
+# and other parts of the arch-dependent part of mini.
+#
+# An opcode name is followed by a colon and optional specifiers.
+# A specifier has a name, a colon and a value. Specifiers are separated by white space.
+# Here is a description of the specifiers valid for this file and their possible values.
+#
+# dest:register       describes the destination register of an instruction
+# src1:register       describes the first source register of an instruction
+# src2:register       describes the second source register of an instruction
+#
+# register may have the following values:
+#      i  integer register
+#      b  base register (used in address references)
+#      f  floating point register
+#      a  EAX register
+#       d  EDX register
+#
+# len:number         describe the maximun length in bytes of the instruction
+# number is a positive integer
+#
+# cost:number        describe how many cycles are needed to complete the instruction (unused)
+#
+# clob:spec          describe if the instruction clobbers registers or has special needs
+#
+# spec can be one of the following characters:
+#      c  clobbers caller-save registers
+#      1  clobbers the first source register
+#      a  EAX is clobbered
+#       d  EAX and EDX are clobbered
+#      s  the src1 operand needs to be in ECX (shift opcodes)
+#      x  both the source operands are clobbered (xchg)
+#
+# flags:spec        describe if the instruction uses or sets the flags (unused)
+#
+# spec can be one of the following chars:
+#      s  sets the flags
+#       u  uses the flags
+#       m  uses and modifies the flags
+#
+# res:spec          describe what units are used in the processor (unused)
+#
+# delay:            describe delay slots (unused)
+#
+# the required specifiers are: len, clob (if registers are clobbered), the registers
+# specifiers if the registers are actually used, flags (when scheduling is implemented).
+#
+# See the code in mini-x86.c for more details on how the specifiers are used.
+#
+nop:
+break: len:1
+ldarg.0:
+ldarg.1:
+ldarg.2:
+ldarg.3:
+ldloc.0:
+ldloc.1:
+ldloc.2:
+ldloc.3:
+stloc.0:
+stloc.1:
+stloc.2:
+stloc.3:
+ldarg.s:
+ldarga.s:
+starg.s:
+ldloc.s:
+ldloca.s:
+stloc.s:
+ldnull:
+ldc.i4.m1:
+ldc.i4.0:
+ldc.i4.1:
+ldc.i4.2:
+ldc.i4.3:
+ldc.i4.4:
+ldc.i4.5:
+ldc.i4.6:
+ldc.i4.7:
+ldc.i4.8:
+ldc.i4.s:
+ldc.i4:
+ldc.i8:
+ldc.r4:
+ldc.r8:
+unused99:
+dup:
+pop:
+jmp: len:32
+call: dest:a clob:c len:8
+calli:
+ret:
+br.s:
+brfalse.s:
+brtrue.s:
+beq.s:
+bge.s:
+bgt.s:
+ble.s:
+blt.s:
+bne.un.s:
+bge.un.s:
+bgt.un.s:
+ble.un.s:
+blt.un.s:
+br: len:5
+brfalse:
+brtrue:
+beq: len:6
+bge: len:6
+bgt: len:6
+ble: len:6
+blt: len:6
+bne.un: len:6
+bge.un: len:6
+bgt.un: len:6
+ble.un: len:6
+blt.un: len:6
+switch:
+ldind.i1: dest:i len:6
+ldind.u1: dest:i len:6
+ldind.i2: dest:i len:6
+ldind.u2: dest:i len:6
+ldind.i4: dest:i len:6
+ldind.u4: dest:i len:6
+ldind.i8:
+ldind.i: dest:i len:6
+ldind.r4:
+ldind.r8:
+ldind.ref: dest:i len:6
+stind.ref: src1:b src2:i
+stind.i1: src1:b src2:i
+stind.i2: src1:b src2:i
+stind.i4: src1:b src2:i
+stind.i8:
+stind.r4: src1:b src2:f
+stind.r8: src1:b src2:f
+add: dest:i src1:i src2:i len:2 clob:1
+sub: dest:i src1:i src2:i len:2 clob:1
+mul: dest:i src1:i src2:i len:3 clob:1
+div: dest:a src1:i src2:i len:15 clob:d
+div.un: dest:a src1:i src2:i len:15 clob:d
+rem: dest:d src1:i src2:i len:15 clob:d
+rem.un: dest:d src1:i src2:i len:15 clob:d
+and: dest:i src1:i src2:i len:2 clob:1
+or: dest:i src1:i src2:i len:2 clob:1
+xor: dest:i src1:i src2:i len:2 clob:1
+shl: dest:i src1:i src2:i clob:s len:2
+shr: dest:i src1:i src2:i clob:s len:2
+shr.un: dest:i src1:i src2:i clob:s len:2
+neg: dest:i src1:i len:2 clob:1
+not: dest:i src1:i len:2 clob:1
+conv.i1: dest:i src1:i len:3
+conv.i2: dest:i src1:i len:3
+conv.i4: dest:i src1:i len:2
+conv.i8:
+conv.r4: dest:f src1:i len:7
+conv.r8: dest:f src1:i len:7
+conv.u4: dest:i src1:i
+conv.u8:
+callvirt:
+cpobj:
+ldobj:
+ldstr:
+newobj:
+castclass:
+isinst:
+conv.r.un:
+unused58:
+unused1:
+unbox:
+throw: src1:i len:6
+ldfld:
+ldflda:
+stfld:
+ldsfld:
+ldsflda:
+stsfld:
+stobj:
+conv.ovf.i1.un:
+conv.ovf.i2.un:
+conv.ovf.i4.un:
+conv.ovf.i8.un:
+conv.ovf.u1.un:
+conv.ovf.u2.un:
+conv.ovf.u4.un:
+conv.ovf.u8.un:
+conv.ovf.i.un:
+conv.ovf.u.un:
+box:
+newarr:
+ldlen:
+ldelema:
+ldelem.i1:
+ldelem.u1:
+ldelem.i2:
+ldelem.u2:
+ldelem.i4:
+ldelem.u4:
+ldelem.i8:
+ldelem.i:
+ldelem.r4:
+ldelem.r8:
+ldelem.ref:
+stelem.i:
+stelem.i1:
+stelem.i2:
+stelem.i4:
+stelem.i8:
+stelem.r4:
+stelem.r8:
+stelem.ref:
+unused2:
+unused3:
+unused4:
+unused5:
+unused6:
+unused7:
+unused8:
+unused9:
+unused10:
+unused11:
+unused12:
+unused13:
+unused14:
+unused15:
+unused16:
+unused17:
+conv.ovf.i1:
+conv.ovf.u1:
+conv.ovf.i2:
+conv.ovf.u2:
+conv.ovf.i4:
+conv.ovf.u4:
+conv.ovf.i8:
+conv.ovf.u8:
+unused50:
+unused18:
+unused19:
+unused20:
+unused21:
+unused22:
+unused23:
+refanyval:
+ckfinite: dest:f src1:f len:22
+unused24:
+unused25:
+mkrefany:
+unused59:
+unused60:
+unused61:
+unused62:
+unused63:
+unused64:
+unused65:
+unused66:
+unused67:
+ldtoken:
+conv.u2: dest:i src1:i len:3
+conv.u1: dest:i src1:i len:3
+conv.i: dest:i src1:i len:3
+conv.ovf.i:
+conv.ovf.u:
+add.ovf:
+add.ovf.un:
+mul.ovf: dest:i src1:i src2:i clob:1 len:9
+# this opcode is handled specially in the code generator
+mul.ovf.un: dest:i src1:i src2:i len:12
+sub.ovf:
+sub.ovf.un:
+endfinally: len:10
+leave:
+leave.s:
+stind.i:
+conv.u: dest:i src1:i len:3
+unused26:
+unused27:
+unused28:
+unused29:
+unused30:
+unused31:
+unused32:
+unused33:
+unused34:
+unused35:
+unused36:
+unused37:
+unused38:
+unused39:
+unused40:
+unused41:
+unused42:
+unused43:
+unused44:
+unused45:
+unused46:
+unused47:
+unused48:
+prefix7:
+prefix6:
+prefix5:
+prefix4:
+prefix3:
+prefix2:
+prefix1:
+prefixref:
+arglist:
+ceq: dest:i len:6
+cgt: dest:i len:6
+cgt.un: dest:i len:6
+clt: dest:i len:6
+clt.un: dest:i len:6
+ldftn:
+ldvirtftn:
+unused56:
+ldarg:
+ldarga:
+starg:
+ldloc:
+ldloca:
+stloc:
+localloc: dest:i src1:i len:30
+unused57:
+endfilter:
+unaligned.:
+volatile.:
+tail.:
+initobj:
+unused68:
+cpblk:
+initblk:
+unused69:
+rethrow:
+unused:
+sizeof:
+refanytype:
+unused52:
+unused53:
+unused54:
+unused55:
+unused70:
+illegal:
+endmac:
+mono_func1:
+mono_proc2:
+mono_proc3:
+mono_free:
+mono_objaddr:
+mono_ldptr:
+mono_vtaddr:
+mono_newobj:
+mono_retobj:
+load:
+ldaddr:
+store:
+phi:
+rename:
+compare: src1:i src2:i len:2
+compare_imm: src1:i len:6
+fcompare: src1:f src2:f clob:a len:9
+lcompare:
+local:
+arg:
+outarg: src1:i len:1
+outarg_imm: len:5
+retarg:
+setret: dest:a src1:i len:2
+setlret: dest:l src1:i src2:i len:4
+checkthis: src1:b len:3
+voidcall: len:8 clob:c
+voidcall_reg: src1:i len:5 clob:c
+voidcall_membase: src1:b len:10 clob:c
+fcall: dest:f len:8 clob:c
+fcall_reg: dest:f src1:i len:5 clob:c
+fcall_membase: dest:f src1:b len:10 clob:c
+lcall: dest:l len:8 clob:c
+lcall_reg: dest:l src1:i len:5 clob:c
+lcall_membase: dest:l src1:b len:10 clob:c
+vcall: len:8 clob:c
+vcall_reg: src1:i len:5 clob:c
+vcall_membase: src1:b len:10 clob:c
+call_reg: dest:i src1:i len:5 clob:c
+call_membase: dest:i src1:b len:10 clob:c
+trap:
+iconst: dest:i len:5
+i8const:
+r4const: dest:f len:6
+r8const: dest:f len:6
+regvar:
+reg:
+regoffset:
+label:
+store_membase_imm: dest:b len:10
+store_membase_reg: dest:b src1:i len:7
+storei1_membase_imm: dest:b len:10
+storei1_membase_reg: dest:b src1:i len:7
+storei2_membase_imm: dest:b len:11
+storei2_membase_reg: dest:b src1:i len:7
+storei4_membase_imm: dest:b len:10
+storei4_membase_reg: dest:b src1:i len:7
+storei8_membase_imm: dest:b 
+storei8_membase_reg: dest:b src1:i 
+storer4_membase_reg: dest:b src1:f len:7
+storer8_membase_reg: dest:b src1:f len:6
+load_membase: dest:i src1:b len:6
+loadi1_membase: dest:i src1:b len:7
+loadu1_membase: dest:i src1:b len:7
+loadi2_membase: dest:i src1:b len:7
+loadu2_membase: dest:i src1:b len:7
+loadi4_membase: dest:i src1:b len:6
+loadu4_membase: dest:i src1:b len:6
+loadi8_membase: dest:i src1:b
+loadr4_membase: dest:f src1:b len:6
+loadr8_membase: dest:f src1:b len:6
+loadu4_mem: dest:i len:9
+move: dest:i src1:i len:2
+add_imm: dest:i src1:i len:6 clob:1
+sub_imm: dest:i src1:i len:6 clob:1
+mul_imm: dest:i src1:i len:6
+# there is no actual support for division or reminder by immediate
+# we simulate them, though (but we need to change the burg rules 
+# to allocate a symbolic reg for src2)
+div_imm: dest:a src1:i src2:i len:15 clob:d
+div_un_imm: dest:a src1:i src2:i len:15 clob:d
+rem_imm: dest:d src1:i src2:i len:15 clob:d
+rem_un_imm: dest:d src1:i src2:i len:15 clob:d
+and_imm: dest:i src1:i len:6 clob:1
+or_imm: dest:i src1:i len:6 clob:1
+xor_imm: dest:i src1:i len:6 clob:1
+shl_imm: dest:i src1:i len:6 clob:1
+shr_imm: dest:i src1:i len:6 clob:1
+shr_un_imm: dest:i src1:i len:6 clob:1
+cond_exc_eq: len:6
+cond_exc_ne_un: len:6
+cond_exc_lt: len:6
+cond_exc_lt_un: len:6
+cond_exc_gt: len:6
+cond_exc_gt_un: len:6
+cond_exc_ge: len:6
+cond_exc_ge_un: len:6
+cond_exc_le: len:6
+cond_exc_le_un: len:6
+cond_exc_ov: len:6
+cond_exc_no: len:6
+cond_exc_c: len:6
+cond_exc_nc: len:6
+long_add:
+long_sub:
+long_mul:
+long_div:
+long_div_un:
+long_rem:
+long_rem_un:
+long_and:
+long_or:
+long_xor:
+long_shl:
+long_shr:
+long_shr_un:
+long_neg:
+long_not:
+long_conv_to_i1:
+long_conv_to_i2:
+long_conv_to_i4:
+long_conv_to_i8:
+long_conv_to_r4:
+long_conv_to_r8:
+long_conv_to_u4:
+long_conv_to_u8:
+long_conv_to_u2:
+long_conv_to_u1:
+long_conv_to_i:
+long_conv_to_ovf_i: dest:i src1:i src2:i len:30
+long_conv_to_ovf_u:
+long_add_ovf:
+long_add_ovf_un:
+long_mul_ovf: 
+long_mul_ovf_un:
+long_sub_ovf:
+long_sub_ovf_un:
+long_conv_to_ovf_i1_un:
+long_conv_to_ovf_i2_un:
+long_conv_to_ovf_i4_un:
+long_conv_to_ovf_i8_un:
+long_conv_to_ovf_u1_un:
+long_conv_to_ovf_u2_un:
+long_conv_to_ovf_u4_un:
+long_conv_to_ovf_u8_un:
+long_conv_to_ovf_i_un:
+long_conv_to_ovf_u_un:
+long_conv_to_ovf_i1:
+long_conv_to_ovf_u1:
+long_conv_to_ovf_i2:
+long_conv_to_ovf_u2:
+long_conv_to_ovf_i4:
+long_conv_to_ovf_u4:
+long_conv_to_ovf_i8:
+long_conv_to_ovf_u8:
+long_ceq:
+long_cgt:
+long_cgt_un:
+long_clt:
+long_clt_un:
+long_conv_to_r_un: dest:f src1:i src2:i len:37 
+long_conv_to_u:
+long_shr_imm:
+long_shr_un_imm:
+long_shl_imm:
+long_add_imm:
+long_sub_imm:
+long_beq:
+long_bne_un:
+long_blt:
+long_blt_un:
+long_bgt:
+long_btg_un:
+long_bge:
+long_bge_un:
+long_ble:
+long_ble_un:
+float_beq: len:12
+float_bne_un: len:12
+float_blt: len:12
+float_blt_un: len:20
+float_bgt: len:12
+float_btg_un: len:20
+float_bge: len:12
+float_bge_un: len:12
+float_ble: len:12
+float_ble_un: len:12
+float_add: len:2
+float_sub: len:2
+float_mul: len:2
+float_div: len:2
+float_div_un: len:2
+float_rem: len:17
+float_rem_un: len:17
+float_neg: dest:f src1:f len:2
+float_not: dest:f src1:f len:2
+float_conv_to_i1: dest:i src1:f len:39
+float_conv_to_i2: dest:i src1:f len:39
+float_conv_to_i4: dest:i src1:f len:39
+float_conv_to_i8: dest:l src1:f len:39
+float_conv_to_r4:
+float_conv_to_r8:
+float_conv_to_u4: dest:i src1:f len:39
+float_conv_to_u8: dest:l src1:f len:39
+float_conv_to_u2: dest:i src1:f len:39
+float_conv_to_u1: dest:i src1:f len:39
+float_conv_to_i: dest:i src1:f len:39
+float_conv_to_ovf_i: dest:a src1:f len:30
+float_conv_to_ovd_u: dest:a src1:f len:30
+float_add_ovf:
+float_add_ovf_un:
+float_mul_ovf:
+float_mul_ovf_un:
+float_sub_ovf:
+float_sub_ovf_un:
+float_conv_to_ovf_i1_un:
+float_conv_to_ovf_i2_un:
+float_conv_to_ovf_i4_un:
+float_conv_to_ovf_i8_un:
+float_conv_to_ovf_u1_un:
+float_conv_to_ovf_u2_un:
+float_conv_to_ovf_u4_un:
+float_conv_to_ovf_u8_un:
+float_conv_to_ovf_i_un:
+float_conv_to_ovf_u_un:
+float_conv_to_ovf_i1:
+float_conv_to_ovf_u1:
+float_conv_to_ovf_i2:
+float_conv_to_ovf_u2:
+float_conv_to_ovf_i4:
+float_conv_to_ovf_u4:
+float_conv_to_ovf_i8:
+float_conv_to_ovf_u8:
+float_ceq: dest:i src1:f src2:f len:25
+float_cgt: dest:i src1:f src2:f len:25
+float_cgt_un: dest:i src1:f src2:f len:37
+float_clt: dest:i src1:f src2:f len:25
+float_clt_un: dest:i src1:f src2:f len:32
+float_conv_to_u: dest:i src1:f len:36
+handler: len:10
+op_endfilter: src1:i len:10
+aot_const: dest:i len:5
+x86_test_null: src1:i len:2
+x86_compare_membase_reg: src1:b src2:i len:6
+x86_compare_membase_imm: src1:b len:10
+x86_compare_reg_membase: src1:i src2:b len:6
+x86_inc_reg: dest:i src1:i clob:1 len:1
+x86_inc_membase: src1:b len:6
+x86_dec_reg: dest:i src1:i clob:1 len:1
+x86_dec_membase: src1:b len:6
+x86_add_membase_imm: src1:b len:8
+x86_sub_membase_imm: src1:b len:8
+x86_push: src1:i len:1
+x86_push_imm: len:5
+x86_push_membase: src1:b len:6
+x86_push_obj: src1:b len:30
+x86_lea: dest:i src1:i src2:i len:7
+x86_xchg: src1:i src2:i clob:x len:1
+x86_fpop: src1:f len:2
+x86_fp_load_i8: dest:f src1:b len:7
+x86_fp_load_i4: dest:f src1:b len:7
+adc: dest:i src1:i src2:i len:2 clob:1
+addcc: dest:i src1:i src2:i len:2 clob:1
+subcc: dest:i src1:i src2:i len:2 clob:1
+adc_imm: dest:i src1:i len:6 clob:1
+sbb: dest:i src1:i src2:i len:2 clob:1
+sbb_imm: dest:i src1:i len:6 clob:1
+br_reg: src1:i len:2
+sin: dest:f src1:f len:2
+cos: dest:f src1:f len:2
+abs: dest:f src1:f len:2
+tan: dest:f src1:f len:2
+atan: dest:f src1:f len:2
+sqrt: dest:f src1:f len:2
diff --git a/mono/mini/debug-dwarf2.c b/mono/mini/debug-dwarf2.c
new file mode 100644 (file)
index 0000000..666f2eb
--- /dev/null
@@ -0,0 +1,1400 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/jit/codegen.h>
+#include <mono/jit/debug.h>
+
+#include "debug-private.h"
+
+#define ABBREV_COMPILE_UNIT            1
+#define ABBREV_SUBPROGRAM              2
+#define ABBREV_SUBPROGRAM_RETVAL       3
+#define ABBREV_BASE_TYPE               4
+#define ABBREV_FORMAL_PARAMETER                5
+#define ABBREV_PARAMETER               6
+#define ABBREV_LOCAL_VARIABLE          7
+#define ABBREV_STRUCT_TYPE             8
+#define ABBREV_STRUCT_MEMBER           9
+#define ABBREV_STRUCT_ACCESS           10
+#define ABBREV_ENUM_TYPE               11
+#define ABBREV_ENUM_VALUE              12
+#define ABBREV_ENUM_VALUE_UNSIGNED     13
+#define ABBREV_ENUM_VALUE_SIGNED       14
+#define ABBREV_CLASS_TYPE              15
+#define ABBREV_CLASS_INHERITANCE       16
+#define ABBREV_POINTER_TYPE            17
+#define ABBREV_CLASS_METHOD            18
+#define ABBREV_CLASS_METHOD_RETVAL     19
+#define ABBREV_ARTIFICIAL_PARAMETER    20
+#define ABBREV_SIMPLE_ARRAY            21
+#define ABBREV_ARRAY                   22
+#define ABBREV_SUBRANGE                        23
+
+// The following constants are defined in the DWARF 2 specification
+#define DW_TAG_array_type              0x01
+#define DW_TAG_class_type              0x02
+#define DW_TAG_enumeration_type                0x04
+#define DW_TAG_formal_parameter                0x05
+#define DW_TAG_member                  0x0d
+#define DW_TAG_pointer_type            0x0f
+#define DW_TAG_compile_unit            0x11
+#define DW_TAG_structure_type          0x13
+#define DW_TAG_inheritance             0x1c
+#define DW_TAG_subrange_type           0x21
+#define DW_TAG_access_declaration      0x23
+#define DW_TAG_base_type               0x24
+#define DW_TAG_enumerator              0x28
+#define DW_TAG_subprogram              0x2e
+#define DW_TAG_variable                        0x34
+
+#define DW_CHILDREN_no                 0
+#define DW_CHILDREN_yes                        1
+
+#define DW_AT_location                 0x02
+#define DW_AT_name                     0x03
+#define DW_AT_byte_size                        0x0b
+#define DW_AT_stmt_list                        0x10
+#define DW_AT_low_pc                   0x11
+#define DW_AT_high_pc                  0x12
+#define DW_AT_language                 0x13
+#define DW_AT_const_value              0x1c
+#define DW_AT_lower_bound              0x22
+#define DW_AT_producer                 0x25
+#define DW_AT_start_scope              0x2c
+#define DW_AT_upper_bound              0x2f
+#define DW_AT_accessibility            0x32
+#define DW_AT_artificial               0x34
+#define DW_AT_calling_convention       0x36
+#define DW_AT_count                    0x37
+#define DW_AT_data_member_location     0x38
+#define DW_AT_encoding                 0x3e
+#define DW_AT_external                 0x3f
+#define DW_AT_type                     0x49
+#define DW_AT_virtuality               0x4c
+#define DW_AT_vtable_elem_location     0x4d
+
+/* Martin Baulig's extensions. */
+#define DW_AT_end_scope                        0x2121
+
+#define DW_FORM_addr                   0x01
+#define DW_FORM_block4                 0x04
+#define DW_FORM_data2                  0x05
+#define DW_FORM_data4                  0x06
+#define DW_FORM_data8                  0x07
+#define DW_FORM_string                 0x08
+#define DW_FORM_data1                  0x0b
+#define DW_FORM_flag                   0x0c
+#define DW_FORM_sdata                  0x0d
+#define DW_FORM_udata                  0x0f
+#define DW_FORM_ref4                   0x13
+
+#define DW_ATE_void                    0x00
+#define DW_ATE_address                 0x01
+#define DW_ATE_boolean                 0x02
+#define DW_ATE_complex_float           0x03
+#define DW_ATE_float                   0x04
+#define DW_ATE_signed                  0x05
+#define DW_ATE_signed_char             0x06
+#define DW_ATE_unsigned                        0x07
+#define DW_ATE_unsigned_char           0x08
+
+#define DW_OP_const1u                  0x08
+#define DW_OP_const1s                  0x09
+#define DW_OP_constu                   0x10
+#define DW_OP_consts                   0x11
+#define DW_OP_plus                     0x22
+#define DW_OP_reg0                     0x50
+#define DW_OP_breg0                    0x70
+#define DW_OP_fbreg                    0x91
+#define DW_OP_piece                    0x93
+
+#define DW_CC_normal                   1
+#define DW_CC_program                  2
+#define DW_CC_nocall                   3
+
+#define DW_ACCESS_public               1
+#define DW_ACCESS_protected            2
+#define DW_ACCESS_private              3
+
+#define DW_VIRTUALITY_none             0
+#define DW_VIRTUALITY_virtual          1
+#define DW_VIRTUALITY_pure_virtual     2
+
+#define DW_LANG_C_plus_plus            0x04
+#define DW_LANG_Java                   0x0b
+// This is NOT in the standard, we're using Java for the moment. */
+#define DW_LANG_C_sharp                        DW_LANG_C_plus_plus
+
+#define DW_LNS_extended_op             0
+#define DW_LNS_copy                    1
+#define DW_LNS_advance_pc              2
+#define DW_LNS_advance_line            3
+#define DW_LNS_set_file                        4
+#define DW_LNS_set_column              5
+#define DW_LNS_negate_stmt             6
+#define DW_LNS_set_basic_block         7
+#define DW_LNS_const_add_pc            8
+#define DW_LNS_fixed_advance_pc                9
+
+#define DW_LNE_end_sequence            1
+#define DW_LNE_set_address             2
+#define DW_LNE_define_file             3
+
+
+static const int line_base = 1, line_range = 8, opcode_base = 10;
+static const int standard_opcode_sizes [10] = {
+    0, 0, 1, 1, 1, 1, 0, 0, 0, 0
+};
+
+static void
+dwarf2_write_byte (FILE *f, int byte)
+{
+       fprintf (f, "\t.byte %d\n", byte);
+}
+
+static void
+dwarf2_write_2byte (FILE *f, int word)
+{
+       fprintf (f, "\t.word %d\n", word);
+}
+
+static void
+dwarf2_write_pair (FILE *f, int a, int b)
+{
+       fprintf (f, "\t.uleb128 %d, %d\n", a, b);
+}
+
+static void
+dwarf2_write_long (FILE *f, unsigned long value)
+{
+       fprintf (f, "\t.long %lu\n", value);
+}
+
+static void
+dwarf2_write_address (FILE *f, const void *address)
+{
+       fprintf (f, "\t.long 0x%lx\n", (long) address);
+}
+
+static void
+dwarf2_write_string (FILE *f, const char *string)
+{
+       fprintf (f, "\t.string \"%s\"\n", string);
+}
+
+static void
+dwarf2_write_sleb128 (FILE *f, long value)
+{
+       fprintf (f, "\t.sleb128 %ld\n", value);
+}
+
+static void
+dwarf2_write_uleb128 (FILE *f, unsigned long value)
+{
+       fprintf (f, "\t.uleb128 %lu\n", value);
+}
+
+static void
+dwarf2_write_section_start (FILE *f, const char *section)
+{
+       fprintf (f, "\t.section .%s\n", section);
+}
+
+static void
+dwarf2_write_label (FILE *f, const char *label)
+{
+       fprintf (f, ".L_%s:\n", label);
+}
+
+static void
+dwarf2_write_section_size (FILE *f, const char *start_label, const char *end_label)
+{
+       fprintf (f, "\t.long .L_%s - .L_%s\n", end_label, start_label);
+}
+
+static void
+dwarf2_write_ref4 (FILE *f, const char *target_label)
+{
+       fprintf (f, "\t.long .L_%s\n", target_label);
+}
+
+static void
+dwarf2_write_type_ref (FILE *f, unsigned long type_index)
+{
+       fprintf (f, "\t.long .L_TYPE_%lu - .L_debug_info_b\n", type_index);
+}
+
+static void
+dwarf2_write_type_ptr_ref (FILE *f, unsigned long idx)
+{
+       fprintf (f, "\t.long .L_TYPE_PTR_%lu - .L_debug_info_b\n", idx);
+}
+
+static void
+dwarf2_write_relative_ref (FILE *f, const gchar *name, unsigned long idx)
+{
+       fprintf (f, "\t.long .L_%s_%lu - .L_debug_info_b\n", name, idx);
+}
+
+static void
+dwarf2_write_dw_lns_copy (FILE *f)
+{
+       dwarf2_write_byte (f, DW_LNS_copy);
+}
+
+static void
+dwarf2_write_dw_lns_advance_pc (FILE *f, unsigned value)
+{
+       dwarf2_write_byte (f, DW_LNS_advance_pc);
+       dwarf2_write_uleb128 (f, value);
+}
+
+static void
+dwarf2_write_dw_lns_advance_line (FILE *f, int value)
+{
+       dwarf2_write_byte (f, DW_LNS_advance_line);
+       dwarf2_write_sleb128 (f, value);
+}
+
+static void
+dwarf2_write_dw_lns_set_file (FILE *f, unsigned value)
+{
+       dwarf2_write_byte (f, DW_LNS_set_file);
+       dwarf2_write_uleb128 (f, value + 1);
+}
+
+static void
+dwarf2_write_dw_lns_negate_stmt (FILE *f)
+{
+       dwarf2_write_byte (f, DW_LNS_negate_stmt);
+}
+
+#if 0 /* never used */
+static void
+dwarf2_write_dw_lns_set_basic_block (FILE *f)
+{
+       dwarf2_write_byte (f, DW_LNS_set_basic_block);
+}
+#endif
+
+static void
+dwarf2_write_dw_lne_end_sequence (FILE *f)
+{
+       dwarf2_write_byte (f, 0);
+       dwarf2_write_byte (f, 1);
+       dwarf2_write_byte (f, DW_LNE_end_sequence);
+}
+
+static void
+dwarf2_write_dw_lne_set_address (FILE *f, const void *address)
+{
+       dwarf2_write_byte (f, 0);
+       dwarf2_write_byte (f, sizeof (address) + 1);
+       dwarf2_write_byte (f, DW_LNE_set_address);
+       dwarf2_write_address (f, address);
+}
+
+static void
+dwarf2_write_base_type (MonoDebugHandle *debug, int idx,
+                       int type, int size, const gchar *name)
+{
+       char buffer [BUFSIZ];
+
+       sprintf (buffer, "TYPE_%d", idx);
+       dwarf2_write_label (debug->f, buffer);
+       // DW_TAG_basic_type
+       dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE);
+       dwarf2_write_string (debug->f, name);
+       dwarf2_write_byte (debug->f, type);
+       dwarf2_write_byte (debug->f, size);
+}
+
+static void
+dwarf2_write_enum_value (MonoDebugHandle *debug, MonoClass *klass, int idx)
+{
+       const void *ptr;
+       guint32 field_index = idx + klass->field.first;
+       guint32 crow;
+
+       crow = mono_metadata_get_constant_index (klass->image, MONO_TOKEN_FIELD_DEF | (field_index + 1));
+       if (!crow) {
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_long (debug->f, 0);
+               return;
+       }
+
+       crow = mono_metadata_decode_row_col (&klass->image->tables [MONO_TABLE_CONSTANT], crow-1,
+                                            MONO_CONSTANT_VALUE);
+
+       ptr = 1 + mono_metadata_blob_heap (klass->image, crow);
+
+       switch (klass->enum_basetype->type) {
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_U1:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_uleb128 (debug->f, *(guint8 *) ptr);
+               break;
+       case MONO_TYPE_I1:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_sleb128 (debug->f, *(gint8 *) ptr);
+               break;
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_U2:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_uleb128 (debug->f, *(guint16 *) ptr);
+               break;
+       case MONO_TYPE_I2:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_sleb128 (debug->f, *(gint16 *) ptr);
+               break;
+       case MONO_TYPE_U4:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_uleb128 (debug->f, *(guint32 *) ptr);
+               break;
+       case MONO_TYPE_I4:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_sleb128 (debug->f, *(gint32 *) ptr);
+               break;
+       case MONO_TYPE_U8:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_uleb128 (debug->f, *(guint64 *) ptr);
+               break;
+       case MONO_TYPE_I8:
+               dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED);
+               dwarf2_write_string (debug->f, klass->fields [idx].name);
+               dwarf2_write_sleb128 (debug->f, *(gint64 *) ptr);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+dwarf2_write_enum_type (MonoDebugHandle *debug, MonoClass *klass)
+{
+       int i;
+
+       // DW_TAG_enumeration_type
+       dwarf2_write_byte (debug->f, ABBREV_ENUM_TYPE);
+       dwarf2_write_string (debug->f, klass->name);
+       dwarf2_write_long (debug->f, klass->instance_size - sizeof (MonoObject));
+
+       for (i = 0; i < klass->field.count; i++) {
+               if (klass->fields [i].type->attrs & FIELD_ATTRIBUTE_LITERAL)
+                       dwarf2_write_enum_value (debug, klass, i);
+       }
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_enumeration_type ends here
+}
+
+static void
+dwarf2_write_class_field (MonoDebugHandle *debug, MonoClass *klass, int idx,
+                         int type_index, int start_offset)
+{
+    MonoClass *subclass = mono_class_from_mono_type (klass->fields [idx].type);
+    char start [BUFSIZ], end [BUFSIZ];
+    static long label_index = 0;
+
+    // Don't include any static fields, they aren't supported yet.
+    // If a struct contains a static field which has the same type as
+    // the struct itself, we'd get a recursion loop there.
+    if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_STATIC)
+           return;
+
+    sprintf (start, "DSF1_%ld", ++label_index);
+    sprintf (end, "DSF2_%ld", label_index);
+
+    // DW_TAG_member
+    dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+    dwarf2_write_string (debug->f, klass->fields [idx].name);
+    if (!subclass->valuetype)
+       dwarf2_write_type_ptr_ref (debug->f, type_index);
+    else
+       dwarf2_write_type_ref (debug->f, type_index);
+
+    if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_PRIVATE)
+       dwarf2_write_byte (debug->f, DW_ACCESS_private);
+    else if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_FAMILY)
+       dwarf2_write_byte (debug->f, DW_ACCESS_protected);
+    else
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+
+    dwarf2_write_section_size (debug->f, start, end);
+    dwarf2_write_label (debug->f, start);
+    dwarf2_write_byte (debug->f, DW_OP_constu);
+    dwarf2_write_uleb128 (debug->f, klass->fields [idx].offset - start_offset);
+    dwarf2_write_label (debug->f, end);
+
+    dwarf2_write_long (debug->f, subclass->instance_size);
+}
+
+static void
+dwarf2_write_class_method (MonoDebugHandle *debug, MonoClass *klass, MonoMethod *method)
+{
+       MonoType *ret_type = NULL;
+       gchar **names;
+       int i;
+
+       if (!MONO_TYPE_IS_VOID (method->signature->ret))
+               ret_type = method->signature->ret;
+
+       // DW_TAG_subprogram
+       if (ret_type)
+               dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD_RETVAL);
+       else
+               dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD);
+       dwarf2_write_string (debug->f, method->name);
+
+       if (method->flags & METHOD_ATTRIBUTE_PUBLIC)
+               dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       else if (method->flags & METHOD_ATTRIBUTE_PRIVATE)
+               dwarf2_write_byte (debug->f, DW_ACCESS_private);
+       else
+               dwarf2_write_byte (debug->f, DW_ACCESS_protected);
+
+       if (method->flags & METHOD_ATTRIBUTE_VIRTUAL)
+               dwarf2_write_byte (debug->f, DW_VIRTUALITY_pure_virtual);
+       else
+               dwarf2_write_byte (debug->f, DW_VIRTUALITY_none);
+
+       dwarf2_write_byte (debug->f, DW_CC_nocall);
+
+       if (ret_type) {
+               MonoClass *k = mono_class_from_mono_type (ret_type);
+               int type_index = mono_debug_get_type (debug, k);
+               dwarf2_write_type_ref (debug->f, type_index);
+       }
+
+       if (method->signature->hasthis) {
+               int type_index = mono_debug_get_type (debug, klass);
+
+               dwarf2_write_byte (debug->f, ABBREV_ARTIFICIAL_PARAMETER);
+               dwarf2_write_string (debug->f, "this");
+               dwarf2_write_type_ptr_ref (debug->f, type_index);
+               dwarf2_write_byte (debug->f, 1);
+       }
+
+       names = g_new (char *, method->signature->param_count);
+       mono_method_get_param_names (method, (const char **) names);
+
+       for (i = 0; i < method->signature->param_count; i++) {
+               MonoType *subtype = method->signature->params [i];
+               MonoClass *subklass = mono_class_from_mono_type (subtype);
+               int type_index = mono_debug_get_type (debug, subklass);
+
+               // DW_TAG_formal_parameter
+               dwarf2_write_byte (debug->f, ABBREV_FORMAL_PARAMETER);
+               dwarf2_write_string (debug->f, names [i]);
+               if (subklass->valuetype)
+                       dwarf2_write_type_ref (debug->f, type_index);
+               else
+                       dwarf2_write_type_ptr_ref (debug->f, type_index);
+       }
+
+       g_free (names);
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_subprogram ends here
+}
+
+static void
+dwarf2_write_struct_type (MonoDebugHandle *debug, MonoClass *klass)
+{
+       guint32 *idxs;
+       int i;
+
+       idxs = g_new0 (guint32, klass->field.last - klass->field.first + 1);
+       for (i = 0; i < klass->field.count; i++) {
+               MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
+               idxs [i] = mono_debug_get_type (debug, subclass);
+       }
+
+       // DW_TAG_structure_type
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE);
+       dwarf2_write_string (debug->f, klass->name);
+       dwarf2_write_long (debug->f, klass->instance_size - sizeof (MonoObject));
+
+       for (i = 0; i < klass->field.count; i++)
+               dwarf2_write_class_field (debug, klass, i, idxs [i], sizeof (MonoObject));
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_structure_type ends here
+
+       g_free (idxs);
+}
+
+static void
+dwarf2_write_class_type (MonoDebugHandle *debug, MonoClass *klass)
+{
+       guint32 *idxs;
+       int i;
+
+       idxs = g_new0 (guint32, klass->field.last - klass->field.first + 1);
+       for (i = 0; i < klass->field.count; i++) {
+               MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
+               idxs [i] = mono_debug_get_type (debug, subclass);
+       }
+
+       // DW_TAG_structure_type
+       dwarf2_write_byte (debug->f, ABBREV_CLASS_TYPE);
+       dwarf2_write_string (debug->f, klass->name);
+       dwarf2_write_long (debug->f, klass->instance_size);
+       if (klass->flags & TYPE_ATTRIBUTE_PUBLIC)
+               dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       else
+               dwarf2_write_byte (debug->f, DW_ACCESS_private);
+
+       if (klass->parent && klass->parent->byval_arg.type == MONO_TYPE_CLASS) {
+               guint32 parent_index = mono_debug_get_type (debug, klass->parent);
+
+               // DW_TAG_inheritance
+               dwarf2_write_byte (debug->f, ABBREV_CLASS_INHERITANCE);
+               dwarf2_write_type_ref (debug->f, parent_index);
+               if (klass->parent->flags & TYPE_ATTRIBUTE_PUBLIC)
+                       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+               else
+                       dwarf2_write_byte (debug->f, DW_ACCESS_private);
+       }
+
+       for (i = 0; i < klass->field.count; i++)
+               dwarf2_write_class_field (debug, klass, i, idxs [i], 0);
+
+       for (i = 0; i < klass->method.count; i++) {
+               if (!strcmp (klass->methods [i]->name, ".ctor"))
+                       continue;
+
+               dwarf2_write_class_method (debug, klass, klass->methods [i]);
+       }
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_class_type ends here
+
+       g_free (idxs);
+}
+
+static void
+dwarf2_write_array (MonoDebugHandle *debug, const gchar *name, MonoClass *element_class,
+                   int rank, int idx)
+{
+       unsigned long uint32_index = mono_debug_get_type (debug, mono_defaults.uint32_class);
+       char buffer [BUFSIZ];
+       MonoArray array;
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE);
+       dwarf2_write_string (debug->f, name);
+       dwarf2_write_long (debug->f, sizeof (MonoArray));
+
+       // DW_TAG_structure_type
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_string (debug->f, "max_length");
+       dwarf2_write_type_ref (debug->f, uint32_index);
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       dwarf2_write_long (debug->f, 2);
+       dwarf2_write_byte (debug->f, DW_OP_const1u);
+       dwarf2_write_byte (debug->f, (guchar *) &array.max_length - (guchar *) &array);
+       dwarf2_write_long (debug->f, 4);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_string (debug->f, "bounds");
+       dwarf2_write_relative_ref (debug->f, "ARRAY_BOUNDS_PTR", idx);
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       dwarf2_write_long (debug->f, 2);
+       dwarf2_write_byte (debug->f, DW_OP_const1u);
+       dwarf2_write_byte (debug->f, (guchar *) &array.bounds - (guchar *) &array);
+       dwarf2_write_long (debug->f, 4);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_string (debug->f, "vector");
+       dwarf2_write_relative_ref (debug->f, "ARRAY_PTR", idx);
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       dwarf2_write_long (debug->f, 2);
+       dwarf2_write_byte (debug->f, DW_OP_const1u);
+       dwarf2_write_byte (debug->f, (guchar *) &array.vector - (guchar *) &array);
+       dwarf2_write_long (debug->f, 4);
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_structure_type ends here
+
+       sprintf (buffer, "ARRAY_BOUNDS_PTR_%u", idx);
+       dwarf2_write_label (debug->f, buffer);
+
+       // DW_TAG_pointer_type
+       dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE);
+       dwarf2_write_relative_ref (debug->f, "ARRAY_BOUNDS", idx);
+
+       sprintf (buffer, "ARRAY_BOUNDS_%u", idx);
+       dwarf2_write_label (debug->f, buffer);
+
+       // DW_TAG_array_type
+       dwarf2_write_byte (debug->f, ABBREV_ARRAY);
+       dwarf2_write_string (debug->f, name);
+       dwarf2_write_type_ref (debug->f, uint32_index);
+       dwarf2_write_long (debug->f, rank * 2);
+
+       // DW_TAG_subrange_type
+       dwarf2_write_byte (debug->f, ABBREV_SUBRANGE);
+       dwarf2_write_long (debug->f, 0);
+       dwarf2_write_long (debug->f, rank-1);
+       dwarf2_write_long (debug->f, rank);
+
+       // DW_TAG_subrange_type
+       dwarf2_write_byte (debug->f, ABBREV_SUBRANGE);
+       dwarf2_write_long (debug->f, 0);
+       dwarf2_write_long (debug->f, 1);
+       dwarf2_write_long (debug->f, 2);
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_array_type ends here
+
+       sprintf (buffer, "ARRAY_PTR_%u", idx);
+       dwarf2_write_label (debug->f, buffer);
+
+       // DW_TAG_array_type
+       dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY);
+       dwarf2_write_string (debug->f, name);
+       if (element_class->valuetype)
+               dwarf2_write_type_ref (debug->f, mono_debug_get_type (debug, element_class));
+       else
+               dwarf2_write_type_ptr_ref (debug->f, mono_debug_get_type (debug, element_class));
+}
+
+static void
+dwarf2_write_array_type (MonoDebugHandle *debug, MonoClass *klass, int idx)
+{
+       char buffer [BUFSIZ], *name;
+       int i;
+
+       buffer[0] = '\0';
+       for (i = 0; i < klass->rank; i++)
+               strcat (buffer, "[]");
+
+       name = g_strdup_printf ("%s%s", klass->element_class->name, buffer);
+
+       dwarf2_write_array (debug, name, klass->element_class, klass->rank, idx);
+
+       g_free (name);
+}
+
+static void
+dwarf2_write_string_type (MonoDebugHandle *debug, MonoClass *klass, int idx)
+{
+       unsigned long uint32_index = mono_debug_get_type (debug, mono_defaults.uint32_class);
+       char buffer [BUFSIZ];
+       MonoString string;
+
+       // DW_TAG_structure_type
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE);
+       dwarf2_write_string (debug->f, klass->name);
+       dwarf2_write_long (debug->f, sizeof (MonoString));
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_string (debug->f, "length");
+       dwarf2_write_type_ref (debug->f, uint32_index);
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       dwarf2_write_long (debug->f, 2);
+       dwarf2_write_byte (debug->f, DW_OP_const1u);
+       dwarf2_write_byte (debug->f, (guchar *) &string.length - (guchar *) &string);
+       dwarf2_write_long (debug->f, 4);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_string (debug->f, "chars");
+       dwarf2_write_relative_ref (debug->f, "CHARS", idx);
+       dwarf2_write_byte (debug->f, DW_ACCESS_public);
+       dwarf2_write_long (debug->f, 2);
+       dwarf2_write_byte (debug->f, DW_OP_const1u);
+       dwarf2_write_byte (debug->f, (guchar *) &string.chars - (guchar *) &string);
+       dwarf2_write_long (debug->f, 4);
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_structure_type ends here
+
+       sprintf (buffer, "CHARS_%u", idx);
+       dwarf2_write_label (debug->f, buffer);
+
+       dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY);
+       dwarf2_write_string (debug->f, "Char[]");
+       dwarf2_write_type_ref (debug->f, mono_debug_get_type (debug, mono_defaults.char_class));
+}
+
+static void
+dwarf2_write_class (MonoDebugHandle *debug, MonoClass *klass, int idx)
+{
+       char buffer [BUFSIZ];
+       int print = 0;
+
+       if (!strncmp (klass->name, "My", 2)) {
+               g_message (G_STRLOC ": %s - %s - %x", klass->name_space, klass->name, klass->flags);
+               print = 1;
+               // G_BREAKPOINT ();
+       }
+
+       if (!klass->valuetype) {
+               sprintf (buffer, "TYPE_PTR_%u", idx);
+               dwarf2_write_label (debug->f, buffer);
+
+               // DW_TAG_pointer_type
+               dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE);
+               dwarf2_write_type_ref (debug->f, idx);
+       }
+
+       sprintf (buffer, "TYPE_%u", idx);
+       dwarf2_write_label (debug->f, buffer);
+
+       if (klass->enumtype) {
+               dwarf2_write_enum_type (debug, klass);
+               return;
+       }
+
+       switch (klass->byval_arg.type) {
+       case MONO_TYPE_VALUETYPE:
+               dwarf2_write_struct_type (debug, klass);
+               break;
+       case MONO_TYPE_CLASS:
+               dwarf2_write_class_type (debug, klass);
+               break;
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:
+               dwarf2_write_array_type (debug, klass, idx);
+               break;
+       case MONO_TYPE_STRING:
+               dwarf2_write_string_type (debug, klass, idx);
+               break;
+       default:
+#if 0
+               g_message (G_STRLOC ": %s.%s - 0x%x - 0x%x", klass->name_space, klass->name,
+                          klass->byval_arg.type, klass->flags);
+#endif
+
+               // DW_TAG_basic_type
+               dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE);
+               dwarf2_write_string (debug->f, klass->name);
+               dwarf2_write_byte (debug->f, DW_ATE_address);
+               dwarf2_write_byte (debug->f, 0);
+               break;
+       }
+}
+
+static void
+dwarf2_write_variable_location (MonoDebugHandle *debug, MonoDebugVarInfo *var)
+{
+       switch (var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) {
+       case MONO_DEBUG_VAR_ADDRESS_MODE_STACK:
+               /*
+                * Variable is on the stack.
+                *
+                * If `index' is zero, use the normal frame register.  Otherwise, bits
+                * 0..4 of `index' contain the frame register.
+                *
+                */
+
+               if (!var->index)
+                       /* Use the normal frame register (%ebp on the i386). */
+                       dwarf2_write_byte (debug->f, DW_OP_fbreg);
+               else
+                       /* Use a custom frame register. */
+                       dwarf2_write_byte (debug->f, DW_OP_breg0 + (var->index & 0x001f));
+               dwarf2_write_sleb128 (debug->f, var->offset);
+               break;
+
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
+               /*
+                * Variable is in the register whose number is contained in bits 0..4
+                * of `index'.
+                *
+                */
+               dwarf2_write_byte (debug->f, DW_OP_reg0 + (var->index & 0x001f));
+               if (var->offset) {
+                       dwarf2_write_byte (debug->f, DW_OP_consts);
+                       dwarf2_write_sleb128 (debug->f, var->offset);
+                       dwarf2_write_byte (debug->f, DW_OP_plus);
+               }
+               break;
+
+       case MONO_DEBUG_VAR_ADDRESS_MODE_TWO_REGISTERS:
+               /*
+                * Variable is in two registers whose numbers are in bits 0..4 and 5..9 of 
+                * the `index' field.
+                */
+               dwarf2_write_byte (debug->f, DW_OP_reg0 + (var->index & 0x001f));
+               dwarf2_write_byte (debug->f, DW_OP_piece);
+               dwarf2_write_byte (debug->f, sizeof (int));
+
+               dwarf2_write_byte (debug->f, DW_OP_reg0 + ((var->index & 0x1f0) >> 5));
+               dwarf2_write_byte (debug->f, DW_OP_piece);
+               dwarf2_write_byte (debug->f, sizeof (int));
+
+               break;
+
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+dwarf2_write_parameter (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo, const gchar *name,
+                       MonoDebugVarInfo *var, MonoClass *klass)
+{
+       static long label_index = 0;
+       int type_index = mono_debug_get_type (debug, klass);
+       char start [BUFSIZ], end [BUFSIZ];
+
+       sprintf (start, "DT1_%ld", ++label_index);
+       sprintf (end, "DT2_%ld", label_index);
+               
+       // DW_TAG_format_parameter
+       dwarf2_write_byte (debug->f, ABBREV_PARAMETER);
+       dwarf2_write_string (debug->f, name);
+       if (klass->valuetype)
+               dwarf2_write_type_ref (debug->f, type_index);
+       else
+               dwarf2_write_type_ptr_ref (debug->f, type_index);
+       dwarf2_write_section_size (debug->f, start, end);
+       dwarf2_write_label (debug->f, start);
+       dwarf2_write_variable_location (debug, var);
+       dwarf2_write_label (debug->f, end);
+       dwarf2_write_long (debug->f, minfo->jit->prologue_end);
+}
+
+static void
+dwarf2_write_variable (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo, const gchar *name,
+                      MonoDebugVarInfo *var, MonoClass *klass)
+{
+       static long label_index = 0;
+       int type_index = mono_debug_get_type (debug, klass);
+       char start [BUFSIZ], end [BUFSIZ];
+
+       sprintf (start, "DT3_%ld", ++label_index);
+       sprintf (end, "DT4_%ld", label_index);
+               
+       // DW_TAG_formal_parameter
+       dwarf2_write_byte (debug->f, ABBREV_LOCAL_VARIABLE);
+       dwarf2_write_string (debug->f, name);
+       if (klass->valuetype)
+               dwarf2_write_type_ref (debug->f, type_index);
+       else
+               dwarf2_write_type_ptr_ref (debug->f, type_index);
+       dwarf2_write_section_size (debug->f, start, end);
+       dwarf2_write_label (debug->f, start);
+       dwarf2_write_variable_location (debug, var);
+       dwarf2_write_label (debug->f, end);
+       dwarf2_write_address (debug->f, minfo->jit->code_start + var->begin_scope);
+       dwarf2_write_address (debug->f, minfo->jit->code_start + var->end_scope);
+}
+
+static void 
+write_method_lines_dwarf2 (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo)
+{
+       guint32 st_line = 0;
+       gconstpointer st_address = 0;
+       DebugMethodInfo *priv = minfo->user_data;
+       int i;
+
+       if (!minfo->jit || !minfo->jit->line_numbers)
+               return;
+
+       // Start of statement program
+       dwarf2_write_dw_lns_set_file (debug->f, priv->source_file);
+       dwarf2_write_dw_lns_advance_line (debug->f, priv->start_line - 1);
+       dwarf2_write_dw_lne_set_address (debug->f, minfo->jit->code_start);
+       dwarf2_write_dw_lns_negate_stmt (debug->f);
+       dwarf2_write_dw_lns_copy (debug->f);
+
+       st_line = priv->start_line;
+       st_address = 0;
+
+       for (i = 1; i < minfo->jit->line_numbers->len; i++) {
+               MonoDebugLineNumberEntry lne = g_array_index (
+                       minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
+               gint32 line_inc, addr_inc, opcode;
+               int used_standard_opcode = 0;
+
+               line_inc = lne.line - st_line;
+               addr_inc = (char *)lne.address - (char *)st_address;
+
+               if (addr_inc < 0) {
+                       dwarf2_write_dw_lne_set_address (debug->f, lne.address + minfo->jit->code_start);
+                       used_standard_opcode = 1;
+               } else if (addr_inc && !line_inc) {
+                       dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc);
+                       used_standard_opcode = 1;
+               }
+
+               if ((line_inc < 0) || (line_inc >= line_range)) {
+                       dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc);
+                       dwarf2_write_dw_lns_advance_line (debug->f, line_inc);
+                       used_standard_opcode = 1;
+               } else if (line_inc > 0) {
+                       opcode = (line_inc - 1) + (line_range * addr_inc) + opcode_base;
+                       g_assert (opcode >= 0);
+
+                       if (opcode >= 256) {
+                               dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc);
+                               dwarf2_write_dw_lns_advance_line (debug->f, line_inc);
+                               used_standard_opcode = 1;
+                       } else
+                               dwarf2_write_byte (debug->f, opcode);
+               }
+
+               if (used_standard_opcode)
+                       dwarf2_write_dw_lns_copy (debug->f);
+
+               st_line += line_inc;
+               st_address = (char *)st_address + addr_inc;
+       }
+
+       dwarf2_write_dw_lne_set_address (debug->f,
+                                        (char *)minfo->jit->code_start +
+                                        minfo->jit->epilogue_begin);
+       dwarf2_write_dw_lns_advance_line (debug->f, priv->last_line - st_line);
+       dwarf2_write_dw_lns_copy (debug->f);
+
+       dwarf2_write_dw_lns_copy (debug->f);
+       dwarf2_write_dw_lne_end_sequence (debug->f);
+}
+
+static void
+write_method_lines_func (gpointer key, gpointer value, gpointer user_data)
+{
+       write_method_lines_dwarf2 (user_data, value);
+}
+
+static void
+write_method_lines_func_1 (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+
+       g_hash_table_foreach (info->methods, write_method_lines_func, user_data);
+}
+
+static void
+write_line_numbers (MonoDebugHandle *debug)
+{
+       /* State machine registers. */
+       int i;
+       
+       // Line number information.
+       dwarf2_write_section_start (debug->f, "debug_line");
+       dwarf2_write_label (debug->f, "debug_line_b");
+       dwarf2_write_section_size (debug->f, "DL1", "debug_line_e");
+       dwarf2_write_label (debug->f, "DL1");
+       dwarf2_write_2byte (debug->f, 2);
+       dwarf2_write_section_size (debug->f, "DL2", "DL3");
+       dwarf2_write_label (debug->f, "DL2");
+       // minimum instruction length
+       dwarf2_write_byte (debug->f, 1);
+       // default is statement
+       dwarf2_write_byte (debug->f, 1);
+       // line base
+       dwarf2_write_byte (debug->f, line_base);
+       // line range
+       dwarf2_write_byte (debug->f, line_range);
+       // opcode base
+       dwarf2_write_byte (debug->f, opcode_base);
+       // standard opcode sizes
+       for (i = 1; i < opcode_base; i++)
+               dwarf2_write_byte (debug->f, standard_opcode_sizes [i]);
+       // include directories
+       dwarf2_write_byte (debug->f, 0);
+       // file names
+       for (i = 0; i < debug->source_files->len; i++) {
+               gchar *source_file = g_ptr_array_index (debug->source_files, i);
+               dwarf2_write_string (debug->f, source_file);
+               dwarf2_write_uleb128 (debug->f, 0);
+               dwarf2_write_uleb128 (debug->f, 0);
+               dwarf2_write_uleb128 (debug->f, 0);
+       }
+       // end of list
+       dwarf2_write_byte (debug->f, 0);
+       dwarf2_write_label (debug->f, "DL3");
+
+       g_hash_table_foreach (debug->images, write_method_lines_func_1, debug);
+
+       dwarf2_write_label (debug->f, "debug_line_e");
+}
+
+static void
+write_class_dwarf2 (MonoDebugHandle *debug, MonoClass *klass, guint idx)
+{
+       switch (klass->byval_arg.type) {
+       case MONO_TYPE_VOID:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 0, "Void");
+               break;
+       case MONO_TYPE_BOOLEAN:
+               dwarf2_write_base_type (debug, idx, DW_ATE_boolean, 1, "Boolean");
+               break;
+       case MONO_TYPE_CHAR:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned_char, 2, "Char");
+               break;
+       case MONO_TYPE_I1:
+               dwarf2_write_base_type (debug, idx, DW_ATE_signed, 1, "SByte");
+               break;
+       case MONO_TYPE_U1:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 1, "Byte");
+               break;
+       case MONO_TYPE_I2:
+               dwarf2_write_base_type (debug, idx, DW_ATE_signed, 2, "Int16");
+               break;
+       case MONO_TYPE_U2:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 2, "UInt16");
+               break;
+       case MONO_TYPE_I4:
+               dwarf2_write_base_type (debug, idx, DW_ATE_signed, 4, "Int32");
+               break;
+       case MONO_TYPE_U4:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 4, "UInt32");
+               break;
+       case MONO_TYPE_I8:
+               dwarf2_write_base_type (debug, idx, DW_ATE_signed, 8, "Int64");
+               break;
+       case MONO_TYPE_U8:
+               dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 8, "UInt64");
+               break;
+       case MONO_TYPE_R4:
+               dwarf2_write_base_type (debug, idx, DW_ATE_float, 4, "Float");
+               break;
+       case MONO_TYPE_R8:
+               dwarf2_write_base_type (debug, idx, DW_ATE_float, 8, "Double");
+               break;
+       default:
+               dwarf2_write_class (debug, klass, idx);
+               break;
+       }
+}
+
+static void
+write_class (gpointer key, gpointer value, gpointer user_data)
+{
+       write_class_dwarf2 (user_data, key, GPOINTER_TO_INT (value));
+}
+
+static void
+write_method_dwarf2 (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo)
+{
+       int is_external = 0, i;
+       MonoType *ret_type = NULL;
+       DebugMethodInfo *priv = minfo->user_data;
+       gchar **names;
+
+       if (!minfo->jit)
+               return;
+
+       if (!MONO_TYPE_IS_VOID (minfo->method->signature->ret))
+               ret_type = minfo->method->signature->ret;
+
+       // DW_TAG_subprogram
+       if (ret_type)
+               dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM_RETVAL);
+       else
+               dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM);
+       dwarf2_write_string (debug->f, priv->name);
+       dwarf2_write_byte (debug->f, is_external);
+       dwarf2_write_address (debug->f, minfo->jit->code_start);
+       dwarf2_write_address (debug->f, (char *)minfo->jit->code_start + minfo->jit->code_size);
+       dwarf2_write_byte (debug->f, DW_CC_nocall);
+       if (ret_type) {
+               MonoClass *klass = mono_class_from_mono_type (ret_type);
+               int type_index = mono_debug_get_type (debug, klass);
+               dwarf2_write_type_ref (debug->f, type_index);
+       }
+
+       if (minfo->method->signature->hasthis)
+               dwarf2_write_parameter (debug, minfo, "this", minfo->jit->this_var,
+                                       minfo->method->klass);
+
+       names = g_new (char *, minfo->method->signature->param_count);
+       mono_method_get_param_names (minfo->method, (const char **) names);
+
+       for (i = 0; i < minfo->jit->num_params; i++) {
+               MonoType *type = minfo->method->signature->params [i];
+               MonoClass *klass = mono_class_from_mono_type (type);
+
+               dwarf2_write_parameter (debug, minfo, names [i], &minfo->jit->params [i], klass);
+       }
+
+       g_free (names);
+
+       for (i = 0; i < minfo->jit->num_locals; i++) {
+               MonoMethodHeader *header = ((MonoMethodNormal*) minfo->method)->header;
+               MonoClass *klass = mono_class_from_mono_type (header->locals [i]);
+               char name [BUFSIZ];
+
+               sprintf (name, "V_%d", i);
+               dwarf2_write_variable (debug, minfo, name, &minfo->jit->locals [i], klass);
+       }
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_subprogram ends here
+}
+
+static void
+write_method_func (gpointer key, gpointer value, gpointer user_data)
+{
+       write_method_dwarf2 (user_data, value);
+}
+
+static void
+write_method_func_1 (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+
+       g_hash_table_foreach (info->methods, write_method_func, user_data);
+}
+
+void
+mono_debug_write_dwarf2 (MonoDebugHandle *debug)
+{
+       if (!(debug->f = fopen (debug->filename, "w"))) {
+               g_warning ("Can't create dwarf file `%s': %s", debug->filename, g_strerror (errno)); 
+               return;
+       }
+
+       // Produce assembler code which is free of comments and extra whitespaces.
+       fprintf (debug->f, "#NOAPP\n");
+
+       // DWARF 2 Abbreviation table.
+       dwarf2_write_section_start (debug->f, "debug_abbrev");
+       dwarf2_write_label (debug->f, "debug_abbrev");
+
+       dwarf2_write_byte (debug->f, ABBREV_COMPILE_UNIT);
+       dwarf2_write_byte (debug->f, DW_TAG_compile_unit);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_language, DW_FORM_data2);
+       dwarf2_write_pair (debug->f, DW_AT_producer, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_stmt_list, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM);
+       dwarf2_write_byte (debug->f, DW_TAG_subprogram);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_external, DW_FORM_flag);
+       dwarf2_write_pair (debug->f, DW_AT_low_pc, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, DW_AT_high_pc, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM_RETVAL);
+       dwarf2_write_byte (debug->f, DW_TAG_subprogram);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_external, DW_FORM_flag);
+       dwarf2_write_pair (debug->f, DW_AT_low_pc, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, DW_AT_high_pc, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE);
+       dwarf2_write_byte (debug->f, DW_TAG_base_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_encoding, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_FORMAL_PARAMETER);
+       dwarf2_write_byte (debug->f, DW_TAG_formal_parameter);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ARTIFICIAL_PARAMETER);
+       dwarf2_write_byte (debug->f, DW_TAG_formal_parameter);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_artificial, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_PARAMETER);
+       dwarf2_write_byte (debug->f, DW_TAG_formal_parameter);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_location, DW_FORM_block4);
+       dwarf2_write_pair (debug->f, DW_AT_start_scope, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_LOCAL_VARIABLE);
+       dwarf2_write_byte (debug->f, DW_TAG_variable);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_location, DW_FORM_block4);
+       dwarf2_write_pair (debug->f, DW_AT_start_scope, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, DW_AT_end_scope, DW_FORM_addr);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE);
+       dwarf2_write_byte (debug->f, DW_TAG_structure_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER);
+       dwarf2_write_byte (debug->f, DW_TAG_member);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_data_member_location, DW_FORM_block4);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_STRUCT_ACCESS);
+       dwarf2_write_byte (debug->f, DW_TAG_access_declaration);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ENUM_TYPE);
+       dwarf2_write_byte (debug->f, DW_TAG_enumeration_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE);
+       dwarf2_write_byte (debug->f, DW_TAG_enumerator);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED);
+       dwarf2_write_byte (debug->f, DW_TAG_enumerator);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_udata);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED);
+       dwarf2_write_byte (debug->f, DW_TAG_enumerator);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_sdata);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_CLASS_TYPE);
+       dwarf2_write_byte (debug->f, DW_TAG_class_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_CLASS_INHERITANCE);
+       dwarf2_write_byte (debug->f, DW_TAG_inheritance);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE);
+       dwarf2_write_byte (debug->f, DW_TAG_pointer_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD);
+       dwarf2_write_byte (debug->f, DW_TAG_subprogram);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_virtuality, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD_RETVAL);
+       dwarf2_write_byte (debug->f, DW_TAG_subprogram);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_virtuality, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY);
+       dwarf2_write_byte (debug->f, DW_TAG_array_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_ARRAY);
+       dwarf2_write_byte (debug->f, DW_TAG_array_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_yes);
+       dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string);
+       dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4);
+       dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_byte (debug->f, ABBREV_SUBRANGE);
+       dwarf2_write_byte (debug->f, DW_TAG_subrange_type);
+       dwarf2_write_byte (debug->f, DW_CHILDREN_no);
+       dwarf2_write_pair (debug->f, DW_AT_lower_bound, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, DW_AT_upper_bound, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, DW_AT_count, DW_FORM_data4);
+       dwarf2_write_pair (debug->f, 0, 0);
+
+       dwarf2_write_label (debug->f, "debug_abbrev_e");
+
+       // Line numbers
+       write_line_numbers (debug);
+
+       // Compile unit header
+       dwarf2_write_section_start (debug->f, "debug_info");
+       dwarf2_write_label (debug->f, "debug_info_b");
+       dwarf2_write_section_size (debug->f, "DI1", "debug_info_e");
+       dwarf2_write_label (debug->f, "DI1");
+       dwarf2_write_2byte (debug->f, 2);
+       dwarf2_write_ref4 (debug->f, "debug_abbrev_b");
+       dwarf2_write_byte (debug->f, sizeof (gpointer));
+
+       // DW_TAG_compile_unit
+       dwarf2_write_byte (debug->f, ABBREV_COMPILE_UNIT);
+       dwarf2_write_string (debug->f, debug->name);
+       dwarf2_write_2byte (debug->f, DW_LANG_C_sharp);
+       dwarf2_write_string (debug->f, debug->producer_name);
+       dwarf2_write_ref4 (debug->f, "debug_lines_b");
+
+       // Methods
+       g_hash_table_foreach (debug->images, write_method_func_1, debug);
+
+       // Derived types
+       g_hash_table_foreach (debug->type_hash, write_class, debug);
+
+       dwarf2_write_byte (debug->f, 0);
+       // DW_TAG_compile_unit ends here
+
+       dwarf2_write_label (debug->f, "debug_info_e");
+
+       fclose (debug->f);
+       debug->f = NULL;
+
+       if (!(debug->flags & MONO_DEBUG_FLAGS_DONT_ASSEMBLE)) {
+               char *buf;
+
+               /* yes, it's completely unsafe */
+               buf = g_strdup_printf ("as %s -o %s", debug->filename, debug->objfile);
+               system (buf);
+               g_free (buf);
+       }
+}
diff --git a/mono/mini/debug-mini.c b/mono/mini/debug-mini.c
new file mode 100644 (file)
index 0000000..2774bc1
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * debug-mini.c: Mini-specific debugging stuff.
+ *
+ * Author:
+ *   Martin Baulig (martin@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+
+#include "mini.h"
+#include "mini-x86.h"
+#include "debug-private.h"
+
+void
+mono_debug_codegen_breakpoint (guint8 **buf)
+{
+       x86_breakpoint (*buf);
+}
+
+void
+mono_debug_codegen_ret (guint8 **buf)
+{
+       x86_ret (*buf);
+}
+
+typedef struct
+{
+       MonoDebugMethodInfo *minfo;
+       guint32 has_line_numbers;
+} MiniDebugMethodInfo;
+
+void
+mono_debug_init_method (MonoCompile *cfg, MonoBasicBlock *start_block)
+{
+       MonoMethod *method = cfg->method;
+       MiniDebugMethodInfo *info;
+
+       if (!mono_debug_handle)
+               return;
+
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
+           (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
+           (method->wrapper_type != MONO_WRAPPER_NONE))
+               return;
+
+       info = g_new0 (MiniDebugMethodInfo, 1);
+
+       cfg->debug_info = info;
+}
+
+void
+mono_debug_open_method (MonoCompile *cfg)
+{
+       MiniDebugMethodInfo *info;
+       MonoDebugMethodJitInfo *jit;
+       MonoMethodHeader *header;
+
+       info = (MiniDebugMethodInfo *) cfg->debug_info;
+       if (!info)
+               return;
+
+       mono_class_init (cfg->method->klass);
+
+       info->minfo = _mono_debug_lookup_method (cfg->method);
+       if (!info->minfo || info->minfo->jit)
+               return;
+
+       mono_debug_handle->dirty = TRUE;
+
+       g_assert (((MonoMethodNormal*)info->minfo->method)->header);
+       header = ((MonoMethodNormal*)info->minfo->method)->header;
+
+       info->minfo->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1);
+       jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
+       jit->num_locals = header->num_locals;
+       jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals);
+}
+
+static void
+write_variable (MonoInst *inst, MonoDebugVarInfo *var)
+{
+       if (inst->opcode == OP_REGVAR)
+               var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
+       else if (inst->inst_basereg != X86_EBP) {
+               g_message (G_STRLOC ": %d - %d", inst->inst_basereg, inst->inst_offset);
+               var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
+               var->offset = inst->inst_offset;
+       } else
+               var->offset = inst->inst_offset;
+}
+
+void
+mono_debug_close_method (MonoCompile *cfg)
+{
+       MiniDebugMethodInfo *info;
+       MonoDebugMethodInfo *minfo;
+       MonoDebugMethodJitInfo *jit;
+       MonoMethodHeader *header;
+       MonoMethod *method;
+       int i;
+
+       info = (MiniDebugMethodInfo *) cfg->debug_info;
+       if (!info || !info->minfo)
+               return;
+
+       minfo = info->minfo;
+       method = minfo->method;
+       header = ((MonoMethodNormal*)method)->header;
+
+       jit = minfo->jit;
+       jit->code_start = cfg->native_code;
+       jit->epilogue_begin = cfg->epilog_begin;
+       jit->code_size = cfg->code_len;
+
+       _mono_debug_generate_line_number (minfo, jit->epilogue_begin, header->code_size, 0);
+
+       jit->num_params = method->signature->param_count;
+       jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
+
+       for (i = 0; i < jit->num_locals; i++)
+               write_variable (cfg->varinfo [cfg->locals_start + i], &jit->locals [i]);
+
+       if (method->signature->hasthis) {
+               jit->this_var = g_new0 (MonoDebugVarInfo, 1);
+               write_variable (cfg->varinfo [0], jit->this_var);
+       }
+
+       for (i = 0; i < jit->num_params; i++)
+               write_variable (cfg->varinfo [i + method->signature->hasthis], &jit->params [i]);
+
+       if (minfo->symfile) {
+               mono_debug_symfile_add_method (minfo->symfile, minfo->method);
+               mono_debugger_event (MONO_DEBUGGER_EVENT_METHOD_ADDED, minfo->symfile, minfo->method);
+       }
+}
+
+void
+mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address)
+{
+       MiniDebugMethodInfo *info;
+       MonoMethodHeader *header;
+       guint32 offset;
+
+       info = (MiniDebugMethodInfo *) cfg->debug_info;
+       if (!info || !info->minfo || !ins->cil_code)
+               return;
+
+       g_assert (((MonoMethodNormal*)info->minfo->method)->header);
+       header = ((MonoMethodNormal*)info->minfo->method)->header;
+
+       if ((ins->cil_code < header->code) ||
+           (ins->cil_code > header->code + header->code_size))
+               return;
+
+       offset = ins->cil_code - header->code;
+       if (!info->has_line_numbers) {
+               info->minfo->jit->prologue_end = address;
+               info->has_line_numbers = TRUE;
+       }
+
+       _mono_debug_generate_line_number (info->minfo, address, offset, 0);
+}
diff --git a/mono/mini/debug-private.h b/mono/mini/debug-private.h
new file mode 100644 (file)
index 0000000..1a96caf
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This is a copy of mono/mono/jit/debug-private.h.
+ *
+ * Please do *not* modify this copy here, if you need to make changes, do them
+ * in the original and then copy it over again.
+ *
+ * All mini-specific stuff is in debug-mini.c.
+ */
+
+#ifndef __MONO_JIT_DEBUG_PRIVATE_H__
+#define __MONO_JIT_DEBUG_PRIVATE_H__
+
+#include <mono/metadata/debug-mono-symfile.h>
+
+#include "debug.h"
+
+typedef struct _AssemblyDebugInfo AssemblyDebugInfo;
+
+typedef enum {
+       MONO_DEBUG_FLAGS_NONE                   = 0,
+       // Don't run the assembler.
+       MONO_DEBUG_FLAGS_DONT_ASSEMBLE          = (1 << 1),
+       // Install the generated *.il files in the assembly dir.
+       MONO_DEBUG_FLAGS_INSTALL_IL_FILES       = (1 << 2),
+       // Don't update the *.il files.
+       MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES   = (1 << 3),
+       // Don't create any new *.il files.
+       MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES   = (1 << 4),
+       // Don't fallback to normal dwarf2.
+       MONO_DEBUG_FLAGS_DONT_FALLBACK          = (1 << 5),
+       // Don't precompile image.
+       MONO_DEBUG_FLAGS_DONT_PRECOMPILE        = (1 << 6),
+       // Update symbol file on exit.
+       MONO_DEBUG_FLAGS_UPDATE_ON_EXIT         = (1 << 7)
+} MonoDebugFlags;
+
+typedef struct {
+       AssemblyDebugInfo *info;
+       gchar *name;
+       int source_file;
+       guint32 method_number;
+       guint32 start_line;
+       guint32 first_line;
+       guint32 last_line;
+} DebugMethodInfo;
+
+typedef struct {
+       MonoMethod *method;
+       const guint8 *code_start;
+       guint32 code_size;
+} DebugWrapperInfo;
+
+struct _AssemblyDebugInfo {
+       MonoDebugFormat format;
+       MonoDebugHandle *handle;
+       MonoSymbolFile *symfile;
+       char *name;
+       char *ilfile;
+       char *filename;
+       char *objfile;
+       int always_create_il;
+       int source_file;
+       int total_lines;
+       int *mlines;
+       int *moffsets;
+       int nmethods;
+       GHashTable *methods;
+       GHashTable *wrapper_methods;
+       MonoImage *image;
+       gpointer _priv;
+};
+
+struct _MonoDebugHandle {
+       MonoDebugFormat format;
+       MonoDebugFlags flags;
+       char *name;
+       char *filename;
+       char *objfile;
+       char *producer_name;
+       GHashTable *type_hash;
+       GPtrArray *source_files;
+       int next_idx;
+       int next_klass_idx;
+       int dirty;
+       GHashTable *images;
+       FILE *f;
+};
+
+guint32               mono_debug_get_type                   (MonoDebugHandle* debug, MonoClass *klass);
+
+void                  mono_debug_write_stabs                (MonoDebugHandle *debug);
+
+void                  mono_debug_write_dwarf2               (MonoDebugHandle *debug);
+
+void                  mono_debug_codegen_breakpoint         (guint8 **buf);
+
+void                  mono_debug_codegen_ret                (guint8 **buf);
+
+MonoDebugMethodInfo *_mono_debug_lookup_method              (MonoMethod *method);
+
+gint32               _mono_debug_address_from_il_offset     (MonoDebugMethodInfo *minfo, guint32 il_offset);
+
+AssemblyDebugInfo   *_mono_debug_get_image                  (MonoDebugHandle* debug, MonoImage *image);
+
+void                 _mono_debug_generate_line_number       (MonoDebugMethodInfo *minfo, guint32 address,
+                                                            guint32 offset, int debug);
+
+extern MonoDebugHandle *mono_debug_handle;
+extern gboolean mono_debug_initialized;
+
+#endif /* __MONO_JIT_DEBUG_PRIVATE_H__ */
diff --git a/mono/mini/debug-stabs.c b/mono/mini/debug-stabs.c
new file mode 100644 (file)
index 0000000..605f029
--- /dev/null
@@ -0,0 +1,231 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/jit/codegen.h>
+#include <mono/jit/debug.h>
+
+#include "debug-private.h"
+
+typedef struct {
+       const char *name;
+       const char *spec;
+} BaseTypes;
+
+/*
+ * Not 64 bit clean.
+ * Note: same order of MonoTypeEnum.
+ */
+static BaseTypes
+base_types[] = {
+       {"", NULL},
+       {"Void", "(0,1)"},
+       {"Boolean", ";0;255;"},
+       {"Char", ";0;65535;"},
+       {"SByte", ";-128;127;"},
+       {"Byte", ";0;255;"},
+       {"Int16", ";-32768;32767;"},
+       {"UInt16", ";0;65535;"},
+       {"Int32", ";0020000000000;0017777777777;"},
+       {"UInt32", ";0000000000000;0037777777777;"},
+       {"Int64", ";01000000000000000000000;0777777777777777777777;"},
+       {"UInt64", ";0000000000000;01777777777777777777777;"},
+       {"Single", "r(0,8);4;0;"},
+       {"Double", "r(0,8);8;0;"},
+       {"String", "(0,41)=*(0,42)=xsMonoString:"}, /*string*/
+       {"", }, /*ptr*/
+       {"", }, /*byref*/
+       {"", }, /*valuetype*/
+       {"Class", "(0,44)=*(0,45)=xsMonoObject:"}, /*class*/
+       {"", }, /*unused*/
+       {"Array", }, /*array*/
+       {"", }, /*typedbyref*/
+       {"", }, /*unused*/
+       {"", }, /*unused*/
+       {"IntPtr", ";0020000000000;0017777777777;"},
+       {"UIntPtr", ";0000000000000;0037777777777;"},
+       {"", }, /*unused*/
+       {"FnPtr", "*(0,1)"}, /*fnptr*/
+       {"Object", "(0,47)=*(0,48)=xsMonoObject:"}, /*object*/
+       {"SzArray", "(0,50)=*(0,51))=xsMonoArray:"}, /*szarray*/
+       {NULL, NULL}
+};
+
+static void
+write_method_stabs (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo)
+{
+       int i;
+       DebugMethodInfo *priv = minfo->user_data;
+       MonoMethod *method = minfo->method;
+       MonoClass *klass = method->klass;
+       MonoMethodSignature *sig = method->signature;
+       char **names = g_new (char*, sig->param_count);
+       gchar *source_file;
+
+       if (!minfo->jit)
+               return;
+
+       source_file = g_ptr_array_index (debug->source_files, priv->source_file);
+
+       fprintf (debug->f, ".stabs \"%s\",100,0,0,0\n", source_file);
+
+       fprintf (debug->f, ".stabs \"%s:F(0,%d)\",36,0,%d,%p\n", priv->name, sig->ret->type,
+                priv->start_line, minfo->jit->code_start);
+
+       /* params */
+       mono_method_get_param_names (method, (const char **)names);
+       if (sig->hasthis)
+               fprintf (debug->f, ".stabs \"this:p(0,%d)=(0,%d)\",160,0,%d,%d\n",
+                        debug->next_idx++, klass->byval_arg.type, priv->start_line,
+                        minfo->jit->this_var->offset);
+       for (i = 0; i < minfo->jit->num_params; i++) {
+               int stack_offset = minfo->jit->params [i].offset;
+
+               fprintf (debug->f, ".stabs \"%s:p(0,%d)=(0,%d)\",160,0,%d,%d\n",
+                        names [i], debug->next_idx++, sig->params [i]->type,
+                        priv->start_line, stack_offset);
+       }
+
+       /* local vars */
+       for (i = 0; i < minfo->jit->num_locals; ++i) {
+               MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
+               int stack_offset = minfo->jit->locals [i].offset;
+
+               fprintf (debug->f, ".stabs \"local_%d:(0,%d)=(0,%d)\",128,0,%d,%d\n",
+                        i, debug->next_idx++, header->locals [i]->type, priv->start_line, stack_offset);
+       }
+
+       if (minfo->jit && minfo->jit->line_numbers) {
+               fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->start_line, 0);
+               fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->first_line,
+                        minfo->jit->prologue_end);
+
+               for (i = 1; i < minfo->jit->line_numbers->len; i++) {
+                       MonoDebugLineNumberEntry lne = g_array_index (
+                               minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
+
+                       fprintf (debug->f, ".stabn 68,0,%d,%d\n", lne.line, lne.address);
+               }
+
+               fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->last_line,
+                        minfo->jit->epilogue_begin);
+       }
+
+       /* end of function */
+       fprintf (debug->f, ".stabs \"\",36,0,0,%d\n", minfo->jit->code_size);
+
+       g_free (names);
+       fflush (debug->f);
+}
+
+static void
+get_enumvalue (MonoClass *klass, int idx, char *buf)
+{
+       guint32 const_cols [MONO_CONSTANT_SIZE];
+       const char *ptr;
+       guint32 crow = mono_metadata_get_constant_index (klass->image, MONO_TOKEN_FIELD_DEF | (idx + 1));
+
+       if (!crow) {
+               buf [0] = '0';
+               buf [1] = 0;
+               return;
+       }
+       mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE);
+       ptr = mono_metadata_blob_heap (klass->image, const_cols [MONO_CONSTANT_VALUE]);
+       switch (const_cols [MONO_CONSTANT_TYPE]) {
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I4:
+               /* FIXME: add other types... */
+       default:
+               g_snprintf (buf, 64, "%d", *(gint32*)ptr);
+       }
+}
+
+static void
+write_method_func (gpointer key, gpointer value, gpointer user_data)
+{
+       write_method_stabs (user_data, value);
+}
+
+static void
+write_method_func_1 (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+
+       g_hash_table_foreach (info->methods, write_method_func, user_data);
+}
+
+static void
+write_class_stabs (MonoDebugHandle *debug, MonoClass *klass, int idx)
+{
+       char *name;
+       int i;
+       char buf [64];
+
+       /* output enums ...*/
+       if (klass->enumtype) {
+               name = g_strdup_printf ("%s%s%s", klass->name_space, klass->name_space [0]? "_": "", klass->name);
+               fprintf (debug->f, ".stabs \"%s:T%d=e", name, ++debug->next_idx);
+               g_free (name);
+               for (i = 0; i < klass->field.count; ++i) {
+                       if (klass->fields [i].type->attrs & FIELD_ATTRIBUTE_LITERAL) {
+                               get_enumvalue (klass, klass->field.first + i, buf);
+                               fprintf (debug->f, "%s_%s=%s,", klass->name, klass->fields [i].name, buf);
+                       }
+               }
+               fprintf (debug->f, ";\",128,0,0,0\n");
+       }
+       fflush (debug->f);
+}
+
+static void
+write_class (gpointer key, gpointer value, gpointer user_data)
+{
+       write_class_stabs (user_data, key, GPOINTER_TO_INT (value));
+}
+
+void
+mono_debug_write_stabs (MonoDebugHandle *debug)
+{
+       gchar *source_file;
+       int i;
+
+       if (!(debug->f = fopen (debug->filename, "w"))) {
+               g_warning ("Can't create stabs file `%s': %s", debug->filename, g_strerror (errno)); 
+               return;
+       }
+
+       source_file = g_ptr_array_index (debug->source_files, 0);
+
+       fprintf (debug->f, ".stabs \"%s\",100,0,0,0\n", source_file);
+
+       for (i = 0; base_types [i].name; ++i) {
+               if (! base_types [i].spec)
+                       continue;
+               fprintf (debug->f, ".stabs \"%s:t(0,%d)=", base_types [i].name, i);
+               if (base_types [i].spec [0] == ';') {
+                       fprintf (debug->f, "r(0,%d)%s\"", i, base_types [i].spec);
+               } else {
+                       fprintf (debug->f, "%s\"", base_types [i].spec);
+               }
+               fprintf (debug->f, ",128,0,0,0\n");
+       }
+
+       g_hash_table_foreach (debug->images, write_method_func_1, debug);
+
+       g_hash_table_foreach (debug->type_hash, write_class, debug);
+
+       fclose (debug->f);
+       debug->f = NULL;
+
+       if (!(debug->flags & MONO_DEBUG_FLAGS_DONT_ASSEMBLE)) {
+               char *buf;
+
+               /* yes, it's completely unsafe */
+               buf = g_strdup_printf ("as %s -o %s", debug->filename, debug->objfile);
+               system (buf);
+               g_free (buf);
+       }
+}
diff --git a/mono/mini/debug.c b/mono/mini/debug.c
new file mode 100644 (file)
index 0000000..69d2d86
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ * debug.c: Debugging support
+ *
+ * Author:
+ *   Martin Baulig (martin@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/debug-mono-symfile.h>
+
+#include "debug-private.h"
+
+/*
+ * NOTE:  Functions and variables starting with `mono_debug_' and `debug_' are
+ *        part of the general debugging code.
+ *
+ *        Functions and variables starting with `mono_debugger_' and `debugger_'
+ *        are only used when the JIT is running inside the Mono Debugger.
+ *
+ * FIXME: This file needs some API loving.
+ */
+
+/* This is incremented each time the symbol table is modified.
+ * The debugger looks at this variable and if it has a higher value than its current
+ * copy of the symbol table, it must call mono_debug_update_symbol_file_table().
+ */
+guint32 mono_debugger_symbol_file_table_generation = 0;
+guint32 mono_debugger_symbol_file_table_modified = 0;
+
+/* Caution: This variable may be accessed at any time from the debugger;
+ *          it is very important not to modify the memory it is pointing to
+ *          without previously setting this pointer back to NULL.
+ */
+MonoDebuggerSymbolFileTable *mono_debugger_symbol_file_table = NULL;
+
+/* Caution: This function MUST be called before touching the symbol table! */
+static void release_symbol_file_table (void);
+
+MonoDebugHandle *mono_debug_handle = NULL;
+gboolean mono_debug_initialized = FALSE;
+
+static CRITICAL_SECTION debugger_lock_mutex;
+
+extern void (*mono_debugger_class_init_func) (MonoClass *klass);
+
+static void mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data);
+static void mono_debug_close_assembly (AssemblyDebugInfo* info);
+static AssemblyDebugInfo *mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image);
+
+static int running_in_the_mono_debugger = FALSE;
+void (*mono_debugger_event_handler) (MonoDebuggerEvent event, gpointer data, gpointer data2) = NULL;
+
+#ifndef PLATFORM_WIN32
+
+MonoDebuggerIOLayer mono_debugger_io_layer = {
+       InitializeCriticalSection, DeleteCriticalSection, TryEnterCriticalSection,
+       EnterCriticalSection, LeaveCriticalSection, WaitForSingleObject, SignalObjectAndWait,
+       WaitForMultipleObjects, CreateSemaphore, ReleaseSemaphore, CreateThread
+};
+
+#endif
+
+void
+mono_debugger_event (MonoDebuggerEvent event, gpointer data, gpointer data2)
+{
+       if (mono_debugger_event_handler)
+               (* mono_debugger_event_handler) (event, data, data2);
+}
+
+void
+mono_debug_init (int in_the_debugger)
+{
+       if (mono_debug_initialized)
+               return;
+
+       InitializeCriticalSection (&debugger_lock_mutex);
+       mono_debug_initialized = TRUE;
+       running_in_the_mono_debugger = in_the_debugger;
+}
+
+gpointer
+mono_debug_create_notification_function (gpointer *notification_address)
+{
+       guint8 *ptr, *buf;
+
+       ptr = buf = g_malloc0 (16);
+       mono_debug_codegen_breakpoint (&buf);
+       if (notification_address)
+               *notification_address = buf;
+       mono_debug_codegen_ret (&buf);
+
+       return ptr;
+}
+
+void
+mono_debug_lock (void)
+{
+       if (mono_debug_initialized)
+               EnterCriticalSection (&debugger_lock_mutex);
+}
+
+void
+mono_debug_unlock (void)
+{
+       if (mono_debug_initialized)
+               LeaveCriticalSection (&debugger_lock_mutex);
+}
+
+static void
+free_method_info (MonoDebugMethodInfo *minfo)
+{
+       DebugMethodInfo *priv = minfo->user_data;
+
+       if (priv) {
+               g_free (priv->name);
+               g_free (priv);
+       }
+
+       if (minfo->jit) {
+               g_array_free (minfo->jit->line_numbers, TRUE);
+               g_free (minfo->jit->this_var);
+               g_free (minfo->jit->params);
+               g_free (minfo->jit->locals);
+               g_free (minfo->jit);
+       }
+
+       g_free (minfo->il_offsets);
+       g_free (minfo);
+}
+
+static void
+free_wrapper_info (DebugWrapperInfo *winfo)
+{
+       g_free (winfo);
+}
+
+static void
+debug_arg_warning (const char *message)
+{
+       g_warning ("Error while processing --debug-args arguments: %s", message);
+}
+
+MonoDebugHandle*
+mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args)
+{
+       MonoDebugHandle *debug;
+       const char **ptr;
+
+       g_assert (!mono_debug_handle);
+
+       debug = g_new0 (MonoDebugHandle, 1);
+       debug->name = g_strdup (assembly->image->name);
+       debug->format = format;
+       debug->source_files = g_ptr_array_new ();
+       debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
+       debug->next_idx = 100;
+       debug->dirty = TRUE;
+
+       debug->type_hash = g_hash_table_new (NULL, NULL);
+
+       debug->images = g_hash_table_new_full (NULL, NULL, NULL,
+                                              (GDestroyNotify) mono_debug_close_assembly);
+
+       for (ptr = args; ptr && *ptr; ptr++) {
+               const char *arg = *ptr;
+               gchar *message;
+
+               switch (debug->format) {
+               case MONO_DEBUG_FORMAT_STABS:
+               case MONO_DEBUG_FORMAT_DWARF2:
+                       if (!strncmp (arg, "filename=", 9)) {
+                               if (debug->filename)
+                                       debug_arg_warning ("The `filename' argument can be given only once.");
+                               debug->filename = g_strdup (arg + 9);
+                               continue;
+                       } else if (!strncmp (arg, "objfile=", 8)) {
+                               if (debug->objfile)
+                                       debug_arg_warning ("The `objfile' argument can be given only once.");
+                               debug->objfile = g_strdup (arg + 8);
+                               continue;
+                       }
+                       break;
+               case MONO_DEBUG_FORMAT_MONO:
+                       debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES |
+                               MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
+                       break;
+               default:
+                       break;
+               }
+
+               if (debug->format != MONO_DEBUG_FORMAT_MONO) {
+                       if (!strcmp (arg, "dont_assemble")) {
+                               debug->flags |= MONO_DEBUG_FLAGS_DONT_ASSEMBLE;
+                               continue;
+                       } else if (!strcmp (arg, "update_on_exit")) {
+                               debug->flags |= MONO_DEBUG_FLAGS_UPDATE_ON_EXIT;
+                               continue;
+                       } else if (!strcmp (arg, "install_il_files")) {
+                               debug->flags |= MONO_DEBUG_FLAGS_INSTALL_IL_FILES;
+                               continue;
+                       } else if (!strcmp (arg, "dont_update_il_files")) {
+                               debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES;
+                               continue;
+                       } else if (!strcmp (arg, "dont_create_il_files")) {
+                               debug->flags |= MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
+                               continue;
+                       }
+               }
+
+               message = g_strdup_printf ("Unknown argument `%s'.", arg);
+               debug_arg_warning (message);
+               g_free (message);
+       }
+
+       switch (debug->format) {
+       case MONO_DEBUG_FORMAT_STABS:
+               if (!debug->filename)
+                       debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name));
+               if (!debug->objfile)
+                       debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
+               break;
+       case MONO_DEBUG_FORMAT_DWARF2:
+               if (!debug->filename)
+                       debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name));
+               if (!debug->objfile)
+                       debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
+               break;
+       case MONO_DEBUG_FORMAT_MONO:
+               mono_debugger_class_init_func = mono_debug_add_type;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       mono_debug_lock ();
+       release_symbol_file_table ();
+
+       mono_debug_handle = debug;
+       mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
+
+       mono_debug_open_image (mono_debug_handle, assembly->image);
+       mono_debug_open_image (mono_debug_handle, mono_defaults.corlib);
+
+       mono_debug_add_type (mono_defaults.object_class);
+       mono_debug_add_type (mono_defaults.object_class);
+       mono_debug_add_type (mono_defaults.byte_class);
+       mono_debug_add_type (mono_defaults.void_class);
+       mono_debug_add_type (mono_defaults.boolean_class);
+       mono_debug_add_type (mono_defaults.sbyte_class);
+       mono_debug_add_type (mono_defaults.int16_class);
+       mono_debug_add_type (mono_defaults.uint16_class);
+       mono_debug_add_type (mono_defaults.int32_class);
+       mono_debug_add_type (mono_defaults.uint32_class);
+       mono_debug_add_type (mono_defaults.int_class);
+       mono_debug_add_type (mono_defaults.uint_class);
+       mono_debug_add_type (mono_defaults.int64_class);
+       mono_debug_add_type (mono_defaults.uint64_class);
+       mono_debug_add_type (mono_defaults.single_class);
+       mono_debug_add_type (mono_defaults.double_class);
+       mono_debug_add_type (mono_defaults.char_class);
+       mono_debug_add_type (mono_defaults.string_class);
+       mono_debug_add_type (mono_defaults.enum_class);
+       mono_debug_add_type (mono_defaults.array_class);
+       mono_debug_add_type (mono_defaults.multicastdelegate_class);
+       mono_debug_add_type (mono_defaults.asyncresult_class);
+       mono_debug_add_type (mono_defaults.waithandle_class);
+       mono_debug_add_type (mono_defaults.typehandle_class);
+       mono_debug_add_type (mono_defaults.fieldhandle_class);
+       mono_debug_add_type (mono_defaults.methodhandle_class);
+       mono_debug_add_type (mono_defaults.monotype_class);
+       mono_debug_add_type (mono_defaults.exception_class);
+       mono_debug_add_type (mono_defaults.threadabortexception_class);
+       mono_debug_add_type (mono_defaults.thread_class);
+       mono_debug_add_type (mono_defaults.transparent_proxy_class);
+       mono_debug_add_type (mono_defaults.real_proxy_class);
+       mono_debug_add_type (mono_defaults.mono_method_message_class);
+       mono_debug_add_type (mono_defaults.appdomain_class);
+       mono_debug_add_type (mono_defaults.field_info_class);
+       mono_debug_add_type (mono_defaults.stringbuilder_class);
+       mono_debug_add_type (mono_defaults.math_class);
+       mono_debug_add_type (mono_defaults.stack_frame_class);
+       mono_debug_add_type (mono_defaults.stack_trace_class);
+       mono_debug_add_type (mono_defaults.marshal_class);
+       mono_debug_add_type (mono_defaults.iserializeable_class);
+       mono_debug_add_type (mono_defaults.serializationinfo_class);
+       mono_debug_add_type (mono_defaults.streamingcontext_class);
+
+       mono_debug_update_symbol_file_table ();
+
+       mono_debug_unlock ();
+
+       return debug;
+}
+
+static void
+mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
+{
+       if (!mono_debug_handle)
+               return;
+
+       mono_debug_lock ();
+       mono_debug_open_image (mono_debug_handle, assembly->image);
+       mono_debug_unlock ();
+}
+
+static void
+generate_il_offsets (AssemblyDebugInfo *info, MonoMethod *method)
+{
+       GPtrArray *il_offsets = g_ptr_array_new ();
+       MonoClass *klass = method->klass;
+       MonoDebugMethodInfo *minfo;
+       DebugMethodInfo *priv;
+       int i;
+
+       g_assert (klass->image == info->image);
+
+       /* FIXME: doesn't work yet. */
+       if (!strcmp (klass->name_space, "System.Runtime.Remoting.Proxies"))
+               return;
+
+       mono_class_init (klass);
+
+       minfo = g_new0 (MonoDebugMethodInfo, 1);
+       minfo->method = method;
+       minfo->user_data = priv = g_new0 (DebugMethodInfo, 1);
+
+       priv->name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
+                                     klass->name, method->name);
+       priv->source_file = info->source_file;
+       priv->info = info;
+
+       /*
+        * Find the method index in the image.
+        */
+       for (i = 0; klass->methods && i < klass->method.count; ++i) {
+               if (klass->methods [i] == minfo->method) {
+                       priv->method_number = klass->method.first + i + 1;
+                       priv->first_line = info->mlines [priv->method_number];
+                       break;
+               }
+       }
+
+       g_assert (priv->method_number);
+
+       /* info->moffsets contains -1 "outside" of functions. */
+       for (i = priv->first_line; (i > 0) && (info->moffsets [i] == 0); i--)
+               ;
+       priv->start_line = i + 1;
+
+       for (i = priv->start_line; info->moffsets [i] != -1; i++) {
+               MonoSymbolFileLineNumberEntry *lne = g_new0 (MonoSymbolFileLineNumberEntry, 1);
+
+               if (!info->moffsets [i] && (i > priv->start_line))
+                       continue;
+
+               lne->offset = info->moffsets [i];
+               lne->row = i;
+
+               g_ptr_array_add (il_offsets, lne);
+       }
+
+       priv->last_line = i;
+
+       minfo->start_line = priv->first_line;
+       minfo->end_line = priv->last_line;
+
+       minfo->num_il_offsets = il_offsets->len;
+       minfo->il_offsets = g_new0 (MonoSymbolFileLineNumberEntry, il_offsets->len);
+       for (i = 0; i < il_offsets->len; i++) {
+               MonoSymbolFileLineNumberEntry *il = g_ptr_array_index (il_offsets, i);
+
+               minfo->il_offsets [i] = *il;
+       }
+
+       g_ptr_array_free (il_offsets, TRUE);
+
+       g_hash_table_insert (info->methods, method, minfo);
+}
+
+static void
+debug_load_method_lines (AssemblyDebugInfo* info)
+{
+       MonoTableInfo *table = &info->image->tables [MONO_TABLE_METHOD];
+       FILE *f;
+       char buf [1024];
+       int i, mnum, idx;
+       int offset = -1;
+
+       if (info->always_create_il || !(info->handle->flags & MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES)) {
+               char *command = g_strdup_printf ("monodis --output=%s %s",
+                                                info->ilfile, info->image->name);
+               struct stat stata, statb;
+               int need_update = FALSE;
+
+               if (stat (info->image->name, &stata)) {
+                       g_warning ("cannot access assembly file (%s): %s",
+                                  info->image->name, g_strerror (errno));
+                       g_free (command);
+                       return;
+               }
+
+               /* If the stat() failed or the file is older. */
+               if (stat (info->ilfile, &statb)) {
+                       need_update = TRUE;
+               } else if (statb.st_mtime < stata.st_mtime)
+                       need_update = TRUE;
+
+               if (need_update) {
+#ifndef PLATFORM_WIN32
+                       struct sigaction act, oldact;
+                       sigset_t old_set;
+#endif
+                       int ret;
+
+#ifndef PLATFORM_WIN32
+                       act.sa_handler = SIG_IGN;
+                       act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+                       sigemptyset (&act.sa_mask);
+                       sigaddset (&act.sa_mask, SIGCHLD);
+                       sigprocmask (SIG_BLOCK, &act.sa_mask, &old_set);
+                       sigaction (SIGCHLD, &act, &oldact);
+#endif
+                       
+                       g_print ("Recreating %s from %s.\n", info->ilfile, info->image->name);
+
+                       ret = system (command);
+
+#ifndef PLATFORM_WIN32
+                       sigaction (SIGCHLD, &oldact, NULL);
+                       sigprocmask (SIG_SETMASK, &old_set, NULL);
+#endif
+
+                       if (ret) {
+                               g_warning ("cannot create IL assembly file (%s): %s",
+                                          command, g_strerror (errno));
+                               g_free (command);
+                               return;
+                       }
+               }
+       }
+
+       /* use an env var with directories for searching. */
+       if (!(f = fopen (info->ilfile, "r"))) {
+               g_warning ("cannot open IL assembly file %s", info->ilfile);
+               return;
+       }
+
+       info->total_lines = 100;
+       info->moffsets = g_malloc (info->total_lines * sizeof (int));
+
+       i = 0;
+       while (fgets (buf, sizeof (buf), f)) {
+               int pos = i;
+
+               info->moffsets [i++] = offset;
+               if (i + 2 >= info->total_lines) {
+                       info->total_lines += 100;
+                       info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
+                       g_assert (info->moffsets);
+               }
+
+               if (!sscanf (buf, " // method line %d", &mnum))
+                       continue;
+
+               offset = 0;
+
+               if (mnum >= info->nmethods)
+                       break;
+
+               while (fgets (buf, sizeof (buf), f)) {
+                       int newoffset;
+
+                       ++i;
+                       if (i + 2 >= info->total_lines) {
+                               info->total_lines += 100;
+                               info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
+                               g_assert (info->moffsets);
+                       }
+
+                       if (strstr (buf, "}")) {
+                               offset = -1;
+                               break;
+                       }
+
+                       if (sscanf (buf, " IL_%x:", &newoffset)) {
+                               offset = newoffset;
+                               if (!offset)
+                                       pos = i;
+                       }
+
+                       info->moffsets [i] = offset;
+               }
+               /* g_print ("method %d found at %d\n", mnum, pos); */
+               info->mlines [mnum] = pos;
+       }
+       fclose (f);
+
+       for (idx = 1; idx <= table->rows; idx++) {
+               guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
+               MonoMethod *method = mono_get_method (info->image, token, NULL);
+
+               if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+                   (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
+                   (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
+                       continue;
+
+               if (method->wrapper_type != MONO_WRAPPER_NONE)
+                       continue;
+
+               generate_il_offsets (info, method);
+       }
+}
+
+void
+_mono_debug_generate_line_number (MonoDebugMethodInfo *minfo, guint32 address, guint32 offset, int debug)
+{
+       int i;
+
+       if (debug)
+               g_message (G_STRLOC ": searching IL offset %x", offset);
+
+       for (i = minfo->num_il_offsets - 1; i >= 0; i--) {
+               MonoDebugLineNumberEntry *lne;
+
+               if (minfo->il_offsets [i].offset > offset)
+                       continue;
+
+               if (debug)
+                       g_message (G_STRLOC ": found entry %d: offset = %x, row = %d",
+                                  i, minfo->il_offsets [i].offset, minfo->il_offsets [i].row);
+
+               if (minfo->jit->line_numbers->len) {
+                       MonoDebugLineNumberEntry last = g_array_index (
+                               minfo->jit->line_numbers, MonoDebugLineNumberEntry,
+                               minfo->jit->line_numbers->len - 1);
+
+                       /* Avoid writing more than one entry for the same line. */
+                       if (minfo->il_offsets [i].row == last.line) {
+                               if (debug)
+                                       g_message (G_STRLOC ": skipping line: line = %d, last line = %d, "
+                                                  "last address = %x, address = %x, "
+                                                  "last offset = %x, offset = %x",
+                                                  last.line, minfo->il_offsets [i].row,
+                                                  last.address, address, last.offset, offset);
+
+                               return;
+                       }
+               }
+
+               if (debug)
+                       g_message (G_STRLOC ": writing entry: line = %d, offfset = %x, address = %x",
+                                  minfo->il_offsets [i].row, offset, address);
+
+               lne = g_new0 (MonoDebugLineNumberEntry, 1);
+               lne->address = address;
+               lne->offset = offset;
+               lne->line = minfo->il_offsets [i].row;
+
+               g_array_append_val (minfo->jit->line_numbers, *lne);
+               return;
+       }
+}
+
+AssemblyDebugInfo *
+_mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image)
+{
+       return g_hash_table_lookup (debug->images, image);
+}
+
+static AssemblyDebugInfo *
+mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image)
+{
+       AssemblyDebugInfo *info;
+       MonoAssembly **ptr;
+
+       info = _mono_debug_get_image (debug, image);
+       if (info != NULL)
+               return info;
+
+       debug->dirty = TRUE;
+
+       info = g_new0 (AssemblyDebugInfo, 1);
+       info->image = image;
+       info->image->ref_count++;
+       info->name = g_strdup (image->assembly_name);
+       info->format = debug->format;
+       info->handle = debug;
+       info->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                              NULL, (GDestroyNotify) free_method_info);
+       info->wrapper_methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                                      NULL, (GDestroyNotify) free_wrapper_info);
+
+       g_hash_table_insert (debug->images, image, info);
+
+       info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
+       info->mlines = g_new0 (int, info->nmethods);
+
+       for (ptr = image->references; ptr && *ptr; ptr++)
+               mono_debug_add_assembly (*ptr, NULL);
+
+       if (image->assembly->dynamic)
+               return info;
+
+       switch (info->format) {
+       case MONO_DEBUG_FORMAT_STABS:
+       case MONO_DEBUG_FORMAT_DWARF2:
+               if (debug->flags & MONO_DEBUG_FLAGS_INSTALL_IL_FILES) {
+                       gchar *dirname = g_path_get_dirname (image->name);
+                       info->ilfile = g_strdup_printf ("%s/%s.il", dirname, info->name);
+                       g_free (dirname);
+               } else
+                       info->ilfile = g_strdup_printf ("%s.il", info->name);
+               info->source_file = debug->source_files->len;
+               g_ptr_array_add (debug->source_files, info->ilfile);
+               break;
+       case MONO_DEBUG_FORMAT_MONO:
+               info->symfile = mono_debug_open_mono_symbol_file (info->image, running_in_the_mono_debugger);
+               mono_debugger_symbol_file_table_generation++;
+               break;
+
+       default:
+               break;
+       }
+
+       if (debug->format != MONO_DEBUG_FORMAT_MONO)
+               debug_load_method_lines (info);
+
+       return info;
+}
+
+void
+mono_debug_write_symbols (MonoDebugHandle *debug)
+{
+       if (!debug || !debug->dirty)
+               return;
+
+       release_symbol_file_table ();
+       
+       switch (debug->format) {
+       case MONO_DEBUG_FORMAT_STABS:
+               mono_debug_write_stabs (debug);
+               break;
+       case MONO_DEBUG_FORMAT_DWARF2:
+               mono_debug_write_dwarf2 (debug);
+               break;
+       case MONO_DEBUG_FORMAT_MONO:
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       debug->dirty = FALSE;
+}
+
+void
+mono_debug_make_symbols (void)
+{
+       if (!mono_debug_handle || !mono_debug_handle->dirty)
+               return;
+       
+       switch (mono_debug_handle->format) {
+       case MONO_DEBUG_FORMAT_STABS:
+               mono_debug_write_stabs (mono_debug_handle);
+               break;
+       case MONO_DEBUG_FORMAT_DWARF2:
+               mono_debug_write_dwarf2 (mono_debug_handle);
+               break;
+       case MONO_DEBUG_FORMAT_MONO:
+               mono_debug_update_symbol_file_table ();
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       mono_debug_handle->dirty = FALSE;
+}
+
+static void
+mono_debug_close_assembly (AssemblyDebugInfo* info)
+{
+       switch (info->format) {
+       case MONO_DEBUG_FORMAT_MONO:
+               if (info->symfile != NULL)
+                       mono_debug_close_mono_symbol_file (info->symfile);
+               break;
+       default:
+               break;
+       }
+       g_hash_table_destroy (info->methods);
+       g_hash_table_destroy (info->wrapper_methods);
+       g_free (info->mlines);
+       g_free (info->moffsets);
+       g_free (info->name);
+       g_free (info->ilfile);
+       g_free (info->filename);
+       g_free (info->objfile);
+       g_free (info);
+}
+
+void
+mono_debug_cleanup (void)
+{
+       release_symbol_file_table ();
+
+       if (!mono_debug_handle)
+               return;
+
+       if (mono_debug_handle->flags & MONO_DEBUG_FLAGS_UPDATE_ON_EXIT)
+               mono_debug_write_symbols (mono_debug_handle);
+
+       g_hash_table_destroy (mono_debug_handle->images);
+       g_ptr_array_free (mono_debug_handle->source_files, FALSE);
+       g_hash_table_destroy (mono_debug_handle->type_hash);
+       g_free (mono_debug_handle->producer_name);
+       g_free (mono_debug_handle->name);
+       g_free (mono_debug_handle);
+
+       mono_debug_handle = NULL;
+}
+
+guint32
+mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass)
+{
+       guint index, i;
+
+       mono_class_init (klass);
+
+       index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass));
+       if (index)
+               return index;
+
+       debug->dirty = TRUE;
+
+       index = ++debug->next_klass_idx;
+       g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index));
+
+       if (klass->enumtype)
+               return index;
+
+       switch (klass->byval_arg.type) {
+       case MONO_TYPE_CLASS:
+               if (klass->parent)
+                       mono_debug_get_type (debug, klass->parent);
+
+               for (i = 0; i < klass->method.count; i++) {
+                       MonoMethod *method = klass->methods [i];
+                       MonoType *ret_type = NULL;
+                       int j;
+
+                       if (!MONO_TYPE_IS_VOID (method->signature->ret))
+                               ret_type = method->signature->ret;
+
+                       if (ret_type) {
+                               MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
+                               mono_debug_get_type (debug, ret_klass);
+                       }
+
+                       for (j = 0; j < method->signature->param_count; j++) {
+                               MonoType *sub_type = method->signature->params [j];
+                               MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
+                               mono_debug_get_type (debug, sub_klass);
+                       }
+               }
+               // fall through
+       case MONO_TYPE_VALUETYPE:
+               for (i = 0; i < klass->field.count; i++) {
+                       MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
+                       mono_debug_get_type (debug, subclass);
+               }
+               break;
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_SZARRAY:
+               mono_debug_get_type (debug, klass->element_class);
+               break;
+       default:
+               break;
+       }
+
+       return index;
+}
+
+static gint32
+il_offset_from_address (MonoDebugMethodInfo *minfo, guint32 address)
+{
+       int i;
+
+       if (!minfo->jit || !minfo->jit->line_numbers)
+               return -1;
+
+       for (i = minfo->jit->line_numbers->len - 1; i >= 0; i--) {
+               MonoDebugLineNumberEntry lne = g_array_index (
+                       minfo->jit->line_numbers, MonoDebugLineNumberEntry, i);
+
+               if (lne.address <= address)
+                       return lne.offset;
+       }
+
+       return -1;
+}
+
+void
+mono_debug_add_type (MonoClass *klass)
+{
+       AssemblyDebugInfo* info;
+
+       if (!mono_debug_handle)
+               return;
+
+       info = _mono_debug_get_image (mono_debug_handle, klass->image);
+       g_assert (info);
+
+       if (mono_debug_handle->format != MONO_DEBUG_FORMAT_MONO)
+               return;
+
+       if (info->symfile) {
+               mono_debug_lock ();
+               mono_debug_symfile_add_type (info->symfile, klass);
+               mono_debugger_event (MONO_DEBUGGER_EVENT_TYPE_ADDED, info->symfile, klass);
+               mono_debug_unlock ();
+       }
+}
+
+struct LookupMethodData
+{
+       MonoDebugMethodInfo *minfo;
+       MonoMethod *method;
+};
+
+static void
+lookup_method_func (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+       struct LookupMethodData *data = (struct LookupMethodData *) user_data;
+
+       if (data->minfo)
+               return;
+
+       if (info->symfile)
+               data->minfo = mono_debug_find_method (info->symfile, data->method);
+       else
+               data->minfo = g_hash_table_lookup (info->methods, data->method);
+}
+
+MonoDebugMethodInfo *
+_mono_debug_lookup_method (MonoMethod *method)
+{
+       struct LookupMethodData data = { NULL, method };
+
+       if (!mono_debug_handle)
+               return NULL;
+
+       g_hash_table_foreach (mono_debug_handle->images, lookup_method_func, &data);
+       return data.minfo;
+}
+
+void
+mono_debug_add_wrapper (MonoMethod *method, MonoMethod *wrapper_method)
+{
+       MonoClass *klass = method->klass;
+       AssemblyDebugInfo* info;
+       MonoDebugMethodInfo *minfo;
+       DebugWrapperInfo *winfo;
+       MonoDebugMethodJitInfo *jit;
+
+       if (!mono_debug_handle)
+               return;
+
+       if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
+               return;
+
+       mono_class_init (klass);
+
+       info = _mono_debug_get_image (mono_debug_handle, klass->image);
+       g_assert (info);
+
+       minfo = _mono_debug_lookup_method (method);
+       if (!minfo || minfo->jit)
+               return;
+
+       winfo = g_hash_table_lookup (info->wrapper_methods, wrapper_method);
+       g_assert (winfo);
+
+       mono_debug_lock ();
+
+       mono_debug_handle->dirty = TRUE;
+
+       minfo->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1);
+       jit->code_start = winfo->code_start;
+       jit->code_size = winfo->code_size;
+       jit->prologue_end = 0;
+       jit->epilogue_begin = winfo->code_size;
+       jit->num_params = 0;
+       jit->wrapper_addr = method->addr;
+
+       if (info->symfile) {
+               mono_debug_symfile_add_method (info->symfile, method);
+               mono_debugger_event (MONO_DEBUGGER_EVENT_METHOD_ADDED, info->symfile, method);
+       }
+
+       mono_debug_unlock ();
+}
+
+gchar *
+mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number)
+{
+       MonoDebugMethodInfo *minfo = _mono_debug_lookup_method (method);
+
+       if (!minfo)
+               return NULL;
+
+       if (minfo->symfile) {
+               gint32 offset = il_offset_from_address (minfo, address);
+               
+               if (offset < 0)
+                       return NULL;
+
+               return mono_debug_find_source_location (minfo->symfile, method, offset, line_number);
+       }
+
+       return NULL;
+}
+
+gint32
+mono_debug_il_offset_from_address (MonoMethod *method, gint32 address)
+{
+       MonoDebugMethodInfo *minfo;
+
+       if (address < 0)
+               return -1;
+
+       minfo = _mono_debug_lookup_method (method);
+       if (!minfo || !minfo->il_offsets)
+               return -1;
+
+       return il_offset_from_address (minfo, address);
+}
+
+gint32
+mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset)
+{
+       MonoDebugMethodInfo *minfo;
+
+       if (il_offset < 0)
+               return -1;
+
+       minfo = _mono_debug_lookup_method (method);
+       if (!minfo || !minfo->il_offsets)
+               return -1;
+
+       return _mono_debug_address_from_il_offset (minfo, il_offset);
+}
+
+static void
+release_symbol_file_table ()
+{
+       MonoDebuggerSymbolFileTable *temp;
+
+       if (!mono_debugger_symbol_file_table)
+               return;
+
+       /*
+        * Caution: The debugger may access the memory pointed to by this variable
+        *          at any time.  It is very important to set the pointer to NULL
+        *          before freeing the area.
+        */
+
+       temp = mono_debugger_symbol_file_table;
+       mono_debugger_symbol_file_table = NULL;
+       g_free (mono_debugger_symbol_file_table);
+}
+
+static void
+update_symbol_file_table_count_func (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+
+       if (!info->symfile)
+               return;
+       if (info->format != MONO_DEBUG_FORMAT_MONO)
+               return;
+
+       ++ (* (int *) user_data);
+}
+
+struct SymfileTableData
+{
+       MonoDebuggerSymbolFileTable *symfile_table;
+       int index;
+};
+
+static void
+update_symbol_file_table_func (gpointer key, gpointer value, gpointer user_data)
+{
+       AssemblyDebugInfo *info = (AssemblyDebugInfo *) value;
+       struct SymfileTableData *data = (struct SymfileTableData *) user_data;
+
+       if (!info->symfile)
+               return;
+       if (info->format != MONO_DEBUG_FORMAT_MONO)
+               return;
+
+       data->symfile_table->symfiles [data->index++] = info->symfile;
+}
+
+int
+mono_debug_update_symbol_file_table (void)
+{
+       int count = 0;
+       MonoDebuggerSymbolFileTable *symfile_table;
+       struct SymfileTableData data;
+       guint32 size;
+
+       if (!mono_debug_handle)
+               return FALSE;
+
+       mono_debug_lock ();
+
+       g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_count_func, &count);
+
+       release_symbol_file_table ();
+
+       size = sizeof (MonoDebuggerSymbolFileTable) + count * sizeof (MonoSymbolFile *);
+       symfile_table = g_malloc0 (size);
+       symfile_table->magic = MONO_SYMBOL_FILE_DYNAMIC_MAGIC;
+       symfile_table->version = MONO_SYMBOL_FILE_DYNAMIC_VERSION;
+       symfile_table->total_size = size;
+       symfile_table->count = count;
+       symfile_table->generation = mono_debugger_symbol_file_table_generation;
+       symfile_table->global_symfile = mono_debugger_global_symbol_file;
+
+       data.symfile_table = symfile_table;
+       data.index = 0;
+
+       g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_func, &data);
+
+       mono_debugger_symbol_file_table = symfile_table;
+
+       mono_debug_unlock ();
+
+       return TRUE;
+}
+
+static GPtrArray *breakpoints = NULL;
+
+int
+mono_insert_breakpoint_full (MonoMethodDesc *desc, gboolean use_trampoline)
+{
+       static int last_breakpoint_id = 0;
+       MonoDebuggerBreakpointInfo *info;
+
+       info = g_new0 (MonoDebuggerBreakpointInfo, 1);
+       info->desc = desc;
+       info->use_trampoline = use_trampoline;
+       info->index = ++last_breakpoint_id;
+
+       if (!breakpoints)
+               breakpoints = g_ptr_array_new ();
+
+       g_ptr_array_add (breakpoints, info);
+
+       return info->index;
+}
+
+int
+mono_remove_breakpoint (int breakpoint_id)
+{
+       int i;
+
+       if (!breakpoints)
+               return 0;
+
+       for (i = 0; i < breakpoints->len; i++) {
+               MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i);
+
+               if (info->index != breakpoint_id)
+                       continue;
+
+               mono_method_desc_free (info->desc);
+               g_ptr_array_remove (breakpoints, info);
+               g_free (info);
+               return 1;
+       }
+
+       return 0;
+}
+
+int
+mono_insert_breakpoint (const gchar *method_name, gboolean include_namespace)
+{
+       MonoMethodDesc *desc;
+
+       desc = mono_method_desc_new (method_name, include_namespace);
+       if (!desc)
+               return 0;
+
+       return mono_insert_breakpoint_full (desc, running_in_the_mono_debugger);
+}
+
+int
+mono_method_has_breakpoint (MonoMethod* method, gboolean use_trampoline)
+{
+       int i;
+
+       if (!breakpoints || (method->wrapper_type != MONO_WRAPPER_NONE))
+               return 0;
+
+       for (i = 0; i < breakpoints->len; i++) {
+               MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i);
+
+               if (info->use_trampoline != use_trampoline)
+                       continue;
+
+               if (!mono_method_desc_full_match (info->desc, method))
+                       continue;
+
+               return info->index;
+       }
+
+       return 0;
+}
+
+void
+mono_debugger_trampoline_breakpoint_callback (void)
+{
+       mono_debugger_event (MONO_DEBUGGER_EVENT_BREAKPOINT_TRAMPOLINE, NULL, NULL);
+}
diff --git a/mono/mini/debug.h b/mono/mini/debug.h
new file mode 100644 (file)
index 0000000..844abb9
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This is a copy of mono/mono/jit/debug.h.
+ *
+ * Please do *not* modify this copy here, if you need to make changes, do them
+ * in the original and then copy it over again.
+ *
+ * All mini-specific stuff is in debug-mini.c.
+ */
+
+#ifndef __MONO_JIT_DEBUG_H__
+#define __MONO_JIT_DEBUG_H__
+
+#include <glib.h>
+#include <stdio.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/debug-mono-symfile.h>
+#include <mono/metadata/loader.h>
+
+typedef struct _MonoDebugHandle                        MonoDebugHandle;
+typedef struct _MonoDebuggerSymbolFileTable    MonoDebuggerSymbolFileTable;
+typedef struct _MonoDebuggerBreakpointInfo     MonoDebuggerBreakpointInfo;
+
+typedef struct _MonoDebuggerIOLayer            MonoDebuggerIOLayer;
+
+typedef enum {
+       MONO_DEBUG_FORMAT_NONE,
+       MONO_DEBUG_FORMAT_STABS,
+       MONO_DEBUG_FORMAT_DWARF2,
+       MONO_DEBUG_FORMAT_MONO
+} MonoDebugFormat;
+
+typedef enum {
+       MONO_DEBUGGER_EVENT_TYPE_ADDED,
+       MONO_DEBUGGER_EVENT_METHOD_ADDED,
+       MONO_DEBUGGER_EVENT_BREAKPOINT_TRAMPOLINE,
+       MONO_DEBUGGER_EVENT_THREAD_CREATED
+} MonoDebuggerEvent;
+
+#ifndef PLATFORM_WIN32
+
+/*
+ * Functions we export to the debugger.
+ */
+struct _MonoDebuggerIOLayer
+{
+       void (*InitializeCriticalSection) (WapiCriticalSection *section);
+       void (*DeleteCriticalSection) (WapiCriticalSection *section);
+       gboolean (*TryEnterCriticalSection) (WapiCriticalSection *section);
+       void (*EnterCriticalSection) (WapiCriticalSection *section);
+       void (*LeaveCriticalSection) (WapiCriticalSection *section);
+
+       guint32 (*WaitForSingleObject) (gpointer handle, guint32 timeout);
+       guint32 (*SignalObjectAndWait) (gpointer signal_handle, gpointer wait,
+                                       guint32 timeout, gboolean alertable);
+       guint32 (*WaitForMultipleObjects) (guint32 numobjects, gpointer *handles,
+                                          gboolean waitall, guint32 timeout);
+
+       gpointer (*CreateSemaphore) (WapiSecurityAttributes *security,
+                                    gint32 initial, gint32 max,
+                                    const guchar *name);
+       gboolean (*ReleaseSemaphore) (gpointer handle, gint32 count, gint32 *prevcount);
+
+       gpointer (*CreateThread) (WapiSecurityAttributes *security,
+                                 guint32 stacksize, WapiThreadStart start,
+                                 gpointer param, guint32 create, guint32 *tid);
+};
+
+extern MonoDebuggerIOLayer mono_debugger_io_layer;
+
+#endif
+
+extern void (*mono_debugger_event_handler) (MonoDebuggerEvent event, gpointer data, gpointer data2);
+
+extern MonoDebugFormat mono_debug_format;
+
+MonoDebugHandle* mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args);
+
+void           mono_debug_cleanup (void);
+
+void           mono_debug_add_wrapper (MonoMethod *method, MonoMethod *wrapper_method);
+
+void           mono_debug_add_type (MonoClass *klass);
+
+gchar *        mono_debug_source_location_from_address (MonoMethod *method, guint32 address,
+                                                       guint32 *line_number);
+
+gint32         mono_debug_il_offset_from_address (MonoMethod *method, gint32 address);
+
+gint32         mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset);
+
+int            mono_method_has_breakpoint (MonoMethod* method, gboolean use_trampoline);
+
+int            mono_insert_breakpoint (const gchar *method_name, gboolean include_namespace);
+
+int            mono_insert_breakpoint_full (MonoMethodDesc *desc, gboolean use_trampoline);
+
+int            mono_remove_breakpoint (int breakpint_id);
+
+void           mono_debugger_trampoline_breakpoint_callback (void);
+
+void           mono_debugger_event (MonoDebuggerEvent event, gpointer data, gpointer data2);
+
+gpointer       mono_debug_create_notification_function (gpointer *notification_address);
+
+void           mono_debug_init (int running_in_the_mono_debugger);
+void           mono_debug_lock (void);
+void           mono_debug_unlock (void);
+int            mono_debug_update_symbol_file_table (void);
+
+
+/* DEBUGGER PUBLIC FUNCTION:
+ *
+ * This is a public function which is supposed to be called from within a debugger
+ * each time the program stops. It's used to recreate the symbol file to tell the
+ * debugger about method addresses and such things. After calling this function,
+ * you must tell your debugger to reload its symbol file.
+ */
+void           mono_debug_make_symbols (void);
+
+void           mono_debug_write_symbols (MonoDebugHandle* debug);
+
+/*
+ * Address of the x86 trampoline code.  This is used by the debugger to check
+ * whether a method is a trampoline.
+ */
+extern guint8 *mono_generic_trampoline_code;
+
+/*
+ * Address of a special breakpoint code which is used by the debugger to get a breakpoint
+ * after compiling a method.
+ */
+extern guint8 *mono_breakpoint_trampoline_code;
+
+/* This is incremented each time the symbol table is modified.
+ * The debugger looks at this variable and if it has a higher value than its current
+ * copy of the symbol table, it must call debugger_update_symbol_file_table().
+ */
+extern guint32 mono_debugger_symbol_file_table_generation;
+extern guint32 mono_debugger_symbol_file_table_modified;
+
+/* Caution: This variable may be accessed at any time from the debugger;
+ *          it is very important not to modify the memory it is pointing to
+ *          without previously setting this pointer back to NULL.
+ */
+extern MonoDebuggerSymbolFileTable *mono_debugger_symbol_file_table;
+
+struct _MonoDebuggerSymbolFileTable {
+       guint64 magic;
+       guint32 version;
+       guint32 total_size;
+       guint32 count;
+       guint32 generation;
+       MonoGlobalSymbolFile *global_symfile;
+       MonoSymbolFile *symfiles [MONO_ZERO_LEN_ARRAY];
+};
+
+struct _MonoDebuggerBreakpointInfo {
+       guint32 index;
+       gboolean use_trampoline;
+       MonoMethodDesc *desc;
+};
+
+#endif /* __MONO_JIT_DEBUG_H__ */
diff --git a/mono/mini/debugger-main.c b/mono/mini/debugger-main.c
new file mode 100644 (file)
index 0000000..272d909
--- /dev/null
@@ -0,0 +1,28 @@
+#include "mini.h"
+
+int
+main (int argc, char **argv, char **envp)
+{
+       MonoDomain *domain;
+       const char *file;
+       int retval;
+
+       g_assert (argc >= 3);
+       file = argv [2];
+
+       g_set_prgname (file);
+
+       mini_parse_default_optimizations (argv [1]);
+
+       domain = mini_init (argv [0]);
+
+       mono_config_parse (NULL);
+       //mono_set_rootdir ();
+
+       retval = mono_debugger_main (domain, file, argc, argv, envp);
+
+       mini_cleanup (domain);
+
+       return retval;
+}
+
diff --git a/mono/mini/dominators.c b/mono/mini/dominators.c
new file mode 100644 (file)
index 0000000..f213c0a
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * dominators.c: Dominator computation on the control flow graph
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include <string.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini.h"
+
+//#define DEBUG_DOMINATORS
+
+/* the simpler, dumber algorithm */
+static void
+compute_dominators (MonoCompile *m) {
+       int change = TRUE;
+       int i, j, bitsize;
+       MonoBasicBlock *bb;
+       MonoBitSet *T;
+       char* mem;
+
+       g_assert (!(m->comp_done & MONO_COMP_DOM));
+
+       bitsize = mono_bitset_alloc_size (m->num_bblocks, 0);
+       /* the first is always the entry */
+       bb = m->bblocks [0];
+       mem = mono_mempool_alloc0 (m->mempool, bitsize * (m->num_bblocks + 1));
+       bb->dominators = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+
+       mem += bitsize;
+       mono_bitset_set (bb->dominators, 0);
+
+       T = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+       mem += bitsize;
+
+
+       for (i = 1; i < m->num_bblocks; ++i) {
+               bb = m->bblocks [i];
+               bb->dominators = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+               mem += bitsize;
+               mono_bitset_invert (bb->dominators);
+
+#ifdef DEBUG_DOMINATORS
+               printf ("BB%d IN: ", bb->block_num);
+               for (j = 0; j < bb->in_count; ++j) 
+                       printf ("%d ", bb->in_bb [j]->block_num);
+               printf ("\n");
+#endif
+       }
+
+       do {
+               change = FALSE;
+               for (i = 1; i < m->num_bblocks; ++i) {
+                       bb = m->bblocks [i];
+                       mono_bitset_set_all (T);
+                       for (j = 0; j < bb->in_count; ++j) {
+                               if (bb->in_bb [j]->dominators)
+                                       mono_bitset_intersection (T, bb->in_bb [j]->dominators);
+                       }
+                       mono_bitset_set (T, i);
+                       if (!mono_bitset_equal (T, bb->dominators)) {
+                               change = TRUE;
+                               mono_bitset_copyto (T, bb->dominators);
+                       }
+               }
+       } while (change);
+
+       m->comp_done |= MONO_COMP_DOM;
+
+#ifdef DEBUG_DOMINATORS
+       printf ("DTREE %s %d\n", mono_method_full_name (m->method, TRUE), 
+               ((MonoMethodNormal *)m->method)->header->num_clauses);
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = m->bblocks [i];
+               printf ("BB%d: ", bb->block_num);
+               mono_blockset_print (m, bb->dominators, NULL, -1);
+       }
+#endif
+}
+
+static void
+compute_idominators (MonoCompile* m) {
+       char *mem;
+       int bitsize, i, s, t;
+       MonoBitSet **T, *temp;
+       MonoBasicBlock *bb;
+
+       g_assert (!(m->comp_done & MONO_COMP_IDOM));
+
+       bitsize = mono_bitset_alloc_size (m->num_bblocks, 0);
+       mem = mono_mempool_alloc (m->mempool, bitsize * (m->num_bblocks + 1));
+       T = mono_mempool_alloc (m->mempool, sizeof (MonoBitSet*) * m->num_bblocks);
+
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = m->bblocks [i];
+               T [i] = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+               mono_bitset_copyto (bb->dominators, T [i]);
+               mono_bitset_clear (T [i], i);
+               if (mono_bitset_count (bb->dominators) - 1 != mono_bitset_count (T [i])) {
+                       mono_blockset_print (m, bb->dominators, "dominators", -1);
+                       mono_blockset_print (m, T [i], "T [i]", -1);
+                       g_error ("problem at %d (%d)\n", i, bb->dfn);
+               }
+               mem += bitsize;
+       }
+       temp = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+
+       for (i = 1; i < m->num_bblocks; ++i) {
+
+               temp = T [i];
+                       
+               mono_bitset_foreach_bit_rev (temp, s, m->num_bblocks) {
+
+                       mono_bitset_foreach_bit_rev (temp, t, m->num_bblocks) {
+                                               
+                               if (t == s)
+                                       continue;
+
+                               //if (mono_bitset_test_fast (T [s], t))
+                               if (mono_bitset_test_fast (m->bblocks [s]->dominators, t))
+                                       mono_bitset_clear (temp, t);
+                       }
+               }
+
+#ifdef DEBUG_DOMINATORS
+               printf ("IDOMSET BB%d %d: ", m->bblocks [i]->block_num, m->num_bblocks);
+               mono_blockset_print (m, T [i], NULL, -1);
+#endif
+       }
+
+       for (i = 1; i < m->num_bblocks; ++i) {
+               bb = m->bblocks [i];
+               s = mono_bitset_find_start (T [i]);
+               g_assert (s != -1);
+               /*fixme:mono_bitset_count does not really work */
+               //g_assert (mono_bitset_count (T [i]) == 1);
+               t = mono_bitset_find_first (T [i], s);
+               g_assert (t == -1 || t >=  m->num_bblocks);
+               bb->idom = m->bblocks [s];
+               bb->idom->dominated = g_list_prepend (bb->idom->dominated, bb);
+       }
+
+       m->comp_done |= MONO_COMP_IDOM;
+}
+
+static void
+postorder_visit (MonoBasicBlock *start, int *idx, MonoBasicBlock **array)
+{
+       int i;
+
+       /* we assume the flag was already cleared by the caller. */
+       start->flags |= BB_VISITED;
+       /*g_print ("visit %d at %p\n", *dfn, start->cil_code);*/
+       for (i = 0; i < start->out_count; ++i) {
+               if (start->out_bb [i]->flags & BB_VISITED)
+                       continue;
+               postorder_visit (start->out_bb [i], idx, array);
+       }
+       array [*idx] = start;
+       (*idx)++;
+}
+
+static void
+check_dominance_frontier (MonoBasicBlock *x, MonoBasicBlock *t)
+{
+       int i, j;
+
+       t->flags |= BB_VISITED;
+
+       if (mono_bitset_test_fast (t->dominators, x->dfn)) {
+               for (i = 0; i < t->out_count; ++i) {
+                       if (!(t->flags & BB_VISITED)) {
+                               int found = FALSE;
+                               check_dominance_frontier (x, t->out_bb [i]);
+                               
+                               for (j = 0; j < t->out_bb [i]->in_count; j++) {
+                                       if (t->out_bb [i]->in_bb [j] == t)
+                                               found = TRUE;
+                               }
+                               g_assert (found);
+                       }
+               }
+       } else {
+               if (!mono_bitset_test_fast (x->dfrontier, t->dfn)) {
+                       printf ("BB%d not in frontier of BB%d\n", t->block_num, x->block_num);
+                       g_assert_not_reached ();
+               }
+       }
+} 
+
+#if 0
+/* there is a bug in this code */
+static void
+compute_dominance_frontier_old (MonoCompile *m) {
+       int i, j, bitsize;
+       MonoBasicBlock **postorder;
+       MonoBasicBlock *bb, *z;
+       char *mem;
+
+       g_assert (!(m->comp_done & MONO_COMP_DFRONTIER));
+
+       postorder = mono_mempool_alloc (m->mempool, sizeof (MonoBasicBlock*) * m->num_bblocks);
+       i = 0;
+       postorder_visit (m->bb_entry, &i, postorder);
+       /*g_print ("postorder traversal:");
+       for (i = 0; i < m->num_bblocks; ++i)
+               g_print (" B%d", postorder [i]->dfn);
+       g_print ("\n");*/
+       
+       /* we could reuse the bitsets allocated in compute_idominators() */
+       bitsize = mono_bitset_alloc_size (m->num_bblocks, 0);
+       mem = mono_mempool_alloc0 (m->mempool, bitsize * m->num_bblocks);
+
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = postorder [i];
+               bb->dfrontier = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+               mem += bitsize;
+       }
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = postorder [i];
+               /* the local component */
+               for (j = 0; j < bb->out_count; ++j) {
+                       //if (bb->out_bb [j] != bb->idom)
+                       if (bb->out_bb [j]->idom != bb)
+                               mono_bitset_set (bb->dfrontier, bb->out_bb [j]->dfn);
+               }
+       }
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = postorder [i];
+               /* the up component */
+               if (bb->idom) {
+                       z = bb->idom;
+                       mono_bitset_foreach_bit (z->dfrontier, j, m->num_bblocks) {
+                               //if (m->bblocks [j] != bb->idom)
+                               if (m->bblocks [j]->idom != bb)
+                                       mono_bitset_set (bb->dfrontier, m->bblocks [j]->dfn);
+                       }
+               }
+       }
+
+       /* this is a check for the dominator frontier */
+       for (i = 0; i < m->num_bblocks; ++i) {
+               MonoBasicBlock *x = m->bblocks [i];
+
+               mono_bitset_foreach_bit ((x->dfrontier), j, (m->num_bblocks)) {
+                       MonoBasicBlock *w = m->bblocks [j];
+                       int k;
+                       /* x must not strictly dominates w */
+                       if (mono_bitset_test_fast (w->dominators, x->dfn) && w != x)
+                               g_assert_not_reached ();
+
+                       for (k = 0; k < m->num_bblocks; ++k)
+                               m->bblocks [k]->flags &= ~BB_VISITED;
+
+                       check_dominance_frontier (x, x);
+               }
+       }
+
+       m->comp_done |= MONO_COMP_DFRONTIER;
+}
+#endif
+
+/* this is an implementation of the dominance frontier algorithm described in
+ * "modern compiler implementation in C" written by Andrew W. Appel
+ */
+static void
+compute_dominance_frontier_appel (MonoCompile *m, int n) 
+{
+       int i, j;
+       MonoBasicBlock *bb;
+
+       bb = m->bblocks [n];
+       g_assert (!(bb->flags & BB_VISITED));
+       bb->flags |= BB_VISITED;
+
+       for (i = 0; i < bb->out_count; ++i) {
+               MonoBasicBlock *y = bb->out_bb [i];
+               if (y->idom != bb) {
+                       g_assert (!(mono_bitset_test_fast (y->dominators, bb->dfn) && bb->dfn != y->dfn));
+                       mono_bitset_set (bb->dfrontier, y->dfn);
+               }
+       }
+       
+       
+       for (i = 0; i < m->num_bblocks; ++i) {
+               MonoBasicBlock *c = m->bblocks [i];
+               if (c->idom == bb) {
+                       if (!(c->flags & BB_VISITED))
+                               compute_dominance_frontier_appel (m, c->dfn);
+                       mono_bitset_foreach_bit (c->dfrontier, j, m->num_bblocks) {
+                               MonoBasicBlock *w = m->bblocks [j];
+                               if (!(mono_bitset_test_fast (w->dominators, bb->dfn) && bb->dfn != w->dfn))
+                                       mono_bitset_set (bb->dfrontier, w->dfn);
+                       }
+               }
+       }
+}
+
+static void
+compute_dominance_frontier (MonoCompile *m) 
+{
+       MonoBasicBlock *bb;
+       char *mem;
+       int i, j, bitsize;
+
+       g_assert (!(m->comp_done & MONO_COMP_DFRONTIER));
+
+       for (i = 0; i < m->num_bblocks; ++i)
+               m->bblocks [i]->flags &= ~BB_VISITED;
+
+       /* we could reuse the bitsets allocated in compute_idominators() */
+       bitsize = mono_bitset_alloc_size (m->num_bblocks, 0);
+       mem = mono_mempool_alloc0 (m->mempool, bitsize * m->num_bblocks);
+       for (i = 0; i < m->num_bblocks; ++i) {
+               bb = m->bblocks [i];
+               bb->dfrontier = mono_bitset_mem_new (mem, m->num_bblocks, 0);
+               mem += bitsize;
+       }
+
+       compute_dominance_frontier_appel (m, 0);
+
+#if 0
+       for (i = 0; i < m->num_bblocks; ++i) {
+               MonoBasicBlock *x = m->bblocks [i];
+               
+               printf ("DFRONT %s BB%d: ", mono_method_full_name (m->method, TRUE), x->block_num);
+               mono_blockset_print (m, x->dfrontier, NULL, -1);
+       }
+#endif
+
+#if 1
+       /* this is a check for the dominator frontier */
+       for (i = 0; i < m->num_bblocks; ++i) {
+               MonoBasicBlock *x = m->bblocks [i];
+               
+               mono_bitset_foreach_bit ((x->dfrontier), j, (m->num_bblocks)) {
+                       MonoBasicBlock *w = m->bblocks [j];
+                       int k;
+                       /* x must not strictly dominates w */
+                       if (mono_bitset_test_fast (w->dominators, x->dfn) && w != x)
+                               g_assert_not_reached ();
+                       
+                       for (k = 0; k < m->num_bblocks; ++k)
+                               m->bblocks [k]->flags &= ~BB_VISITED;
+
+                       check_dominance_frontier (x, x);
+               }
+       }
+#endif
+
+       m->comp_done |= MONO_COMP_DFRONTIER;
+}
+
+void    
+mono_compile_dominator_info (MonoCompile *cfg, int dom_flags)
+{
+       if ((dom_flags & MONO_COMP_DOM) && !(cfg->comp_done & MONO_COMP_DOM))
+               compute_dominators (cfg);
+       if ((dom_flags & MONO_COMP_IDOM) && !(cfg->comp_done & MONO_COMP_IDOM))
+               compute_idominators (cfg);
+       if ((dom_flags & MONO_COMP_DFRONTIER) && !(cfg->comp_done & MONO_COMP_DFRONTIER))
+               compute_dominance_frontier (cfg);
+}
+
+static void
+df_set (MonoCompile *m, MonoBitSet* dest, MonoBitSet *set) 
+{
+       int i;
+
+       mono_bitset_clear_all (dest);
+       mono_bitset_foreach_bit (set, i, m->num_bblocks) {
+               mono_bitset_union (dest, m->bblocks [i]->dfrontier);
+       }
+}
+
+/* TODO: alloc tmp and D on the stack */
+MonoBitSet*
+mono_compile_iterated_dfrontier (MonoCompile *m, MonoBitSet *set) 
+{
+       MonoBitSet *result, *D;
+       int bitsize, change = TRUE;
+
+       bitsize = mono_bitset_alloc_size (m->num_bblocks, 0);
+       result = mono_bitset_mem_new (mono_mempool_alloc (m->mempool, bitsize), m->num_bblocks, 0);
+       D = mono_bitset_mem_new (mono_mempool_alloc (m->mempool, bitsize), m->num_bblocks, 0);
+
+       df_set (m, result, set);
+       do {
+               change = FALSE;
+               df_set (m, D, result);
+               mono_bitset_union (D, result);
+
+               if (!mono_bitset_equal (D, result)) {
+                       mono_bitset_copyto (D, result);
+                       change = TRUE;
+               }
+       } while (change);
+       
+       return result;
+}
+
+//#define DEBUG_NATURAL_LOOPS
+
+/*
+ * code to detect loops and loop nesting level
+ */
+void 
+mono_compute_natural_loops (MonoCompile *cfg)
+{
+       int i, j, k;
+
+       g_assert (!(cfg->comp_done & MONO_COMP_LOOPS));
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *n = cfg->bblocks [i];
+
+               for (j = 0; j < n->out_count; j++) {
+                       MonoBasicBlock *h = n->out_bb [j];
+                       /* check for back-edge from n to h */
+                       if (n != h && mono_bitset_test (n->dominators, h->dfn)) {
+                               GList *todo;
+
+                               /* already in loop_blocks? */
+                               if (h->loop_blocks && g_list_find (h->loop_blocks, n))
+                                       continue;
+                               
+                               todo = g_list_prepend (NULL, n);
+
+                               while (todo) {
+                                       MonoBasicBlock *cb = (MonoBasicBlock *)todo->data;
+                                       todo = g_list_delete_link (todo, todo);
+
+                                       if (g_list_find (h->loop_blocks, cb))
+                                               continue;
+
+                                       h->loop_blocks = g_list_prepend (h->loop_blocks, cb);
+                                       cb->nesting++;
+
+                                       for (k = 0; k < cb->in_count; k++) {
+                                               MonoBasicBlock *prev = cb->in_bb [k];
+                                               /* add all previous blocks */
+                                               if (prev != h && !g_list_find (h->loop_blocks, prev)) {
+                                                       todo = g_list_prepend (todo, prev);
+                                               }
+                                       }
+                               }
+
+                               /* add the header if not already there */
+                               if (!g_list_find (h->loop_blocks, h)) {
+                                       h->loop_blocks = g_list_prepend (h->loop_blocks, h);
+                                       h->nesting++;
+                               }
+                       }
+               }
+       }
+
+       cfg->comp_done |= MONO_COMP_LOOPS;
+       
+#ifdef DEBUG_NATURAL_LOOPS
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               if (cfg->bblocks [i]->loop_blocks) {
+                       MonoBasicBlock *h = (MonoBasicBlock *)cfg->bblocks [i]->loop_blocks->data;
+                       GList *l;
+                       printf ("LOOP START %d\n", h->block_num);
+                       for (l = h->loop_blocks; l; l = l->next) {
+                               MonoBasicBlock *cb = (MonoBasicBlock *)l->data;
+                               printf (" BB%d %d %p\n", cb->block_num, cb->nesting, cb->loop_blocks);
+                       }
+               }
+       }
+#endif
+
+}
+
diff --git a/mono/mini/driver.c b/mono/mini/driver.c
new file mode 100644 (file)
index 0000000..c06deaa
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * driver.c: The new mono JIT compiler.
+ *
+ * Author:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/loader.h>
+#include <mono/metadata/cil-coff.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/exception.h>
+#include <mono/metadata/opcodes.h>
+#include <mono/metadata/mono-endian.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/threads.h>
+#include <mono/metadata/marshal.h>
+#include <mono/metadata/socket-io.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/io-layer/io-layer.h>
+#include "mono/metadata/profiler.h"
+#include <mono/metadata/profiler-private.h>
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/environment.h>
+
+#include "mini.h"
+#include <string.h>
+#include <ctype.h>
+#include "inssel.h"
+#include "debug.h"
+
+static FILE *mini_stats_fd = NULL;
+
+static void mini_usage (void);
+
+typedef void (*OptFunc) (const char *p);
+
+/* keep in sync with enum in mini.h */
+typedef struct {
+       const char* name;
+       const char* desc;
+       const OptFunc func;
+} OptName;
+
+static const OptName 
+opt_names [] = {
+       {"peephole", "Peephole postpass"},
+       {"branch",   "Branch optimizations"},
+       {"inline",   "Inline method calls"},
+       {"cfold",    "Constant folding"},
+       {"consprop", "Constant propagation"},
+       {"copyprop", "Copy propagation"},
+       {"deadce",   "Dead code elimination"},
+       {"linears",  "Linear scan global reg allocation"},
+       {"cmov",     "Conditional moves"},
+       {"shared",   "Emit per-domain code"},
+       {"sched",    "Instruction scheduling"},
+       {"instrins", "Intrinsic method implementations"},
+       {"tailc",    "Tail recursion and tail calls"},
+       {"loop",     "Loop related optimizations"},
+       {"fcmov",    "Fast x86 FP compares"}
+};
+
+static guint32
+parse_optimizations (const char* p)
+{
+       /* the default value */
+       guint32 opt = MONO_OPT_PEEPHOLE | MONO_OPT_CFOLD /* | MONO_OPT_CONSPROP | MONO_OPT_INLINE*/ | MONO_OPT_BRANCH | /* | MONO_OPT_SAHRED |*/ MONO_OPT_LINEARS;
+       const char *n;
+       int i, invert, len;
+
+       /* call out to cpu detection code here that sets the defaults ... */
+       opt |= mono_arch_cpu_optimizazions ();
+       if (!p)
+               return opt;
+
+       while (*p) {
+               if (*p == '-') {
+                       p++;
+                       invert = TRUE;
+               } else {
+                       invert = FALSE;
+               }
+               for (i = 0; i < G_N_ELEMENTS (opt_names); ++i) {
+                       n = opt_names [i].name;
+                       len = strlen (n);
+                       if (strncmp (p, n, len) == 0) {
+                               if (invert)
+                                       opt &= ~ (1 << i);
+                               else
+                                       opt |= 1 << i;
+                               p += len;
+                               if (*p == ',') {
+                                       p++;
+                                       break;
+                               } else if (*p == '=') {
+                                       p++;
+                                       if (opt_names [i].func)
+                                               opt_names [i].func (p);
+                                       while (*p && *p++ != ',');
+                                       break;
+                               }
+                               /* error out */
+                               break;
+                       }
+               }
+               if (i == G_N_ELEMENTS (opt_names)) {
+                       if (strncmp (p, "all", 3) == 0) {
+                               if (invert)
+                                       opt = 0;
+                               else
+                                       opt = ~(MONO_OPT_SAHRED);
+                               p += 3;
+                               if (*p == ',')
+                                       p++;
+                       } else {
+                               fprintf (stderr, "Invalid optimization name `%s'\n", p);
+                               exit (1);
+                       }
+               }
+       }
+       return opt;
+}
+
+typedef struct {
+       const char* name;
+       const char* desc;
+       MonoGraphOptions value;
+} GraphName;
+
+static const GraphName 
+graph_names [] = {
+       {"cfg",      "Control Flow Graph (CFG)" ,               MONO_GRAPH_CFG},
+       {"dtree",    "Dominator Tree",                          MONO_GRAPH_DTREE},
+       {"code",     "CFG showing code",                        MONO_GRAPH_CFG_CODE},
+       {"ssa",      "CFG showing code after SSA translation",  MONO_GRAPH_CFG_SSA},
+       {"optcode",  "CFG showing code after IR optimizations", MONO_GRAPH_CFG_OPTCODE}
+};
+
+static MonoGraphOptions
+mono_parse_graph_options (const char* p)
+{
+       const char *n;
+       int i, len;
+
+       for (i = 0; i < G_N_ELEMENTS (graph_names); ++i) {
+               n = graph_names [i].name;
+               len = strlen (n);
+               if (strncmp (p, n, len) == 0)
+                       return graph_names [i].value;
+       }
+
+       fprintf (stderr, "Invalid graph name provided: %s\n", p);
+       exit (1);
+}
+
+int
+mini_parse_default_optimizations (const char* p)
+{
+       guint32 opt;
+
+       opt = parse_optimizations (p);
+       return opt;
+}
+
+static char*
+opt_descr (guint32 flags) {
+       GString *str = g_string_new ("");
+       int i, need_comma;
+
+       need_comma = 0;
+       for (i = 0; i < G_N_ELEMENTS (opt_names); ++i) {
+               if (flags & (1 << i)) {
+                       if (need_comma)
+                               g_string_append_c (str, ',');
+                       g_string_append (str, opt_names [i].name);
+                       need_comma = 1;
+               }
+       }
+       return g_string_free (str, FALSE);
+}
+
+static const guint32
+opt_sets [] = {
+       0,
+       MONO_OPT_PEEPHOLE,
+       MONO_OPT_BRANCH,
+       MONO_OPT_CFOLD,
+       MONO_OPT_FCMOV,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_CFOLD,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP
+};
+
+typedef int (*TestMethod) (void);
+
+#if 0
+static void
+domain_dump_native_code (MonoDomain *domain) {
+       // need to poke into the domain, move to metadata/domain.c
+       // need to empty jit_info_table and code_mp
+}
+#endif
+
+static int
+mini_regression (MonoImage *image, int verbose) {
+       guint32 i, opt, opt_flags;
+       MonoMethod *method;
+       MonoCompile *cfg;
+       char *n;
+       int result, expected, failed, cfailed, run, code_size, total;
+       TestMethod func;
+       GTimer *timer = g_timer_new ();
+
+       if (mini_stats_fd) {
+               fprintf (mini_stats_fd, "$stattitle = \'Mono Benchmark Results (various optimizations)\';\n");
+
+               fprintf (mini_stats_fd, "$graph->set_legend(qw(");
+               for (opt = 0; opt < G_N_ELEMENTS (opt_sets); opt++) {
+                       opt_flags = opt_sets [opt];
+                       n = opt_descr (opt_flags);
+                       if (!n [0])
+                               n = (char *)"none";
+                       if (opt)
+                               fprintf (mini_stats_fd, " ");
+                       fprintf (mini_stats_fd, "%s", n);
+               
+
+               }
+               fprintf (mini_stats_fd, "));\n");
+
+               fprintf (mini_stats_fd, "@data = (\n");
+               fprintf (mini_stats_fd, "[");
+       }
+
+       /* load the metadata */
+       for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
+                       method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
+               mono_class_init (method->klass);
+
+               if (!strncmp (method->name, "test_", 5) && mini_stats_fd) {
+                       fprintf (mini_stats_fd, "\"%s\",", method->name);
+               }
+       }
+       if (mini_stats_fd)
+               fprintf (mini_stats_fd, "],\n");
+
+
+       total = 0;
+       for (opt = 0; opt < G_N_ELEMENTS (opt_sets); ++opt) {
+               double elapsed, comp_time, start_time;
+               opt_flags = opt_sets [opt];
+               mini_set_defaults (verbose, opt_flags);
+               n = opt_descr (opt_flags);
+               g_print ("Test run: image=%s, opts=%s\n", image->name, n);
+               g_free (n);
+               cfailed = failed = run = code_size = 0;
+               comp_time = elapsed = 0.0;
+
+               /* fixme: ugly hack - delete all previously compiled methods */
+               for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
+                       method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
+                       method->info = NULL;
+               }
+
+               g_timer_start (timer);
+               if (mini_stats_fd)
+                       fprintf (mini_stats_fd, "[");
+               for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
+                       method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
+                       if (strncmp (method->name, "test_", 5) == 0) {
+                               expected = atoi (method->name + 5);
+                               run++;
+                               start_time = g_timer_elapsed (timer, NULL);
+                               comp_time -= start_time; 
+                               cfg = mini_method_compile (method, opt_flags, mono_root_domain, 0);
+                               comp_time += g_timer_elapsed (timer, NULL);
+                               if (cfg) {
+                                       if (verbose >= 2)
+                                               g_print ("Running '%s' ...\n", method->name);
+#ifdef MONO_USE_AOT_COMPILER
+                                       if (!(func = mono_aot_get_method (method)))
+#endif
+                                               func = (TestMethod)cfg->native_code;
+                                       result = func ();
+                                       if (result != expected) {
+                                               failed++;
+                                               if (verbose)
+                                                       g_print ("Test '%s' failed result (got %d, expected %d).\n", method->name, result, expected);
+                                       }
+                                       code_size += cfg->code_len;
+                                       mono_destroy_compile (cfg);
+
+                                       if (mono_trace_coverage) {
+                                               MonoCoverageInfo *cov = mono_get_coverage_info (method);
+
+                                               if (cov) {
+                                                       int k;
+                                                       printf ("COVERAGE INFO %s\n", mono_method_full_name (method, TRUE));
+                                               
+                                                       for (k = 0; k < cov->entries; k++) {
+                                                               printf ("  BBLOCK %3d %d\n", cov->data [k].iloffset, cov->data [k].count);
+                                                       }
+                                               }
+                                       }
+
+                               } else {
+                                       cfailed++;
+                                       if (verbose)
+                                               g_print ("Test '%s' failed compilation.\n", method->name);
+                               }
+                               if (mini_stats_fd)
+                                       fprintf (mini_stats_fd, "%f, ", 
+                                                g_timer_elapsed (timer, NULL) - start_time);
+                       }
+               }
+               if (mini_stats_fd)
+                       fprintf (mini_stats_fd, "],\n");
+               g_timer_stop (timer);
+               elapsed = g_timer_elapsed (timer, NULL);
+               g_print ("Results: total tests: %d, failed: %d, cfailed: %d (pass: %.2f%%)\n", 
+                       run, failed, cfailed, 100.0*(run-failed-cfailed)/run);
+               g_print ("Elapsed time: %f secs (%f, %f), Code size: %d\n\n", elapsed, 
+                        elapsed - comp_time, comp_time, code_size);
+               total += failed + cfailed;
+       }
+
+       if (mini_stats_fd) {
+               fprintf (mini_stats_fd, ");\n");
+               fflush (mini_stats_fd);
+       }
+
+       g_timer_destroy (timer);
+       return total;
+}
+
+static int
+mini_regression_list (int verbose, int count, char *images [])
+{
+       int i, total;
+       MonoAssembly *ass;
+       
+       total = 0;
+       for (i = 0; i < count; ++i) {
+               ass = mono_assembly_open (images [i], NULL);
+               if (!ass) {
+                       g_warning ("failed to load assembly: %s", images [i]);
+                       continue;
+               }
+               total += mini_regression (ass->image, verbose);
+               mono_assembly_close (ass);
+       }
+       return total;
+}
+
+enum {
+       DO_BENCH,
+       DO_REGRESSION,
+       DO_COMPILE,
+       DO_EXEC,
+       DO_DRAW,
+};
+
+static void
+compile_all_methods (MonoAssembly *ass, int verbose)
+{
+       MonoImage *image = ass->image;
+       MonoMethod *method;
+       int i, count = 0;
+
+       for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
+               method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
+               if (method->flags & METHOD_ATTRIBUTE_ABSTRACT)
+                       continue;
+               if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
+                       continue;
+
+               count++;
+               if (verbose) {
+                       char * desc = mono_method_full_name (method, TRUE);
+                       g_print ("Compiling %d %s\n", count, desc + 3);
+                       g_free (desc);
+               }
+               mono_compile_method (method);
+       }
+
+}
+
+/**
+ * mono_jit_exec:
+ * @assembly: reference to an assembly
+ * @argc: argument count
+ * @argv: argument vector
+ *
+ * Start execution of a program.
+ */
+static int 
+mono_jit_exec (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[])
+{
+       MonoImage *image = assembly->image;
+       MonoMethod *method;
+
+       method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
+
+       return mono_runtime_run_main (method, argc, argv, NULL);
+}
+
+typedef struct 
+{
+       MonoDomain *domain;
+       const char *file;
+       int argc;
+       char **argv;
+       guint32 opts;
+} MainThreadArgs;
+
+static void main_thread_handler (gpointer user_data)
+{
+       MainThreadArgs *main_args = user_data;
+       MonoAssembly *assembly;
+       
+       assembly = mono_domain_assembly_open (main_args->domain, main_args->file);
+       if (!assembly){
+               fprintf (stderr, "Can not open image %s\n", main_args->file);
+               exit (1);
+       }
+
+       if (mono_compile_aot) {
+               int res = mono_compile_assembly (assembly, main_args->opts);
+               printf ("AOT RESULT %d\n", res);
+       } else {
+               mono_jit_exec (main_args->domain, assembly, main_args->argc, main_args->argv);
+       }
+}
+
+static void
+mini_usage (void)
+{
+       int i;
+
+       fprintf (stderr,
+               "Usage is: mini [options] assembly\n\n"
+               "Runtime and JIT debugging:\n"
+               "    --compile METHOD       Just compile METHOD in assembly\n"
+               "    --ncompile N           Number of times to compile METHOD (default: 1)\n"
+               "    --regression           Runs the regression test contained in the assembly\n"
+               "    --trace                Enable tracing\n"
+               "    --compile-all          Compiles all the methods in the assembly\n"
+               "    --breakonex            Inserts a breakpoint on exceptions\n"
+               "    --break METHOD         Inserts a breakpoint at METHOD entry\n"
+               "\n"
+               "Development:\n"
+               "    --statfile FILE        Sets the stat file to FILE\n"
+               "    --aot                  Compiles the assembly to native code\n"
+               "    --coverage             Performs coverage analysis\n"
+               "    --profile              Runs in profiling mode\n"
+               "    --graph[=TYPE] METHOD  Draws a graph of the specified method:\n");
+       
+       for (i = 0; i < G_N_ELEMENTS (graph_names); ++i) {
+               fprintf (stderr, "                           %-10s %s\n", graph_names [i].name, graph_names [i].desc);
+       }
+
+       fprintf (stderr,
+               "\n"
+               "Runtime:\n"
+               "    --config FILE          Loads FILE as the Mono config\n"
+               "    --verbose, -v          Increases the verbosity level\n"
+               "    --help, -h             Show usage information\n"
+               "    --optimize=OPT         Turns on a specific optimization:\n");
+
+       for (i = 0; i < G_N_ELEMENTS (opt_names); ++i)
+               fprintf (stderr, "                           %-10s %s\n", opt_names [i].name, opt_names [i].desc);
+}
+
+int
+mini_main (int argc, char* argv[]) {
+       MainThreadArgs main_args;
+       MonoAssembly *assembly;
+       MonoMethodDesc *desc;
+       MonoMethod *method;
+       MonoCompile *cfg;
+       MonoDomain *domain;
+       const char* aname, *mname = NULL;
+       char *config_file = NULL;
+       int i, count = 1;
+       guint32 opt, action = DO_EXEC;
+       MonoGraphOptions mono_graph_options = 0;
+       int mini_verbose = 0;
+
+       opt = parse_optimizations (NULL);
+
+       for (i = 1; i < argc; ++i) {
+               if (argv [i] [0] != '-')
+                       break;
+               if (strcmp (argv [i], "--regression") == 0) {
+                       action = DO_REGRESSION;
+               } else if (strcmp (argv [i], "--verbose") == 0 || strcmp (argv [i], "-v") == 0) {
+                       mini_verbose++;
+               } else if (strcmp (argv [i], "--help") == 0 || strcmp (argv [i], "-h") == 0) {
+                       mini_usage ();
+                       return 0;
+               } else if (strncmp (argv [i], "--statfile", 10) == 0) {
+                       mini_stats_fd = fopen (argv [++i], "w+");
+               } else if (strncmp (argv [i], "--optimize=", 11) == 0) {
+                       opt = parse_optimizations (argv [i] + 11);
+               } else if (strncmp (argv [i], "-O=", 3) == 0) {
+                       opt = parse_optimizations (argv [i] + 3);
+               } else if (strcmp (argv [i], "--config") == 0) {
+                       config_file = argv [++i];
+               } else if (strcmp (argv [i], "--ncompile") == 0) {
+                       count = atoi (argv [++i]);
+                       action = DO_BENCH;
+               } else if (strcmp (argv [i], "--trace") == 0) {
+                       mono_jit_trace_calls = TRUE;
+               } else if (strcmp (argv [i], "--breakonex") == 0) {
+                       mono_break_on_exc = TRUE;
+               } else if (strcmp (argv [i], "--break") == 0) {
+                       if (!mono_insert_breakpoint (argv [++i], FALSE))
+                               g_error ("Invalid method name '%s'", argv [i]);
+               } else if (strcmp (argv [i], "--stats") == 0) {
+                       mono_jit_stats.enabled = TRUE;
+               } else if (strcmp (argv [i], "--aot") == 0) {
+                       mono_compile_aot = TRUE;
+               } else if (strcmp (argv [i], "--coverage") == 0) {
+                       mono_trace_coverage = TRUE;
+               } else if (strcmp (argv [i], "--compile-all") == 0) {
+                       action = DO_COMPILE;
+               } else if (strcmp (argv [i], "--profile") == 0) {
+                       mono_jit_profile = TRUE;
+                       mono_profiler_install_simple ();
+               } else if (strcmp (argv [i], "--compile") == 0) {
+                       mname = argv [++i];
+                       action = DO_BENCH;
+               } else if (strncmp (argv [i], "--graph=", 8) == 0) {
+                       mono_graph_options = mono_parse_graph_options (argv [i] + 8);
+                       mname = argv [++i];
+                       action = DO_DRAW;
+               } else if (strcmp (argv [i], "--graph") == 0) {
+                       mname = argv [++i];
+                       mono_graph_options = MONO_GRAPH_CFG;
+                       action = DO_DRAW;
+               } else {
+                       fprintf (stderr, "Unknown command line option: %s", argv [i]);
+                       return 1;
+               }
+       }
+
+       mini_set_defaults (mini_verbose, opt);
+       domain = mini_init (argv [0]);
+
+       switch (action) {
+       case DO_REGRESSION:
+               if (mini_regression_list (mini_verbose, argc -i, argv + i)) {
+                       g_print ("Regression ERRORS!\n");
+                       mini_cleanup (domain);
+                       return 1;
+               }
+               mini_cleanup (domain);
+               return 0;
+       case DO_BENCH:
+               if (argc - i != 1 || mname == NULL) {
+                       g_print ("Usage: mini --ncompile num --compile method assembly\n");
+                       mini_cleanup (domain);
+                       return 1;
+               }
+               aname = argv [i];
+               break;
+       case DO_COMPILE:
+               if (argc - i != 1) {
+                       fprintf (stderr, "Missing assembly name in --compile-all");
+                       mini_cleanup (domain);
+                       return 1;
+               }
+               aname = argv [i];
+               break;
+       case DO_DRAW:
+               if (argc - i != 1 || mname == NULL) {
+                       fprintf (stderr, "Usage: mini --graph[=TYPE] method assembly\n");
+                       mini_cleanup (domain);
+                       return 1;
+               }
+               aname = argv [i];
+               break;
+       default:
+               if (argc - i < 1) {
+                       mini_usage ();
+                       mini_cleanup (domain);
+                       return 1;
+               }
+               aname = argv [i];
+               break;
+       }
+
+       assembly = mono_assembly_open (aname, NULL);
+       if (!assembly) {
+               fprintf (stderr, "cannot open assembly %s\n", aname);
+               mini_cleanup (domain);
+               return 2;
+       }
+
+       if (mono_compile_aot || action == DO_EXEC) {
+               g_set_prgname (aname);
+               mono_config_parse (config_file);
+               //mono_set_rootdir ();
+
+               main_args.domain = domain;
+               main_args.file = aname;         
+               main_args.argc = argc - i;
+               main_args.argv = argv + i;
+               main_args.opts = opt;
+            
+               mono_runtime_exec_managed_code (domain, main_thread_handler, &main_args);
+               mini_cleanup (domain);
+               /* Look up return value from System.Environment.ExitCode */
+               i = mono_environment_exitcode_get ();
+               return i;
+       } else if (action == DO_COMPILE) {
+               compile_all_methods (assembly, mini_verbose);
+               mini_cleanup (domain);
+               return 0;
+       }
+       desc = mono_method_desc_new (mname, 0);
+       if (!desc) {
+               g_print ("Invalid method name %s\n", mname);
+               mini_cleanup (domain);
+               return 3;
+       }
+       method = mono_method_desc_search_in_image (desc, assembly->image);
+       if (!method) {
+               g_print ("Cannot find method %s\n", mname);
+               mini_cleanup (domain);
+               return 3;
+       }
+
+       if (action == DO_DRAW) {
+               int part = 0;
+
+               switch (mono_graph_options) {
+               case MONO_GRAPH_DTREE:
+                       part = 1;
+                       opt |= MONO_OPT_LOOP;
+                       break;
+               case MONO_GRAPH_CFG_CODE:
+                       part = 1;
+                       break;
+               case MONO_GRAPH_CFG_SSA:
+                       part = 2;
+                       break;
+               case MONO_GRAPH_CFG_OPTCODE:
+                       part = 3;
+                       break;
+               default:
+                       break;
+               }
+
+               cfg = mini_method_compile (method, opt, mono_root_domain, part);
+               if ((mono_graph_options & MONO_GRAPH_CFG_SSA) && !(cfg->comp_done & MONO_COMP_SSA)) {
+                       g_warning ("no SSA info available (use -O=deadce)");
+                       return 1;
+               }
+               mono_draw_graph (cfg, mono_graph_options);
+               mono_destroy_compile (cfg);
+
+       } else if (action == DO_BENCH) {
+               if (mini_stats_fd) {
+                       const char *n;
+                       double no_opt_time = 0.0;
+                       GTimer *timer = g_timer_new ();
+                       fprintf (mini_stats_fd, "$stattitle = \'Compilations times for %s\';\n", 
+                                mono_method_full_name (method, TRUE));
+                       fprintf (mini_stats_fd, "@data = (\n");
+                       fprintf (mini_stats_fd, "[");
+                       for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) {
+                               opt = opt_sets [i];
+                               n = opt_descr (opt);
+                               if (!n [0])
+                                       n = "none";
+                               fprintf (mini_stats_fd, "\"%s\",", n);
+                       }
+                       fprintf (mini_stats_fd, "],\n[");
+
+                       for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) {
+                               int j;
+                               double elapsed;
+                               opt = opt_sets [i];
+                               g_timer_start (timer);
+                               for (j = 0; j < count; ++j) {
+                                       cfg = mini_method_compile (method, opt, mono_root_domain, 0);
+                                       mono_destroy_compile (cfg);
+                               }
+                               g_timer_stop (timer);
+                               elapsed = g_timer_elapsed (timer, NULL);
+                               if (!opt)
+                                       no_opt_time = elapsed;
+                               fprintf (mini_stats_fd, "%f, ", elapsed);
+                       }
+                       fprintf (mini_stats_fd, "]");
+                       if (no_opt_time > 0.0) {
+                               fprintf (mini_stats_fd, ", \n[");
+                               for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) 
+                                       fprintf (mini_stats_fd, "%f,", no_opt_time);
+                               fprintf (mini_stats_fd, "]");
+                       }
+                       fprintf (mini_stats_fd, ");\n");
+               } else {
+                       for (i = 0; i < count; ++i) {
+                               cfg = mini_method_compile (method, opt, mono_root_domain, 0);
+                               mono_destroy_compile (cfg);
+                       }
+               }
+       } else {
+               cfg = mini_method_compile (method, opt, mono_root_domain, 0);
+               mono_destroy_compile (cfg);
+       }
+
+       mini_cleanup (domain);
+       return 0;
+}
+
diff --git a/mono/mini/emullong.brg b/mono/mini/emullong.brg
new file mode 100644 (file)
index 0000000..fe65083
--- /dev/null
@@ -0,0 +1,86 @@
+%%
+
+#
+# emullong.brg: emulate 64 bit instructions with 32bit instructions
+#
+# we dont use this, its just a prototype!
+#
+# Author:
+#   Dietmar Maurer (dietmar@ximian.com)
+#
+# (C) 2002 Ximian, Inc.
+#
+
+lreg: OP_I8CONST {
+       MonoInst *inst;
+
+       MONO_INST_NEW (s, inst, OP_ICONST);
+       inst->dreg = state->reg1;
+       inst->inst_c0 = tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, inst);
+
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->reg2;
+       tree->inst_c0 = tree->inst_c1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+lreg: CEE_CONV_I8 (OP_ICONST) {
+       MonoInst *inst;
+
+       inst = state->left->tree;
+       inst->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, inst);
+       
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->reg2;
+
+       if (inst->inst_c0 >= 0)
+               tree->inst_c0 = 0;
+       else
+               tree->inst_c0 = -1;
+
+       mono_bblock_add_inst (s->cbb, inst);
+}
+
+lreg: CEE_CONV_U8 (OP_ICONST) {
+       MonoInst *inst;
+
+       inst = state->left->tree;
+       inst->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, inst);
+
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->reg2;
+       tree->inst_c0 = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+lreg: CEE_CONV_U8 (reg) {
+       MonoInst *inst;
+       
+       if (state->reg1 != state->left->reg1) {
+               MONO_INST_NEW (s, inst, OP_MOVE);
+               inst->dreg = state->reg1;
+               inst->sreg1 = state->left->reg1;
+               mono_bblock_add_inst (s->cbb, inst);
+       }
+
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->reg2;
+       tree->inst_c0 = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+lreg: CONV_OVF_I8_UN (OP_ICONST) {
+       state->left->tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, state->left->tree);
+
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->reg2;
+       tree->inst_c0 = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+
+%%
diff --git a/mono/mini/exceptions-ppc.c b/mono/mini/exceptions-ppc.c
new file mode 100644 (file)
index 0000000..07679c7
--- /dev/null
@@ -0,0 +1,953 @@
+/*
+ * exception.c: exception support
+ *
+ * Authors:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <signal.h>
+
+#include <mono/arch/x86/x86-codegen.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/threads.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/exception.h>
+
+#include "mini.h"
+#include "mini-ppc.h"
+#include "debug-private.h"
+
+typedef struct sigcontext MonoContext;
+
+/* we have the stack pointer, not the base pointer in sigcontext */
+#define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->sc_ir = (int)ip; } while (0); 
+#define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->sc_sp = (int)bp; } while (0); 
+
+#define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->sc_ir))
+#define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->sc_sp))
+
+/* disbale this for now */
+#undef MONO_USE_EXC_TABLES
+
+#ifdef MONO_USE_EXC_TABLES
+
+/*************************************/
+/*    STACK UNWINDING STUFF          */
+/*************************************/
+
+/* These definitions are from unwind-dw2.c in glibc 2.2.5 */
+
+/* For x86 */
+#define DWARF_FRAME_REGISTERS 17
+
+typedef struct frame_state
+{
+  void *cfa;
+  void *eh_ptr;
+  long cfa_offset;
+  long args_size;
+  long reg_or_offset[DWARF_FRAME_REGISTERS+1];
+  unsigned short cfa_reg;
+  unsigned short retaddr_column;
+  char saved[DWARF_FRAME_REGISTERS+1];
+} frame_state;
+
+#if 0
+
+static long
+get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
+{
+       switch (dwarf_regnum) {
+       case X86_EAX:
+               return ctx->eax;
+       case X86_EBX:
+               return ctx->ebx;
+       case X86_ECX:
+               return ctx->ecx;
+       case X86_EDX:
+               return ctx->edx;
+       case X86_ESI:
+               return ctx->esi;
+       case X86_EDI:
+               return ctx->edi;
+       case X86_EBP:
+               return ctx->ebp;
+       case X86_ESP:
+               return ctx->esp;
+       default:
+               g_assert_not_reached ();
+       }
+
+       return 0;
+}
+
+static void
+set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
+{
+       switch (dwarf_regnum) {
+       case X86_EAX:
+               ctx->eax = value;
+               break;
+       case X86_EBX:
+               ctx->ebx = value;
+               break;
+       case X86_ECX:
+               ctx->ecx = value;
+               break;
+       case X86_EDX:
+               ctx->edx = value;
+               break;
+       case X86_ESI:
+               ctx->esi = value;
+               break;
+       case X86_EDI:
+               ctx->edi = value;
+               break;
+       case X86_EBP:
+               ctx->ebp = value;
+               break;
+       case X86_ESP:
+               ctx->esp = value;
+               break;
+       case 8:
+               ctx->eip = value;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+typedef struct frame_state * (*framesf) (void *, struct frame_state *);
+
+static framesf frame_state_for = NULL;
+
+static gboolean inited = FALSE;
+
+typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
+
+static get_backtrace_symbols_type get_backtrace_symbols = NULL;
+
+static void
+init_frame_state_for (void)
+{
+       GModule *module;
+
+       /*
+        * There are two versions of __frame_state_for: one in libgcc.a and the
+        * other in glibc.so. We need the version from glibc.
+        * For more info, see this:
+        * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
+        */
+       if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
+       
+               if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
+                       frame_state_for = NULL;
+
+               if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
+                       get_backtrace_symbols = NULL;
+                       frame_state_for = NULL;
+               }
+
+               g_module_close (module);
+       }
+
+       inited = TRUE;
+}
+
+#endif
+
+/* mono_arch_has_unwind_info:
+ *
+ * Tests if a function has an DWARF exception table able to restore
+ * all caller saved registers. 
+ */
+gboolean
+mono_arch_has_unwind_info (MonoMethod *method)
+{
+#if 0
+       struct frame_state state_in;
+       struct frame_state *res;
+
+       if (!inited) 
+               init_frame_state_for ();
+       
+       if (!frame_state_for)
+               return FALSE;
+
+       g_assert (method->addr);
+
+       memset (&state_in, 0, sizeof (state_in));
+
+       /* offset 10 is just a guess, but it works for all methods tested */
+       if ((res = frame_state_for ((char *)method->addr + 10, &state_in))) {
+
+               if (res->saved [X86_EBX] != 1 ||
+                   res->saved [X86_EDI] != 1 ||
+                   res->saved [X86_EBP] != 1 ||
+                   res->saved [X86_ESI] != 1) {
+                       return FALSE;
+               }
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+#else
+       return FALSE;
+#endif
+}
+
+struct stack_frame
+{
+  void *next;
+  void *return_address;
+};
+
+static MonoJitInfo *
+ppc_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, 
+                        struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
+{
+#if 0
+       struct stack_frame *frame;
+       gpointer max_stack;
+       MonoJitInfo *ji;
+       struct frame_state state_in;
+       struct frame_state *res;
+
+       if (trace)
+               *trace = NULL;
+
+       if (!inited) 
+               init_frame_state_for ();
+
+       if (!frame_state_for)
+               return FALSE;
+
+       frame = MONO_CONTEXT_GET_BP (ctx);
+
+       max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
+
+       *new_ctx = *ctx;
+
+       memset (&state_in, 0, sizeof (state_in));
+
+       while ((gpointer)frame->next < (gpointer)max_stack) {
+               gpointer ip, addr = frame->return_address;
+               void *cfa;
+               char *tmp, **symbols;
+
+               if (trace) {
+                       ip = MONO_CONTEXT_GET_IP (new_ctx);
+                       symbols = get_backtrace_symbols (&ip, 1);
+                       if (*trace)
+                               tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
+                       else
+                               tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
+
+                       free (symbols);
+                       g_free (*trace);
+                       *trace = tmp;
+               }
+
+               if ((res = frame_state_for (addr, &state_in))) {        
+                       int i;
+
+                       cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
+                       frame = (struct stack_frame *)((gint8*)cfa - 8);
+                       for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
+                               int how = res->saved[i];
+                               long val;
+                               g_assert ((how == 0) || (how == 1));
+                       
+                               if (how == 1) {
+                                       val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
+                                       set_sigcontext_reg (new_ctx, i, val);
+                               }
+                       }
+                       new_ctx->esp = (long)cfa;
+
+                       if (res->saved [X86_EBX] == 1 &&
+                           res->saved [X86_EDI] == 1 &&
+                           res->saved [X86_EBP] == 1 &&
+                           res->saved [X86_ESI] == 1 &&
+                           (ji = mono_jit_info_table_find (domain, frame->return_address))) {
+                               //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
+                               return ji;
+                       }
+
+               } else {
+                       //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx)));
+
+                       MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
+                       frame = frame->next;
+                       MONO_CONTEXT_SET_BP (new_ctx, frame);
+
+                       /* stop if !frame or when we detect an unexpected managed frame */
+                       if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
+                               if (trace) {
+                                       g_free (*trace);
+                                       *trace = NULL;
+                               }
+                               return NULL;
+                       }
+               }
+       }
+
+       //if (!lmf)
+       //g_assert_not_reached ();
+
+       if (trace) {
+               g_free (*trace);
+               *trace = NULL;
+       }
+#endif
+       return NULL;
+}
+
+#endif
+
+/*
+ * arch_get_restore_context:
+ *
+ * Returns a pointer to a method which restores a previously saved sigcontext.
+ */
+static gpointer
+arch_get_restore_context (void)
+{
+       static guint8 *start = NULL;
+       guint8 *code;
+
+       if (start)
+               return start;
+
+#if 0
+       /* restore_contect (struct sigcontext *ctx) */
+       /* we do not restore X86_EAX, X86_EDX */
+
+       start = code = g_malloc (1024);
+       
+       /* load ctx */
+       x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
+
+       /* get return address, stored in EDX */
+       x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
+       /* restore EBX */
+       x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
+       /* restore EDI */
+       x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+       /* restore ESI */
+       x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
+       /* restore ESP */
+       x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
+       /* restore EBP */
+       x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+
+       /* jump to the saved IP */
+       x86_jump_reg (code, X86_EDX);
+
+#endif
+       return start;
+}
+
+/*
+ * arch_get_call_filter:
+ *
+ * Returns a pointer to a method which calls an exception filter. We
+ * also use this function to call finally handlers (we pass NULL as 
+ * @exc object in this case).
+ */
+static gpointer
+arch_get_call_filter (void)
+{
+       static guint8 start [64];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */
+       code = start;
+
+#if 0
+       x86_push_reg (code, X86_EBP);
+       x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
+       x86_push_reg (code, X86_EBX);
+       x86_push_reg (code, X86_EDI);
+       x86_push_reg (code, X86_ESI);
+
+       /* load ctx */
+       x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
+       /* load eip */
+       x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
+       /* save EBP */
+       x86_push_reg (code, X86_EBP);
+       /* push exc */
+       x86_push_membase (code, X86_EBP, 16);
+       /* set new EBP */
+       x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+       /* restore registers used by global register allocation (EBX & ESI) */
+       x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
+       x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
+       x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+       /* save the ESP - this is used by endfinally */
+       x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
+       /* call the handler */
+       x86_call_reg (code, X86_ECX);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+       /* restore EBP */
+       x86_pop_reg (code, X86_EBP);
+       /* restore saved regs */
+       x86_pop_reg (code, X86_ESI);
+       x86_pop_reg (code, X86_EDI);
+       x86_pop_reg (code, X86_EBX);
+       x86_leave (code);
+       x86_ret (code);
+
+       g_assert ((code - start) < 64);
+#endif
+       return start;
+}
+
+static void
+throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
+                unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
+                unsigned long eip,  unsigned long esp)
+{
+       static void (*restore_context) (struct sigcontext *);
+       struct sigcontext ctx;
+
+       if (!restore_context)
+               restore_context = arch_get_restore_context ();
+
+       /* adjust eip so that it point into the call instruction */
+       eip -= 1;
+
+#if 0
+       ctx.SC_ESP = esp;
+       ctx.SC_EIP = eip;
+       ctx.SC_EBP = ebp;
+       ctx.SC_EDI = edi;
+       ctx.SC_ESI = esi;
+       ctx.SC_EBX = ebx;
+       ctx.SC_EDX = edx;
+       ctx.SC_ECX = ecx;
+       ctx.SC_EAX = eax;
+#endif 
+       mono_arch_handle_exception (&ctx, exc, FALSE);
+       restore_context (&ctx);
+
+       g_assert_not_reached ();
+}
+
+/**
+ * arch_get_throw_exception:
+ *
+ * Returns a function pointer which can be used to raise 
+ * exceptions. The returned function has the following 
+ * signature: void (*func) (MonoException *exc); 
+ * For example to raise an arithmetic exception you can use:
+ *
+ * x86_push_imm (code, mono_get_exception_arithmetic ()); 
+ * x86_call_code (code, arch_get_throw_exception ()); 
+ *
+ */
+gpointer 
+mono_arch_get_throw_exception (void)
+{
+       static guint8 start [24];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       code = start;
+
+#if 0
+       x86_push_reg (code, X86_ESP);
+       x86_push_membase (code, X86_ESP, 4); /* IP */
+       x86_push_membase (code, X86_ESP, 12); /* exception */
+       x86_push_reg (code, X86_EBP);
+       x86_push_reg (code, X86_EDI);
+       x86_push_reg (code, X86_ESI);
+       x86_push_reg (code, X86_EBX);
+       x86_push_reg (code, X86_EDX);
+       x86_push_reg (code, X86_ECX);
+       x86_push_reg (code, X86_EAX);
+       x86_call_code (code, throw_exception);
+       /* we should never reach this breakpoint */
+       x86_breakpoint (code);
+
+       g_assert ((code - start) < 24);
+#endif
+       return start;
+}
+
+/**
+ * arch_get_throw_exception_by_name:
+ *
+ * Returns a function pointer which can be used to raise 
+ * corlib exceptions. The returned function has the following 
+ * signature: void (*func) (char *exc_name); 
+ * For example to raise an arithmetic exception you can use:
+ *
+ * x86_push_imm (code, "ArithmeticException"); 
+ * x86_call_code (code, arch_get_throw_exception_by_name ()); 
+ *
+ */
+gpointer 
+mono_arch_get_throw_exception_by_name (void)
+{
+       static guint8 start [32];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       code = start;
+#if 0
+       x86_push_membase (code, X86_ESP, 4); /* exception name */
+       x86_push_imm (code, "System");
+       x86_push_imm (code, mono_defaults.exception_class->image);
+       x86_call_code (code, mono_exception_from_name);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
+       /* save the newly create object (overwrite exception name)*/
+       x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
+       x86_jump_code (code, mono_arch_get_throw_exception ());
+
+       g_assert ((code - start) < 32);
+#endif
+       return start;
+}      
+
+static MonoArray *
+glist_to_array (GList *list) 
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray *res;
+       int len, i;
+
+       if (!list)
+               return NULL;
+
+       len = g_list_length (list);
+       res = mono_array_new (domain, mono_defaults.int_class, len);
+
+       for (i = 0; list; list = list->next, i++)
+               mono_array_set (res, gpointer, i, list->data);
+
+       return res;
+}
+
+/* mono_arch_find_jit_info:
+ *
+ * This function is used to gather information from @ctx. It return the 
+ * MonoJitInfo of the corresponding function, unwinds one stack frame and
+ * stores the resulting context into @new_ctx. It also stores a string 
+ * describing the stack location into @trace (if not NULL), and modifies
+ * the @lmf if necessary. @native_offset return the IP offset from the 
+ * start of the function or -1 if that info is not available.
+ */
+static MonoJitInfo *
+mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *ctx, 
+                        MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
+                        gboolean *managed)
+{
+       MonoJitInfo *ji;
+       gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+
+       ji = mono_jit_info_table_find (domain, ip);
+
+       if (trace)
+               *trace = NULL;
+
+       if (native_offset)
+               *native_offset = -1;
+
+       if (managed)
+               *managed = FALSE;
+
+       if (ji != NULL) {
+               char *source_location, *tmpaddr, *fname;
+               gint32 address, iloffset;
+               int offset;
+
+               *new_ctx = *ctx;
+
+               if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
+                       /* remove any unused lmf */
+                       *lmf = (*lmf)->previous_lmf;
+               }
+
+               address = (char *)ip - (char *)ji->code_start;
+
+               if (native_offset)
+                       *native_offset = address;
+
+               if (managed)
+                       if (!ji->method->wrapper_type)
+                               *managed = TRUE;
+
+               if (trace) {
+                       if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
+                               mono_debug_make_symbols ();
+
+                       source_location = mono_debug_source_location_from_address (ji->method, address, NULL);
+                       iloffset = mono_debug_il_offset_from_address (ji->method, address);
+                       source_location = NULL;
+                       iloffset = -1;
+
+                       if (iloffset < 0)
+                               tmpaddr = g_strdup_printf ("<0x%05x>", address);
+                       else
+                               tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
+               
+                       fname = mono_method_full_name (ji->method, TRUE);
+
+                       if (source_location)
+                               *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
+                       else
+                               *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
+
+                       g_free (fname);
+                       g_free (source_location);
+                       g_free (tmpaddr);
+               }
+#if 0                          
+               offset = -1;
+               /* restore caller saved registers */
+               if (ji->used_regs & X86_EBX_MASK) {
+                       new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
+                       offset--;
+               }
+               if (ji->used_regs & X86_EDI_MASK) {
+                       new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
+                       offset--;
+               }
+               if (ji->used_regs & X86_ESI_MASK) {
+                       new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
+               }
+
+               new_ctx->SC_ESP = ctx->SC_EBP;
+               /* we substract 1, so that the IP points into the call instruction */
+               new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
+               new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
+#endif
+               return ji;
+#ifdef MONO_USE_EXC_TABLES
+       } else if ((ji = ppc_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
+               return ji;
+#endif
+       } else if (*lmf) {
+               
+               *new_ctx = *ctx;
+
+               if (!(*lmf)->method)
+                       return (gpointer)-1;
+
+               if (trace)
+                       *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
+               
+               ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip);
+               g_assert (ji != NULL);
+
+#if 0
+               new_ctx->SC_ESI = (*lmf)->esi;
+               new_ctx->SC_EDI = (*lmf)->edi;
+               new_ctx->SC_EBX = (*lmf)->ebx;
+               new_ctx->SC_EBP = (*lmf)->ebp;
+               new_ctx->SC_EIP = (*lmf)->eip;
+               /* the lmf is always stored on the stack, so the following
+                * expression points to a stack location which can be used as ESP */
+               new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
+#endif
+               *lmf = (*lmf)->previous_lmf;
+
+               return ji;
+               
+       }
+
+       return NULL;
+}
+
+MonoArray *
+ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray *res;
+       MonoArray *ta = exc->trace_ips;
+       int i, len;
+       
+       len = mono_array_length (ta);
+
+       res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
+
+       for (i = skip; i < len; i++) {
+               MonoJitInfo *ji;
+               MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
+               gpointer ip = mono_array_get (ta, gpointer, i);
+
+               ji = mono_jit_info_table_find (domain, ip);
+               g_assert (ji != NULL);
+
+               sf->method = mono_method_get_object (domain, ji->method, NULL);
+               sf->native_offset = (char *)ip - (char *)ji->code_start;
+
+               sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
+               sf->il_offset = -1;
+
+               if (need_file_info) {
+                       gchar *filename;
+                       
+                       filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
+                       filename = NULL;
+
+                       sf->filename = filename? mono_string_new (domain, filename): NULL;
+                       sf->column = 0;
+
+                       g_free (filename);
+               }
+
+               mono_array_set (res, gpointer, i, sf);
+       }
+
+       return res;
+}
+
+void
+mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;
+       MonoJitInfo *ji;
+       gint native_offset, il_offset;
+       gboolean managed;
+
+       MonoContext ctx, new_ctx;
+
+       MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
+       MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
+
+       while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
+               
+               ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
+               g_assert (ji);
+
+               if (ji == (gpointer)-1)
+                       return;
+
+               il_offset = mono_debug_il_offset_from_address (ji->method, native_offset);
+
+               if (func (ji->method, native_offset, il_offset, managed, user_data))
+                       return;
+               
+               ctx = new_ctx;
+       }
+}
+
+MonoBoolean
+ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
+                         MonoReflectionMethod **method, 
+                         gint32 *iloffset, gint32 *native_offset,
+                         MonoString **file, gint32 *line, gint32 *column)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;
+       MonoJitInfo *ji;
+       MonoContext ctx, new_ctx;
+
+       MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
+       MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
+
+       skip++;
+
+       do {
+               ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
+
+               ctx = new_ctx;
+               
+               if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
+                       return FALSE;
+
+               if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)
+                       continue;
+
+               skip--;
+
+       } while (skip >= 0);
+
+       *method = mono_method_get_object (domain, ji->method, NULL);
+       *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset);
+       *iloffset = -1;
+
+       if (need_file_info) {
+               gchar *filename;
+
+               filename = mono_debug_source_location_from_address (ji->method, *native_offset, line);
+               filename = NULL;
+
+               *file = filename? mono_string_new (domain, filename): NULL;
+               *column = 0;
+
+               g_free (filename);
+       }
+
+       return TRUE;
+}
+
+/**
+ * arch_handle_exception:
+ * @ctx: saved processor state
+ * @obj: the exception object
+ * @test_only: only test if the exception is caught, but dont call handlers
+ *
+ *
+ */
+gboolean
+mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitInfo *ji;
+       static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;            
+       GList *trace_ips = NULL;
+
+       g_assert (ctx != NULL);
+       if (!obj) {
+               MonoException *ex = mono_get_exception_null_reference ();
+               ex->message = mono_string_new (domain, 
+                       "Object reference not set to an instance of an object");
+               obj = (MonoObject *)ex;
+       } 
+
+       g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
+
+       if (!call_filter)
+               call_filter = arch_get_call_filter ();
+
+       g_assert (jit_tls->end_of_stack);
+       g_assert (jit_tls->abort_func);
+
+       if (!test_only) {
+               MonoContext ctx_cp = *ctx;
+               if (mono_jit_trace_calls)
+                       g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
+               if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) {
+                       if (mono_break_on_exc) {
+                               if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
+                                       mono_debug_make_symbols ();
+                               G_BREAKPOINT ();
+                       }
+                       mono_unhandled_exception (obj);
+               }
+       }
+
+       while (1) {
+               MonoContext new_ctx;
+               char *trace = NULL;
+               
+               ji = mono_arch_find_jit_info (domain, jit_tls, ctx, &new_ctx, 
+                                             test_only ? &trace : NULL, &lmf, NULL, NULL);
+               if (!ji) {
+                       g_warning ("Exception inside function without unwind info");
+                       g_assert_not_reached ();
+               }
+
+               if (ji != (gpointer)-1) {
+                       
+                       if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
+                               char *tmp, *strace;
+
+                               trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
+
+                               if (!((MonoException*)obj)->stack_trace)
+                                       strace = g_strdup ("");
+                               else
+                                       strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
+                       
+                               tmp = g_strdup_printf ("%s%s\n", strace, trace);
+                               g_free (strace);
+
+                               ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
+
+                               g_free (tmp);
+                       }
+
+                       if (ji->num_clauses) {
+                               int i;
+                               
+                               g_assert (ji->clauses);
+                       
+                               for (i = 0; i < ji->num_clauses; i++) {
+                                       MonoJitExceptionInfo *ei = &ji->clauses [i];
+
+                                       if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
+                                           MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
+                                               /* catch block */
+                                               if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
+                                                    mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
+                                                   ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
+                                                     call_filter (ctx, ei->data.filter, obj)))) {
+                                                       if (test_only) {
+                                                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                                                               g_list_free (trace_ips);
+                                                               return TRUE;
+                                                       }
+                                                       if (mono_jit_trace_calls)
+                                                               g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
+                                                       MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+                                                       *((gpointer *)(MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
+                                                       jit_tls->lmf = lmf;
+                                                       return 0;
+                                               }
+                                       }
+                               }
+
+                               /* no handler found - we need to call all finally handlers */
+                               if (!test_only) {
+                                       for (i = 0; i < ji->num_clauses; i++) {
+                                               MonoJitExceptionInfo *ei = &ji->clauses [i];
+                                               
+                                               if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
+                                                   MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
+                                                   (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
+                                                       if (mono_jit_trace_calls)
+                                                               g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
+                                                       call_filter (ctx, ei->handler_start, NULL);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               g_free (trace);
+                       
+               *ctx = new_ctx;
+
+               if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
+                       if (!test_only) {
+                               jit_tls->lmf = lmf;
+                               jit_tls->abort_func (obj);
+                               g_assert_not_reached ();
+                       } else {
+                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                               g_list_free (trace_ips);
+                               return FALSE;
+                       }
+               }
+       }
+
+       g_assert_not_reached ();
+}
+
diff --git a/mono/mini/exceptions-x86.c b/mono/mini/exceptions-x86.c
new file mode 100644 (file)
index 0000000..2fb177e
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * exception.c: exception support
+ *
+ * Authors:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <signal.h>
+#include <string.h>
+
+#include <mono/arch/x86/x86-codegen.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/threads.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/exception.h>
+
+#include "mini.h"
+#include "mini-x86.h"
+#include "debug-private.h"
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# define SC_EAX sc_eax
+# define SC_EBX sc_ebx
+# define SC_ECX sc_ecx
+# define SC_EDX sc_edx
+# define SC_EBP sc_ebp
+# define SC_EIP sc_eip
+# define SC_ESP sc_esp
+# define SC_EDI sc_edi
+# define SC_ESI sc_esi
+#else
+# define SC_EAX eax
+# define SC_EBX ebx
+# define SC_ECX ecx
+# define SC_EDX edx
+# define SC_EBP ebp
+# define SC_EIP eip
+# define SC_ESP esp
+# define SC_EDI edi
+# define SC_ESI esi
+#endif
+
+typedef struct sigcontext MonoContext;
+
+#define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0); 
+#define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0); 
+
+#define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP))
+#define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP))
+
+#ifdef MONO_USE_EXC_TABLES
+
+/*************************************/
+/*    STACK UNWINDING STUFF          */
+/*************************************/
+
+/* These definitions are from unwind-dw2.c in glibc 2.2.5 */
+
+/* For x86 */
+#define DWARF_FRAME_REGISTERS 17
+
+typedef struct frame_state
+{
+  void *cfa;
+  void *eh_ptr;
+  long cfa_offset;
+  long args_size;
+  long reg_or_offset[DWARF_FRAME_REGISTERS+1];
+  unsigned short cfa_reg;
+  unsigned short retaddr_column;
+  char saved[DWARF_FRAME_REGISTERS+1];
+} frame_state;
+
+static long
+get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
+{
+       switch (dwarf_regnum) {
+       case X86_EAX:
+               return ctx->eax;
+       case X86_EBX:
+               return ctx->ebx;
+       case X86_ECX:
+               return ctx->ecx;
+       case X86_EDX:
+               return ctx->edx;
+       case X86_ESI:
+               return ctx->esi;
+       case X86_EDI:
+               return ctx->edi;
+       case X86_EBP:
+               return ctx->ebp;
+       case X86_ESP:
+               return ctx->esp;
+       default:
+               g_assert_not_reached ();
+       }
+
+       return 0;
+}
+
+static void
+set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
+{
+       switch (dwarf_regnum) {
+       case X86_EAX:
+               ctx->eax = value;
+               break;
+       case X86_EBX:
+               ctx->ebx = value;
+               break;
+       case X86_ECX:
+               ctx->ecx = value;
+               break;
+       case X86_EDX:
+               ctx->edx = value;
+               break;
+       case X86_ESI:
+               ctx->esi = value;
+               break;
+       case X86_EDI:
+               ctx->edi = value;
+               break;
+       case X86_EBP:
+               ctx->ebp = value;
+               break;
+       case X86_ESP:
+               ctx->esp = value;
+               break;
+       case 8:
+               ctx->eip = value;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+typedef struct frame_state * (*framesf) (void *, struct frame_state *);
+
+static framesf frame_state_for = NULL;
+
+static gboolean inited = FALSE;
+
+typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
+
+static get_backtrace_symbols_type get_backtrace_symbols = NULL;
+
+static void
+init_frame_state_for (void)
+{
+       GModule *module;
+
+       /*
+        * There are two versions of __frame_state_for: one in libgcc.a and the
+        * other in glibc.so. We need the version from glibc.
+        * For more info, see this:
+        * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
+        */
+       if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
+       
+               if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
+                       frame_state_for = NULL;
+
+               if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
+                       get_backtrace_symbols = NULL;
+                       frame_state_for = NULL;
+               }
+
+               g_module_close (module);
+       }
+
+       inited = TRUE;
+}
+
+/* mono_arch_has_unwind_info:
+ *
+ * Tests if a function has an DWARF exception table able to restore
+ * all caller saved registers. 
+ */
+gboolean
+mono_arch_has_unwind_info (gconstpointer addr)
+{
+       struct frame_state state_in;
+       struct frame_state *res;
+
+       if (!inited) 
+               init_frame_state_for ();
+       
+       if (!frame_state_for)
+               return FALSE;
+
+       g_assert (addr);
+
+       memset (&state_in, 0, sizeof (state_in));
+
+       /* offset 10 is just a guess, but it works for all methods tested */
+       if ((res = frame_state_for ((char *)addr + 10, &state_in))) {
+
+               if (res->saved [X86_EBX] != 1 ||
+                   res->saved [X86_EDI] != 1 ||
+                   res->saved [X86_EBP] != 1 ||
+                   res->saved [X86_ESI] != 1) {
+                       return FALSE;
+               }
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+}
+
+struct stack_frame
+{
+  void *next;
+  void *return_address;
+};
+
+static MonoJitInfo *
+x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, 
+                        struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
+{
+       struct stack_frame *frame;
+       gpointer max_stack;
+       MonoJitInfo *ji;
+       struct frame_state state_in;
+       struct frame_state *res;
+
+       if (trace)
+               *trace = NULL;
+
+       if (!inited) 
+               init_frame_state_for ();
+
+       if (!frame_state_for)
+               return FALSE;
+
+       frame = MONO_CONTEXT_GET_BP (ctx);
+
+       max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
+
+       *new_ctx = *ctx;
+
+       memset (&state_in, 0, sizeof (state_in));
+
+       while ((gpointer)frame->next < (gpointer)max_stack) {
+               gpointer ip, addr = frame->return_address;
+               void *cfa;
+               char *tmp, **symbols;
+
+               if (trace) {
+                       ip = MONO_CONTEXT_GET_IP (new_ctx);
+                       symbols = get_backtrace_symbols (&ip, 1);
+                       if (*trace)
+                               tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
+                       else
+                               tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
+
+                       free (symbols);
+                       g_free (*trace);
+                       *trace = tmp;
+               }
+
+               if ((res = frame_state_for (addr, &state_in))) {        
+                       int i;
+
+                       cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
+                       frame = (struct stack_frame *)((gint8*)cfa - 8);
+                       for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
+                               int how = res->saved[i];
+                               long val;
+                               g_assert ((how == 0) || (how == 1));
+                       
+                               if (how == 1) {
+                                       val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
+                                       set_sigcontext_reg (new_ctx, i, val);
+                               }
+                       }
+                       new_ctx->esp = (long)cfa;
+
+                       if (res->saved [X86_EBX] == 1 &&
+                           res->saved [X86_EDI] == 1 &&
+                           res->saved [X86_EBP] == 1 &&
+                           res->saved [X86_ESI] == 1 &&
+                           (ji = mono_jit_info_table_find (domain, frame->return_address))) {
+                               //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
+                               return ji;
+                       }
+
+               } else {
+                       //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx)));
+
+                       MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
+                       frame = frame->next;
+                       MONO_CONTEXT_SET_BP (new_ctx, frame);
+
+                       /* stop if !frame or when we detect an unexpected managed frame */
+                       if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
+                               if (trace) {
+                                       g_free (*trace);
+                                       *trace = NULL;
+                               }
+                               return NULL;
+                       }
+               }
+       }
+
+       //if (!lmf)
+       //g_assert_not_reached ();
+
+       if (trace) {
+               g_free (*trace);
+               *trace = NULL;
+       }
+       return NULL;
+}
+
+#endif
+
+/*
+ * arch_get_restore_context:
+ *
+ * Returns a pointer to a method which restores a previously saved sigcontext.
+ */
+static gpointer
+arch_get_restore_context (void)
+{
+       static guint8 *start = NULL;
+       guint8 *code;
+
+       if (start)
+               return start;
+
+       /* restore_contect (struct sigcontext *ctx) */
+       /* we do not restore X86_EAX, X86_EDX */
+
+       start = code = g_malloc (1024);
+       
+       /* load ctx */
+       x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
+
+       /* get return address, stored in EDX */
+       x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
+       /* restore EBX */
+       x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
+       /* restore EDI */
+       x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+       /* restore ESI */
+       x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
+       /* restore ESP */
+       x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
+       /* restore EBP */
+       x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+
+       /* jump to the saved IP */
+       x86_jump_reg (code, X86_EDX);
+
+       return start;
+}
+
+/*
+ * arch_get_call_filter:
+ *
+ * Returns a pointer to a method which calls an exception filter. We
+ * also use this function to call finally handlers (we pass NULL as 
+ * @exc object in this case).
+ */
+static gpointer
+arch_get_call_filter (void)
+{
+       static guint8 start [64];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */
+       code = start;
+
+       x86_push_reg (code, X86_EBP);
+       x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
+       x86_push_reg (code, X86_EBX);
+       x86_push_reg (code, X86_EDI);
+       x86_push_reg (code, X86_ESI);
+
+       /* load ctx */
+       x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
+       /* load eip */
+       x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
+       /* save EBP */
+       x86_push_reg (code, X86_EBP);
+       /* push exc */
+       x86_push_membase (code, X86_EBP, 16);
+       /* set new EBP */
+       x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+       /* restore registers used by global register allocation (EBX & ESI) */
+       x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
+       x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
+       x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+       /* save the ESP - this is used by endfinally */
+       x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
+       /* call the handler */
+       x86_call_reg (code, X86_ECX);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+       /* restore EBP */
+       x86_pop_reg (code, X86_EBP);
+       /* restore saved regs */
+       x86_pop_reg (code, X86_ESI);
+       x86_pop_reg (code, X86_EDI);
+       x86_pop_reg (code, X86_EBX);
+       x86_leave (code);
+       x86_ret (code);
+
+       g_assert ((code - start) < 64);
+       return start;
+}
+
+static void
+throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
+                unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
+                unsigned long eip,  unsigned long esp)
+{
+       static void (*restore_context) (struct sigcontext *);
+       struct sigcontext ctx;
+
+       if (!restore_context)
+               restore_context = arch_get_restore_context ();
+
+       /* adjust eip so that it point into the call instruction */
+       eip -= 1;
+
+       ctx.SC_ESP = esp;
+       ctx.SC_EIP = eip;
+       ctx.SC_EBP = ebp;
+       ctx.SC_EDI = edi;
+       ctx.SC_ESI = esi;
+       ctx.SC_EBX = ebx;
+       ctx.SC_EDX = edx;
+       ctx.SC_ECX = ecx;
+       ctx.SC_EAX = eax;
+       
+       mono_arch_handle_exception (&ctx, exc, FALSE);
+       restore_context (&ctx);
+
+       g_assert_not_reached ();
+}
+
+/**
+ * arch_get_throw_exception:
+ *
+ * Returns a function pointer which can be used to raise 
+ * exceptions. The returned function has the following 
+ * signature: void (*func) (MonoException *exc); 
+ * For example to raise an arithmetic exception you can use:
+ *
+ * x86_push_imm (code, mono_get_exception_arithmetic ()); 
+ * x86_call_code (code, arch_get_throw_exception ()); 
+ *
+ */
+gpointer 
+mono_arch_get_throw_exception (void)
+{
+       static guint8 start [24];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       code = start;
+
+       x86_push_reg (code, X86_ESP);
+       x86_push_membase (code, X86_ESP, 4); /* IP */
+       x86_push_membase (code, X86_ESP, 12); /* exception */
+       x86_push_reg (code, X86_EBP);
+       x86_push_reg (code, X86_EDI);
+       x86_push_reg (code, X86_ESI);
+       x86_push_reg (code, X86_EBX);
+       x86_push_reg (code, X86_EDX);
+       x86_push_reg (code, X86_ECX);
+       x86_push_reg (code, X86_EAX);
+       x86_call_code (code, throw_exception);
+       /* we should never reach this breakpoint */
+       x86_breakpoint (code);
+
+       g_assert ((code - start) < 24);
+       return start;
+}
+
+/**
+ * arch_get_throw_exception_by_name:
+ *
+ * Returns a function pointer which can be used to raise 
+ * corlib exceptions. The returned function has the following 
+ * signature: void (*func) (char *exc_name); 
+ * For example to raise an arithmetic exception you can use:
+ *
+ * x86_push_imm (code, "ArithmeticException"); 
+ * x86_call_code (code, arch_get_throw_exception_by_name ()); 
+ *
+ */
+gpointer 
+mono_arch_get_throw_exception_by_name (void)
+{
+       static guint8 start [32];
+       static int inited = 0;
+       guint8 *code;
+
+       if (inited)
+               return start;
+
+       inited = 1;
+       code = start;
+
+       x86_push_membase (code, X86_ESP, 4); /* exception name */
+       x86_push_imm (code, "System");
+       x86_push_imm (code, mono_defaults.exception_class->image);
+       x86_call_code (code, mono_exception_from_name);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
+       /* save the newly create object (overwrite exception name)*/
+       x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
+       x86_jump_code (code, mono_arch_get_throw_exception ());
+
+       g_assert ((code - start) < 32);
+
+       return start;
+}      
+
+static MonoArray *
+glist_to_array (GList *list) 
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray *res;
+       int len, i;
+
+       if (!list)
+               return NULL;
+
+       len = g_list_length (list);
+       res = mono_array_new (domain, mono_defaults.int_class, len);
+
+       for (i = 0; list; list = list->next, i++)
+               mono_array_set (res, gpointer, i, list->data);
+
+       return res;
+}
+
+/* mono_arch_find_jit_info:
+ *
+ * This function is used to gather information from @ctx. It return the 
+ * MonoJitInfo of the corresponding function, unwinds one stack frame and
+ * stores the resulting context into @new_ctx. It also stores a string 
+ * describing the stack location into @trace (if not NULL), and modifies
+ * the @lmf if necessary. @native_offset return the IP offset from the 
+ * start of the function or -1 if that info is not available.
+ */
+static MonoJitInfo *
+mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoContext *ctx, 
+                        MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
+                        gboolean *managed)
+{
+       MonoJitInfo *ji;
+       gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+
+       ji = mono_jit_info_table_find (domain, ip);
+
+       if (trace)
+               *trace = NULL;
+
+       if (native_offset)
+               *native_offset = -1;
+
+       if (managed)
+               *managed = FALSE;
+
+       if (ji != NULL) {
+               char *source_location, *tmpaddr, *fname;
+               gint32 address, iloffset;
+               int offset;
+
+               *new_ctx = *ctx;
+
+               if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
+                       /* remove any unused lmf */
+                       *lmf = (*lmf)->previous_lmf;
+               }
+
+               address = (char *)ip - (char *)ji->code_start;
+
+               if (native_offset)
+                       *native_offset = address;
+
+               if (managed)
+                       if (!ji->method->wrapper_type)
+                               *managed = TRUE;
+
+               if (trace) {
+                       if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
+                               mono_debug_make_symbols ();
+
+                       source_location = mono_debug_source_location_from_address (ji->method, address, NULL);
+                       iloffset = mono_debug_il_offset_from_address (ji->method, address);
+                       source_location = NULL;
+                       iloffset = -1;
+
+                       if (iloffset < 0)
+                               tmpaddr = g_strdup_printf ("<0x%05x>", address);
+                       else
+                               tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
+               
+                       fname = mono_method_full_name (ji->method, TRUE);
+
+                       if (source_location)
+                               *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
+                       else
+                               *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
+
+                       g_free (fname);
+                       g_free (source_location);
+                       g_free (tmpaddr);
+               }
+                               
+               offset = -1;
+               /* restore caller saved registers */
+               if (ji->used_regs & X86_EBX_MASK) {
+                       new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
+                       offset--;
+               }
+               if (ji->used_regs & X86_EDI_MASK) {
+                       new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
+                       offset--;
+               }
+               if (ji->used_regs & X86_ESI_MASK) {
+                       new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
+               }
+
+               new_ctx->SC_ESP = ctx->SC_EBP;
+               /* we substract 1, so that the IP points into the call instruction */
+               new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
+               new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
+
+               *res = *ji;
+               return res;
+#ifdef MONO_USE_EXC_TABLES
+       } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
+               *res = *ji;             
+               return res;
+#endif
+       } else if (*lmf) {
+               
+               *new_ctx = *ctx;
+
+               if (!(*lmf)->method)
+                       return (gpointer)-1;
+
+               if (trace)
+                       *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
+               
+               if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
+                       *res = *ji;
+               } else {
+                       memset (res, 0, sizeof (MonoJitInfo));
+                       res->method = (*lmf)->method;
+               }
+
+               new_ctx->SC_ESI = (*lmf)->esi;
+               new_ctx->SC_EDI = (*lmf)->edi;
+               new_ctx->SC_EBX = (*lmf)->ebx;
+               new_ctx->SC_EBP = (*lmf)->ebp;
+               new_ctx->SC_EIP = (*lmf)->eip;
+               /* the lmf is always stored on the stack, so the following
+                * expression points to a stack location which can be used as ESP */
+               new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
+
+               *lmf = (*lmf)->previous_lmf;
+
+               return res;
+               
+       }
+
+       return NULL;
+}
+
+MonoArray *
+ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray *res;
+       MonoArray *ta = exc->trace_ips;
+       int i, len;
+
+       if (ta == NULL) {
+               /* Exception is not thrown yet */
+               return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
+       }
+       
+       len = mono_array_length (ta);
+
+       res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
+
+       for (i = skip; i < len; i++) {
+               MonoJitInfo *ji;
+               MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
+               gpointer ip = mono_array_get (ta, gpointer, i);
+
+               ji = mono_jit_info_table_find (domain, ip);
+               if (ji == NULL) {
+                       /* Unmanaged frame */
+                       mono_array_set (res, gpointer, i, sf);
+                       continue;
+               }
+
+               g_assert (ji != NULL);
+
+               sf->method = mono_method_get_object (domain, ji->method, NULL);
+               sf->native_offset = (char *)ip - (char *)ji->code_start;
+
+               sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
+               sf->il_offset = -1;
+
+               if (need_file_info) {
+                       gchar *filename;
+                       
+                       filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
+                       filename = NULL;
+
+                       sf->filename = filename? mono_string_new (domain, filename): NULL;
+                       sf->column = 0;
+
+                       g_free (filename);
+               }
+
+               mono_array_set (res, gpointer, i, sf);
+       }
+
+       return res;
+}
+
+void
+mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;
+       MonoJitInfo *ji, rji;
+       gint native_offset, il_offset;
+       gboolean managed;
+
+       MonoContext ctx, new_ctx;
+
+       MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
+       MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
+
+       while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
+               
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
+               g_assert (ji);
+
+               if (ji == (gpointer)-1)
+                       return;
+
+               il_offset = mono_debug_il_offset_from_address (ji->method, native_offset);
+
+               if (func (ji->method, native_offset, il_offset, managed, user_data))
+                       return;
+               
+               ctx = new_ctx;
+       }
+}
+
+MonoBoolean
+ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
+                         MonoReflectionMethod **method, 
+                         gint32 *iloffset, gint32 *native_offset,
+                         MonoString **file, gint32 *line, gint32 *column)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;
+       MonoJitInfo *ji, rji;
+       MonoContext ctx, new_ctx;
+
+       MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
+       MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
+
+       skip++;
+
+       do {
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
+
+               ctx = new_ctx;
+               
+               if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
+                       return FALSE;
+
+               if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)
+                       continue;
+
+               skip--;
+
+       } while (skip >= 0);
+
+       *method = mono_method_get_object (domain, ji->method, NULL);
+       *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset);
+       *iloffset = -1;
+
+       if (need_file_info) {
+               gchar *filename;
+
+               filename = mono_debug_source_location_from_address (ji->method, *native_offset, line);
+               filename = NULL;
+
+               *file = filename? mono_string_new (domain, filename): NULL;
+               *column = 0;
+
+               g_free (filename);
+       }
+
+       return TRUE;
+}
+
+/**
+ * arch_handle_exception:
+ * @ctx: saved processor state
+ * @obj: the exception object
+ * @test_only: only test if the exception is caught, but dont call handlers
+ *
+ *
+ */
+gboolean
+mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitInfo *ji, rji;
+       static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       MonoLMF *lmf = jit_tls->lmf;            
+       GList *trace_ips = NULL;
+
+       g_assert (ctx != NULL);
+       if (!obj) {
+               MonoException *ex = mono_get_exception_null_reference ();
+               ex->message = mono_string_new (domain, 
+                       "Object reference not set to an instance of an object");
+               obj = (MonoObject *)ex;
+       } 
+
+       g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
+
+       if (!call_filter)
+               call_filter = arch_get_call_filter ();
+
+       g_assert (jit_tls->end_of_stack);
+       g_assert (jit_tls->abort_func);
+
+       if (!test_only) {
+               MonoContext ctx_cp = *ctx;
+               if (mono_jit_trace_calls)
+                       g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
+               if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) {
+                       if (mono_break_on_exc) {
+                               if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
+                                       mono_debug_make_symbols ();
+                               G_BREAKPOINT ();
+                       }
+                       mono_unhandled_exception (obj);
+               }
+       }
+
+       while (1) {
+               MonoContext new_ctx;
+               char *trace = NULL;
+               
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx, 
+                                             test_only ? &trace : NULL, &lmf, NULL, NULL);
+               if (!ji) {
+                       g_warning ("Exception inside function without unwind info");
+                       g_assert_not_reached ();
+               }
+
+               if (ji != (gpointer)-1) {
+                       
+                       if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
+                               char *tmp, *strace;
+
+                               trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
+
+                               if (!((MonoException*)obj)->stack_trace)
+                                       strace = g_strdup ("");
+                               else
+                                       strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
+                       
+                               tmp = g_strdup_printf ("%s%s\n", strace, trace);
+                               g_free (strace);
+
+                               ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
+
+                               g_free (tmp);
+                       }
+
+                       if (ji->num_clauses) {
+                               int i;
+                               
+                               g_assert (ji->clauses);
+                       
+                               for (i = 0; i < ji->num_clauses; i++) {
+                                       MonoJitExceptionInfo *ei = &ji->clauses [i];
+
+                                       if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
+                                           MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
+                                               /* catch block */
+                                               if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
+                                                    mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
+                                                   ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
+                                                     call_filter (ctx, ei->data.filter, obj)))) {
+                                                       if (test_only) {
+                                                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                                                               g_list_free (trace_ips);
+                                                               return TRUE;
+                                                       }
+                                                       if (mono_jit_trace_calls)
+                                                               g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
+                                                       MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+                                                       *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
+                                                       jit_tls->lmf = lmf;
+                                                       return 0;
+                                               }
+                                       }
+                               }
+
+                               /* no handler found - we need to call all finally handlers */
+                               if (!test_only) {
+                                       for (i = 0; i < ji->num_clauses; i++) {
+                                               MonoJitExceptionInfo *ei = &ji->clauses [i];
+                                               
+                                               if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
+                                                   MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
+                                                   (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
+                                                       if (mono_jit_trace_calls)
+                                                               g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
+                                                       call_filter (ctx, ei->handler_start, NULL);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               g_free (trace);
+                       
+               *ctx = new_ctx;
+
+               if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
+                       if (!test_only) {
+                               jit_tls->lmf = lmf;
+                               jit_tls->abort_func (obj);
+                               g_assert_not_reached ();
+                       } else {
+                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                               g_list_free (trace_ips);
+                               return FALSE;
+                       }
+               }
+       }
+
+       g_assert_not_reached ();
+}
+
+
diff --git a/mono/mini/exceptions.cs b/mono/mini/exceptions.cs
new file mode 100644 (file)
index 0000000..85213e7
--- /dev/null
@@ -0,0 +1,1317 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+
+       static int test_0_catch () {
+               Exception x = new Exception ();
+               
+               try {
+                       throw x;
+               } catch (Exception e) {
+                       if (e == x)
+                               return 0;
+               }
+               return 1;
+       }
+
+       static int test_0_finally_without_exc () {
+               int x;
+               
+               try {
+                       x = 1;
+               } catch (Exception e) {
+                       x = 2;
+               } finally {
+                       x = 0;
+               }
+               
+               return x;
+       }
+
+       static int test_0_finally () {
+               int x = 1;
+               
+               try {
+                       throw new Exception ();
+               } catch (Exception e) {
+                       x = 2;
+               } finally {
+                       x = 0;
+               }
+               return x;
+       }
+
+       static int test_0_byte_cast () {
+               int a;
+               long l;
+               byte b;
+               bool failed;
+
+               try {
+                       a = 255;
+                       failed = false;
+                       checked {
+                               b = (byte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       a = 0;
+                       failed = false;
+                       checked {
+                               b = (byte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+
+               try {
+                       a = 256;
+                       failed = true;
+                       checked {
+                               b = (byte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       a = -1;
+                       failed = true;
+                       checked {
+                               b = (byte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               try {
+                       double d = 0;
+                       failed = false;
+                       checked {
+                               b = (byte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 5;
+               
+               try {
+                       double d = -1;
+                       failed = true;
+                       checked {
+                               b = (byte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 6;
+
+               try {
+                       double d = 255;
+                       failed = false;
+                       checked {
+                               b = (byte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               
+               if (failed)
+                       return 7;
+
+               try {
+                       double d = 256;
+                       failed = true;
+                       checked {
+                               b = (byte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+
+               try {
+                       l = 255;
+                       failed = false;
+                       checked {
+                               b = (byte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 9;
+
+               try {
+                       l = 0;
+                       failed = false;
+                       checked {
+                               b = (byte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 10;
+
+               try {
+                       l = 256;
+                       failed = true;
+                       checked {
+                               b = (byte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 11;
+
+               try {
+                       l = -1;
+                       failed = true;
+                       checked {
+                               b = (byte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 12;
+               
+               return 0;
+       }
+       
+       static int test_0_sbyte_cast () {
+               int a;
+               long l;
+               sbyte b;
+               bool failed;
+
+               try {
+                       a = 255;
+                       failed = true;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       a = 0;
+                       failed = false;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+
+               try {
+                       a = 256;
+                       failed = true;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       a = -129;
+                       failed = true;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               try {
+                       a = -1;
+                       failed = false;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 5;
+
+               try {
+                       a = -128;
+                       failed = false;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 6;
+
+               try {
+                       a = 127;
+                       failed = false;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 7;
+
+               try {
+                       a = 128;
+                       failed = true;
+                       checked {
+                               b = (sbyte)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+
+               try {
+                       double d = 127;
+                       failed = false;
+                       checked {
+                               b = (sbyte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 9;
+
+               try {
+                       double d = -128;
+                       failed = false;
+                       checked {
+                               b = (sbyte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 10;
+
+               try {
+                       double d = 128;
+                       failed = true;
+                       checked {
+                               b = (sbyte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 11;
+
+               try {
+                       double d = -129;
+                       failed = true;
+                       checked {
+                               b = (sbyte)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 12;
+
+               try {
+                       l = 255;
+                       failed = true;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 13;
+
+               try {
+                       l = 0;
+                       failed = false;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 14;
+
+               try {
+                       l = 256;
+                       failed = true;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 15;
+
+               try {
+                       l = -129;
+                       failed = true;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 16;
+
+               try {
+                       l = -1;
+                       failed = false;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 17;
+
+               try {
+                       l = -128;
+                       failed = false;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 18;
+
+               try {
+                       l = 127;
+                       failed = false;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 19;
+
+               try {
+                       l = 128;
+                       failed = true;
+                       checked {
+                               b = (sbyte)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 20;
+
+               return 0;
+       }
+
+       static int test_0_ushort_cast () {
+               int a;
+               long l;
+               ushort b;
+               bool failed;
+
+               try {
+                       a = System.UInt16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (ushort)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       a = 0;
+                       failed = false;
+                       checked {
+                               b = (ushort)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+
+               try {
+                       a = System.UInt16.MaxValue + 1;
+                       failed = true;
+                       checked {
+                               b = (ushort)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       a = -1;
+                       failed = true;
+                       checked {
+                               b = (ushort)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               try {
+                       double d = 0;
+                       failed = false;
+                       checked {
+                               b = (ushort)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 5;
+
+               try {
+                       double d = System.UInt16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (ushort)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 6;
+
+               try {
+                       double d = -1;
+                       failed = true;
+                       checked {
+                               b = (ushort)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 7;
+
+               try {
+                       double d = System.UInt16.MaxValue + 1.0;
+                       failed = true;
+                       checked {
+                               b = (ushort)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+
+               try {
+                       l = System.UInt16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (ushort)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 9;
+
+               try {
+                       l = 0;
+                       failed = false;
+                       checked {
+                               b = (ushort)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 10;
+
+               try {
+                       l = System.UInt16.MaxValue + 1;
+                       failed = true;
+                       checked {
+                               b = (ushort)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 11;
+
+               try {
+                       l = -1;
+                       failed = true;
+                       checked {
+                               b = (ushort)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 12;
+
+               return 0;
+       }
+       
+       static int test_0_short_cast () {
+               int a;
+               long l;
+               short b;
+               bool failed;
+
+               try {
+                       a = System.UInt16.MaxValue;
+                       failed = true;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       a = 0;
+                       failed = false;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+
+               try {
+                       a = System.Int16.MaxValue + 1;
+                       failed = true;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       a = System.Int16.MinValue - 1;
+                       failed = true;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               try {
+                       a = -1;
+                       failed = false;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 5;
+
+               try {
+                       a = System.Int16.MinValue;
+                       failed = false;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 6;
+
+               try {
+                       a = System.Int16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 7;
+
+               try {
+                       a = System.Int16.MaxValue + 1;
+                       failed = true;
+                       checked {
+                               b = (short)a;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+
+               try {
+                       double d = System.Int16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (short)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 9;
+               
+               try {
+                       double d = System.Int16.MinValue;
+                       failed = false;
+                       checked {
+                               b = (short)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 10;
+               
+               try {
+                       double d = System.Int16.MaxValue + 1.0;
+                       failed = true;
+                       checked {
+                               b = (short)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 11;
+
+               try {
+                       double d = System.Int16.MinValue - 1.0;
+                       failed = true;
+                       checked {
+                               b = (short)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 12;
+
+               try {
+                       l = System.Int16.MaxValue + 1;
+                       failed = true;
+                       checked {
+                               b = (short)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 13;
+
+               try {
+                       l = System.Int16.MaxValue;
+                       failed = false;
+                       checked {
+                               b = (short)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 14;
+
+               try {
+                       l = System.Int16.MinValue - 1;
+                       failed = true;
+                       checked {
+                               b = (short)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 15;
+
+               
+               try {
+                       l = System.Int16.MinValue;
+                       failed = false;
+                       checked {
+                               b = (short)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 16;
+
+               return 0;
+       }
+       
+       static int test_0_int_cast () {
+               int a;
+               long l;
+               bool failed;
+
+               try {
+                       double d = System.Int32.MaxValue + 1.0;
+                       failed = true;
+                       checked {
+                               a = (int)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       double d = System.Int32.MaxValue;
+                       failed = false;
+                       checked {
+                               a = (int)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+               
+
+               try {
+                       double d = System.Int32.MinValue;
+                       failed = false;                 
+                       checked {
+                               a = (int)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 3;
+
+
+               try {
+                       double d =  System.Int32.MinValue - 1.0;
+                       failed = true;
+                       checked {
+                               a = (int)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               try {
+                       l = System.Int32.MaxValue + (long)1;
+                       failed = true;
+                       checked {
+                               a = (int)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 5;
+
+               try {
+                       l = System.Int32.MaxValue;
+                       failed = false;
+                       checked {
+                               a = (int)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 6;
+               
+
+               try {
+                       l = System.Int32.MinValue;
+                       failed = false;                 
+                       checked {
+                               a = (int)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 7;
+
+
+               try {
+                       l =  System.Int32.MinValue - (long)1;
+                       failed = true;
+                       checked {
+                               a = (int)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+
+               return 0;
+       }
+
+       static int test_0_uint_cast () {
+               uint a;
+               long l;
+               bool failed;
+
+               try {
+                       double d =  System.UInt32.MaxValue;
+                       failed = false;
+                       checked {
+                               a = (uint)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       double d = System.UInt32.MaxValue + 1.0;
+                       failed = true;
+                       checked {
+                               a = (uint)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 2;
+
+               try {
+                       double d = System.UInt32.MinValue;
+                       failed = false;
+                       checked {
+                               a = (uint)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       double d = System.UInt32.MinValue - 1.0;
+                       failed = true;
+                       checked {
+                               a = (uint)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+               
+               try {
+                       l =  System.UInt32.MaxValue;
+                       failed = false;
+                       checked {
+                               a = (uint)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 5;
+
+               try {
+                       l = System.UInt32.MaxValue + (long)1;
+                       failed = true;
+                       checked {
+                               a = (uint)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 6;
+
+               try {
+                       l = System.UInt32.MinValue;
+                       failed = false;
+                       checked {
+                               a = (uint)l;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 7;
+
+               try {
+                       l = System.UInt32.MinValue - (long)1;
+                       failed = true;
+                       checked {
+                               a = (uint)l;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 8;
+               
+               return 0;
+       }
+       
+       static int test_0_long_cast () {
+               long a;
+               bool failed;
+
+               try {
+                       double d = System.Int64.MaxValue - 512.0;
+                       failed = true;
+                       checked {
+                               a = (long)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       double d = System.Int64.MaxValue - 513.0;
+                       failed = false;
+                       checked {
+                               a = (long)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+               
+
+               try {
+                       double d = System.Int64.MinValue - 1024.0;
+                       failed = false;                 
+                       checked {
+                               a = (long)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       double d = System.Int64.MinValue - 1025.0;
+                       failed = true;
+                       checked {
+                               a = (long)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               return 0;
+       }
+
+       static int test_0_ulong_cast () {
+               ulong a;
+               bool failed;
+
+               try {
+                       double d = System.UInt64.MaxValue - 1024.0;
+                       failed = true;
+                       checked {
+                               a = (ulong)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       double d = System.UInt64.MaxValue - 1025.0;
+                       failed = false;
+                       checked {
+                               a = (ulong)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+               
+
+               try {
+                       double d = 0;
+                       failed = false;                 
+                       checked {
+                               a = (ulong)d;
+                       }
+               } catch (OverflowException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 3;
+
+               try {
+                       double d = -1;
+                       failed = true;
+                       checked {
+                               a = (ulong)d;
+                       }
+               } catch (OverflowException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 4;
+
+               return 0;
+       }
+
+       static int test_0_simple_double_casts () {
+
+               double d = 0xffffffff;
+
+               if ((uint)d != 4294967295)
+                       return 1;
+
+               d = 0xffffffffffffffff;
+
+               if ((ulong)d != 0)
+                       return 2;
+
+               if ((ushort)d != 0)
+                       return 3;
+                       
+               if ((byte)d != 0)
+                       return 4;
+                       
+               d = 0xffff;
+
+               if ((ushort)d != 0xffff)
+                       return 5;
+               
+               if ((byte)d != 0xff)
+                       return 6;
+                       
+               return 0;
+       }
+       
+       static int test_0_div_zero () {
+               int d = 1;
+               int q = 0;
+               int val;
+               bool failed;
+
+               try {
+                       failed = true;
+                       val = d / q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       failed = true;
+                       val = d % q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 2;
+
+               return 0;
+       }
+
+       static int test_0_udiv_zero () {
+               uint d = 1;
+               uint q = 0;
+               uint val;
+               bool failed;
+
+               try {
+                       failed = true;
+                       val = d / q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       failed = true;
+                       val = d % q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 2;
+
+               return 0;
+       }
+
+       static int test_0_long_div_zero () {
+               long d = 1;
+               long q = 0;
+               long val;
+               bool failed;
+
+               try {
+                       failed = true;
+                       val = d / q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       failed = true;
+                       val = d % q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 2;
+
+               return 0;
+       }
+
+       static int test_0_ulong_div_zero () {
+               ulong d = 1;
+               ulong q = 0;
+               ulong val;
+               bool failed;
+
+               try {
+                       failed = true;
+                       val = d / q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       failed = true;
+                       val = d % q;
+               } catch (DivideByZeroException) {
+                       failed = false;
+               }
+               if (failed)
+                       return 2;
+
+               return 0;
+       }
+
+       static int test_0_float_div_zero () {
+               double d = 1;
+               double q = 0;
+               double val;
+               bool failed;
+
+               try {
+                       failed = false;
+                       val = d / q;
+               } catch (DivideByZeroException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 1;
+
+               try {
+                       failed = false;
+                       val = d % q;
+               } catch (DivideByZeroException) {
+                       failed = true;
+               }
+               if (failed)
+                       return 2;
+
+               return 0;
+       }
+
+}
diff --git a/mono/mini/genmdesc.c b/mono/mini/genmdesc.c
new file mode 100644 (file)
index 0000000..e749698
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * genmdesc: Generates the machine description
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include "mini.h"
+#include <ctype.h>
+#include <string.h>
+#include <mono/metadata/opcodes.h>
+
+typedef struct {
+       int num;
+       const char *name;
+       char *desc;
+       char *comment;
+       char spec [MONO_INST_MAX];
+} OpDesc;
+
+static GHashTable *table;
+
+#define eat_whitespace(s) while (*(s) && isspace (*(s))) s++;
+
+static int
+load_file (const char *name) {
+       FILE *f;
+       char buf [256];
+       char *str, *p;
+       int line;
+       OpDesc *desc;
+       GString *comment;
+
+       if (!(f = fopen (name, "r")))
+               g_error ("Cannot open file '%s'", name);
+
+       comment = g_string_new ("");
+       /*
+        * The format of the lines are:
+        * # comment
+        * opcode: [dest:format] [src1:format] [src2:format] [flags:format] [clob:format] 
+        *      [cost:num] [res:format] [delay:num] [len:num]
+        * format is a single letter that depends on the field
+        * NOTE: no space between the field name and the ':'
+        *
+        * len: maximum instruction length
+        */
+       line = 0;
+       while ((str = fgets (buf, sizeof (buf), f))) {
+               ++line;
+               eat_whitespace (str);
+               if (!str [0])
+                       continue;
+               if (str [0] == '#') {
+                       g_string_append (comment, str);
+                       continue;
+               }
+               p = strchr (str, ':');
+               if (!p)
+                       g_error ("Invalid format at line %d in %s\n", line, name);
+               *p++ = 0;
+               eat_whitespace (p);
+               desc = g_hash_table_lookup (table, str);
+               if (!desc)
+                       g_error ("Invalid opcode '%s' at line %d in %s\n", str, line, name);
+               if (desc->desc)
+                       g_error ("Duplicated opcode '%s' at line %d in %s\n", str, line, name);
+               desc->desc = g_strdup (p);
+               desc->comment = g_strdup (comment->str);
+               g_string_truncate (comment, 0);
+               while (*p) {
+                       if (strncmp (p, "dest:", 5) == 0) {
+                               desc->spec [MONO_INST_DEST] = p [5];
+                               p += 6;
+                       } else if (strncmp (p, "src1:", 5) == 0) {
+                               desc->spec [MONO_INST_SRC1] = p [5];
+                               p += 6;
+                       } else if (strncmp (p, "src2:", 5) == 0) {
+                               desc->spec [MONO_INST_SRC2] = p [5];
+                               p += 6;
+                       } else if (strncmp (p, "cost:", 5) == 0) {
+                               desc->spec [MONO_INST_COST] = p [5];
+                               p += 6;
+                       } else if (strncmp (p, "clob:", 5) == 0) {
+                               desc->spec [MONO_INST_CLOB] = p [5];
+                               p += 6;
+                       } else if (strncmp (p, "res:", 4) == 0) {
+                               desc->spec [MONO_INST_RES] = p [4];
+                               p += 5;
+                       } else if (strncmp (p, "flags:", 6) == 0) {
+                               desc->spec [MONO_INST_FLAGS] = p [6];
+                               p += 7;
+                       } else if (strncmp (p, "delay:", 6) == 0) {
+                               desc->spec [MONO_INST_DELAY] = p [6];
+                               p += 7;
+                       } else if (strncmp (p, "len:", 4) == 0) {
+                               p += 4;
+                               desc->spec [MONO_INST_LEN] = strtoul (p, &p, 10);
+                       } else {
+                               g_error ("Parse error at '%s' at line %d in %s\n", p, line, name);
+                       }
+                       eat_whitespace (p);
+               }
+       }
+       fclose (f);
+       return 0;
+}
+
+static OpDesc *opcodes = NULL;
+
+static void
+init_table (void) {
+       int i;
+       OpDesc *desc;
+
+       table = g_hash_table_new (g_str_hash, g_str_equal);
+
+       opcodes = g_new0 (OpDesc, OP_LAST);
+       for (i = 0; i < MONO_CEE_LAST; ++i) {
+               desc = opcodes + i;
+               desc->num = i;
+               desc->name = mono_inst_name (i);
+               g_hash_table_insert (table, (char *)desc->name, desc);
+       }
+       for (i = OP_LOAD; i < OP_LAST; ++i) {
+               desc = opcodes + i;
+               desc->num = i;
+               desc->name = mono_inst_name (i);
+               g_hash_table_insert (table, (char *)desc->name, desc);
+       }
+}
+
+static void
+output_char (FILE *f, char c) {
+       if (isalnum (c))
+               fprintf (f, "%c", c);
+       else
+               fprintf (f, "\\x%x\" \"", c);
+}
+
+static void
+build_table (const char *fname, const char *name) {
+       FILE *f;
+       int i, j;
+       OpDesc *desc;
+
+       if (!(f = fopen (fname, "w")))
+               g_error ("Cannot open file '%s'", fname);
+       fprintf (f, "/* File automatically generated by genmdesc, don't change */\n\n");
+       fprintf (f, "static const char * const %s [OP_LAST] = {\n", name);
+
+       for (i = 0; i < MONO_CEE_LAST; ++i) {
+               desc = opcodes + i;
+               if (!desc->desc)
+                       fprintf (f, "\tNULL,\t/* %s */\n", desc->name);
+               else {
+                       fprintf (f, "\t\"");
+                       for (j = 0; j < MONO_INST_MAX; ++j)
+                               output_char (f, desc->spec [j]);
+                       fprintf (f, "\",\t/* %s */\n", desc->name);
+               }
+       }
+       for (i = MONO_CEE_LAST; i < OP_LOAD; ++i) {
+               fprintf (f, "\tNULL, /* unassigned */\n");
+       }
+       for (i = OP_LOAD; i < OP_LAST; ++i) {
+               desc = opcodes + i;
+               if (!desc->desc)
+                       fprintf (f, "\tNULL,\t/* %s */\n", desc->name);
+               else {
+                       fprintf (f, "\t\"");
+                       for (j = 0; j < MONO_INST_MAX; ++j)
+                               output_char (f, desc->spec [j]);
+                       fprintf (f, "\",\t/* %s */\n", desc->name);
+               }
+       }
+       fprintf (f, "};\n\n");
+       fclose (f);
+}
+
+static void
+dump (void) {
+       int i;
+       OpDesc *desc;
+       
+       for (i = 0; i < MONO_CEE_LAST; ++i) {
+               desc = opcodes + i;
+               if (desc->comment)
+                       g_print ("%s", desc->comment);
+               if (!desc->desc)
+                       g_print ("%s:\n", desc->name);
+               else {
+                       g_print ("%s: %s", desc->name, desc->desc);
+                       if (!strchr (desc->desc, '\n'))
+                               g_print ("\n");
+               }
+       }
+       for (i = OP_LOAD; i < OP_LAST; ++i) {
+               desc = opcodes + i;
+               if (!desc->desc)
+                       g_print ("%s:\n", desc->name);
+               else {
+                       g_print ("%s: %s", desc->name, desc->desc);
+                       if (!strchr (desc->desc, '\n'))
+                               g_print ("\n");
+               }
+       }
+}
+
+/*
+ * TODO: output the table (possibly merged), in the input format 
+ */
+int 
+main (int argc, char* argv [])
+{
+       init_table ();
+       switch (argc) {
+       case 2:
+               /* useful to get a new file when some opcodes are added: looses the comments, though */
+               load_file (argv [1]);
+               dump ();
+               break;
+       case 4:
+               load_file (argv [1]);
+               build_table (argv [2], argv [3]);
+               break;
+       default:
+               g_print ("Usage: genmdesc arguments\n");
+               g_print ("\tgenmdesc desc             Output to stdout the description file.\n");
+               g_print ("\tgenmdesc desc output name Write to output the description in a table named 'name'.\n");
+               return 1;
+       }
+       return 0;
+}
+
diff --git a/mono/mini/graph.c b/mono/mini/graph.c
new file mode 100644 (file)
index 0000000..f69239e
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * graph.c: Helper routines to graph various internal states of the code generator
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include <string.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini.h"
+
+extern guint8 mono_burg_arity [];
+
+static char *
+convert_name (const char *str)
+{
+       int i, j, len = strlen (str);
+       char *res = g_malloc (len * 2);
+
+       j = 0;
+       for (i = 0; i < len; i++) {
+               char c = str [i];
+
+               switch (c) {
+               case '.':
+                       res [j++] = '_';
+                       break;
+               default:
+                       res [j++] = c;
+               }
+       }
+
+       res [j] = 0;
+
+       return res;
+}
+
+static void
+dtree_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h)
+{
+       MonoBasicBlock *bb;
+       int i, level = 0;
+
+       if (h) {
+               level = h->nesting;
+               fprintf (fp, "subgraph cluster_%d {\n", h->block_num);
+               fprintf (fp, "label=\"loop_%d\"\n", h->block_num);
+       } 
+
+       for (i = 1; i < cfg->num_bblocks; ++i) {
+               bb = cfg->bblocks [i];
+
+               if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) {
+                       if (bb->nesting == level) {
+                               fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num);
+                       }
+               
+                       if (bb->nesting == (level + 1) && bb->loop_blocks) {
+                               fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num);
+                               dtree_emit_one_loop_level (cfg, fp, bb);
+                       }
+               }
+       }
+       
+       if (h) {
+               fprintf (fp, "}\n");
+       }
+}
+
+static void
+cfg_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h)
+{
+       MonoBasicBlock *bb;
+       int i, j, level = 0;
+
+       if (h) {
+               level = h->nesting;
+               fprintf (fp, "subgraph cluster_%d {\n", h->block_num);
+               fprintf (fp, "label=\"loop_%d\"\n", h->block_num);
+       } 
+
+       for (i = 1; i < cfg->num_bblocks; ++i) {
+               bb = cfg->bblocks [i];
+
+               if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) {
+                       if (bb->nesting == level) {
+                               for (j = 0; j < bb->in_count; j++) 
+                                       fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num);
+                       }
+               
+                       if (bb->nesting == (level + 1) && bb->loop_blocks) {
+                               for (j = 0; j < bb->in_count; j++) 
+                                       fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num);
+                               cfg_emit_one_loop_level (cfg, fp, bb);
+                       }
+               }
+       }
+       
+       if (h) {
+               fprintf (fp, "}\n");
+       }
+}
+
+static void
+mono_draw_dtree (MonoCompile *cfg, FILE *fp)
+{
+       g_assert ((cfg->comp_done & MONO_COMP_IDOM));
+
+       fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name));
+       fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n");
+       fprintf (fp, "label=\"Dominator tree for %s\";\n", mono_method_full_name (cfg->method, TRUE));
+
+       fprintf (fp, "BB0 [shape=doublecircle];\n");
+       fprintf (fp, "BB1 [color=red];\n");
+
+       dtree_emit_one_loop_level (cfg, fp, NULL);
+
+       fprintf (fp, "}\n");
+}
+
+static void
+mono_draw_cfg (MonoCompile *cfg, FILE *fp)
+{
+       fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name));
+       fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n");
+       fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE));
+
+       fprintf (fp, "BB0 [shape=doublecircle];\n");
+       fprintf (fp, "BB1 [color=red];\n");
+
+       cfg_emit_one_loop_level (cfg, fp, NULL);
+
+       fprintf (fp, "}\n");
+}
+
+static void
+mono_print_label (FILE *fp, MonoInst *tree) {
+       int arity;
+
+       if (!tree)
+               return;
+
+       arity = mono_burg_arity [tree->opcode];
+
+       fprintf (fp, "\\ %s%s", arity? "(": "",  mono_inst_name (tree->opcode));
+
+       switch (tree->opcode) {
+       case OP_ICONST:
+               fprintf (fp, "[%d]", tree->inst_c0);
+               break;
+       case OP_I8CONST:
+               fprintf (fp, "[%lld]", tree->inst_l);
+               break;
+       case OP_R8CONST:
+               fprintf (fp, "[%f]", *(double*)tree->inst_p0);
+               break;
+       case OP_R4CONST:
+               fprintf (fp, "[%f]", *(float*)tree->inst_p0);
+               break;
+       case OP_ARG:
+       case OP_LOCAL:
+               fprintf (fp, "[%d]", tree->inst_c0);
+               break;
+       case OP_REGOFFSET:
+               fprintf (fp, "[0x%x(%s)]", tree->inst_offset, mono_arch_regname (tree->inst_basereg));
+               break;
+       case OP_REGVAR:
+               fprintf (fp, "[%s]", mono_arch_regname (tree->dreg));
+               break;
+       case CEE_NEWARR:
+               fprintf (fp, "[%s]",  tree->inst_newa_class->name);
+               mono_print_label (fp, tree->inst_newa_len);
+               break;
+       case CEE_CALL:
+       case CEE_CALLVIRT:
+       case OP_FCALL:
+       case OP_FCALLVIRT:
+       case OP_LCALL:
+       case OP_LCALLVIRT:
+       case OP_VCALL:
+       case OP_VCALLVIRT:
+       case OP_VOIDCALL:
+       case OP_VOIDCALLVIRT: {
+               MonoCallInst *call = (MonoCallInst*)tree;
+               if (call->method) {
+                       if (call->method->signature->hasthis && tree->inst_left) {
+                               mono_print_label (fp, tree->inst_left);
+                       }
+                       fprintf (fp, "[%s]", call->method->name);
+               }
+               break;
+       }
+       case OP_PHI: {
+               int i;
+               fprintf (fp, "[%d\\ (", tree->inst_c0);
+               for (i = 0; i < tree->inst_phi_args [0]; i++) {
+                       if (i)
+                               fprintf (fp, ",\\ ");
+                       fprintf (fp, "%d", tree->inst_phi_args [i + 1]);
+               }
+               fprintf (fp, ")]");
+               break;
+       }
+       case OP_RENAME:
+       case OP_RETARG:
+       case CEE_NOP:
+       case CEE_JMP:
+       case CEE_BREAK:
+               break;
+       case CEE_BR:
+               fprintf (fp, "[B%d]", tree->inst_target_bb->block_num);
+               break;
+       case CEE_SWITCH:
+       case CEE_ISINST:
+       case CEE_CASTCLASS:
+       case OP_OUTARG:
+       case OP_CALL_REG:
+       case OP_FCALL_REG:
+       case OP_LCALL_REG:
+       case OP_VCALL_REG:
+       case OP_VOIDCALL_REG:
+               mono_print_label (fp, tree->inst_left);
+               break;
+       case CEE_BNE_UN:
+       case CEE_BEQ:
+       case CEE_BLT:
+       case CEE_BLT_UN:
+       case CEE_BGT:
+       case CEE_BGT_UN:
+       case CEE_BGE:
+       case CEE_BGE_UN:
+       case CEE_BLE:
+       case CEE_BLE_UN:
+               fprintf (fp, "[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num);
+               mono_print_label (fp, tree->inst_left);
+               break;
+       default:
+               if (arity) {
+                       mono_print_label (fp, tree->inst_left);
+                       if (arity > 1)
+                               mono_print_label (fp, tree->inst_right);
+               }
+               break;
+       }
+
+       if (arity)
+               fprintf (fp, ")");
+}
+
+static void
+mono_draw_code_cfg (MonoCompile *cfg, FILE *fp)
+{
+       MonoBasicBlock *bb;
+               
+       fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name));
+       fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n");
+       fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE));
+
+       fprintf (fp, "BB0 [shape=doublecircle];\n");
+       fprintf (fp, "BB1 [color=red];\n");
+
+       for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
+               MonoInst *inst;
+               const char *color;
+
+               if (bb == cfg->bb_exit)
+                       continue;
+
+               if ((cfg->comp_done & MONO_COMP_REACHABILITY) && (bb->flags & BB_REACHABLE))
+                       color = "color=red,";
+               else
+                       color = "";
+
+               fprintf (fp, "BB%d [%sshape=record,labeljust=l,label=\"{BB%d|", bb->block_num, color, bb->block_num);
+                       
+               for (inst = bb->code; inst; inst = inst->next) {
+                       mono_print_label (fp, inst);
+                       fprintf (fp, "\\n");                    
+               }
+
+               fprintf (fp, "}\"];\n");                        
+       }
+
+       cfg_emit_one_loop_level (cfg, fp, NULL);
+
+       fprintf (fp, "}\n");
+}
+
+void
+mono_draw_graph (MonoCompile *cfg, MonoGraphOptions draw_options)
+{
+       char *com;
+       const char *fn;
+       FILE *fp;
+
+       fn = "/tmp/minidtree.graph";
+       fp = fopen (fn, "w+");
+       g_assert (fp);
+
+       switch (draw_options) {
+       case MONO_GRAPH_DTREE:
+               mono_draw_dtree (cfg, fp);
+               break;
+       case MONO_GRAPH_CFG:
+               mono_draw_cfg (cfg, fp);
+               break;
+       case MONO_GRAPH_CFG_CODE:
+       case MONO_GRAPH_CFG_OPTCODE:
+       case MONO_GRAPH_CFG_SSA:
+               mono_draw_code_cfg (cfg, fp);
+               break;
+       }
+
+       fclose (fp);
+
+       //com = g_strdup_printf ("dot %s -Tpng -o %s.png; eog %s.png", fn, fn, fn);
+       com = g_strdup_printf ("dot %s -Tps -o %s.ps;gv %s.ps", fn, fn, fn);
+       system (com);
+       g_free (com);
+}
+
diff --git a/mono/mini/helpers.c b/mono/mini/helpers.c
new file mode 100644 (file)
index 0000000..04ea50f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * helpers.c: Assorted routines
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include "mini.h"
+#include <ctype.h>
+#include <mono/metadata/opcodes.h>
+
+#ifdef MINI_OP
+#undef MINI_OP
+#endif
+#define MINI_OP(a,b) b,
+/* keep in sync with the enum in mini.h */
+static const char* 
+opnames[] = {
+#include "mini-ops.h"
+};
+#undef MINI_OP
+
+const char*
+mono_inst_name (int op) {
+       if (op >= OP_LOAD && op <= OP_LAST)
+               return opnames [op - OP_LOAD];
+       if (op < OP_LOAD)
+               return mono_opcode_names [op];
+       g_error ("unknown opcode name for %d", op);
+       return NULL;
+}
+
+void
+mono_blockset_print (MonoCompile *cfg, MonoBitSet *set, const char *name, guint idom) 
+{
+       int i;
+
+       if (name)
+               g_print ("%s:", name);
+       
+       mono_bitset_foreach_bit (set, i, cfg->num_bblocks) {
+               if (idom == i)
+                       g_print (" [BB%d]", cfg->bblocks [i]->block_num);
+               else
+                       g_print (" BB%d", cfg->bblocks [i]->block_num);
+               
+       }
+       g_print ("\n");
+}
+
+/**
+ * mono_disassemble_code:
+ * @code: a pointer to the code
+ * @size: the code size in bytes
+ *
+ * Disassemble to code to stdout.
+ */
+void
+mono_disassemble_code (guint8 *code, int size, char *id)
+{
+       int i;
+       FILE *ofd;
+
+       if (!(ofd = fopen ("/tmp/test.s", "w")))
+               g_assert_not_reached ();
+
+       for (i = 0; id [i]; ++i) {
+               if (!isalnum (id [i]))
+                       fprintf (ofd, "_");
+               else
+                       fprintf (ofd, "%c", id [i]);
+       }
+       fprintf (ofd, ":\n");
+
+       for (i = 0; i < size; ++i) 
+               fprintf (ofd, ".byte %d\n", (unsigned int) code [i]);
+
+       fclose (ofd);
+
+       system ("as /tmp/test.s -o /tmp/test.o;objdump -d /tmp/test.o"); 
+}
+
diff --git a/mono/mini/iltests.il b/mono/mini/iltests.il
new file mode 100644 (file)
index 0000000..4df34c9
--- /dev/null
@@ -0,0 +1,94 @@
+.assembly iltests {}\r
+.assembly extern TestDriver {}\r
+.assembly extern mscorlib {}\r
+\r
+.class public auto ansi sealed beforefieldinit Tests {\r
+\r
+       .method static public int32 Main() il managed {\r
+               .entrypoint\r
+               \r
+               ldtoken Tests\r
+               call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)\r
+               call       int32 [TestDriver]TestDriver::RunTests(class [mscorlib]System.Type)\r
+               ret\r
+       }\r
+\r
+       .method static public int32 test_3_copy_used_bug () il managed {\r
+\r
+               .locals init (\r
+                       int32 size,\r
+                       int32 res\r
+               )\r
+\r
+               ldc.i4 0\r
+               stloc res\r
+\r
+               ldc.i4 1\r
+               stloc size\r
+\r
+               ldloc size\r
+               ldloc size\r
+               ldloc size\r
+               add\r
+               stloc size\r
+               ldloc size\r
+               add\r
+               stloc res\r
+\r
+               ldloc res\r
+               ret\r
+       }\r
+\r
+       // demonstrate that the copy_used_var is not a fix for the above bug\r
+       .method static public int32 test_3_copy_used_indir_bug () il managed {\r
+\r
+               .locals init (\r
+                       int32 size,\r
+                       int32 res\r
+               )\r
+\r
+               ldc.i4 0\r
+               stloc res\r
+\r
+               ldc.i4 1\r
+               stloc size\r
+\r
+               ldloc size\r
+               ldloca size\r
+               ldloc size\r
+               ldloc size\r
+               add\r
+               stind.i4\r
+               ldloc size\r
+               add\r
+               stloc res\r
+\r
+               ldloc res\r
+               ret\r
+       }\r
+\r
+       .method static public void do_nothing (int32 a) il managed {\r
+               ret\r
+       }\r
+       \r
+       // demonstrate the block_split failure: needs -O=inline\r
+       // mini -O=inline --compile Tests:test_0_split_block_bug iltests.exe\r
+       .method static public int32 test_0_split_block_bug () il managed {\r
+\r
+               .locals init (\r
+                       int32 i1\r
+               )\r
+\r
+               ldc.i4 1\r
+               stloc i1\r
+ test_label:\r
+               ldloc i1\r
+               call void class Tests::do_nothing (int32)\r
+               ldc.i4 0\r
+               brtrue test_label\r
+               \r
+               ldc.i4 0\r
+               ret\r
+       }\r
+\r
+}\r
diff --git a/mono/mini/inssel-float.brg b/mono/mini/inssel-float.brg
new file mode 100644 (file)
index 0000000..e92fa24
--- /dev/null
@@ -0,0 +1,233 @@
+%%
+
+#
+# inssel-float.brg: burg file for floating point instructions
+#
+# Author:
+#   Dietmar Maurer (dietmar@ximian.com)
+#
+# (C) 2002 Ximian, Inc.
+#
+
+#
+# load/store
+#
+
+freg: CEE_LDIND_R4 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADR4_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+freg: CEE_LDIND_R8 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADR8_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+stmt: CEE_STIND_R4 (base, freg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORER4_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_R8 (base, freg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORER8_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+freg: OP_R4CONST {
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+freg: OP_R8CONST {
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+#
+# fp alu operations 
+
+
+freg: OP_FADD (freg, freg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+freg: OP_FSUB (freg, freg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+freg: OP_FMUL (freg, freg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+freg: OP_FDIV (freg, freg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+freg: OP_FREM (freg, freg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+freg: OP_FNEG (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: CEE_CKFINITE (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_SIN (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_COS (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_ABS (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_TAN (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_ATAN (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_SQRT (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+#
+# floating point conversions
+#
+
+reg: OP_FCONV_TO_I4 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_U4 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_I4 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_U4 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_I8 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_U8 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_I (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_OVF_U (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_I (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_U (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_I2 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_U2 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_I1 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_FCONV_TO_U1 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: OP_FCONV_TO_R8 (freg) {
+       /* nothing to do */
+}
+
+freg: OP_FCONV_TO_R4 (freg) {
+       /* fixme: nothing to do ??*/
+}
+
+freg: CEE_CONV_R4 (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: CEE_CONV_R8 (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+freg: CEE_CONV_R_UN (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+#
+# control flow
+#
+
+# CPUs using the same condition flags for integers and float
+# can use the following chain rule:
+# cflags: fpcflags "0"
+# that way all branches are handled by inssel.brg
+
+fpcflags: OP_COMPARE (freg, freg) {
+       tree->opcode = OP_FCOMPARE;
+       tree->sreg1 = state->left->reg1;
+       tree->sreg2 = state->right->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+#
+# miscellaneous fp operations
+#
+
+stmt: CEE_POP (freg) {
+       /* do nothing */
+}     
+
+
+freg: CEE_CKFINITE (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+%%
diff --git a/mono/mini/inssel-long.brg b/mono/mini/inssel-long.brg
new file mode 100644 (file)
index 0000000..11c7070
--- /dev/null
@@ -0,0 +1,47 @@
+%%
+
+#
+# inssel-long.brg: burg file for 64bit architectures
+#
+# Author:
+#   Dietmar Maurer (dietmar@ximian.com)
+#
+# (C) 2002 Ximian, Inc.
+#
+
+reg: CEE_LDIND_I8 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI8_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+stmt: CEE_STIND_I8 (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI8_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I8 (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI8_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+reg: OP_FCONV_TO_I8 (freg) {
+       g_assert_not_reached ();
+}
+
+reg: OP_FCONV_TO_U8 (freg) {
+       g_assert_not_reached ();
+}
+
+freg: CEE_CONV_R_UN (reg) {
+       g_assert_not_reached ();
+}
+
+freg: CEE_CONV_R4 (reg) {
+       g_assert_not_reached ();
+}
+
+freg: CEE_CONV_R8 (reg) {
+       g_assert_not_reached ();
+}
+
+%%
diff --git a/mono/mini/inssel-long32.brg b/mono/mini/inssel-long32.brg
new file mode 100644 (file)
index 0000000..e4fbce5
--- /dev/null
@@ -0,0 +1,606 @@
+%%
+
+#
+# inssel-long32.brg: burg file for 64bit instructions on 32bit architectures
+#
+# Author:
+#   Dietmar Maurer (dietmar@ximian.com)
+#
+# (C) 2002 Ximian, Inc.
+#
+
+#
+# We use a new non-terminal called "lreg" for 64bit registers, and
+# emulate lreg with 2 32bit registers.
+#
+
+stmt: CEE_POP (lreg) {
+       /* do nothing */
+}
+
+i8con: CEE_CONV_I8 (OP_ICONST) "0" {
+       int data = state->left->tree->inst_c0;
+       tree->opcode = OP_I8CONST;
+       tree->inst_c0 = data;
+       if (data < 0)
+               tree->inst_c1 = -1;
+       else
+               tree->inst_c1 = 0;
+}
+
+i8con: CEE_CONV_U8 (OP_ICONST) "0" {
+       int data = state->left->tree->inst_c0;
+       tree->opcode = OP_I8CONST;
+       tree->inst_c0 = data;
+       tree->inst_c1 = 0;
+}
+
+i8con: OP_I8CONST "0"
+
+lreg: OP_ICONST {
+       int data = state->tree->inst_c0;
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, data);
+
+       if (data >= 0)
+               MONO_EMIT_NEW_ICONST (s, state->reg2, 0);
+       else 
+               MONO_EMIT_NEW_ICONST (s, state->reg2, -1);
+}
+
+lreg: OP_I8CONST {
+       MONO_EMIT_NEW_ICONST (s, state->reg1, tree->inst_c0);
+       MONO_EMIT_NEW_ICONST (s, state->reg2, tree->inst_c1);
+}
+
+lreg: CEE_LDIND_I8 (base) {
+       MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, state->reg1, 
+                                      state->left->tree->inst_basereg, state->left->tree->inst_offset);
+       MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, state->reg2, 
+                                      state->left->tree->inst_basereg, state->left->tree->inst_offset + 4);
+}
+
+stmt: CEE_STIND_I8 (base, lreg) {
+       MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset + 4, state->right->reg2);
+       MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I8 (base, i8con) {
+       MONO_EMIT_NEW_STORE_MEMBASE_IMM (s, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                        state->left->tree->inst_offset + 4, state->right->tree->inst_c1);
+       MONO_EMIT_NEW_STORE_MEMBASE_IMM (s, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                        state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+
+lreg: OP_LADD (lreg, lreg) {
+       MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_BIALU (s, tree, OP_ADC, state->reg2, state->left->reg2, state->right->reg2);
+}
+
+lreg: OP_LADD_OVF (lreg, lreg) {
+       /* ADC sets the condition code */
+       MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_BIALU (s, OP_ADC, state->reg2, state->left->reg2, state->right->reg2);
+       MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException");
+}
+
+lreg: OP_LADD_OVF_UN (lreg, lreg) {
+       /* ADC sets the condition code */
+       MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_BIALU (s, OP_ADC, state->reg2, state->left->reg2, state->right->reg2);
+       MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException");
+}
+
+lreg: OP_LADD (lreg, i8con) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1);
+}
+
+lreg: OP_LSUB (lreg, lreg) {
+       MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_BIALU (s, tree, OP_SBB, state->reg2, state->left->reg2, state->right->reg2);
+}
+
+lreg: OP_LSUB (lreg, i8con) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_SBB_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1);
+}
+
+lreg: OP_LSUB_OVF (lreg, lreg) {
+       /* SBB sets the condition code */
+       MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_BIALU (s, OP_SBB, state->reg2, state->left->reg2, state->right->reg2);
+       MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException");
+}
+
+lreg: OP_LSUB_OVF_UN (lreg, lreg) {
+       /* SBB sets the condition code */
+       MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_BIALU (s, OP_SBB, state->reg2, state->left->reg2, state->right->reg2);
+       MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException");
+}
+
+lreg: OP_LAND (lreg, lreg) {   
+       MONO_EMIT_NEW_BIALU (s, CEE_AND, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_BIALU (s, tree, CEE_AND, state->reg2, state->left->reg2, state->right->reg2);
+}
+
+lreg: OP_LAND (lreg, i8con) {  
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_AND_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1);
+}
+
+lreg: OP_LOR (lreg, lreg) {
+       MONO_EMIT_NEW_BIALU (s, CEE_OR, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_BIALU (s, tree, CEE_OR, state->reg2, state->left->reg2, state->right->reg2);
+}
+
+lreg: OP_LOR (lreg, i8con) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_OR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_OR_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1);
+}
+
+lreg: OP_LXOR (lreg, lreg) {
+       MONO_EMIT_NEW_BIALU (s, CEE_XOR, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_BIALU (s, tree, CEE_XOR, state->reg2, state->left->reg2, state->right->reg2);
+}
+
+lreg: OP_LXOR (lreg, i8con) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_XOR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_XOR_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1);
+}
+
+lreg: OP_LNOT (lreg) {
+       MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg1, state->left->reg1);
+       MONO_EMIT_UNALU (s, tree, CEE_NOT, state->reg2, state->left->reg2);
+}
+
+lreg: OP_LNEG (lreg) "4" {
+       MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg1, state->left->reg1);
+       MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg2, state->left->reg2);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->reg1, 1);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, state->reg2, state->reg2, 0);
+}
+
+reg: OP_CEQ (OP_COMPARE (lreg, lreg)) {        
+       MonoInst *word_differs;
+       
+       MONO_NEW_LABEL (s, word_differs);
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, word_differs);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, word_differs);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 1);
+       
+       mono_bblock_add_inst (s->cbb, word_differs);
+}
+
+reg: OP_CLT (OP_COMPARE (lreg, lreg)) {        
+       MonoInst *set_to_0, *set_to_1;
+       
+       MONO_NEW_LABEL (s, set_to_0);
+       MONO_NEW_LABEL (s, set_to_1);
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT, set_to_0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0);
+       mono_bblock_add_inst (s->cbb, set_to_1);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 1);
+       mono_bblock_add_inst (s->cbb, set_to_0);
+}      
+
+reg: OP_CLT_UN (OP_COMPARE (lreg, lreg)) {     
+       MonoInst *set_to_0, *set_to_1;
+       
+       MONO_NEW_LABEL (s, set_to_0);
+       MONO_NEW_LABEL (s, set_to_1);
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT_UN, set_to_0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0);
+       mono_bblock_add_inst (s->cbb, set_to_1);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 1);
+       mono_bblock_add_inst (s->cbb, set_to_0);
+}      
+
+reg: OP_CGT (OP_COMPARE (lreg, lreg)) {        
+       MonoInst *set_to_0, *set_to_1;
+       
+       MONO_NEW_LABEL (s, set_to_0);
+       MONO_NEW_LABEL (s, set_to_1);
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg2, state->left->left->reg2);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT, set_to_0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg1, state->left->left->reg1);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0);
+       mono_bblock_add_inst (s->cbb, set_to_1);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 1);
+       mono_bblock_add_inst (s->cbb, set_to_0);
+}      
+
+reg: OP_CGT_UN (OP_COMPARE (lreg, lreg)) {     
+       MonoInst *set_to_0, *set_to_1;
+       
+       MONO_NEW_LABEL (s, set_to_0);
+       MONO_NEW_LABEL (s, set_to_1);
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg2, state->left->left->reg2);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT_UN, set_to_0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg1, state->left->left->reg1);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0);
+       mono_bblock_add_inst (s->cbb, set_to_1);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 1);
+       mono_bblock_add_inst (s->cbb, set_to_0);
+}      
+
+stmt: CEE_BNE_UN (OP_COMPARE (lreg, lreg)) {
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BNE_UN (OP_COMPARE (lreg, i8con)) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BEQ (OP_COMPARE (lreg, lreg)) {
+       
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BEQ (OP_COMPARE (lreg, i8con)) {
+       
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLE (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLE (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLE_UN (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLE_UN (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGE (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGE (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGE_UN (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGE_UN (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLT (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLT (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLT_UN (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BLT_UN (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGT (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGT (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGT_UN (OP_COMPARE (lreg, lreg)) {
+
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+}
+
+stmt: CEE_BGT_UN (OP_COMPARE (lreg, i8con)) {
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb);
+}
+
+lreg: CEE_CONV_I8 (OP_ICONST) {
+       int data = state->left->tree->inst_c0;
+
+       MONO_EMIT_NEW_ICONST (s, state->reg1, data);
+
+       if (data >= 0)
+               MONO_EMIT_NEW_ICONST (s, state->reg2, 0);
+       else 
+               MONO_EMIT_NEW_ICONST (s, state->reg2, -1);
+}
+
+lreg: CEE_CONV_I8 (reg) {
+       MonoInst *is_negative, *end_label;
+       int tmpreg = mono_regstate_next_int (s->rs);
+       
+       MONO_NEW_LABEL (s, is_negative);
+       MONO_NEW_LABEL (s, end_label);
+
+       /* branchless code:
+        * low = reg;
+        * tmp = low > -1 ? 1: 0;
+        * high = tmp - 1; if low is zero or pos high becomes 0, else -1
+        * not sure why it doesn't work in practice
+        */
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg1, state->left->reg1);
+       /*MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->reg1, -1);
+       tree->dreg = tmpreg;
+       tree->opcode = OP_CGT;
+       mono_bblock_add_inst (s->cbb, tree);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, state->reg2, tmpreg, -1);*/
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->reg1, 0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT, is_negative);
+       MONO_EMIT_NEW_ICONST (s, tmpreg, 0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, end_label);
+       mono_bblock_add_inst (s->cbb, is_negative);
+       MONO_EMIT_NEW_ICONST (s, tmpreg, -1);
+       mono_bblock_add_inst (s->cbb, end_label);
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg2, tmpreg);
+}
+
+lreg: CEE_CONV_U8 (reg) {
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg1, state->left->reg1);
+       MONO_EMIT_NEW_ICONST (s, state->reg2, 0);
+}
+
+lreg: CEE_CONV_OVF_U8 (reg) {
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+       MONO_EMIT_NEW_ICONST (s, state->reg2, 0);
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+freg: OP_LCONV_TO_R_UN (lreg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->left->reg2);
+}
+
+lreg: OP_FCONV_TO_I8 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+lreg: OP_FCONV_TO_U8 (freg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_I4 (i8con) {
+       MONO_EMIT_NEW_ICONST (s, state->reg1, state->left->tree->inst_c0);
+}
+
+reg: OP_LCONV_TO_I4 (lreg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_U4 (lreg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_U (lreg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_I (lreg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_I1 (lreg) {
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_U1 (lreg) {
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_U1, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_I2 (lreg) {
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_U2 (lreg) {
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_U2, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_I1 (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, -1);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -128);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_U1 (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException");
+
+       /* probe value to be within 0 to 255 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1));
+}
+
+reg: OP_LCONV_TO_OVF_I2 (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, -1);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+
+       /* Probe value to be within -32768 and 32767 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -32768);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_U2 (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException");
+
+       /* Probe value to be within 0 and 65535 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1));
+}
+
+
+reg: OP_LCONV_TO_OVF_U4_UN (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_I_UN (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_U4 (lreg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0);
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+}
+
+reg: OP_LCONV_TO_OVF_I (lreg) {
+       tree->dreg = state->reg1;
+       tree->sreg1 = state->left->reg1;
+       tree->sreg2 = state->left->reg2;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_LCONV_TO_OVF_I4 (lreg) {
+       tree->dreg = state->reg1;
+       tree->sreg1 = state->left->reg1;
+       tree->sreg2 = state->left->reg2;
+       tree->opcode = OP_LCONV_TO_OVF_I;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+%%
diff --git a/mono/mini/inssel-x86.brg b/mono/mini/inssel-x86.brg
new file mode 100644 (file)
index 0000000..19495c6
--- /dev/null
@@ -0,0 +1,497 @@
+%%
+
+#
+# inssel-x86.brg: burg file for special x86 instructions
+#
+# Author:
+#   Dietmar Maurer (dietmar@ximian.com)
+#   Paolo Molaro (lupus@ximian.com)
+#
+# (C) 2002 Ximian, Inc.
+#
+
+stmt: CEE_STIND_I8 (OP_REGVAR, lreg) {
+       /* this should only happen for methods returning a long */
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EAX, state->right->reg1);
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EDX, state->right->reg2);
+}
+
+lreg: OP_LNEG (lreg) "3" {
+       int tmpr = mono_regstate_next_int (s->rs);
+       MONO_EMIT_NEW_UNALU (s, CEE_NEG, state->reg1, state->left->reg1);
+       MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, tmpr, state->left->reg2, 0);
+       MONO_EMIT_NEW_UNALU (s, CEE_NEG, state->reg2, tmpr);
+}
+
+freg: OP_LCONV_TO_R8 (lreg) {
+       MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg2);
+       MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1);
+       tree->opcode = OP_X86_FP_LOAD_I8;
+       tree->inst_basereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8);
+}
+
+freg: OP_LCONV_TO_R4 (lreg) {
+       MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1);
+       tree->opcode = OP_X86_FP_LOAD_I4;
+       tree->inst_basereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 4);
+}
+
+freg: CEE_CONV_R_UN (reg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_X86_PUSH_IMM, -1, -1, 0);
+       MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1);
+       tree->opcode = OP_X86_FP_LOAD_I8;
+       tree->inst_basereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8);
+}
+
+cflags: OP_COMPARE (CEE_LDIND_I4 (base), reg) {
+       tree->opcode = OP_X86_COMPARE_MEMBASE_REG;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       tree->sreg2 = state->right->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (CEE_LDIND_I4 (base), OP_ICONST) {
+       tree->opcode = OP_X86_COMPARE_MEMBASE_IMM;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       tree->inst_imm = state->right->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (reg, CEE_LDIND_I4 (base)) {
+       tree->opcode = OP_X86_COMPARE_REG_MEMBASE;
+       tree->sreg2 = state->right->left->tree->inst_basereg;
+       tree->inst_offset = state->right->left->tree->inst_offset;
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_LOCALLOC (OP_ICONST) {
+       if (tree->flags & MONO_INST_INIT) {
+               /* microcoded in mini-x86.c */
+               tree->sreg1 = mono_regstate_next_int (s->rs);
+               MONO_EMIT_NEW_ICONST (s, tree->sreg1, state->left->tree->inst_c0);
+               mono_bblock_add_inst (s->cbb, tree);
+       } else {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, state->left->tree->inst_c0);
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, X86_ESP);
+       }
+}
+
+reg: OP_LOCALLOC (reg) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_SETRET (reg) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = X86_EAX;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_SETRET (lreg) {
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EDX, state->left->reg2);
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = X86_EAX;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_SETRET (CEE_LDIND_REF (OP_REGVAR)) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->left->tree->dreg;
+       tree->dreg = X86_EAX;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_SETRET (freg) {
+       /* nothing to do */
+}
+
+stmt: OP_SETRET (OP_ICONST) {
+       tree->opcode = OP_ICONST;
+       tree->inst_c0 = state->left->tree->inst_c0;
+       tree->dreg = X86_EAX;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (reg) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+# we need to reduce this code duplication with some burg syntax extension
+stmt: OP_OUTARG (CEE_LDIND_REF (OP_REGVAR)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->left->tree->dreg;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_I4 (OP_REGVAR)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->left->tree->dreg;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_U4 (OP_REGVAR)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->left->tree->dreg;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_I (OP_REGVAR)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->left->tree->dreg;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (lreg) {
+       MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg2);
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_I8 (base)) {
+       MonoInst *ins;
+       ins = mono_mempool_alloc0 (s->mempool, sizeof (MonoInst));
+       ins->opcode = OP_X86_PUSH_MEMBASE;
+       ins->inst_basereg = state->left->left->tree->inst_basereg;
+       ins->inst_offset = state->left->left->tree->inst_offset + 4;
+       mono_bblock_add_inst (s->cbb, ins);
+
+       tree->opcode = OP_X86_PUSH_MEMBASE;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (OP_ICONST) {
+       tree->opcode = OP_X86_PUSH_IMM;
+       tree->inst_imm = state->left->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_I4 (base)) {
+       tree->opcode = OP_X86_PUSH_MEMBASE;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_U4 (base)) {
+       tree->opcode = OP_X86_PUSH_MEMBASE;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_I (base)) {
+       tree->opcode = OP_X86_PUSH_MEMBASE;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_REF (base)) {
+       tree->opcode = OP_X86_PUSH_MEMBASE;
+       tree->inst_basereg = state->left->left->tree->inst_basereg;
+       tree->inst_offset = state->left->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDIND_REF (OP_REGVAR)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->left->tree->dreg;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (CEE_LDOBJ (reg)) {
+       tree->opcode = OP_X86_PUSH;
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG (freg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8);
+       tree->opcode = OP_STORER8_MEMBASE_REG;
+       tree->sreg1 = state->left->reg1;
+       tree->inst_destbasereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG_R4 (freg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 4);
+       tree->opcode = OP_STORER4_MEMBASE_REG;
+       tree->sreg1 = state->left->reg1;
+       tree->inst_destbasereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG_R8 (freg) {
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8);
+       tree->opcode = OP_STORER8_MEMBASE_REG;
+       tree->sreg1 = state->left->reg1;
+       tree->inst_destbasereg = X86_ESP;
+       tree->inst_offset = 0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_OUTARG_VT (CEE_LDOBJ (base)) {
+       MonoInst *vt = state->left->left->tree;
+       //g_print ("vt size: %d at R%d + %d\n", tree->inst_imm, vt->inst_basereg, vt->inst_offset);
+       if (tree->inst_imm <= 4) {
+               tree->opcode = OP_X86_PUSH_MEMBASE;
+               tree->inst_basereg = vt->inst_basereg;
+               tree->inst_offset = vt->inst_offset;
+               mono_bblock_add_inst (s->cbb, tree);
+       } else {
+               tree->opcode = OP_X86_PUSH_OBJ;
+               tree->inst_basereg = vt->inst_basereg;
+               tree->inst_offset = vt->inst_offset;
+               mono_bblock_add_inst (s->cbb, tree);
+       }
+}
+
+stmt: OP_OUTARG_VT (OP_ICONST) {
+       tree->opcode = OP_X86_PUSH_IMM;
+       tree->inst_imm = state->left->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_LDELEMA (reg, reg) "15" {
+       int length_reg = mono_regstate_next_int (s->rs);
+       guint32 size = mono_class_array_element_size (tree->klass);
+       
+       MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, 
+                                      state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length));
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, length_reg, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException");
+
+       if (size == 1 || size == 2 || size == 4 || size == 8) {
+               static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
+               tree->opcode = OP_X86_LEA;
+               tree->dreg = state->reg1;
+               tree->sreg1 = state->left->reg1;
+               tree->sreg2 = state->right->reg1;
+               tree->inst_imm = G_STRUCT_OFFSET (MonoArray, vector);
+               tree->unused = fast_log2 [size];
+               mono_bblock_add_inst (s->cbb, tree);
+       } else {
+               int mult_reg = mono_regstate_next_int (s->rs);
+               int add_reg = mono_regstate_next_int (s->rs);
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_MUL_IMM, mult_reg, state->right->reg1, size);
+               MONO_EMIT_NEW_BIALU (s, CEE_ADD, add_reg, mult_reg, state->left->reg1);
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, add_reg, G_STRUCT_OFFSET (MonoArray, vector));
+       }
+}
+
+stmt: CEE_STIND_R8 (OP_REGVAR, freg) {
+       /* nothing to do: the value is already on the FP stack */
+}
+
+stmt: CEE_STIND_I4 (base, CEE_ADD (CEE_LDIND_I4 (base), OP_ICONST)) {
+       int con = state->right->right->tree->inst_c0;   
+
+       if (con == 1) {
+               tree->opcode = OP_X86_INC_MEMBASE;
+       } else {
+               tree->opcode = OP_X86_ADD_MEMBASE_IMM;
+               tree->inst_imm = con;
+       }
+
+       tree->inst_basereg = state->left->tree->inst_basereg;
+       tree->inst_offset = state->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+} cost {
+       MBTREE_TYPE *t1 = state->right->left->left->tree;
+       MBTREE_TYPE *t2 = state->left->tree;
+       MBCOND (t1->inst_basereg == t2->inst_basereg &&
+               t1->inst_offset == t2->inst_offset);
+       return 2;
+}
+
+stmt: CEE_STIND_I4 (base, CEE_SUB (CEE_LDIND_I4 (base), OP_ICONST)) {
+       int con = state->right->right->tree->inst_c0;   
+
+       if (con == 1) {
+               tree->opcode = OP_X86_DEC_MEMBASE;
+       } else {
+               tree->opcode = OP_X86_SUB_MEMBASE_IMM;
+               tree->inst_imm = con;
+       }
+
+       tree->inst_basereg = state->left->tree->inst_basereg;
+       tree->inst_offset = state->left->tree->inst_offset;
+       mono_bblock_add_inst (s->cbb, tree);
+} cost {
+       MBTREE_TYPE *t1 = state->right->left->left->tree;
+       MBTREE_TYPE *t2 = state->left->tree;
+       MBCOND (t1->inst_basereg == t2->inst_basereg &&
+               t1->inst_offset == t2->inst_offset);
+       return 2;
+}
+
+#
+# this rules is incorrect, it needs to do an indirect inc (inc_membase)
+#stmt: CEE_STIND_I4 (reg, CEE_ADD (reg, OP_ICONST)) {
+#      tree->opcode = OP_X86_INC_REG;
+#      tree->dreg = state->left->reg1;
+#      mono_bblock_add_inst (s->cbb, tree);
+#} cost {
+#      MBState *s1 = state->left;
+#      MBState *s2 = state->right->left;
+#      int con = state->right->right->tree->inst_c0;   
+#      MBCOND (con == 1 && s1->reg1 == s2->reg1);
+#      return 1;
+#}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, CEE_SUB (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST)) {
+       int con = state->right->right->tree->inst_c0;   
+       int dreg = state->left->tree->dreg;
+       int sreg = state->right->left->left->tree->dreg;
+
+       if (con == 1) {
+               if (dreg != sreg)
+                       MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg);
+               tree->opcode = OP_X86_DEC_REG;
+               tree->dreg = tree->sreg1 = dreg;
+       } else if (con == -1) {
+               if (dreg != sreg)
+                       MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg);
+               tree->opcode = OP_X86_INC_REG;
+               tree->dreg = tree->sreg1 = dreg;
+       } else {
+               tree->opcode = OP_SUB_IMM;
+               tree->inst_imm = con;
+               tree->sreg1 = sreg;
+               tree->dreg = dreg;
+       }
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, CEE_ADD (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST)) {
+       int con = state->right->right->tree->inst_c0;
+       int dreg = state->left->tree->dreg;
+       int sreg = state->right->left->left->tree->dreg;
+
+       if (con == 1) {
+               if (dreg != sreg)
+                       MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg);
+               tree->opcode = OP_X86_INC_REG;
+               tree->dreg = tree->sreg1 = dreg;
+       } else if (con == -1) {
+               if (dreg != sreg)
+                       MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg);
+               tree->opcode = OP_X86_DEC_REG;
+               tree->dreg = tree->sreg1 = dreg;
+       } else {
+               tree->opcode = OP_ADD_IMM;
+               tree->inst_imm = con;
+               tree->sreg1 = sreg;
+               tree->dreg = dreg;
+       }
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+# on x86, fp compare overwrites EAX, so we must
+# either improve the local register allocator or
+# emit coarse opcodes which saves EAX for us.
+
+reg: OP_CEQ (OP_COMPARE (freg, freg)) {        
+       MONO_EMIT_BIALU (s, tree, OP_FCEQ, state->reg1, state->left->left->reg1,
+                        state->left->right->reg1);
+}
+
+reg: OP_CLT (OP_COMPARE (freg, freg)) {        
+       MONO_EMIT_BIALU (s, tree, OP_FCLT, state->reg1, state->left->left->reg1,
+                        state->left->right->reg1);
+}
+
+reg: OP_CLT_UN (OP_COMPARE (freg, freg)) {     
+       MONO_EMIT_BIALU (s, tree, OP_FCLT_UN, state->reg1, state->left->left->reg1,
+                        state->left->right->reg1);
+}
+
+reg: OP_CGT (OP_COMPARE (freg, freg)) {        
+       MONO_EMIT_BIALU (s, tree, OP_FCGT, state->reg1, state->left->left->reg1,
+                        state->left->right->reg1);
+}
+
+reg: OP_CGT_UN (OP_COMPARE (freg, freg)) {     
+       MONO_EMIT_BIALU (s, tree, OP_FCGT_UN, state->reg1, state->left->left->reg1,
+                        state->left->right->reg1);
+}
+
+# fpcflags overwrites EAX, but this does not matter for statements
+# because we are the last operation in the tree.
+stmt: CEE_BNE_UN (fpcflags) {
+       tree->opcode = OP_FBNE_UN;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BEQ (fpcflags) {
+       tree->opcode = OP_FBEQ;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLT (fpcflags) {
+       tree->opcode = OP_FBLT;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLT_UN (fpcflags) {
+       tree->opcode = OP_FBLT_UN;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGT (fpcflags) {
+       tree->opcode = OP_FBGT;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGT_UN (fpcflags) {
+       tree->opcode = OP_FBGT_UN;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGE  (fpcflags) {
+       tree->opcode = OP_FBGE;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGE_UN (fpcflags) {
+       tree->opcode = OP_FBGE_UN;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLE  (fpcflags) {
+       tree->opcode = OP_FBLE;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLE_UN (fpcflags) {
+       tree->opcode = OP_FBLE_UN;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_POP (freg) "0" {
+       /* we need to pop the value from the x86 FP stack */
+       MONO_EMIT_UNALU (s, tree, OP_X86_FPOP, -1, state->left->reg1);  
+}     
+
+%%
diff --git a/mono/mini/inssel.brg b/mono/mini/inssel.brg
new file mode 100644 (file)
index 0000000..80cef24
--- /dev/null
@@ -0,0 +1,1589 @@
+/*
+ * inssel.brg: instruction selection
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ *
+ */
+
+#include <string.h>
+
+#include "mini.h"
+#include "mini-arch.h"
+#include <mono/metadata/marshal.h>
+
+#define MBTREE_TYPE  MonoInst
+#define MBCGEN_TYPE  MonoCompile
+#define MBCOST_DATA  MonoCompile
+#define MBALLOC_STATE mono_mempool_alloc (data->state_pool, sizeof (MBState))
+#define MBMAX_OPCODES OP_LAST
+#define MBGET_OP_NAME(op) mono_inst_name (op)
+
+#define MBTREE_OP(t) ((t)->opcode)
+#define MBTREE_LEFT(t) ((t)->inst_left)
+#define MBTREE_RIGHT(t) ((t)->inst_right)
+
+#define MONO_EMIT_UNALU(cfg,inst,op,dr,sr1) do { \
+                (inst)->opcode = op; \
+                (inst)->dreg = dr; \
+                (inst)->sreg1 = sr1; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_UNALU(cfg,op,dr,sr1) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->dreg = dr; \
+                inst->sreg1 = sr1; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_BIALU(cfg,inst,op,dr,sr1,sr2) do { \
+                (inst)->opcode = op; \
+                (inst)->dreg = dr; \
+                (inst)->sreg1 = sr1; \
+                (inst)->sreg2 = sr2; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_BIALU(cfg,op,dr,sr1,sr2) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->dreg = dr; \
+                inst->sreg1 = sr1; \
+                inst->sreg2 = sr2; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_BIALU_IMM(cfg,inst,op,dr,sr,imm) do { \
+                (inst)->opcode = op; \
+                (inst)->dreg = dr; \
+                (inst)->sreg1 = sr; \
+                (inst)->inst_p1 = (gpointer)imm; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_BIALU_IMM(cfg,op,dr,sr,imm) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->dreg = dr; \
+                inst->sreg1 = sr; \
+                inst->inst_p1 = (gpointer)imm; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_LOAD_MEMBASE(cfg,inst,dr,base,offset) do { \
+                (inst)->opcode = OP_LOAD_MEMBASE; \
+                (inst)->dreg = dr; \
+                (inst)->inst_basereg = base; \
+                (inst)->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_LOAD_MEMBASE_OP(cfg,inst,op,dr,base,offset) do { \
+                (inst)->opcode = op; \
+                (inst)->dreg = dr; \
+                (inst)->inst_basereg = base; \
+                (inst)->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_LOAD_MEM(cfg,dr,addr) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = OP_LOADU4_MEM; \
+                inst->dreg = dr; \
+                inst->inst_p0 = addr; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_LOAD_MEMBASE(cfg,dr,base,offset) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = OP_LOAD_MEMBASE; \
+                inst->dreg = dr; \
+                inst->inst_basereg = base; \
+                inst->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg,op,dr,base,offset) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->dreg = dr; \
+                inst->inst_basereg = base; \
+                inst->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_STORE_MEMBASE(cfg,inst,op,base,offset,sr) do { \
+                (inst)->opcode = op; \
+                (inst)->sreg1 = sr; \
+                (inst)->inst_destbasereg = base; \
+                (inst)->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_STORE_MEMBASE(cfg,op,base,offset,sr) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->sreg1 = sr; \
+                inst->inst_destbasereg = base; \
+                inst->inst_offset = offset; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_STORE_MEMBASE_IMM(cfg,inst,op,base,offset,imm) do { \
+                (inst)->opcode = op; \
+                (inst)->inst_destbasereg = base; \
+                (inst)->inst_offset = offset; \
+                (inst)->inst_p1 = (gpointer)imm; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define MONO_EMIT_NEW_STORE_MEMBASE_IMM(cfg,op,base,offset,imm) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+                inst->opcode = op; \
+                inst->inst_destbasereg = base; \
+                inst->inst_offset = offset; \
+                inst->inst_p1 = (gpointer)imm; \
+               mono_bblock_add_inst (cfg->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_COMPARE_IMM(cfg,sr1,imm) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = OP_COMPARE_IMM;  \
+                inst->sreg1 = sr1; \
+                inst->inst_p1 = (gpointer)imm; \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_COND_EXC(cfg,cond,name) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = OP_COND_EXC_##cond;  \
+                inst->inst_p1 = (char*)name; \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_ICONST(cfg,dr,imm) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = OP_ICONST;       \
+                inst->dreg = dr; \
+                inst->inst_c0 = imm; \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_AOTCONST(cfg,dr,imm,type) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = OP_AOTCONST;     \
+                inst->dreg = dr; \
+                inst->inst_p0 = imm; \
+                inst->inst_c1 = type; \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_CLASSCONST(cfg,dr,imm) MONO_EMIT_NEW_AOTCONST(cfg,dr,imm,MONO_PATCH_INFO_CLASS)
+
+#define        MONO_EMIT_NEW_BRANCH_BLOCK(cfg,op,targetbb) do { \
+                MonoInst *inst; \
+               MonoInst *target_label; \
+               target_label = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               target_label->opcode = OP_LABEL;        \
+               target_label->next = (targetbb)->code; \
+               target_label->inst_c0 = (targetbb)->native_offset; \
+               (targetbb)->code = target_label; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = op;      \
+               inst->inst_i0 = target_label;   \
+               inst->flags = MONO_INST_BRLABEL;        \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define        MONO_EMIT_NEW_BRANCH_LABEL(cfg,op,label) do { \
+                MonoInst *inst; \
+               inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
+               inst->opcode = op;      \
+               inst->inst_i0 = label;  \
+               inst->flags = MONO_INST_BRLABEL;        \
+               mono_bblock_add_inst ((cfg)->cbb, inst); \
+       } while (0)
+
+#define MONO_NEW_LABEL(cfg,inst) do { \
+               (inst) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (inst)->opcode = OP_LABEL;      \
+       } while (0)
+
+/* we need to kludge state because monoburg puts this stuff before the definition of MBState */
+void mini_emit_virtual_call (MonoCompile *s, void *state, MonoInst *tree, int novirt_op, int virtop);
+void mini_emit_isninst_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target);
+void mini_emit_isninst_iface_cast (MonoCompile *s, int vtable_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target);
+void mini_emit_isninst_iface_class_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target);
+void mini_emit_castclass (MonoCompile *s, int klass_reg, MonoClass *klass);
+void mini_emit_castclass_iface (MonoCompile *s, int vtable_reg, MonoClass *klass);
+void mini_emit_castclass_iface_class (MonoCompile *s, int klass_reg, MonoClass *klass);
+
+
+%%
+
+%termprefix OP_ CEE_
+
+%start stmt
+
+#
+# base addressing mode 
+#
+
+base: reg "0" {
+       /* we create a new MonoInst in chain rules and set state->tree to it
+        * since a MBState (and hence state->tree) is reused in chain rules and
+        * normal rules.
+        */
+       MonoInst *inst;
+       inst = mono_mempool_alloc0 (s->mempool, sizeof (MonoInst));
+       inst->opcode = OP_ICONST; /* doesn't matter */
+       inst->inst_basereg = state->reg1;
+       inst->inst_offset = 0;
+       state->tree = inst;
+       //g_print ("mybase: (assigned R%d from R%d - R%d)\n", state->reg1, tree->dreg, state->reg2);
+       //tree->inst_offset = 0;
+       //tree->inst_basereg = state->reg1;
+}
+
+base: OP_REGOFFSET "0" {
+       /* nothing to do */
+}
+
+base: OP_LDADDR (OP_REGOFFSET) "0" {
+       tree->inst_offset = state->left->tree->inst_offset;
+       tree->inst_basereg = state->left->tree->inst_basereg;
+}
+
+base: CEE_LDOBJ (OP_REGOFFSET) "0" {
+       tree->inst_offset = state->left->tree->inst_offset;
+       tree->inst_basereg = state->left->tree->inst_basereg;
+}
+
+base: CEE_ADD (base, OP_ICONST) "0" {
+       tree->inst_offset = state->left->tree->inst_offset + state->right->tree->inst_c0;
+       tree->inst_basereg = state->left->tree->inst_basereg;
+}
+
+#
+# helpers
+#
+
+reg: OP_ICONST {
+       MONO_EMIT_NEW_ICONST (s, state->reg1, tree->inst_c0);
+}
+
+reg: OP_AOTCONST {
+       MONO_EMIT_NEW_AOTCONST (s, state->reg1, tree->inst_p0, tree->inst_c1);
+}
+
+#
+# load/store operations
+#
+
+reg: CEE_LDIND_I (base) {
+       MONO_EMIT_LOAD_MEMBASE (s, tree, state->reg1, state->left->tree->inst_basereg, 
+                               state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_REF (base) {
+       MONO_EMIT_LOAD_MEMBASE (s, tree, state->reg1, state->left->tree->inst_basereg, 
+                               state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_I1 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI1_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_U1 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU1_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_I2 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI2_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_U2 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU2_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: OP_LDADDR (OP_REGOFFSET) {
+       /* use LEA */
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->tree->inst_basereg;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+       if (state->left->tree->inst_offset) {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, tree->dreg, tree->dreg, state->left->tree->inst_offset);
+       }
+}
+
+reg: CEE_LDOBJ (OP_REGOFFSET) {
+       /* use LEA */
+       /* FIXME: this is just an hack */
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->tree->inst_basereg;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+       if (state->left->tree->inst_offset) {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, tree->dreg, tree->dreg, state->left->tree->inst_offset);
+       }
+}
+
+reg: OP_OBJADDR (reg) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_VTADDR (reg) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_LDIND_REF (OP_REGVAR) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->tree->dreg;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_LDIND_I4 (OP_REGVAR) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->tree->dreg;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_LDIND_U4 (OP_REGVAR) {
+       tree->opcode = OP_MOVE;
+       tree->sreg1 = state->left->tree->dreg;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_STIND_REF (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_REF (base, CEE_LDIND_REF (OP_REGVAR)) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->left->tree->dreg);
+}
+
+stmt: CEE_STIND_REF (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STORE_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+stmt: CEE_STIND_REF (OP_REGVAR, CEE_LDIND_REF (OP_REGVAR)) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->left->tree->dreg);
+}
+
+
+stmt: CEE_STIND_I (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STORE_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+reg: CEE_LDIND_I4 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_U4 (base) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU4_MEMBASE, state->reg1, 
+                                  state->left->tree->inst_basereg, state->left->tree->inst_offset);
+}
+
+reg: CEE_LDIND_I4 (OP_REGVAR) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg);
+}
+
+reg: CEE_LDIND_U4 (OP_REGVAR) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg);
+}
+
+reg: CEE_LDIND_I (OP_REGVAR) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg);
+}
+
+stmt: CEE_STIND_I4 (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I4 (base, CEE_LDIND_I4 (OP_REGVAR)) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->left->tree->dreg);
+}
+
+stmt: CEE_STIND_I4 (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+
+
+stmt: CEE_STIND_I1 (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI1_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I1 (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI1_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+stmt: CEE_STIND_I2 (base, reg) {
+       MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI2_MEMBASE_REG, state->left->tree->inst_basereg,
+                                state->left->tree->inst_offset, state->right->reg1);
+}
+
+stmt: CEE_STIND_I2 (base, OP_ICONST) {
+       MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI2_MEMBASE_IMM, state->left->tree->inst_basereg,
+                                    state->left->tree->inst_offset, state->right->tree->inst_c0);
+}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, reg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1);
+}
+
+stmt: CEE_STIND_I2 (OP_REGVAR, reg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1);
+}
+
+stmt: CEE_STIND_I1 (OP_REGVAR, reg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1);
+}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, CEE_LDIND_I4 (OP_REGVAR)) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->left->tree->dreg);
+}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, CEE_LDIND_I4 (base)) {
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->left->tree->dreg, 
+                                  state->right->left->tree->inst_basereg, 
+                                  state->right->left->tree->inst_offset);
+}
+
+stmt: CEE_STIND_I4 (OP_REGVAR, OP_ICONST) {
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->left->tree->dreg;
+       tree->inst_c0 = state->right->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_STIND_REF (OP_REGVAR, reg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1);
+}
+
+stmt: CEE_STIND_REF (OP_REGVAR, OP_ICONST) {
+       tree->opcode = OP_ICONST;
+       tree->dreg = state->left->tree->dreg;
+       tree->inst_c0 = state->right->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_STIND_I (OP_REGVAR, reg) {
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1);
+}
+
+#
+# conversions: conv_u can be implemented with AND, also all _ovf conversions?
+#
+
+reg: CEE_CONV_I1 (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_I2 (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_I4 (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_U1 (reg) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, 0xff);
+}
+
+reg: CEE_CONV_U2 (reg) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, 0xffff);
+}
+
+reg: CEE_CONV_U4 (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_U (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_I (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_OVF_I4 (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_OVF_U4 (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               /* Keep in sync with CONV_OVF_I4_UN below, they are the same on 32-bit machines */              
+               MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0);
+               MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_OVF_I4_UN (reg) {
+       if (sizeof (void *) == 8) {
+               MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+       } else {
+               /* Keep in sync with CONV_OVF_U4 above, they are the same on 32-bit machines */
+               MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0);
+               MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+               MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1);
+       }
+}
+
+reg: CEE_CONV_OVF_I1 (reg) {     
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -128);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_OVF_I1_UN (reg) {
+       /* probe values between 0 to 127 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_OVF_U1 (reg) {
+       /* probe value to be within 0 to 255 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1));
+}
+
+reg: CEE_CONV_OVF_U1_UN (reg) {
+       /* probe value to be within 0 to 255 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1));
+}
+
+reg: CEE_CONV_OVF_I2 (reg) {   
+       /* Probe value to be within -32768 and 32767 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767);
+       MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException");
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -32768);
+       MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_OVF_I2_UN (reg) {
+       /* Convert uint value into short, value within 0 and 32767 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1);
+}
+
+reg: CEE_CONV_OVF_U2 (reg) {
+       /* Probe value to be within 0 and 65535 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1));
+}
+
+reg: CEE_CONV_OVF_U2_UN (reg) {
+       /* Probe value to be within 0 and 65535 */
+       MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff);
+       MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException");
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1));
+}
+
+#
+# basic alu operations
+#
+
+reg: CEE_AND (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_AND (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_OR (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_OR (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_OR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_XOR (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_XOR (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_XOR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_NEG (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: CEE_NOT (reg) {
+       MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1);
+}
+
+reg: CEE_ADD (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_ADD (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_ADD_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_ADD_OVF (reg, reg) {
+       MONO_EMIT_NEW_BIALU (s, OP_ADDCC, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException");
+}
+
+reg: CEE_ADD_OVF_UN (reg, reg) {
+       MONO_EMIT_NEW_BIALU (s, OP_ADDCC, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException");
+}
+
+reg: CEE_SUB (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_SUB (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_SUB_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_SUB_OVF (reg, reg) {
+       MONO_EMIT_NEW_BIALU (s, OP_SUBCC, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException");
+}
+
+reg: CEE_SUB_OVF_UN (reg, reg) {
+       MONO_EMIT_NEW_BIALU (s, OP_SUBCC, state->reg1, state->left->reg1, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException");
+}
+
+#
+# mult/div operations
+#
+
+reg: CEE_MUL (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_MUL (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_MUL_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_MUL_OVF (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_MUL_OVF_UN (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_DIV (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+#reg: CEE_DIV (reg, OP_ICONST) {
+#      MONO_EMIT_BIALU_IMM (s, tree, OP_DIV_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+#}
+
+reg: CEE_DIV_UN (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+#reg: CEE_DIV_UN (reg, OP_ICONST) {
+#      MONO_EMIT_BIALU_IMM (s, tree, OP_DIV_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+#}
+
+reg: CEE_REM (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+#reg: CEE_REM (reg, OP_ICONST) {
+#      MONO_EMIT_BIALU_IMM (s, tree, OP_REM_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+#}
+
+reg: CEE_REM_UN (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+#reg: CEE_REM_UN (reg, OP_ICONST) {
+#      MONO_EMIT_BIALU_IMM (s, tree, OP_REM_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+#}
+
+#
+# shift operations
+#
+
+reg: CEE_SHL (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_SHL (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_SHL_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_SHR (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_SHR (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_SHR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+reg: CEE_SHR_UN (reg, reg) {
+       MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1);
+}
+
+reg: CEE_SHR_UN (reg, OP_ICONST) {
+       MONO_EMIT_BIALU_IMM (s, tree, OP_SHR_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0);
+}
+
+
+#
+# other alu operations
+#
+
+reg: OP_CEQ (cflags) { 
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_CLT (cflags) { 
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_CLT_UN (cflags) {      
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_CGT (cflags) { 
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: OP_CGT_UN (cflags) {      
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+#
+# control flow commands 
+#
+
+stmt: OP_LABEL {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_NOP "0" {
+}
+
+stmt: CEE_BREAK "0" {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_SWITCH (reg) {
+       MonoInst *label;
+       int offset_reg = mono_regstate_next_int (s->rs);
+       int target_reg = mono_regstate_next_int (s->rs);
+       int n = GPOINTER_TO_INT (tree->klass);
+       
+       MONO_NEW_LABEL (s, label);
+       mono_create_jump_table (s, label, tree->inst_many_bb, n);
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg1, n);
+       MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_many_bb [n]);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_SHL_IMM, offset_reg, state->left->reg1, 2);
+       mono_bblock_add_inst (s->cbb, label);
+        /* the backend must patch the address. we use 0xf0f0f0f0 to avoid the usage 
+         * of special (short) opcodes on x86 */
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, target_reg, offset_reg, 0xf0f0f0f0);
+       MONO_EMIT_UNALU (s, tree, OP_BR_REG, -1, target_reg);
+}
+
+stmt: CEE_BR "0" {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_CALL {
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+reg: CEE_CALLVIRT (reg) {
+       mini_emit_virtual_call (s, state, tree, CEE_CALL, OP_CALL_MEMBASE);
+}
+
+stmt: OP_VOIDCALLVIRT (reg) {
+       mini_emit_virtual_call (s, state, tree, OP_VOIDCALL, OP_VOIDCALL_MEMBASE);
+}
+
+lreg: OP_LCALLVIRT (reg) {
+       mini_emit_virtual_call (s, state, tree, OP_LCALL, OP_LCALL_MEMBASE);
+}
+
+freg: OP_FCALLVIRT (reg) {
+       mini_emit_virtual_call (s, state, tree, OP_FCALL, OP_FCALL_MEMBASE);
+}
+
+stmt: OP_VCALLVIRT (reg, reg) {
+       mini_emit_virtual_call (s, state, tree, OP_VCALL, OP_VCALL_MEMBASE);
+}
+
+reg: OP_CALL_REG (reg) {
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VOIDCALL {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VOIDCALL_REG (reg) {
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+freg: OP_FCALL {
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+freg: OP_FCALL_REG (reg) {
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+lreg: OP_LCALL {
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+lreg: OP_LCALL_REG (reg) {
+       tree->sreg1 = state->left->reg1;
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VCALL (reg) {
+       MonoInst *vtarg;
+       /* FIXME: this is actually arch-specific... */
+       MONO_INST_NEW (s, vtarg, OP_OUTARG);
+       vtarg->inst_left = state->left->tree;
+       vtarg->type = STACK_MP;
+       vtarg->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, vtarg);
+
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VCALL_REG (reg, reg) {
+       MonoInst *vtarg;
+       /* FIXME: this is actually arch-specific... */
+       MONO_INST_NEW (s, vtarg, OP_OUTARG);
+       vtarg->inst_left = state->right->tree;
+       vtarg->type = STACK_MP;
+       vtarg->sreg1 = state->right->reg1;
+       mono_bblock_add_inst (s->cbb, vtarg);
+       
+       tree->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_RET "0" {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (reg, reg) {
+       tree->sreg1 = state->left->reg1;
+       tree->sreg2 = state->right->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (CEE_LDIND_I4 (OP_REGVAR), reg) {
+       tree->sreg1 = state->left->left->tree->dreg;
+       tree->sreg2 = state->right->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST) {
+       tree->opcode = OP_COMPARE_IMM;
+       tree->sreg1 = state->left->left->tree->dreg;
+       tree->inst_imm = state->right->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+cflags: OP_COMPARE (reg, OP_ICONST) {
+       tree->opcode = OP_COMPARE_IMM;
+       tree->sreg1 = state->left->reg1;
+       tree->inst_imm = state->right->tree->inst_c0;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BNE_UN (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BEQ (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLT (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLT_UN (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGT (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGT_UN (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGE  (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BGE_UN (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLE  (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_BLE_UN (cflags) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_POP (reg)
+
+stmt: CEE_JMP "0" {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+# exception handling
+
+stmt: CEE_THROW (reg) {
+       MONO_EMIT_UNALU (s, tree, CEE_THROW, -1, state->left->reg1);
+}
+
+stmt: CEE_THROW (CEE_LDIND_REF (OP_REGVAR)) {
+       MONO_EMIT_UNALU (s, tree, CEE_THROW, -1, state->left->left->tree->dreg);
+}
+
+stmt: OP_HANDLER {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: CEE_ENDFINALLY {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_ENDFILTER (reg) "0" {
+       MONO_EMIT_UNALU (s, tree, OP_ENDFILTER, -1, state->left->reg1);
+}
+
+stmt: OP_CHECK_THIS (reg) {
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+# object related opcodes 
+
+reg: CEE_ISINST (reg) {
+       MonoClass *klass = tree->inst_newa_class;
+       MonoInst *object_is_null, *end_label, *false_label;
+       int obj_reg = state->left->reg1;
+       int vtable_reg = mono_regstate_next_int (s->rs);
+
+       MONO_NEW_LABEL (s, object_is_null);
+       MONO_NEW_LABEL (s, end_label);
+       MONO_NEW_LABEL (s, false_label);
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, obj_reg, 0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+
+       if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+               /* the object_is_null target simply copies the input register to the output */
+               mini_emit_isninst_iface_cast (s, vtable_reg, klass, false_label, object_is_null);
+       } else {
+               int klass_reg = mono_regstate_next_int (s->rs);
+
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
+
+               if (klass->rank) {
+                       int rank_reg = mono_regstate_next_int (s->rs);
+                       int eclass_reg = mono_regstate_next_int (s->rs);
+
+                       MONO_EMIT_NEW_LOAD_MEMBASE (s, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank));
+                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
+                       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, false_label);
+                       MONO_EMIT_NEW_LOAD_MEMBASE (s, eclass_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, cast_class));
+                       if (klass->cast_class == mono_defaults.object_class) {
+                               int parent_reg = mono_regstate_next_int (s->rs);
+                               int const_reg;
+                               MONO_EMIT_NEW_LOAD_MEMBASE (s, parent_reg, eclass_reg, G_STRUCT_OFFSET (MonoClass, parent));
+                               if (mono_compile_aot) {
+                                       const_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, parent_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, parent_reg, mono_defaults.enum_class->parent);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, object_is_null);
+                               if (mono_compile_aot) {
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label);
+                       } else if (klass->cast_class == mono_defaults.enum_class->parent) {
+                               int const_reg;
+                               
+                               if (mono_compile_aot) {
+                                       const_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class->parent);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+                               if (mono_compile_aot) {
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label);
+                       } else if (klass->cast_class == mono_defaults.enum_class) {
+                               if (mono_compile_aot) {
+                                       int const_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label);
+                       } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
+                               mini_emit_isninst_iface_class_cast (s, eclass_reg, klass->cast_class, false_label, object_is_null);
+                       } else {
+                               /* the object_is_null target simply copies the input register to the output */
+                               mini_emit_isninst_cast (s, eclass_reg, klass->cast_class, false_label, object_is_null);
+                       }
+               } else {
+
+                       if (klass->marshalbyref) {
+                               MonoInst *no_proxy;
+
+                               MONO_NEW_LABEL (s, no_proxy);
+
+                               if (mono_compile_aot) {
+                                       int tproxy_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, tproxy_reg, mono_defaults.transparent_proxy_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, klass_reg, tproxy_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, klass_reg, mono_defaults.transparent_proxy_class);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, no_proxy);
+                               MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, obj_reg, G_STRUCT_OFFSET (MonoTransparentProxy, klass));
+                               mono_bblock_add_inst (s->cbb, no_proxy);
+                       }
+                    
+                       /* the object_is_null target simply copies the input register to the output */
+                       mini_emit_isninst_cast (s, klass_reg, klass, false_label, object_is_null);
+               }
+       }
+
+       mono_bblock_add_inst (s->cbb, false_label);
+       MONO_EMIT_NEW_ICONST (s, state->reg1, 0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, end_label);
+       mono_bblock_add_inst (s->cbb, object_is_null);
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, obj_reg);
+       mono_bblock_add_inst (s->cbb, end_label);
+}
+
+reg: CEE_CASTCLASS (reg) {
+       MonoClass *klass = tree->inst_newa_class;
+       MonoInst *object_is_null;
+       int obj_reg = state->left->reg1;
+       int vtable_reg = mono_regstate_next_int (s->rs);
+
+       MONO_NEW_LABEL (s, object_is_null);
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, obj_reg, 0);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+
+       if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+               mini_emit_castclass_iface (s, vtable_reg, klass);
+       } else {
+               int klass_reg = mono_regstate_next_int (s->rs);
+
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
+
+               if (klass->rank) {
+                       int rank_reg = mono_regstate_next_int (s->rs);
+                       int eclass_reg = mono_regstate_next_int (s->rs);
+
+                       MONO_EMIT_NEW_LOAD_MEMBASE (s, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank));
+                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
+                       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+                       MONO_EMIT_NEW_LOAD_MEMBASE (s, eclass_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, cast_class));
+                       if (klass->cast_class == mono_defaults.object_class) {
+                               int parent_reg = mono_regstate_next_int (s->rs);
+                               int const_reg;
+                               MONO_EMIT_NEW_LOAD_MEMBASE (s, parent_reg, eclass_reg, G_STRUCT_OFFSET (MonoClass, parent));
+                               if (mono_compile_aot) {
+                                       const_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, parent_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, parent_reg, mono_defaults.enum_class->parent);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, object_is_null);
+                               if (mono_compile_aot) {
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+                       } else if (klass->cast_class == mono_defaults.enum_class->parent) {
+                               int const_reg = mono_regstate_next_int (s->rs);
+                               if (mono_compile_aot) {
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class->parent);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null);
+                               if (mono_compile_aot) {
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+                       } else if (klass->cast_class == mono_defaults.enum_class) {
+                               if (mono_compile_aot) {
+                                       int const_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class);
+                               }
+                               MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+                       } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
+                               mini_emit_castclass_iface_class (s, eclass_reg, klass->cast_class);
+                       } else {
+                               mini_emit_castclass (s, eclass_reg, klass->cast_class);
+                       }
+               } else {
+
+                       if (klass->marshalbyref) {
+                               MonoInst *no_proxy;
+
+                               MONO_NEW_LABEL (s, no_proxy);
+
+                               if (mono_compile_aot) {
+                                       int tproxy_reg = mono_regstate_next_int (s->rs);
+                                       MONO_EMIT_NEW_CLASSCONST (s, tproxy_reg, mono_defaults.transparent_proxy_class);
+                                       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, klass_reg, tproxy_reg);
+                               } else {
+                                       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, klass_reg, mono_defaults.transparent_proxy_class);
+                               }
+                               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, no_proxy);
+                               MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, obj_reg, G_STRUCT_OFFSET (MonoTransparentProxy, klass));
+                               mono_bblock_add_inst (s->cbb, no_proxy);
+                       }
+                    
+                       mini_emit_castclass (s, klass_reg, klass);
+               }
+       }
+
+       mono_bblock_add_inst (s->cbb, object_is_null);
+       MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, obj_reg);
+}
+
+reg: CEE_NEWARR (reg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LMUL (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LMUL_OVF (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LMUL_OVF_UN (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LDIV (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LDIV_UN (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LREM (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LREM_UN (lreg, lreg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LSHL (lreg, reg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LSHR (lreg, reg) {
+       g_assert_not_reached ();
+}
+
+lreg: OP_LSHR_UN (lreg, reg) {
+       g_assert_not_reached ();
+}
+
+reg: CEE_UNBOX (reg) {
+       int vtable_reg = mono_regstate_next_int (s->rs);
+       int class_reg = mono_regstate_next_int (s->rs);
+       int element_class_reg = mono_regstate_next_int (s->rs);
+
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, state->left->reg1, G_STRUCT_OFFSET (MonoObject, vtable));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, class_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, element_class_reg, class_reg, G_STRUCT_OFFSET (MonoClass, element_class));
+
+       if (mono_compile_aot) {
+               /* fixme: make it work with the AOT compiler */
+               g_assert_not_reached ();
+       } else {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, element_class_reg, tree->inst_newa_class);
+       }
+
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+       MONO_EMIT_BIALU_IMM (s, tree, CEE_ADD, state->reg1, state->left->reg1, sizeof (MonoObject));
+       g_warning ("untested code!!");
+}
+
+#
+# array support
+#
+
+reg: CEE_LDLEN (reg) { 
+       MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->reg1, 
+                                  state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length));
+}
+
+reg: CEE_LDELEMA (reg, OP_ICONST) "15" {
+       int length_reg = mono_regstate_next_int (s->rs);
+       guint32 size = mono_class_array_element_size (tree->klass);
+       int ind;
+
+       MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, 
+                                      state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length));
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, length_reg, state->right->tree->inst_c0);
+       MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException");
+       
+       ind = size * state->right->tree->inst_c0 + G_STRUCT_OFFSET (MonoArray, vector);
+       
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->left->reg1, ind);
+}
+
+reg: CEE_LDELEMA (reg, reg) "20" {
+       int length_reg = mono_regstate_next_int (s->rs);
+       int mult_reg = mono_regstate_next_int (s->rs);
+       int add_reg = mono_regstate_next_int (s->rs);
+       guint32 size = mono_class_array_element_size (tree->klass);
+       
+       MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, 
+                                      state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length));
+       MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, length_reg, state->right->reg1);
+       MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException");
+
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_MUL_IMM, mult_reg, state->right->reg1, size);
+       MONO_EMIT_NEW_BIALU (s, CEE_ADD, add_reg, mult_reg, state->left->reg1);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, add_reg, G_STRUCT_OFFSET (MonoArray, vector));
+}
+
+%%
+
+void 
+mini_emit_virtual_call (MonoCompile *cfg, void *st, MonoInst *tree, int novirtop, int virtop)
+{
+       MonoInst *this, *vtarg;
+       MonoMethod *method = ((MonoCallInst*)tree)->method;
+       int vtable_reg, slot_reg;
+       MBState *state = st;
+
+       /* add the this argument */
+       MONO_INST_NEW (cfg, this, OP_OUTARG);
+       this->inst_left = state->left->tree;
+       this->type = this->inst_left->type;
+       this->sreg1 = state->left->reg1;
+       mono_bblock_add_inst (cfg->cbb, this);
+
+       if (novirtop == OP_VCALL) {
+               /* FIXME: this is actually arch-specific... */
+               MONO_INST_NEW (cfg, vtarg, OP_OUTARG);
+               vtarg->inst_left = state->right->tree;
+               vtarg->type = STACK_MP;
+               vtarg->sreg1 = state->right->reg1;
+               mono_bblock_add_inst (cfg->cbb, vtarg);
+       }
+
+       if (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || 
+           ((method->flags & METHOD_ATTRIBUTE_FINAL) && 
+            method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) {
+               /* 
+                * the method is not virtual, we just need to ensure this is not null
+                * and then we can call the method directly.
+                */
+               if (method->klass->marshalbyref || method->klass == mono_defaults.object_class) {
+                       method = ((MonoCallInst*)tree)->method = mono_marshal_get_remoting_invoke_with_check (method);
+               }
+
+               if (!method->string_ctor)
+                       MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this->sreg1);
+
+               tree->dreg = state->reg1;
+               tree->opcode = novirtop;
+               mono_bblock_add_inst (cfg->cbb, tree);
+               return;
+       }
+
+       vtable_reg = mono_regstate_next_int (cfg->rs);
+       MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this->sreg1, G_STRUCT_OFFSET (MonoObject, vtable));
+       if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+               int ifoffset_reg = mono_regstate_next_int (cfg->rs);
+               slot_reg = mono_regstate_next_int (cfg->rs);
+               MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ifoffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets));
+               MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, ifoffset_reg, method->klass->interface_id << 2);
+               tree->inst_offset = method->slot << 2;
+               cfg->disable_aot = TRUE;
+       } else {
+               slot_reg = vtable_reg;
+               tree->inst_offset = G_STRUCT_OFFSET (MonoVTable, vtable) + (method->slot << 2);
+       }
+       
+       tree->dreg = state->reg1;
+       tree->opcode = virtop;
+       tree->sreg1 = slot_reg;
+       mono_bblock_add_inst (cfg->cbb, tree);
+}
+
+void 
+mini_emit_isninst_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target)
+{
+       int idepth_reg = mono_regstate_next_int (s->rs);
+       int stypes_reg = mono_regstate_next_int (s->rs);
+
+       if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, idepth_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, idepth));
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
+               MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target);
+       }
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, stypes_reg, ((klass->idepth - 1) << 2));
+       if (mono_compile_aot) {
+               int const_reg = mono_regstate_next_int (s->rs);
+               MONO_EMIT_NEW_CLASSCONST (s, const_reg, klass);
+               MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, stypes_reg, const_reg);
+       } else {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, stypes_reg, klass);
+       }
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, true_target);
+}
+
+void 
+mini_emit_isninst_iface_cast (MonoCompile *s, int vtable_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target)
+{
+       int max_iid_reg = mono_regstate_next_int (s->rs);
+       int ioffset_reg = mono_regstate_next_int (s->rs);
+       int intf_reg = mono_regstate_next_int (s->rs);
+               
+       s->disable_aot = TRUE;
+
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, max_interface_id));
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target);
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0);
+       /* the object_is_null target simply copies the input register to the output */
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, true_target);
+}
+
+/*
+ * Variant of the aboce that takes a register to the class, not the vtable.
+ * Note that inside interfaces_offsets the empty value is -1, not NULL, in this case.
+ */
+void 
+mini_emit_isninst_iface_class_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target)
+{
+       int max_iid_reg = mono_regstate_next_int (s->rs);
+       int ioffset_reg = mono_regstate_next_int (s->rs);
+       int intf_reg = mono_regstate_next_int (s->rs);
+               
+       s->disable_aot = TRUE;
+
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, max_interface_id));
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target);
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_offsets));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0);
+       /* the object_is_null target simply copies the input register to the output */
+       MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE, true_target);
+}
+
+void 
+mini_emit_castclass (MonoCompile *s, int klass_reg, MonoClass *klass)
+{
+       int idepth_reg = mono_regstate_next_int (s->rs);
+       int stypes_reg = mono_regstate_next_int (s->rs);
+
+       if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
+               MONO_EMIT_NEW_LOAD_MEMBASE (s, idepth_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, idepth));
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
+               MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException");
+       }
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, stypes_reg, ((klass->idepth - 1) << 2));
+       if (mono_compile_aot) {
+               int const_reg = mono_regstate_next_int (s->rs);
+               MONO_EMIT_NEW_CLASSCONST (s, const_reg, klass);
+               MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, stypes_reg, const_reg);
+       } else {
+               MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, stypes_reg, klass);
+       }
+       MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException");
+}
+
+void 
+mini_emit_castclass_iface (MonoCompile *s, int vtable_reg, MonoClass *klass)
+{
+       int max_iid_reg = mono_regstate_next_int (s->rs);
+       int ioffset_reg = mono_regstate_next_int (s->rs);
+       int intf_reg = mono_regstate_next_int (s->rs);
+               
+       s->disable_aot = TRUE;
+
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, max_interface_id));
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
+       MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException");
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0);
+       MONO_EMIT_NEW_COND_EXC (s, EQ, "InvalidCastException");
+}
+
+/*
+ * Variant of the aboce that takes a register to the class, not the vtable.
+ * Note that inside interfaces_offsets the empty value is -1, not NULL, in this case.
+ */
+void 
+mini_emit_castclass_iface_class (MonoCompile *s, int klass_reg, MonoClass *klass)
+{
+       int max_iid_reg = mono_regstate_next_int (s->rs);
+       int ioffset_reg = mono_regstate_next_int (s->rs);
+       int intf_reg = mono_regstate_next_int (s->rs);
+               
+       s->disable_aot = TRUE;
+
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, max_interface_id));
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
+       MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException");
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_offsets));
+       MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2);
+       MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0);
+       MONO_EMIT_NEW_COND_EXC (s, EQ, "InvalidCastException");
+}
+
diff --git a/mono/mini/jit-icalls.c b/mono/mini/jit-icalls.c
new file mode 100644 (file)
index 0000000..50a5125
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * jit-icalls.c: internal calls used by the JIT
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include <math.h>
+
+static void*
+mono_ldftn (MonoMethod *method) 
+{
+       gpointer addr;
+
+       MONO_ARCH_SAVE_REGS;
+
+       EnterCriticalSection (metadata_section);
+       addr = mono_compile_method (method);
+       LeaveCriticalSection (metadata_section);
+
+       return addr;
+}
+
+static void*
+mono_ldvirtfn (MonoObject *obj, MonoMethod *method) 
+{
+       MONO_ARCH_SAVE_REGS;
+       
+       method = mono_object_get_virtual_method (obj, method);
+       return mono_ldftn (method);
+}
+
+static void
+helper_initobj (void *addr, int size)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       memset (addr, 0, size);
+}
+
+static void
+helper_memcpy (void *addr, void *src, int size)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       memcpy (addr, src, size);
+}
+
+static void
+helper_memset (void *addr, int val, int size)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       memset (addr, val, size);
+}
+
+static gint64 
+mono_llmult (gint64 a, gint64 b)
+{
+       MONO_ARCH_SAVE_REGS;
+       return a * b;
+}
+
+static guint64  
+mono_llmult_ovf_un (guint32 al, guint32 ah, guint32 bl, guint32 bh)
+{
+       guint64 res, t1;
+
+       MONO_ARCH_SAVE_REGS;
+
+       // fixme: this is incredible slow
+
+       if (ah && bh)
+               goto raise_exception;
+
+       res = (guint64)al * (guint64)bl;
+
+       t1 = (guint64)ah * (guint64)bl + (guint64)al * (guint64)bh;
+
+       if (t1 > 0xffffffff)
+               goto raise_exception;
+
+       res += ((guint64)t1) << 32; 
+
+       return res;
+
+ raise_exception:
+       mono_raise_exception (mono_get_exception_overflow ());
+       return 0;
+}
+
+
+static guint64  
+mono_llmult_ovf (guint32 al, gint32 ah, guint32 bl, gint32 bh) 
+{
+       /*
+       Use Karatsuba algorithm where:
+               a*b is: AhBh(R^2+R)+(Ah-Al)(Bl-Bh)R+AlBl(R+1)
+               where Ah is the "high half" (most significant 32 bits) of a and
+               where Al is the "low half" (least significant 32 bits) of a and
+               where  Bh is the "high half" of b and Bl is the "low half" and
+               where R is the Radix or "size of the half" (in our case 32 bits)
+
+       Note, for the product of two 64 bit numbers to fit into a 64
+       result, ah and/or bh must be 0.  This will save us from doing
+       the AhBh term at all.
+
+       Also note that we refactor so that we don't overflow 64 bits with 
+       intermediate results. So we use [(Ah-Al)(Bl-Bh)+AlBl]R+AlBl
+       */
+
+       gint64 res, t1;
+       gint32 sign;
+
+       MONO_ARCH_SAVE_REGS;
+
+       /* need to work with absoulte values, so find out what the
+          resulting sign will be and convert any negative numbers
+          from two's complement
+       */
+       sign = ah ^ bh;
+       if (ah < 0) {
+               /* flip the bits and add 1 */
+               ah ^= ~0;
+               if (al ==  0)
+                       ah += 1;
+               else {
+                       al ^= ~0;
+                       al +=1;
+               }
+       }
+
+       if (bh < 0) {
+               /* flip the bits and add 1 */
+               bh ^= ~0;
+               if (bl ==  0)
+                       bh += 1;
+               else {
+                       bl ^= ~0;
+                       bl +=1;
+               }
+       }
+               
+       /* we overflow for sure if both upper halves are greater 
+          than zero because we would need to shift their 
+          product 64 bits to the left and that will not fit
+          in a 64 bit result */
+       if (ah && bh)
+               goto raise_exception;
+
+       /* do the AlBl term first */
+       t1 = (gint64)al * (gint64)bl;
+
+       res = t1;
+
+       /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */
+       t1 += (gint64)(ah - al) * (gint64)(bl - bh);
+       t1 <<= 32;
+       /* check for overflow */
+       if (t1 > (0x7FFFFFFFFFFFFFFF - res))
+               goto raise_exception;
+
+       res += t1;
+
+       if (sign < 0)
+               return -res;
+       else
+               return res;
+
+ raise_exception:
+       mono_raise_exception (mono_get_exception_overflow ());
+       return 0;
+}
+
+static gint64 
+mono_lldiv (gint64 a, gint64 b)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return a / b;
+}
+
+static gint64 
+mono_llrem (gint64 a, gint64 b)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return a % b;
+}
+
+static guint64 
+mono_lldiv_un (guint64 a, guint64 b)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return a / b;
+}
+
+static guint64 
+mono_llrem_un (guint64 a, guint64 b)
+{
+       MONO_ARCH_SAVE_REGS;
+
+       return a % b;
+}
+
+static guint64 
+mono_lshl (guint64 a, gint32 shamt)
+{
+       guint64 res;
+
+       MONO_ARCH_SAVE_REGS;
+       res = a << shamt;
+
+       /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
+
+       return res;
+}
+
+static guint64 
+mono_lshr_un (guint64 a, gint32 shamt)
+{
+       guint64 res;
+
+       MONO_ARCH_SAVE_REGS;
+       res = a >> shamt;
+
+       /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
+
+       return res;
+}
+
+static gint64 
+mono_lshr (gint64 a, gint32 shamt)
+{
+       gint64 res;
+
+       MONO_ARCH_SAVE_REGS;
+       res = a >> shamt;
+
+       /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
+
+       return res;
+}
+
+/**
+ * ves_array_element_address:
+ * @this: a pointer to the array object
+ *
+ * Returns: the address of an array element.
+ */
+static gpointer 
+ves_array_element_address (MonoArray *this, ...)
+{
+       MonoClass *class;
+       va_list ap;
+       int i, ind, esize;
+       gpointer ea;
+
+       MONO_ARCH_SAVE_REGS;
+
+       g_assert (this != NULL);
+
+       va_start(ap, this);
+
+       class = this->obj.vtable->klass;
+
+       ind = va_arg(ap, int);
+       g_assert (this->bounds != NULL);
+
+       ind -= this->bounds [0].lower_bound;
+       for (i = 1; i < class->rank; i++) {
+               ind = ind*this->bounds [i].length + va_arg(ap, int) -
+                       this->bounds [i].lower_bound;;
+       }
+
+       if (ind >= this->max_length)
+               mono_raise_exception (mono_get_exception_index_out_of_range ());
+
+       esize = mono_array_element_size (class);
+       ea = (gpointer*)((char*)this->vector + (ind * esize));
+
+       va_end(ap);
+
+       return ea;
+}
+
+static MonoArray *
+mono_array_new_va (MonoMethod *cm, ...)
+{
+       MonoDomain *domain = mono_domain_get ();
+       va_list ap;
+       guint32 *lengths;
+       guint32 *lower_bounds;
+       int pcount;
+       int rank;
+       int i, d;
+
+       MONO_ARCH_SAVE_REGS;
+
+       pcount = cm->signature->param_count;
+       rank = cm->klass->rank;
+
+       va_start (ap, cm);
+       
+       lengths = alloca (sizeof (guint32) * pcount);
+       for (i = 0; i < pcount; ++i)
+               lengths [i] = d = va_arg(ap, int);
+
+       if (rank == pcount) {
+               /* Only lengths provided. */
+               lower_bounds = NULL;
+       } else {
+               g_assert (pcount == (rank * 2));
+               /* lower bounds are first. */
+               lower_bounds = lengths;
+               lengths += rank;
+       }
+       va_end(ap);
+
+       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+}
+
+static gpointer
+mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
+{
+       MonoVTable *vtable;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
+
+       mono_class_init (field->parent);
+
+       vtable = mono_class_vtable (domain, field->parent);
+
+       //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
+
+       
+       return (char*)vtable->data + field->offset;
+}
+
+static gpointer
+mono_ldtoken_wrapper (MonoImage *image, int token)
+{
+       MonoClass *handle_class;
+       gpointer res;
+
+       MONO_ARCH_SAVE_REGS;
+       res = mono_ldtoken (image, token, &handle_class);       
+       mono_class_init (handle_class);
+
+       return res;
+}
+
+static guint64
+mono_fconv_u8 (double v)
+{
+       MONO_ARCH_SAVE_REGS;
+       return (guint64)v;
+}
+
+static guint32
+mono_fconv_u4 (double v)
+{
+       MONO_ARCH_SAVE_REGS;
+       return (guint32)v;
+}
+
+static gint64
+mono_fconv_ovf_i8 (double v)
+{
+       gint64 res;
+
+       MONO_ARCH_SAVE_REGS;
+
+       res = (gint64)v;
+
+       if (isnan(v) || v != res) {
+               mono_raise_exception (mono_get_exception_overflow ());
+       }
+       return res;
+}
+
+static guint64
+mono_fconv_ovf_u8 (double v)
+{
+       guint64 res;
+
+       MONO_ARCH_SAVE_REGS;
+    
+       res = (guint64)v;
+
+       if (isnan(v) || v != res) {
+               mono_raise_exception (mono_get_exception_overflow ());
+       }
+       return res;
+}
diff --git a/mono/mini/linear-scan.c b/mono/mini/linear-scan.c
new file mode 100644 (file)
index 0000000..3cf0775
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * liveness.c: liveness analysis
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include "mini.h"
+
+GList *
+mono_varlist_insert_sorted (MonoCompile *cfg, GList *list, MonoMethodVar *mv, int sort_type)
+{
+       GList *l;
+
+       if (!list)
+               return g_list_prepend (NULL, mv);
+
+       for (l = list; l; l = l->next) {
+               MonoMethodVar *v1 = l->data;
+               
+               if (sort_type == 2) {
+                       if (mv->spill_costs >= v1->spill_costs) {
+                               list = g_list_insert_before (list, l, mv);
+                               break;
+                       }                       
+               } else if (sort_type == 1) {
+                       if (mv->range.last_use.abs_pos <= v1->range.last_use.abs_pos) {
+                               list = g_list_insert_before (list, l, mv);
+                               break;
+                       }
+               } else {
+                       if (mv->range.first_use.abs_pos <= v1->range.first_use.abs_pos) {
+                               list = g_list_insert_before (list, l, mv);
+                               break;
+                       }
+               }
+       }
+       if (!l)
+               list = g_list_append (list, mv);
+
+       return list;
+}
+
+//#define DEBUG_LSCAN
+
+void
+mono_linear_scan (MonoCompile *cfg, GList *vars, GList *regs, guint32 *used_mask)
+{
+       GList *l, *a, *active = NULL;
+       MonoMethodVar *vmv, *amv;
+       int max_regs, gains [32];
+       guint32 used_regs = 0;
+       gboolean cost_driven;
+
+       cost_driven = (cfg->comp_done & MONO_COMP_LOOPS);
+
+#ifdef DEBUG_LSCAN
+       printf ("Linears scan for %s\n", mono_method_full_name (cfg->method, TRUE));
+#endif
+
+#ifdef DEBUG_LSCAN
+       for (l = vars; l; l = l->next) {
+               vmv = l->data;
+               printf ("VAR %d %08x %08x C%d\n", vmv->idx, vmv->range.first_use.abs_pos, 
+                       vmv->range.last_use.abs_pos, vmv->spill_costs);
+       }
+#endif
+       max_regs = g_list_length (regs);
+
+       for (l = regs; l; l = l->next) {
+               int regnum = (int)l->data;
+               g_assert (regnum < G_N_ELEMENTS (gains));
+               gains [regnum] = 0;
+       }
+
+       /* linear scan */
+       for (l = vars; l; l = l->next) {
+               vmv = l->data;
+
+#ifdef DEBUG_LSCAN
+               printf ("START  %2d %08x %08x\n",  vmv->idx, vmv->range.first_use.abs_pos, 
+                       vmv->range.last_use.abs_pos);
+#endif
+               /* expire old intervals in active */
+               while (active) {
+                       amv = (MonoMethodVar *)active->data;
+
+                       if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos)
+                               break;
+
+#ifdef DEBUG_LSCAN
+                       printf ("EXPIR  %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos, 
+                               amv->range.last_use.abs_pos, amv->spill_costs, amv->reg);
+#endif
+                       active = g_list_remove_link (active, active);
+                       regs = g_list_prepend (regs, (gpointer)amv->reg);
+                       gains [amv->reg] += amv->spill_costs;
+               }
+               
+               if (active && g_list_length (active) == max_regs) {
+                       /* Spill */
+
+                       a = g_list_nth (active, max_regs - 1);
+                       amv = (MonoMethodVar *)a->data;
+
+                       if ((cost_driven && amv->spill_costs < vmv->spill_costs) ||                          
+                           (!cost_driven && amv->range.last_use.abs_pos > vmv->range.last_use.abs_pos)) {
+                               vmv->reg = amv->reg;
+                               amv->reg = -1;
+                               active = g_list_remove_link (active, a);
+
+                               if (cost_driven)
+                                       active = mono_varlist_insert_sorted (cfg, active, vmv, 2);      
+                               else
+                                       active = mono_varlist_insert_sorted (cfg, active, vmv, 1);      
+
+#ifdef DEBUG_LSCAN
+                               printf ("SPILL0 %2d %08x %08x C%d\n",  amv->idx, 
+                                       amv->range.first_use.abs_pos, amv->range.last_use.abs_pos,
+                                       amv->spill_costs);
+#endif
+                       } else {
+#ifdef DEBUG_LSCAN
+                               printf ("SPILL1 %2d %08x %08x C%d\n",  vmv->idx, 
+                                       vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos,
+                                       vmv->spill_costs);
+#endif
+                               vmv->reg = -1;
+                       }
+               } else {
+                       /* assign register */
+
+                       g_assert (regs);
+
+                       vmv->reg = (int)regs->data;
+
+                       used_regs |= 1 << vmv->reg;
+
+                       regs = g_list_remove_link (regs, regs);
+
+#ifdef DEBUG_LSCAN
+                       printf ("ADD    %2d %08x %08x C%d\n",  vnum, 
+                               vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos, 
+                               vmv->spill_costs);
+#endif
+                       active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE);           
+               }
+
+
+#ifdef DEBUG_LSCAN
+               for (a = active; a; a = a->next) {
+                       amv = (MonoMethodVar *)a->data;         
+                       printf ("ACT    %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos,  
+                               amv->range.last_use.abs_pos, amv->spill_costs, amv->reg);
+               }
+               printf ("NEXT\n");
+#endif
+       }       
+
+       for (a = active; a; a = a->next) {
+               amv = (MonoMethodVar *)a->data;         
+               gains [amv->reg] += amv->spill_costs;
+       }
+
+       for (l = vars; l; l = l->next) {
+               vmv = l->data;
+               
+               if (vmv->reg >= 0)  {
+                       if (gains [vmv->reg] > 5) {
+                               cfg->varinfo [vmv->idx]->opcode = OP_REGVAR;
+                               cfg->varinfo [vmv->idx]->dreg = vmv->reg;
+#ifdef DEBUG_LSCAN
+                               printf ("REGVAR %d C%d R%d\n", vmv->idx, vmv->spill_costs, vmv->reg);
+#endif
+                       } else {
+                               used_regs &= ~(1 << vmv->reg); 
+                               vmv->reg = -1;
+                       }
+               }
+       }
+
+       *used_mask |= used_regs;
+
+       g_list_free (regs);
+       g_list_free (active);
+       g_list_free (vars);
+}
diff --git a/mono/mini/liveness.c b/mono/mini/liveness.c
new file mode 100644 (file)
index 0000000..9bfaa8c
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * liveness.c: liveness analysis
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include "mini.h"
+#include "inssel.h"
+
+//#define DEBUG_LIVENESS
+
+
+extern guint8 mono_burg_arity [];
+
+/* mono_bitset_mp_new:
+ * 
+ * allocates a MonoBitSet inside a memory pool
+ */
+static MonoBitSet* 
+mono_bitset_mp_new (MonoMemPool *mp,  guint32 max_size)
+{
+       int size = mono_bitset_alloc_size (max_size, 0);
+       gpointer mem;
+
+       mem = mono_mempool_alloc0 (mp, size);
+       return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
+}
+
+#ifdef DEBUG_LIVENESS
+static void
+mono_bitset_print (MonoBitSet *set)
+{
+       int i;
+
+       printf ("{");
+       for (i = 0; i < mono_bitset_size (set); i++) {
+
+               if (mono_bitset_test (set, i))
+                       printf ("%d, ", i);
+
+       }
+       printf ("}");
+}
+#endif
+
+static void
+update_live_range (MonoCompile *cfg, int idx, int block_dfn, int tree_pos)
+{
+       MonoLiveRange *range = &MONO_VARINFO (cfg, idx)->range;
+       guint32 abs_pos = (block_dfn << 16) | tree_pos;
+
+       if (range->first_use.abs_pos > abs_pos)
+               range->first_use.abs_pos = abs_pos;
+
+       if (range->last_use.abs_pos < abs_pos)
+               range->last_use.abs_pos = abs_pos;
+}
+
+static void
+update_gen_kill_set (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int inst_num)
+{
+       int arity = mono_burg_arity [inst->opcode];
+       int max_vars = cfg->num_varinfo;
+
+       if (arity)
+               update_gen_kill_set (cfg, bb, inst->inst_i0, inst_num);
+
+       if (arity > 1)
+               update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num);
+
+       if (inst->ssa_op == MONO_SSA_LOAD) {
+               int idx = inst->inst_i0->inst_c0;
+               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+               g_assert (idx < max_vars);
+               update_live_range (cfg, idx, bb->dfn, inst_num); 
+               if (!mono_bitset_test (bb->kill_set, idx))
+                       mono_bitset_set (bb->gen_set, idx);
+               vi->spill_costs += 1 + (bb->nesting * 2);
+       } else if (inst->ssa_op == MONO_SSA_STORE) {
+               int idx = inst->inst_i0->inst_c0;
+               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+               g_assert (idx < max_vars);
+               g_assert (inst->inst_i1->opcode != OP_PHI);
+
+               update_live_range (cfg, idx, bb->dfn, inst_num); 
+               mono_bitset_set (bb->kill_set, idx);
+               vi->spill_costs += 1 + (bb->nesting * 2);
+       }
+} 
+
+/* generic liveness analysis code. CFG specific parts are 
+ * in update_gen_kill_set()
+ */
+void
+mono_analyze_liveness (MonoCompile *cfg)
+{
+       MonoBitSet *old_live_in_set, *old_live_out_set;
+       gboolean changes;
+       int i, j, max_vars = cfg->num_varinfo;
+
+#ifdef DEBUG_LIVENESS
+       printf ("LIVENLESS %s\n", mono_method_full_name (cfg->method, TRUE));
+#endif
+
+       g_assert (!(cfg->comp_done & MONO_COMP_LIVENESS));
+
+       cfg->comp_done |= MONO_COMP_LIVENESS;
+       
+       if (max_vars == 0)
+               return;
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+
+               bb->gen_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+               bb->kill_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+               bb->live_in_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+               bb->live_out_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+       }
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+               MonoInst *inst;
+               int tree_num;
+
+               for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) {
+                       //mono_print_tree (inst); printf ("\n");
+                       update_gen_kill_set (cfg, bb, inst, tree_num);
+               }
+
+#ifdef DEBUG_LIVENESS
+               printf ("BLOCK BB%d (", bb->block_num);
+               for (j = 0; j < bb->out_count; j++) 
+                       printf ("BB%d, ", bb->out_bb [j]->block_num);
+               
+               printf (")\n");
+               printf ("GEN  BB%d: ", bb->block_num); mono_bitset_print (bb->gen_set); printf ("\n");
+               printf ("KILL BB%d: ", bb->block_num); mono_bitset_print (bb->kill_set); printf ("\n");
+#endif
+       }
+
+       old_live_in_set = mono_bitset_new (max_vars, 0);
+       old_live_out_set = mono_bitset_new (max_vars, 0);
+       do {
+               changes = FALSE;
+
+               for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+                       MonoBasicBlock *bb = cfg->bblocks [i];
+
+                       mono_bitset_copyto (bb->live_in_set, old_live_in_set);
+                       mono_bitset_copyto (bb->live_out_set, old_live_out_set);
+
+                       mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
+                       mono_bitset_sub (bb->live_in_set, bb->kill_set);
+                       mono_bitset_union (bb->live_in_set, bb->gen_set);
+
+                       mono_bitset_clear_all (bb->live_out_set);
+                       
+                       for (j = 0; j < bb->out_count; j++) 
+                               mono_bitset_union (bb->live_out_set, bb->out_bb [j]->live_in_set);
+
+                       if (!(mono_bitset_equal (old_live_in_set, bb->live_in_set) &&
+                             mono_bitset_equal (old_live_out_set, bb->live_out_set)))
+                               changes = TRUE;
+               }
+
+       } while (changes);
+
+       mono_bitset_free (old_live_in_set);
+       mono_bitset_free (old_live_out_set);
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+
+               for (j = max_vars - 1; j >= 0; j--) {
+                       
+                       if (mono_bitset_test (bb->live_in_set, j))
+                               update_live_range (cfg, j, bb->dfn, 0);
+
+                       if (mono_bitset_test (bb->live_out_set, j))
+                               update_live_range (cfg, j, bb->dfn, 0xffff);
+               } 
+       } 
+
+#ifdef DEBUG_LIVENESS
+       for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+               
+               printf ("LIVE IN  BB%d: ", bb->block_num); 
+               mono_bitset_print (bb->live_in_set); 
+               printf ("\n");
+               printf ("LIVE OUT BB%d: ", bb->block_num); 
+               mono_bitset_print (bb->live_out_set); 
+               printf ("\n");
+       }
+#endif
+}
+
diff --git a/mono/mini/local-regalloc.txt b/mono/mini/local-regalloc.txt
new file mode 100644 (file)
index 0000000..a6e5235
--- /dev/null
@@ -0,0 +1,208 @@
+
+* Proposal for the local register allocator
+
+       The local register allocator deals with allocating registers
+       for temporaries inside a single basic block, while the global 
+       register allocator is concerned with method-wide allocation of 
+       variables.
+       The global register allocator uses callee-saved register for it's 
+       purpouse so that there is no need to save and restore these registers
+       at call sites.
+
+       There are a number of issues the local allocator needs to deal with:
+       *) some instructions expect operands in specific registers (for example
+               the shl instruction on x86, or the call instruction with thiscall
+               convention, or the equivalent call instructions on other architectures, 
+               such as the need to put output registers in %oX on sparc)
+       *) some instructions deliver results only in specific registers (for example
+               the div instruction on x86, or the call instructionson on almost all
+               the architectures).
+       *) it needs to know what registers may be clobbered by an instruction
+               (such as in a method call)
+       *) it should avoid excessive reloads or stores to improve performance
+       
+       While which specific instructions have limitations is architecture-dependent,
+       the problem shold be solved in an arch-independent way to reduce code duplication.
+       The register allocator will be 'driven' by the arch-dependent code, but it's 
+       implementation should be arch-independent.
+
+       To improve the current local register allocator, we need to
+       keep more state in it than the current setup that only keeps busy/free info.
+
+       Possible state information is:
+
+       free: the resgister is free to use and it doesn't contain useful info
+       freeable: the register contains data loaded from a local (there is 
+               also info about _which_ local it contains) as a result from previous
+               instructions (like, there was a store from the register to the local)
+       moveable: it contains live data that is needed in a following instruction, but
+               the contents may be moved to a different register
+       busy: the register contains live data and it is placed there because
+               the following instructions need it exactly in that register
+       allocated: the register is used by the global allocator
+
+       The local register allocator will have the following interfaces:
+
+       int get_register ();
+               Searches for a register in the free state. If it doesn't find it,
+               searches for a freeable register. Sets the status to moveable.
+               Looking for a 'free' register before a freeable one should allow for
+               removing a few redundant loads (though I'm still unsure if such
+               things should be delegated entirely to the peephole pass).
+       
+       int get_register_force (int reg);
+               Returns 'reg' if it is free or freeable. If it is moveable, it moves it 
+               to another free or freeable register.
+               Sets the status of 'reg' to busy.
+       
+       void set_register_freeable (int reg);
+               Sets the status of 'reg' to freeable.
+       
+       void set_register_free (int reg);
+               Sets the status of 'reg' to free.
+
+       void will_clobber (int reg);
+               Spills the register to the stack. Sets the status to freeable.
+               After the clobbering has occurred, set the status to free.
+
+       void register_unspill (int reg);
+               Un-spills register reg and sets the status to moveable.
+
+       FIXME: how is the 'local' information represented? Maybe a MonoInst* pointer.
+
+       Note: the register allocator will insert instructions in the basic block
+       during it's operation.
+
+* Examples
+
+       Given the tree (on x86 the right argument to shl needs to be in ecx):
+
+       store (local1, shl (local1, call (some_arg)))
+
+       At the start of the basic block, the registers are set to the free state.
+       The sequence of instructions may be:
+               instruction             register status -> [%eax %ecx %edx]
+               start                                       free free free
+               eax = load local1                           mov  free free
+               /* call clobbers eax, ecx, edx */
+               spill eax                                   free free free
+               call                                        mov  free free
+               /* now eax contains the right operand of the shl */
+               mov %eax -> %ecx                            free busy free
+               un-spill                                    mov  busy free
+               shl %cl, %eax                               mov  free free
+       
+       The resulting x86 code is:
+               mov $fffc(%ebp), %eax
+               mov %eax, $fff0(%ebp)
+               push some_arg
+               call func
+               mov %eax, %ecx
+               mov $fff0(%ebp), %eax
+               shl %cl, %eax
+               
+       Note that since shl could operate directly on memory, we could have:
+       
+               push some_arg
+               call func
+               mov %eax, %ecx
+               shl %cl, $fffc(%ebp)
+
+       The above example with loading the operand in a register is just to complicate
+       the example and show that the algorithm should be able to handle it.
+
+       Let's take another example with the this-call call convention (the first argument 
+       is passed in %ecx).
+       In this case, will_clobber() will be called only on %eax and %edx, while %ecx
+       will be allocated with get_register_force ().
+       Note: when a register is allocated with get_register_force(), it should be set
+       to a different state as soon as possible.
+
+       store (local1, shl (local1, this-call (local1)))
+
+               instruction             register status -> [%eax %ecx %edx]
+               start                                       free free free
+               eax = load local1                           mov  free free
+               /* force load in %ecx */
+               ecx = load local1                           mov  busy free
+               spill eax                                   free busy free
+               call                                        mov  free free
+               /* now eax contains the right operand of the shl */
+               mov %eax -> %ecx                            free busy free
+               un-spill                                    mov  busy free
+               shl %cl, %eax                               mov  free free
+
+       What happens when a register that we need to allocate with get_register_force ()
+       contains an operand for the next instruction?
+
+               instruction             register status -> [%eax %ecx %edx]
+               eax = load local0                           mov  free free
+               ecx = load local1                           mov  mov  free
+               get_register_force (ecx) here.
+               We have two options: 
+                       mov %ecx, %edx
+               or:
+                       spill %ecx
+               The first option is way better (and allows the peephole pass to
+               just load the value in %edx directly, instead of loading first to %ecx).
+               This doesn't work, though, if the instruction clobbers the %edx register
+               (like in a this-call). So, we first need to clobber the registers
+               (so the state of %ecx changes to freebale and there is no issue
+               with get_register_force ()).
+               What if an instruction both clobbers a register and requires it as 
+               an operand? Lets' take the x86 idiv instruction as an example: it
+               requires the dividend in edx:eax and returns the result in eax,
+               with the modulus in edx.
+       
+       store (local1, div (local1, local2))
+               
+               instruction             register status -> [%eax %ecx %edx]
+               eax = load local0                           mov  free free
+               will_clobber eax, edx                       free mov  free
+               force mov %ecx, %eax                        busy free free
+               set %edx                                    busy free busy
+               idiv                                        mov  free free
+       
+       Note: edx is set to free after idiv, because the modulus is not needed
+       (if it was a rem, eax would have been freed).
+       If we load the divisor before will_clobber(), we'll have to spill
+       eax and reload it later. If we load it just after the idiv, there is no issue.
+       In any case, the algorithm should give the correct results and allow the operation.
+               
+       Working recursively on the isntructions there shouldn't be huge issues
+       with this algorithm (though, of course, it's not optimal and it may
+       introduce excessive spills or register moves). The advantage over the current
+       local reg allocator is that:
+       1) the number of spills/moves would be smaller anyway
+       2) a separate peephole pass could be able to eliminate reg moves
+       3) we'll be able to remove the 'forced' spills we currently do with
+               the return value of method calls
+
+* Issues
+
+       How to best integrate such a reg allocator with the burg stuff.
+
+       Think about a call os sparc with two arguments: they got into %o0 and %o1
+       and each of them sets the register as busy. But what if the values to put there
+       are themselves the result of a call? %o0 is no problem, but for all the 
+       next argument n the above algorithm would spill all the 0...n-1 registers...
+
+* Papers
+
+       More complex solutions to the local register allocator problem:
+       http://dimacs.rutgers.edu/TechnicalReports/abstracts/1997/97-33.html
+
+       Combining register allocation and instruction scheduling:
+       http://citeseer.nj.nec.com/motwani95combining.html
+
+       More on LRA euristics:
+       http://citeseer.nj.nec.com/liberatore97hardness.html
+
+       Linear-time optimal code scheduling for delayedload architectures
+       http://www.cs.wisc.edu/~fischer/cs701.f01/inst.sched.ps.gz
+
+       Precise Register Allocation for Irregular Architectures
+       http://citeseer.nj.nec.com/kong98precise.html
+
+       Allocate registers first to subtrees that need more of them.
+       http://www.upb.de/cs/ag-kastens/compii/folien/comment401-409.2.pdf
diff --git a/mono/mini/main.c b/mono/mini/main.c
new file mode 100644 (file)
index 0000000..4942586
--- /dev/null
@@ -0,0 +1,8 @@
+#include "mini.h"
+
+int
+main (int argc, char* argv[])
+{
+       return mini_main (argc, argv);
+}
+
diff --git a/mono/mini/makefile b/mono/mini/makefile
new file mode 100644 (file)
index 0000000..1c776f8
--- /dev/null
@@ -0,0 +1,99 @@
+count=100000
+mtest=for_loop
+monodir=../mono/
+libs=  \
+       $(monodir)/mono/metadata/libmonoruntime.la      \
+       $(monodir)/mono/metadata/libmetadata.la \
+       $(monodir)/mono/io-layer/libwapi.la     \
+       $(monodir)/mono/utils/libmonoutils.la
+debugger_libs= \
+       ../debugger/wrapper/libmonodebuggerwrapper.a
+
+MCS=mcs
+RUNTIME=mono
+CC=gcc
+WARN=-Wall -Wunused -Wmissing-prototypes -Wmissing-declarations \
+       -Wstrict-prototypes  -Wmissing-prototypes -Wnested-externs \
+       -Wpointer-arith -Wno-cast-qual -Wcast-align -Wwrite-strings
+CFLAGS=-g $(WARN) -fexceptions -DMONO_USE_EXC_TABLES
+CPPFLAGS=-g $(WARN) -I$(monodir) `pkg-config --cflags glib-2.0 gmodule-2.0 gthread-2.0`
+LDFLAGS=-g $(libs) -lgc -lm `pkg-config --libs glib-2.0 gmodule-2.0 gthread-2.0`
+debugger_LDFLAGS=-g $(libs) $(debugger_libs) -lgc -lm `pkg-config --libs glib-2.0 gmodule-2.0 gthread-2.0`
+
+arch_objects=mini-x86.o exceptions-x86.o tramp-x86.o
+lib_objects=mini.o dominators.o cfold.o regalloc.o inssel.o regset.o helpers.o liveness.o ssa.o driver.o \
+       debug.o debug-stabs.o debug-dwarf2.o debug-mini.o linear-scan.o aot.o graph.o $(arch_objects)
+objects=main.o $(lib_objects)
+debugger_objects=debugger-main.o $(lib_objects)
+regtests=basic.exe arrays.exe basic-float.exe basic-long.exe objects.exe iltests.exe exceptions.exe bench.exe
+
+%.o: %.c mini.h makefile regset.h mini-x86.h inssel.h cpu-pentium.h mini-ops.h
+       $(CC) -c $< $(CFLAGS) $(CPPFLAGS)
+
+%.exe: %.cs TestDriver.dll
+       $(MCS) /unsafe $< /r:TestDriver.dll
+
+%.exe: %.il
+       ilasm /OUTPUT=$*.exe $<
+
+all: mini genmdesc
+
+TestDriver.dll: TestDriver.cs
+       $(MCS) /out:TestDriver.dll /target:library TestDriver.cs
+
+mini: mini.h $(objects) $(libs) makefile cpu-pentium.h jit-icalls.c
+       libtool --mode=link gcc -o mini $(objects) $(LDFLAGS)
+
+mono-debugger-mini-wrapper: mini.h $(debugger_objects) $(libs) makefile cpu-pentium.h jit-icalls.c
+       libtool --mode=link gcc -o mono-debugger-mini-wrapper $(debugger_objects) $(debugger_LDFLAGS)
+
+genmdesc: mini.h genmdesc.o helpers.o
+       libtool --mode=link gcc -o genmdesc genmdesc.o helpers.o $(LDFLAGS)
+
+cpu-pentium.h: cpu-pentium.md genmdesc
+       ./genmdesc cpu-pentium.md cpu-pentium.h pentium
+
+BURGSRC= inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg
+
+inssel.c inssel.h: $(BURGSRC)
+       $(monodir)/mono/monoburg/monoburg -c 1 -p -e $(BURGSRC) -d inssel.h -s inssel.c
+
+testi: mini test.exe
+       ./mini -v -v --ncompile 1 --compile Test:$(mtest) test.exe
+
+# ensure the tests are actually correct
+checktests: $(regtests)
+       for i in $(regtests); do $(RUNTIME) $$i; done
+
+check: mini $(regtests)
+       ./mini --verbose --regression $(regtests)
+
+aotcheck: mini $(regtests)
+       for i in $(regtests); do ./mini --aot $$i; done
+       ./mini --verbose --regression $(regtests)
+       rm -f *.exe.so
+
+bench: mini test.exe
+       time ./mini --ncompile $(count) --compile Test:$(mtest) test.exe
+
+mbench: test.exe
+       time $(monodir)/mono/jit/mono --ncompile $(count) --compile Test:$(mtest) test.exe
+
+stat1: mini bench.exe
+       ./mini --verbose --statfile stats.pl --regression bench.exe
+       perl viewstat.pl stats.pl
+
+stat2: mini basic.exe
+       ./mini --verbose --statfile stats.pl --regression basic.exe
+       perl viewstat.pl -e stats.pl
+
+stat3: mini bench.exe
+       ./mini --statfile stats.pl --ncompile 1000 --compile Tests:test_0_many_nested_loops bench.exe 
+       perl viewstat.pl stats.pl
+
+docu: mini.sgm
+       docbook2txt mini.sgm
+
+clean:
+       rm -f mini a.out gmon.out *.o test.exe
+
diff --git a/mono/mini/mini-arch.h b/mono/mini/mini-arch.h
new file mode 100644 (file)
index 0000000..dd9383d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __MONO_MINI_ARCH_H__
+#define __MONO_MINI_ARCH_H__
+
+#ifdef __i386__
+#include "mini-x86.h"
+#elif defined(__ppc__)
+#include "mini-ppc.h"
+#else
+#error add arch specific include file in mini-arch.h
+#endif
+
+#endif /* __MONO_MINI_ARCH_H__ */  
diff --git a/mono/mini/mini-doc.txt b/mono/mini/mini-doc.txt
new file mode 100644 (file)
index 0000000..cfe1a91
--- /dev/null
@@ -0,0 +1,628 @@
+
+              A new JIT compiler for the Mono Project
+
+          Miguel de Icaza (miguel@{ximian.com,gnome.org}),
+
+  
+* Abstract
+
+       Mini is a new compilation engine for the Mono runtime.  The
+       new engine is designed to bring new code generation
+       optimizations, portability and precompilation. 
+
+       In this document we describe the design decisions and the
+       architecture of the new compilation engine. 
+
+* Introduction
+
+       First we discuss the overall architecture of the Mono runtime,
+       and how code generation fits into it; Then we discuss the
+       development and basic architecture of our first JIT compiler
+       for the ECMA CIL framework.  The next section covers the
+       objectives for the work on the new JIT compiler, then we
+       discuss the new features available in the new JIT compiler,
+       and finally a technical description of the new code generation
+       engine.
+
+* Architecture of the Mono Runtime
+
+       The Mono runtime is an implementation of the ECMA Common
+       Language Infrastructure (CLI), whose aim is to be a common
+       platform for executing code in multiple languages.
+
+       Languages that target the CLI generate images that contain
+       code in high-level intermediate representation called the
+       "Common Intermediate Language".  This intermediate language is
+       rich enough to allow for programs and pre-compiled libraries
+       to be reflected.  The execution environment allows for an
+       object oriented execution environment with single inheritance
+       and multiple interface implementations.
+
+       This runtime provides a number of services for programs that
+       are targeted to it: Just-in-Time compilation of CIL code into
+       native code, garbage collection, thread management, I/O
+       routines, single, double and decimal floating point,
+       asynchronous method invocation, application domains, and a
+       framework for building arbitrary RPC systems (remoting) and
+       integration with system libraries through the Platform Invoke
+       functionality.
+
+       The focus of this document is on the services provided by the
+       Mono runtime to transform CIL bytecodes into code that is
+       native to the underlying architecture.
+
+       The code generation interface is a set of macros that allow a
+       C programmer to generate code on the fly, this is done
+       through a set of macros found in the mono/jit/arch/ directory.
+       These macros are used by the JIT compiler to generate native
+       code. 
+
+       The platform invocation code is interesting, as it generates
+       CIL code on the fly to marshal parameters, and then this
+       code is in turned processed by the JIT engine.
+
+* Previous Experiences
+
+       Mono has built a JIT engine, which has been used to bootstrap
+       Mono since January, 2002.  This JIT engine has reasonable
+       performance, and uses an tree pattern matching instruction
+       selector based on the BURS technology.  This JIT compiler was
+       designed by Dietmar Maurer, Paolo Molaro and Miguel de Icaza.
+
+       The existing JIT compiler has three phases:
+
+               * Re-creation of the semantic tree from CIL
+                 byte-codes.
+
+               * Instruction selection, with a cost-driven
+                 engine. 
+
+               * Code generation and register allocation.
+
+       It is also hooked into the rest of the runtime to provide
+       services like marshaling, just-in-time compilation and
+       invocation of "internal calls". 
+
+       This engine constructed a collection of trees, which we
+       referred to as the "forest of trees", this forest is created by
+       "hydrating" the CIL instruction stream.
+
+       The first step was to identify the basic blocks on the method,
+       and computing the control flow graph (cfg) for it.  Once this
+       information was computed, a stack analysis on each basic block
+       was performed to create a forest of trees for each one of
+       them. 
+
+       So for example, the following statement:
+
+              int a, b;
+              ...
+              b = a + 1;
+
+       Which would be represented in CIL as:
+
+                       ldloc.0 
+                       ldc.i4.1 
+                       add 
+                       stloc.1 
+
+       After the stack analysis would create the following tree:
+
+               (STIND_I4 ADDR_L[EBX|2] (
+                        ADD (LDIND_I4 ADDR_L[ESI|1]) 
+                        CONST_I4[1]))
+
+        This tree contains information from the stack analysis: for
+        instance, notice that the operations explicitly encode the
+        data types they are operating on, there is no longer an
+        ambiguity on the types, because this information has been
+        inferred. 
+
+       At this point the JIT would pass the constructed forest of
+       trees to the architecture-dependant JIT compiler.  
+
+       The architecture dependent code then performed register
+       allocation (optionally using linear scan allocation for
+       variables, based on life analysis).  
+
+       Once variables had been assigned, a tree pattern matching with
+       dynamic programming is used (the tree pattern matcher is
+       custom build for each architecture, using a code
+       generator: monoburg). The instruction selector used cost
+       functions to select the best instruction patterns.  
+
+       The instruction selector is able to produce instructions that
+       take advantage of the x86 instruction indexing instructions
+       for example. 
+
+       One problem though is that the code emitter and the register
+       allocator did not have any visibility outside the current
+       tree, which meant that some redundant instructions were
+       generated.  A peephole optimizer with this architecture was
+       hard to write, given the tree-based representation that is
+       used.
+
+       This JIT was functional, but it did not provide a good
+       architecture to base future optimizations on.  Also the
+       line between architecture neutral and architecture
+       specific code and optimizations was hard to draw.
+
+       The JIT engine supported two code generation modes to support
+       the two optimization modes for applications that host multiple
+       application domains: generate code that will be shared across
+       application domains, or generate code that will not be shared
+       across application domains.
+
+* Objectives of the new JIT engine.
+
+       We wanted to support a number of features that were missing:
+
+          * Ahead-of-time compilation.  
+
+            The idea is to allow developers to pre-compile their code
+            to native code to reduce startup time, and the working
+            set that is used at runtime in the just-in-time compiler.
+
+            Although in Mono this has not been a visible problem, we
+            wanted to pro-actively address this problem.
+
+            When an assembly (a Mono/.NET executable) is installed in
+            the system, it would then be possible to pre-compile the
+            code, and have the JIT compiler tune the generated code
+            to the particular CPU on which the software is
+            installed. 
+
+            This is done in the Microsoft.NET world with a tool
+            called ngen.exe
+
+          * Have a good platform for doing code optimizations. 
+
+            The design called for a good architecture that would
+            enable various levels of optimizations: some
+            optimizations are better performed on high-level
+            intermediate representations, some on medium-level and
+            some at low-level representations.
+
+            Also it should be possible to conditionally turn these on
+            or off.  Some optimizations are too expensive to be used
+            in just-in-time compilation scenarios, but these
+            expensive optimizations can be turned on for
+            ahead-of-time compilations or when using profile-guided
+            optimizations on a subset of the executed methods.
+
+          * Reduce the effort required to port the Mono code
+             generator to new architectures.
+
+            For Mono to gain wide adoption in the Unix world, it is
+            necessary that the JIT engine works in most of today's
+            commercial hardware platforms. 
+
+* Features of the new JIT engine.
+
+       The new JIT engine was architected by Dietmar Maurer and Paolo
+       Molaro, based on the new objectives.
+
+       Mono provides a number of services to applications running
+       with the new JIT compiler:
+
+            * Just-in-Time compilation of CLI code into native code.
+
+            * Ahead-of-Time compilation of CLI code, to reduce
+               startup time of applications. 
+
+       A number of software development features are also available:
+
+            * Execution time profiling (--profile)
+
+              Generates a report of the times consumed by routines,
+              as well as the invocation times, as well as the
+              callers.
+
+            * Memory usage profiling (--profile)
+
+              Generates a report of the memory usage by a program
+              that is ran under the Mono JIT.
+
+            * Code coverage (--coverage)
+
+            * Execution tracing.
+
+        People who are interested in developing and improving the Mini
+        JIT compiler will also find a few useful routines:
+
+            * Compilation times
+
+              This is used to time the execution time for the JIT
+              when compiling a routine. 
+
+            * Control Flow Graph and Dominator Tree drawing.
+
+              These are visual aids for the JIT developer: they
+              render representations of the Control Flow graph, and
+              for the more advanced optimizations, they draw the
+              dominator tree graph. 
+
+              This requires Dot (from the graphwiz package) and Ghostview.
+
+            * Code generator regression tests.  
+
+              The engine contains support for running regression
+              tests on the virtual machine, which is very helpful to
+              developers interested in improving the engine.
+
+            * Optimization benchmark framework.
+
+              The JIT engine will generate graphs that compare
+              various benchmarks embedded in an assembly, and run the
+              various tests with different optimization flags.  
+
+              This requires Perl, GD::Graph.
+
+* Flexibility
+
+       This is probably the most important component of the new code
+       generation engine.  The internals are relatively easy to
+       replace and update, even large passes can be replaced and
+       implemented differently.
+
+* New code generator
+
+       Compiling a method begins with the `mini_method_to_ir' routine
+       that converts the CIL representation into a medium
+       intermediate representation.
+
+       The mini_method_to_ir routine performs a number of operations:
+
+           * Flow analysis and control flow graph computation.
+
+             Unlike the previous version, stack analysis and control
+             flow graphs are computed in a single pass in the
+             mini_method_to_ir function, this is done for performance
+             reasons: although the complexity increases, the benefit
+             for a JIT compiler is that there is more time available
+             for performing other optimizations.
+
+           * Basic block computation.
+
+             mini_method_to_ir populates the MonoCompile structure
+             with an array of basic blocks each of which contains
+             forest of trees made up of MonoInst structures.
+
+           * Inlining
+
+             Inlining is no longer restricted to methods containing
+             one single basic block, instead it is possible to inline
+             arbitrary complex methods.
+
+             The heuristics to choose what to inline are likely going
+             to be tuned in the future.
+
+           * Method to opcode conversion.
+
+             Some method call invocations like `call Math.Sin' are
+             transformed into an opcode: this transforms the call
+             into a semantically rich node, which is later inline
+             into an FPU instruction.
+
+             Various Array methods invocations are turned into
+             opcodes as well (The Get, Set and Address methods)
+
+           * Tail recursion elimination
+
+       Basic blocks ****
+
+       The MonoInst structure holds the actual decoded instruction,
+       with the semantic information from the stack analysis.
+       MonoInst is interesting because initially it is part of a tree
+       structure, here is a sample of the same tree with the new JIT
+       engine:
+
+                (stind.i4 regoffset[0xffffffd4(%ebp)] 
+                          (add (ldind.i4 regoffset[0xffffffd8(%ebp)])
+                                iconst[1]))
+
+       This is a medium-level intermediate representation (MIR). 
+
+       Some complex opcodes are decomposed at this stage into a
+       collection of simpler opcodes.  Not every complex opcode is
+       decomposed at this stage, as we need to preserve the semantic
+       information during various optimization phases.  
+
+       For example a NEWARR opcode carries the length and the type of
+       the array that could be used later to avoid type checking or
+       array bounds check.
+
+        There are a number of operations supported on this
+       representation:
+
+               * Branch optimizations.
+
+               * Variable liveness.
+
+               * Loop optimizations: the dominator trees are
+                 computed, loops are detected, and their nesting
+                 level computed.
+
+               * Conversion of the method into static single assignment
+                  form (SSA form).
+
+               * Dead code elimination.
+
+               * Constant propagation.
+
+               * Copy propagation.
+
+               * Constant folding.
+
+       Once the above optimizations are optionally performed, a
+       decomposition phase is used to turn some complex opcodes into
+       internal method calls.  In the initial version of the JIT
+       engine, various operations on longs are emulated instead of
+       being inlined.  Also the newarr invocation is turned into a
+       call to the runtime.
+
+       At this point, after computing variable liveness, it is
+       possible to use the linear scan algorithm for allocating
+       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. 
+
+       Stack space is then reserved for the local variables and any
+       temporary variables generated during the various
+       optimizations.
+
+** Instruction selection 
+
+       At this point, the BURS instruction selector is invoked to
+       transform the tree-based representation into a list of
+       instructions.  This is done using a tree pattern matcher that
+       is generated for the architecture using the `monoburg' tool. 
+
+       Monoburg takes as input a file that describes tree patterns,
+       which are matched against the trees that were produced by the
+       engine in the previous stages.
+
+       The pattern matching might have more than one match for a
+       particular tree.  In this case, the match selected is the one
+       whose cost is the smallest.  A cost can be attached to each
+       rule, and if no cost is provided, the implicit cost is one.
+       Smaller costs are selected over higher costs.
+
+       The cost function can be used to select particular blocks of
+       code for a given architecture, or by using a prohibitive high
+       number to avoid having the rule match.
+
+       The various rules that our JIT engine uses transform a tree of
+       MonoInsts into a list of monoinsts:
+
+       +-----------------------------------------------------------+
+       | Tree                                           List       |
+       | of           ===> Instruction selection ===>   of         |
+       | MonoInst                                       MonoInst.  |
+        +-----------------------------------------------------------+
+
+       During this process various "types" of MonoInst kinds 
+       disappear and turned into lower-level representations.  The
+       JIT compiler just happens to reuse the same structure (this is
+       done to reduce memory usage and improve memory locality).
+
+       The instruction selection rules are split in a number of
+       files, each one with a particular purpose:
+
+               inssel.brg
+                       Contains the generic instruction selection
+                       patterns.
+
+               inssel-x86.brg
+                       Contains x86 specific rules.
+
+               inssel-ppc.brg
+                       Contains PowerPC specific rules.
+
+               inssel-long32.brg
+                       burg file for 64bit instructions on 32bit architectures.
+
+               inssel-long.brg
+                       burg file for 64bit architectures.
+
+               inssel-float.brg
+                       burg file for floating point instructions
+               
+       For a given build, a set of those files would be included.
+       For example, for the build of Mono on the x86, the following
+       set is used:
+
+           inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg
+
+** Native method generation
+
+       The native method generation has a number of steps:
+
+               * Architecture specific register allocation.
+
+                 The information about loop nesting that was
+                 previously gathered is used here to hint the
+                 register allocator. 
+
+               * Generating the method prolog/epilog.
+
+               * Optionally generate code to introduce tracing facilities.
+
+               * Hooking into the debugger.
+
+               * Performing any pending fixups. 
+
+               * Code generation.
+
+*** Code Generation
+
+       The actual code generation is contained in the architecture
+       specific portion of the compiler.  The input to the code
+       generator is each one of the basic blocks with its list of
+       instructions that were produced in the instruction selection
+       phase.
+
+       During the instruction selection phase, virtual registers are
+       assigned.  Just before the peephole optimization is performed,
+       physical registers are assigned.
+
+       A simple peephole and algebraic optimizer is ran at this
+       stage.  
+
+       The peephole optimizer removes some redundant operations at
+       this point.  This is possible because the code generation at
+       this point has visibility into the basic block that spans the
+       original trees.  
+
+       The algebraic optimizer performs some simple algebraic
+       optimizations that replace expensive operations with cheaper
+       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
+
+       We always try to allocate code in sequence, instead of just using
+       malloc. This way we increase spatial locality which gives a massive
+       speedup on most architectures.
+
+*** Ahead of Time compilation
+
+       Ahead-of-Time compilation is a new feature of our new
+       compilation engine.  The compilation engine is shared by the
+       Just-in-Time (JIT) compiler and the Ahead-of-Time compiler
+       (AOT).
+
+       The difference is on the set of optimizations that are turned
+       on for each mode: Just-in-Time compilation should be as fast
+       as possible, while Ahead-of-Time compilation can take as long
+       as required, because this is not done at a time criticial
+       time. 
+
+       With AOT compilation, we can afford to turn all of the
+       computationally expensive optimizations on.
+
+       After the code generation phase is done, the code and any
+       required fixup information is saved into a file that is
+       readable by "as" (the native assembler available on all
+       systems). This assembly file is then passed to the native
+       assembler, which generates a loadable module.
+
+       At execution time, when an assembly is loaded from the disk,
+       the runtime engine will probe for the existance of a
+       pre-compiled image.  If the pre-compiled image exists, then it
+       is loaded, and the method invocations are resolved to the code
+       contained in the loaded module.
+
+       The code generated under the AOT scenario is slightly
+       different than the JIT scenario.  It generates code that is
+       application-domain relative and that can be shared among
+       multiple thread.
+
+       This is the same code generation that is used when the runtime
+       is instructed to maximize code sharing on a multi-application
+       domain scenario.
+
+* SSA-based optimizations
+
+       SSA form simplifies many optimization because each variable has exactly
+       one definition site. All uses of a variable are "dominated" by its
+       definition, which enables us to implement algorithm like:
+
+               * conditional constant propagation
+
+               * array bound check removal
+
+               * dead code elimination
+
+       And we can implement those algorithm in a efficient way using SSA. 
+
+
+* Register allocation.
+
+       Global register allocation is performed on the medium
+       intermediate representation just before instruction selection
+       is performed on the method.  Local register allocation is
+       later performed at the basic-block level on the 
+
+       Global register allocation uses the following input:
+
+        1) set of register-sized variables that can be allocated to a
+        register (this is an architecture specific setting, for x86
+        these registers are the callee saved register ESI, EDI and
+        EBX). 
+
+        2) liveness information for the variables
+
+        3) (optionally) loop info to favour variables that are used in
+        inner loops.
+
+       During instruction selection phase, symbolic registers are
+       assigned to temporary values in expressions.
+
+       Local register allocation assigns hard registers to the
+       symbolic registers, and it is performed just before the code
+       is actually emitted and is performed at the basic block level.
+       A CPU description file describes the input registers, output
+       registers, fixed registers and clobbered registers by each
+       operation.
+
+
+----------
+* Bootstrap 
+
+       The Mini bootstrap parses the arguments passed on the command
+       line, and initializes the JIT runtime. Each time the
+       mini_init() routine is invoked, a new Application Domain will
+       be returned.
+
+* Signal handlers
+
+       mono_runtime_install_handlers
+
+* BURG Code Generator Generator
+
+       monoburg was written by Dietmar Maurer. It is based on the
+       papers from Christopher W. Fraser, Robert R. Henry and Todd
+       A. Proebsting: "BURG - Fast Optimal Instruction Selection and
+       Tree Parsing" and "Engineering a Simple, Efficient Code
+       Generator Generator".
+
+       The original BURG implementation is unable to work on DAGs, instead only
+       trees are allowed. Our monoburg implementations is able to generate tree
+       matcher which works on DAGs, and we use this feature in the new
+       JIT. This simplifies the code because we can directly pass DAGs and
+       don't need to convert them to trees.
+
+* Future
+
+        Profile-based optimization is something that we are very
+        interested in supporting.  There are two possible usage
+        scenarios: 
+
+          * Based on the profile information gathered during
+             the execution of a program, hot methods can be compiled
+             with the highest level of optimizations, while bootstrap
+             code and cold methods can be compiled with the least set
+             of optimizations and placed in a discardable list.
+
+          * Code reordering: this profile-based optimization would
+             only make sense for pre-compiled code.  The profile
+             information is used to re-order the assembly code on disk
+             so that the code is placed on the disk in a way that
+             increments locality.  
+
+            This is the same principle under which SGI's cord program
+            works.  
+
+       The nature of the CIL allows the above optimizations to be
+       easy to implement and deploy.  Since we live and define our
+       universe for these things, there are no interactions with
+       system tools required, nor upgrades on the underlying
+       infrastructure required.
+
+       Instruction scheduling is important for certain kinds of
+       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
diff --git a/mono/mini/mini-ops.h b/mono/mini/mini-ops.h
new file mode 100644 (file)
index 0000000..6222414
--- /dev/null
@@ -0,0 +1,314 @@
+
+MINI_OP(OP_LOAD,       "load")
+MINI_OP(OP_LDADDR,     "ldaddr")
+MINI_OP(OP_STORE,      "store")
+MINI_OP(OP_OBJADDR,    "objaddr")
+MINI_OP(OP_VTADDR,     "vtaddr")
+MINI_OP(OP_PHI,        "phi")
+MINI_OP(OP_RENAME,     "rename")
+MINI_OP(OP_COMPARE,    "compare")
+MINI_OP(OP_COMPARE_IMM,        "compare_imm")
+MINI_OP(OP_FCOMPARE,   "fcompare")
+MINI_OP(OP_LCOMPARE,   "lcompare")
+MINI_OP(OP_LOCAL,      "local")
+MINI_OP(OP_ARG,        "arg")
+MINI_OP(OP_OUTARG,     "outarg")
+MINI_OP(OP_OUTARG_IMM, "outarg_imm")
+MINI_OP(OP_OUTARG_R4,  "outarg_r4")
+MINI_OP(OP_OUTARG_R8,  "outarg_r8")
+MINI_OP(OP_OUTARG_VT,  "outarg_vt")
+MINI_OP(OP_RETARG,     "retarg")
+MINI_OP(OP_SETRET,     "setret")
+MINI_OP(OP_SETLRET,    "setlret")
+MINI_OP(OP_SETREG,     "setreg")
+MINI_OP(OP_SETREGIMM,  "setregimm")
+MINI_OP(OP_SETFREG,    "setfreg")
+MINI_OP(OP_CHECK_THIS, "checkthis")
+MINI_OP(OP_VOIDCALL,   "voidcall")
+MINI_OP(OP_VOIDCALLVIRT,       "voidcallvirt")
+MINI_OP(OP_VOIDCALL_REG,       "voidcall_reg")
+MINI_OP(OP_VOIDCALL_MEMBASE,   "voidcall_membase")
+MINI_OP(OP_FCALL,      "fcall")
+MINI_OP(OP_FCALLVIRT,  "fcallvirt")
+MINI_OP(OP_FCALL_REG,  "fcall_reg")
+MINI_OP(OP_FCALL_MEMBASE,      "fcall_membase")
+MINI_OP(OP_LCALL,      "lcall")
+MINI_OP(OP_LCALLVIRT,  "lcallvirt")
+MINI_OP(OP_LCALL_REG,  "lcall_reg")
+MINI_OP(OP_LCALL_MEMBASE,      "lcall_membase")
+MINI_OP(OP_VCALL,      "vcall")
+MINI_OP(OP_VCALLVIRT,  "vcallvirt")
+MINI_OP(OP_VCALL_REG,  "vcall_reg")
+MINI_OP(OP_VCALL_MEMBASE,      "vcall_membase")
+MINI_OP(OP_CALL_REG,   "call_reg")
+MINI_OP(OP_CALL_MEMBASE,       "call_membase")
+MINI_OP(OP_TRAP,       "trap")
+MINI_OP(OP_ICONST,     "iconst")
+MINI_OP(OP_I8CONST,    "i8const")
+MINI_OP(OP_R4CONST,    "r4const")
+MINI_OP(OP_R8CONST,    "r8const")
+MINI_OP(OP_REGVAR,     "regvar")
+MINI_OP(OP_REG,                "reg")
+MINI_OP(OP_REGOFFSET,  "regoffset")
+MINI_OP(OP_LABEL,      "label")
+
+MINI_OP(OP_STORE_MEMBASE_IMM,"store_membase_imm")
+MINI_OP(OP_STORE_MEMBASE_REG,"store_membase_reg")
+MINI_OP(OP_STOREI1_MEMBASE_IMM, "storei1_membase_imm")
+MINI_OP(OP_STOREI1_MEMBASE_REG, "storei1_membase_reg")
+MINI_OP(OP_STOREI2_MEMBASE_IMM, "storei2_membase_imm")
+MINI_OP(OP_STOREI2_MEMBASE_REG, "storei2_membase_reg")
+MINI_OP(OP_STOREI4_MEMBASE_IMM, "storei4_membase_imm")
+MINI_OP(OP_STOREI4_MEMBASE_REG, "storei4_membase_reg")
+MINI_OP(OP_STOREI8_MEMBASE_IMM, "storei8_membase_imm")
+MINI_OP(OP_STOREI8_MEMBASE_REG, "storei8_membase_reg")
+MINI_OP(OP_STORER4_MEMBASE_REG, "storer4_membase_reg")
+MINI_OP(OP_STORER8_MEMBASE_REG, "storer8_membase_reg")
+MINI_OP(OP_LOAD_MEMBASE,       "load_membase")
+MINI_OP(OP_LOADI1_MEMBASE,"loadi1_membase")
+MINI_OP(OP_LOADU1_MEMBASE,"loadu1_membase")
+MINI_OP(OP_LOADI2_MEMBASE,"loadi2_membase")
+MINI_OP(OP_LOADU2_MEMBASE,"loadu2_membase")
+MINI_OP(OP_LOADI4_MEMBASE,"loadi4_membase")
+MINI_OP(OP_LOADU4_MEMBASE,"loadu4_membase")
+MINI_OP(OP_LOADI8_MEMBASE,"loadi8_membase")
+MINI_OP(OP_LOADR4_MEMBASE,"loadr4_membase")
+MINI_OP(OP_LOADR8_MEMBASE,"loadr8_membase")
+MINI_OP(OP_LOADU4_MEM,"loadu4_mem")
+MINI_OP(OP_MOVE,       "move")
+
+MINI_OP(OP_ADD_IMM,    "add_imm")
+MINI_OP(OP_SUB_IMM,    "sub_imm")
+MINI_OP(OP_MUL_IMM,    "mul_imm")
+MINI_OP(OP_DIV_IMM,    "div_imm")
+MINI_OP(OP_DIV_UN_IMM, "div_un_imm")
+MINI_OP(OP_REM_IMM,    "rem_imm")
+MINI_OP(OP_REM_UN_IMM, "rem_un_imm")
+MINI_OP(OP_AND_IMM,    "and_imm")
+MINI_OP(OP_OR_IMM,     "or_imm")
+MINI_OP(OP_XOR_IMM,    "xor_imm")
+MINI_OP(OP_SHL_IMM,    "shl_imm")
+MINI_OP(OP_SHR_IMM,    "shr_imm")
+MINI_OP(OP_SHR_UN_IMM, "shr_un_imm")
+
+/* exceptions: must be in the same order as the matching CEE_ branch opcodes */
+MINI_OP(OP_COND_EXC_EQ, "cond_exc_eq")
+MINI_OP(OP_COND_EXC_GE, "cond_exc_ge")
+MINI_OP(OP_COND_EXC_GT, "cond_exc_gt")
+MINI_OP(OP_COND_EXC_LE, "cond_exc_le")
+MINI_OP(OP_COND_EXC_LT, "cond_exc_lt")
+MINI_OP(OP_COND_EXC_NE_UN, "cond_exc_ne_un")
+MINI_OP(OP_COND_EXC_GE_UN, "cond_exc_ge_un")
+MINI_OP(OP_COND_EXC_GT_UN, "cond_exc_gt_un")
+MINI_OP(OP_COND_EXC_LE_UN, "cond_exc_le_un")
+MINI_OP(OP_COND_EXC_LT_UN, "cond_exc_lt_un")
+
+MINI_OP(OP_COND_EXC_OV, "cond_exc_ov")
+MINI_OP(OP_COND_EXC_NO, "cond_exc_no")
+MINI_OP(OP_COND_EXC_C, "cond_exc_c")
+MINI_OP(OP_COND_EXC_NC, "cond_exc_nc")
+
+/* 64 bit opcodes: must be in the same order as the matching CEE_ opcodes: binops_op_map */
+MINI_OP(OP_LADD,    "long_add")
+MINI_OP(OP_LSUB,    "long_sub")
+MINI_OP(OP_LMUL,    "long_mul")
+MINI_OP(OP_LDIV,    "long_div")
+MINI_OP(OP_LDIV_UN, "long_div_un")
+MINI_OP(OP_LREM,    "long_rem")
+MINI_OP(OP_LREM_UN, "long_rem_un")
+MINI_OP(OP_LAND,    "long_and")
+MINI_OP(OP_LOR,     "long_or")
+MINI_OP(OP_LXOR,    "long_xor")
+MINI_OP(OP_LSHL,    "long_shl")
+MINI_OP(OP_LSHR,    "long_shr")
+MINI_OP(OP_LSHR_UN, "long_shr_un")
+
+/* 64 bit opcodes: must be in the same order as the matching CEE_ opcodes: unops_op_map */
+MINI_OP(OP_LNEG,       "long_neg")
+MINI_OP(OP_LNOT,       "long_not")
+MINI_OP(OP_LCONV_TO_I1,"long_conv_to_i1")
+MINI_OP(OP_LCONV_TO_I2,"long_conv_to_i2")
+MINI_OP(OP_LCONV_TO_I4,"long_conv_to_i4")
+MINI_OP(OP_LCONV_TO_I8,"long_conv_to_i8")
+MINI_OP(OP_LCONV_TO_R4,"long_conv_to_r4")
+MINI_OP(OP_LCONV_TO_R8,"long_conv_to_r8")
+MINI_OP(OP_LCONV_TO_U4,"long_conv_to_u4")
+MINI_OP(OP_LCONV_TO_U8,"long_conv_to_u8")
+
+MINI_OP(OP_LCONV_TO_U2,   "long_conv_to_u2")
+MINI_OP(OP_LCONV_TO_U1,   "long_conv_to_u1")
+MINI_OP(OP_LCONV_TO_I,    "long_conv_to_i")
+MINI_OP(OP_LCONV_TO_OVF_I,"long_conv_to_ovf_i")
+MINI_OP(OP_LCONV_TO_OVF_U,"long_conv_to_ovf_u")
+MINI_OP(OP_LADD_OVF,      "long_add_ovf")
+MINI_OP(OP_LADD_OVF_UN,   "long_add_ovf_un")
+MINI_OP(OP_LMUL_OVF,      "long_mul_ovf")
+MINI_OP(OP_LMUL_OVF_UN,   "long_mul_ovf_un")
+MINI_OP(OP_LSUB_OVF,      "long_sub_ovf")
+MINI_OP(OP_LSUB_OVF_UN,   "long_sub_ovf_un")
+
+MINI_OP(OP_LCONV_TO_OVF_I1_UN,"long_conv_to_ovf_i1_un")
+MINI_OP(OP_LCONV_TO_OVF_I2_UN,"long_conv_to_ovf_i2_un")
+MINI_OP(OP_LCONV_TO_OVF_I4_UN,"long_conv_to_ovf_i4_un")
+MINI_OP(OP_LCONV_TO_OVF_I8_UN,"long_conv_to_ovf_i8_un")
+MINI_OP(OP_LCONV_TO_OVF_U1_UN,"long_conv_to_ovf_u1_un")
+MINI_OP(OP_LCONV_TO_OVF_U2_UN,"long_conv_to_ovf_u2_un")
+MINI_OP(OP_LCONV_TO_OVF_U4_UN,"long_conv_to_ovf_u4_un")
+MINI_OP(OP_LCONV_TO_OVF_U8_UN,"long_conv_to_ovf_u8_un")
+MINI_OP(OP_LCONV_TO_OVF_I_UN, "long_conv_to_ovf_i_un")
+MINI_OP(OP_LCONV_TO_OVF_U_UN, "long_conv_to_ovf_u_un")
+
+MINI_OP(OP_LCONV_TO_OVF_I1,"long_conv_to_ovf_i1")
+MINI_OP(OP_LCONV_TO_OVF_U1,"long_conv_to_ovf_u1")
+MINI_OP(OP_LCONV_TO_OVF_I2,"long_conv_to_ovf_i2")
+MINI_OP(OP_LCONV_TO_OVF_U2,"long_conv_to_ovf_u2")
+MINI_OP(OP_LCONV_TO_OVF_I4,"long_conv_to_ovf_i4")
+MINI_OP(OP_LCONV_TO_OVF_U4,"long_conv_to_ovf_u4")
+MINI_OP(OP_LCONV_TO_OVF_I8,"long_conv_to_ovf_i8")
+MINI_OP(OP_LCONV_TO_OVF_U8,"long_conv_to_ovf_u8")
+
+MINI_OP(OP_LCEQ,   "long_ceq")
+MINI_OP(OP_LCGT,   "long_cgt")
+MINI_OP(OP_LCGT_UN,"long_cgt_un")
+MINI_OP(OP_LCLT,   "long_clt")
+MINI_OP(OP_LCLT_UN,"long_clt_un")
+
+MINI_OP(OP_LCONV_TO_R_UN,"long_conv_to_r_un")
+MINI_OP(OP_LCONV_TO_U,   "long_conv_to_u")
+MINI_OP(OP_LSHR_IMM,    "long_shr_imm")
+MINI_OP(OP_LSHR_UN_IMM,  "long_shr_un_imm")
+MINI_OP(OP_LSHL_IMM,     "long_shl_imm")
+MINI_OP(OP_LADD_IMM,     "long_add_imm")
+MINI_OP(OP_LSUB_IMM,     "long_sub_imm")
+
+MINI_OP(OP_LBEQ,    "long_beq")
+MINI_OP(OP_LBNE_UN, "long_bne_un")
+MINI_OP(OP_LBLT,    "long_blt")
+MINI_OP(OP_LBLT_UN, "long_blt_un")
+MINI_OP(OP_LBGT,    "long_bgt")
+MINI_OP(OP_LBGT_UN, "long_btg_un")
+MINI_OP(OP_LBGE,    "long_bge")
+MINI_OP(OP_LBGE_UN, "long_bge_un")
+MINI_OP(OP_LBLE,    "long_ble")
+MINI_OP(OP_LBLE_UN, "long_ble_un")
+
+MINI_OP(OP_FBEQ,   "float_beq")
+MINI_OP(OP_FBNE_UN,"float_bne_un")
+MINI_OP(OP_FBLT,   "float_blt")
+MINI_OP(OP_FBLT_UN,"float_blt_un")
+MINI_OP(OP_FBGT,   "float_bgt")
+MINI_OP(OP_FBGT_UN,"float_btg_un")
+MINI_OP(OP_FBGE,   "float_bge")
+MINI_OP(OP_FBGE_UN,"float_bge_un")
+MINI_OP(OP_FBLE,   "float_ble")
+MINI_OP(OP_FBLE_UN,"float_ble_un")
+
+/* float opcodes: must be in the same order as the matching CEE_ opcodes: binops_op_map */
+MINI_OP(OP_FADD,   "float_add")
+MINI_OP(OP_FSUB,   "float_sub")
+MINI_OP(OP_FMUL,   "float_mul")
+MINI_OP(OP_FDIV,   "float_div")
+MINI_OP(OP_FDIV_UN,"float_div_un")
+MINI_OP(OP_FREM,   "float_rem")
+MINI_OP(OP_FREM_UN,"float_rem_un")
+
+/* float opcodes: must be in the same order as the matching CEE_ opcodes: unops_op_map */
+MINI_OP(OP_FNEG,       "float_neg")
+MINI_OP(OP_FNOT,       "float_not")
+MINI_OP(OP_FCONV_TO_I1,"float_conv_to_i1")
+MINI_OP(OP_FCONV_TO_I2,"float_conv_to_i2")
+MINI_OP(OP_FCONV_TO_I4,"float_conv_to_i4")
+MINI_OP(OP_FCONV_TO_I8,"float_conv_to_i8")
+MINI_OP(OP_FCONV_TO_R4,"float_conv_to_r4")
+MINI_OP(OP_FCONV_TO_R8,"float_conv_to_r8")
+MINI_OP(OP_FCONV_TO_U4,"float_conv_to_u4")
+MINI_OP(OP_FCONV_TO_U8,"float_conv_to_u8")
+
+MINI_OP(OP_FCONV_TO_U2,   "float_conv_to_u2")
+MINI_OP(OP_FCONV_TO_U1,   "float_conv_to_u1")
+MINI_OP(OP_FCONV_TO_I,    "float_conv_to_i")
+MINI_OP(OP_FCONV_TO_OVF_I,"float_conv_to_ovf_i")
+MINI_OP(OP_FCONV_TO_OVF_U,"float_conv_to_ovd_u")
+MINI_OP(OP_FADD_OVF,      "float_add_ovf")
+MINI_OP(OP_FADD_OVF_UN,   "float_add_ovf_un")
+MINI_OP(OP_FMUL_OVF,      "float_mul_ovf")
+MINI_OP(OP_FMUL_OVF_UN,   "float_mul_ovf_un")
+MINI_OP(OP_FSUB_OVF,      "float_sub_ovf")
+MINI_OP(OP_FSUB_OVF_UN,   "float_sub_ovf_un")
+
+MINI_OP(OP_FCONV_TO_OVF_I1_UN,"float_conv_to_ovf_i1_un")
+MINI_OP(OP_FCONV_TO_OVF_I2_UN,"float_conv_to_ovf_i2_un")
+MINI_OP(OP_FCONV_TO_OVF_I4_UN,"float_conv_to_ovf_i4_un")
+MINI_OP(OP_FCONV_TO_OVF_I8_UN,"float_conv_to_ovf_i8_un")
+MINI_OP(OP_FCONV_TO_OVF_U1_UN,"float_conv_to_ovf_u1_un")
+MINI_OP(OP_FCONV_TO_OVF_U2_UN,"float_conv_to_ovf_u2_un")
+MINI_OP(OP_FCONV_TO_OVF_U4_UN,"float_conv_to_ovf_u4_un")
+MINI_OP(OP_FCONV_TO_OVF_U8_UN,"float_conv_to_ovf_u8_un")
+MINI_OP(OP_FCONV_TO_OVF_I_UN, "float_conv_to_ovf_i_un")
+MINI_OP(OP_FCONV_TO_OVF_U_UN, "float_conv_to_ovf_u_un")
+
+MINI_OP(OP_FCONV_TO_OVF_I1,"float_conv_to_ovf_i1")
+MINI_OP(OP_FCONV_TO_OVF_U1,"float_conv_to_ovf_u1")
+MINI_OP(OP_FCONV_TO_OVF_I2,"float_conv_to_ovf_i2")
+MINI_OP(OP_FCONV_TO_OVF_U2,"float_conv_to_ovf_u2")
+MINI_OP(OP_FCONV_TO_OVF_I4,"float_conv_to_ovf_i4")
+MINI_OP(OP_FCONV_TO_OVF_U4,"float_conv_to_ovf_u4")
+MINI_OP(OP_FCONV_TO_OVF_I8,"float_conv_to_ovf_i8")
+MINI_OP(OP_FCONV_TO_OVF_U8,"float_conv_to_ovf_u8")
+
+MINI_OP(OP_FCEQ,   "float_ceq")
+MINI_OP(OP_FCGT,   "float_cgt")
+MINI_OP(OP_FCGT_UN,"float_cgt_un")
+MINI_OP(OP_FCLT,   "float_clt")
+MINI_OP(OP_FCLT_UN,"float_clt_un")
+
+MINI_OP(OP_FCONV_TO_U, "float_conv_to_u")
+
+/* aot compiler */
+MINI_OP(OP_AOTCONST, "aot_const")
+
+/* exception related opcodes */
+MINI_OP(OP_HANDLER  , "handler")
+MINI_OP(OP_ENDFILTER,  "op_endfilter")
+
+/* opcodes most architecture have */
+MINI_OP(OP_ADC,     "adc")
+MINI_OP(OP_ADC_IMM, "adc_imm")
+MINI_OP(OP_SBB,     "sbb")
+MINI_OP(OP_SBB_IMM, "sbb_imm")
+MINI_OP(OP_ADDCC,   "addcc")
+MINI_OP(OP_SUBCC,   "subcc")
+MINI_OP(OP_BR_REG,  "br_reg")
+
+/* FP functions usually done by the CPU */
+MINI_OP(OP_SIN,     "sin")
+MINI_OP(OP_COS,     "cos")
+MINI_OP(OP_ABS,     "abs")
+MINI_OP(OP_TAN,     "tan")
+MINI_OP(OP_ATAN,    "atan")
+MINI_OP(OP_SQRT,    "sqrt")
+
+/* x86 specific */
+MINI_OP(OP_X86_TEST_NULL,          "x86_test_null")
+MINI_OP(OP_X86_COMPARE_MEMBASE_REG,"x86_compare_membase_reg")
+MINI_OP(OP_X86_COMPARE_MEMBASE_IMM,"x86_compare_membase_imm")
+MINI_OP(OP_X86_COMPARE_REG_MEMBASE,"x86_compare_reg_membase")
+MINI_OP(OP_X86_INC_REG,            "x86_inc_reg")
+MINI_OP(OP_X86_INC_MEMBASE,        "x86_inc_membase")
+MINI_OP(OP_X86_DEC_REG,            "x86_dec_reg")
+MINI_OP(OP_X86_DEC_MEMBASE,        "x86_dec_membase")
+MINI_OP(OP_X86_ADD_MEMBASE_IMM,    "x86_add_membase_imm")
+MINI_OP(OP_X86_SUB_MEMBASE_IMM,    "x86_sub_membase_imm")
+MINI_OP(OP_X86_PUSH_MEMBASE,       "x86_push_membase")
+MINI_OP(OP_X86_PUSH_IMM,           "x86_push_imm")
+MINI_OP(OP_X86_PUSH,               "x86_push")
+MINI_OP(OP_X86_PUSH_FP,            "x86_push_fp")
+MINI_OP(OP_X86_PUSH_OBJ,           "x86_push_obj")
+MINI_OP(OP_X86_LEA,                "x86_lea")
+MINI_OP(OP_X86_XCHG,               "x86_xchg")
+MINI_OP(OP_X86_FPOP,               "x86_fpop")
+MINI_OP(OP_X86_FP_LOAD_I8,         "x86_fp_load_i8")
+MINI_OP(OP_X86_FP_LOAD_I4,         "x86_fp_load_i4")
+
+
+
diff --git a/mono/mini/mini-ppc.c b/mono/mini/mini-ppc.c
new file mode 100644 (file)
index 0000000..fc8a3e8
--- /dev/null
@@ -0,0 +1,2854 @@
+/*
+ * mini-ppc.c: PowerPC backend for the Mono code generator
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include "mini.h"
+#include <string.h>
+
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini-ppc.h"
+#include "inssel.h"
+#include "regset.h"
+#include "cpu-g4.h"
+
+int mono_exc_esp_offset = 0;
+
+const char*
+mono_arch_regname (int reg) {
+       static const char * rnames[] = {
+               "ppc_r0", "ppc_sp", "ppc_r2", "ppc_r3", "ppc_r4",
+               "ppc_r5", "ppc_r6", "ppc_r7", "ppc_r8", "ppc_r9",
+               "ppc_r10", "ppc_r11", "ppc_r12", "ppc_r13", "ppc_r14",
+               "ppc_r15", "ppc_r16", "ppc_r17", "ppc_r18", "ppc_r19",
+               "ppc_r20", "ppc_r21", "ppc_r22", "ppc_r23", "ppc_r24",
+               "ppc_r25", "ppc_r26", "ppc_r27", "ppc_r28", "ppc_r29",
+               "ppc_r30", "ppc_r31"
+       };
+       if (reg >= 0 && reg < 32)
+               return rnames [reg];
+       return "unknown";
+}
+
+typedef struct {
+       guint16 size;
+       guint16 offset;
+       guint8  pad;
+} MonoJitArgumentInfo;
+
+/*
+ * arch_get_argument_info:
+ * @csig:  a method signature
+ * @param_count: the number of parameters to consider
+ * @arg_info: an array to store the result infos
+ *
+ * Gathers information on parameters such as size, alignment and
+ * padding. arg_info should be large enought to hold param_count + 1 entries. 
+ *
+ * Returns the size of the activation frame.
+ */
+static int
+arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
+{
+       int k, frame_size = 0;
+       int size, align, pad;
+       int offset = 8;
+
+       if (MONO_TYPE_ISSTRUCT (csig->ret)) { 
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].offset = offset;
+
+       if (csig->hasthis) {
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].size = frame_size;
+
+       for (k = 0; k < param_count; k++) {
+               
+               if (csig->pinvoke)
+                       size = mono_type_native_stack_size (csig->params [k], &align);
+               else
+                       size = mono_type_stack_size (csig->params [k], &align);
+
+               /* ignore alignment for now */
+               align = 1;
+
+               frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); 
+               arg_info [k].pad = pad;
+               frame_size += size;
+               arg_info [k + 1].pad = 0;
+               arg_info [k + 1].size = size;
+               offset += pad;
+               arg_info [k + 1].offset = offset;
+               offset += size;
+       }
+
+       align = MONO_ARCH_FRAME_ALIGNMENT;
+       frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
+       arg_info [k].pad = pad;
+
+       return frame_size;
+}
+
+static int indent_level = 0;
+
+static void indent (int diff) {
+       int v = indent_level;
+       while (v-- > 0) {
+               printf (". ");
+       }
+       indent_level += diff;
+}
+
+static void
+enter_method (MonoMethod *method, char *ebp)
+{
+       int i, j;
+       MonoClass *class;
+       MonoObject *o;
+       MonoJitArgumentInfo *arg_info;
+       MonoMethodSignature *sig;
+       char *fname;
+
+       fname = mono_method_full_name (method, TRUE);
+       indent (1);
+       printf ("ENTER: %s(", fname);
+       g_free (fname);
+       
+       if (((int)ebp & (MONO_ARCH_FRAME_ALIGNMENT - 1)) != 0) {
+               g_error ("unaligned stack detected (%p)", ebp);
+       }
+
+       sig = method->signature;
+
+       arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1));
+
+       arch_get_argument_info (sig, sig->param_count, arg_info);
+
+       if (MONO_TYPE_ISSTRUCT (method->signature->ret)) {
+               g_assert (!method->signature->ret->byref);
+
+               printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8)));
+       }
+
+       if (method->signature->hasthis) {
+               gpointer *this = (gpointer *)(ebp + arg_info [0].offset);
+               if (method->klass->valuetype) {
+                       printf ("value:%p, ", *this);
+               } else {
+                       o = *((MonoObject **)this);
+
+                       if (o) {
+                               class = o->vtable->klass;
+
+                               if (class == mono_defaults.string_class) {
+                                       printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
+                               } else {
+                                       printf ("this:%p[%s.%s], ", o, class->name_space, class->name);
+                               }
+                       } else 
+                               printf ("this:NULL, ");
+               }
+       }
+
+       for (i = 0; i < method->signature->param_count; ++i) {
+               gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset);
+               int size = arg_info [i + 1].size;
+
+               MonoType *type = method->signature->params [i];
+               
+               if (type->byref) {
+                       printf ("[BYREF:%p], ", *cpos); 
+               } else switch (type->type) {
+                       
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+                       printf ("%p, ", (gpointer)*((int *)(cpos)));
+                       break;
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+                       printf ("%d, ", *((int *)(cpos)));
+                       break;
+               case MONO_TYPE_STRING: {
+                       MonoString *s = *((MonoString **)cpos);
+                       if (s) {
+                               g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
+                               printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s));
+                       } else 
+                               printf ("[STRING:null], ");
+                       break;
+               }
+               case MONO_TYPE_CLASS:
+               case MONO_TYPE_OBJECT: {
+                       o = *((MonoObject **)cpos);
+                       if (o) {
+                               class = o->vtable->klass;
+                   
+                               if (class == mono_defaults.string_class) {
+                                       printf ("[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
+                               } else if (class == mono_defaults.int32_class) {
+                                       printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject)));
+                               } else
+                                       printf ("[%s.%s:%p], ", class->name_space, class->name, o);
+                       } else {
+                               printf ("%p, ", *((gpointer *)(cpos)));                         
+                       }
+                       break;
+               }
+               case MONO_TYPE_PTR:
+               case MONO_TYPE_FNPTR:
+               case MONO_TYPE_ARRAY:
+               case MONO_TYPE_SZARRAY:
+                       printf ("%p, ", *((gpointer *)(cpos)));
+                       break;
+               case MONO_TYPE_I8:
+                       printf ("%lld, ", *((gint64 *)(cpos)));
+                       break;
+               case MONO_TYPE_R4:
+                       printf ("%f, ", *((float *)(cpos)));
+                       break;
+               case MONO_TYPE_R8:
+                       printf ("%f, ", *((double *)(cpos)));
+                       break;
+               case MONO_TYPE_VALUETYPE: 
+                       printf ("[");
+                       for (j = 0; j < size; j++)
+                               printf ("%02x,", *((guint8*)cpos +j));
+                       printf ("], ");
+                       break;
+               default:
+                       printf ("XX, ");
+               }
+       }
+
+       printf (")\n");
+}
+
+static void
+leave_method (MonoMethod *method, ...)
+{
+       MonoType *type;
+       char *fname;
+       va_list ap;
+
+       va_start(ap, method);
+
+       fname = mono_method_full_name (method, TRUE);
+       indent (-1);
+       printf ("LEAVE: %s", fname);
+       g_free (fname);
+
+       type = method->signature->ret;
+
+handle_enum:
+       switch (type->type) {
+       case MONO_TYPE_VOID:
+               break;
+       case MONO_TYPE_BOOLEAN: {
+               int eax = va_arg (ap, int);
+               if (eax)
+                       printf ("TRUE:%d", eax);
+               else 
+                       printf ("FALSE");
+                       
+               break;
+       }
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U: {
+               int eax = va_arg (ap, int);
+               printf ("EAX=%d", eax);
+               break;
+       }
+       case MONO_TYPE_STRING: {
+               MonoString *s = va_arg (ap, MonoString *);
+;
+               if (s) {
+                       g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
+                       printf ("[STRING:%p:%s]", s, mono_string_to_utf8 (s));
+               } else 
+                       printf ("[STRING:null], ");
+               break;
+       }
+       case MONO_TYPE_CLASS: 
+       case MONO_TYPE_OBJECT: {
+               MonoObject *o = va_arg (ap, MonoObject *);
+
+               if (o) {
+                       if (o->vtable->klass == mono_defaults.boolean_class) {
+                               printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
+                       } else if  (o->vtable->klass == mono_defaults.int32_class) {
+                               printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject))));    
+                       } else if  (o->vtable->klass == mono_defaults.int64_class) {
+                               printf ("[INT64:%p:%lld]", o, *((gint64 *)((char *)o + sizeof (MonoObject))));  
+                       } else
+                               printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o);
+               } else
+                       printf ("[OBJECT:%p]", o);
+              
+               break;
+       }
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_SZARRAY: {
+               gpointer p = va_arg (ap, gpointer);
+               printf ("result=%p", p);
+               break;
+       }
+       case MONO_TYPE_I8: {
+               gint64 l =  va_arg (ap, gint64);
+               printf ("lresult=%lld", l);
+               break;
+       }
+       case MONO_TYPE_R8: {
+               double f = va_arg (ap, double);
+               printf ("FP=%f\n", f);
+               break;
+       }
+       case MONO_TYPE_VALUETYPE: 
+               if (type->data.klass->enumtype) {
+                       type = type->data.klass->enum_basetype;
+                       goto handle_enum;
+               } else {
+                       guint8 *p = va_arg (ap, gpointer);
+                       int j, size, align;
+                       size = mono_type_size (type, &align);
+                       printf ("[");
+                       for (j = 0; p && j < size; j++)
+                               printf ("%02x,", p [j]);
+                       printf ("]");
+               }
+               break;
+       default:
+               printf ("(unknown return type %x)", method->signature->ret->type);
+       }
+
+       printf ("\n");
+}
+
+static gboolean
+is_regsize_var (MonoType *t) {
+       if (t->byref)
+               return TRUE;
+       switch (t->type) {
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+               return TRUE;
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:
+               return FALSE;
+       case MONO_TYPE_VALUETYPE:
+               if (t->data.klass->enumtype)
+                       return is_regsize_var (t->data.klass->enum_basetype);
+               return FALSE;
+       }
+       return FALSE;
+}
+
+GList *
+mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
+{
+       GList *vars = NULL;
+       int i;
+
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoInst *ins = cfg->varinfo [i];
+               MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+               /* unused vars */
+               if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos)
+                       continue;
+
+               if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
+                       continue;
+
+               /* we can only allocate 32 bit values */
+               if (is_regsize_var (ins->inst_vtype)) {
+                       g_assert (MONO_VARINFO (cfg, i)->reg == -1);
+                       g_assert (i == vmv->idx);
+                       vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
+               }
+       }
+
+       return vars;
+}
+
+GList *
+mono_arch_get_global_int_regs (void)
+{
+       GList *regs = NULL;
+       int i;
+       
+       for (i = 13; i < 32; ++i)
+               regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
+
+       return regs;
+}
+
+// code from ppc/tramp.c, try to keep in sync
+#define MIN_CACHE_LINE 8
+
+void
+mono_arch_flush_icache (guint8 *code, gint size)
+{
+       guint i;
+       guint8 *p;
+
+       p = code;
+       for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
+               asm ("dcbst 0,%0;" : : "r"(p) : "memory");
+       }
+       asm ("sync");
+       p = code;
+       for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
+               asm ("icbi 0,%0; sync;" : : "r"(p) : "memory");
+       }
+       asm ("sync");
+       asm ("isync");
+}
+
+#define NOT_IMPLEMENTED(x) \
+                g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
+
+#define PROLOG_INS 8
+#define CALL_INS   2
+#define EPILOG_INS 6
+#define FLOAT_REGS 8
+#define GENERAL_REGS 8
+#ifdef __APPLE__
+#define MINIMAL_STACK_SIZE 10
+#define ALWAYS_ON_STACK(s) s
+#define FP_ALSO_IN_REG(s) s
+#define RET_ADDR_OFFSET 8
+#define STACK_PARAM_OFFSET 24
+#else
+#define MINIMAL_STACK_SIZE 5
+#define ALWAYS_ON_STACK(s)
+#define FP_ALSO_IN_REG(s) s
+#define ALIGN_DOUBLES
+#define RET_ADDR_OFFSET 4
+#define STACK_PARAM_OFFSET 8
+#endif
+
+typedef struct {
+       gint16 offset;
+       gint8  reg;
+       gint8  regtype; /* 0 general, 1 basereg, 2 floating point register */
+} ArgInfo;
+
+typedef struct {
+       int nargs;
+       guint32 stack_usage;
+       ArgInfo ret;
+       ArgInfo args [1];
+} CallInfo;
+
+#define DEBUG(a)
+
+static void inline
+add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
+{
+       if (simple) {
+               if (*gr >= 3 + GENERAL_REGS) {
+                       ainfo->offset = *stack_size;
+                       ainfo->reg = ppc_sp; /* in the caller */
+                       ainfo->regtype = 1;
+                       *stack_size += 4;
+               } else {
+                       ALWAYS_ON_STACK (*stack_size += 4);
+                       ainfo->reg = *gr;
+               }
+       } else {
+               if (*gr >= 3 + GENERAL_REGS - 1) {
+                       ainfo->offset = *stack_size;
+                       ainfo->reg = ppc_sp; /* in the caller */
+                       ainfo->regtype = 1;
+                       *stack_size += 8;
+#ifdef ALIGN_DOUBLES
+                       *stack_size += (*stack_size % 8);
+#endif
+               } else {
+                       ALWAYS_ON_STACK (*stack_size += 8);
+                       ainfo->reg = *gr;
+               }
+#ifdef ALIGN_DOUBLES
+               if ((*gr) & 1)
+                       (*gr) ++;
+#endif
+               (*gr) ++;
+       }
+       (*gr) ++;
+}
+
+static CallInfo*
+calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
+{
+       guint i, fr, gr;
+       int n = sig->hasthis + sig->param_count;
+       guint32 simpletype;
+       guint32 stack_size = 0;
+       CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
+
+       fr = 1;
+       gr = 3;
+
+       /* FIXME: handle returning a struct */
+
+       n = 0;
+       if (sig->hasthis) {
+               add_general (&gr, &stack_size, cinfo->args + n, TRUE);
+               n++;
+       }
+        DEBUG(printf("params: %d\n", sig->param_count));
+       for (i = 0; i < sig->param_count; ++i) {
+                DEBUG(printf("param %d: ", i));
+               if (sig->params [i]->byref) {
+                        DEBUG(printf("byref\n"));
+                       add_general (&gr, &stack_size, cinfo->args + n, TRUE);
+                       n++;
+                       continue;
+               }
+               simpletype = sig->params [i]->type;
+       enum_calc_size:
+               switch (simpletype) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+               case MONO_TYPE_PTR:
+               case MONO_TYPE_CLASS:
+               case MONO_TYPE_OBJECT:
+               case MONO_TYPE_STRING:
+               case MONO_TYPE_SZARRAY:
+               case MONO_TYPE_ARRAY:
+                       add_general (&gr, &stack_size, cinfo->args + n, TRUE);
+                       n++;
+                       break;
+               case MONO_TYPE_VALUETYPE: {
+                       gint size;
+                       if (sig->params [i]->data.klass->enumtype) {
+                               simpletype = sig->params [i]->data.klass->enum_basetype->type;
+                               goto enum_calc_size;
+                       }
+#if 0
+                       size = mono_class_value_size (sig->params [i]->data.klass, NULL);
+                       if (size != 4) {
+                               DEBUG(printf ("copy %d bytes struct on stack\n",
+                                             mono_class_value_size (sig->params [i]->data.klass, NULL)));
+                               *stack_size += (size + 3) & (~3);
+                               if (gr > 3 + GENERAL_REGS) {
+                                       *stack_size += 4;
+                               }
+                       } else {
+                               DEBUG(printf ("load %d bytes struct\n",
+                                             mono_class_value_size (sig->params [i]->data.klass, NULL)));
+                               add_general (&gr, stack_size, code_size, TRUE);
+                       }
+#endif
+                       g_assert_not_reached ();
+                       break;
+               }
+               case MONO_TYPE_U8:
+               case MONO_TYPE_I8:
+                       add_general (&gr, &stack_size, cinfo->args + n, FALSE);
+                       n++;
+                       break;
+               case MONO_TYPE_R4:
+                       if (fr < 7) {
+                               fr ++;
+                               FP_ALSO_IN_REG (gr ++);
+                               ALWAYS_ON_STACK (stack_size += 4);
+                       } else {
+                               NOT_IMPLEMENTED ("R4 arg");
+                       }
+                       n++;
+                       break;
+               case MONO_TYPE_R8:
+                       if (fr < 7) {
+                               fr ++;
+                               FP_ALSO_IN_REG (gr += 2);
+                               ALWAYS_ON_STACK (stack_size += 8);
+                       } else {
+                               NOT_IMPLEMENTED ("R8 arg");
+                       }
+                       n++;
+                       break;
+               default:
+                       g_error ("Can't trampoline 0x%x", sig->params [i]->type);
+               }
+       }
+
+       {
+               simpletype = sig->ret->type;
+enum_retvalue:
+               switch (simpletype) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+               case MONO_TYPE_CLASS:
+               case MONO_TYPE_OBJECT:
+               case MONO_TYPE_SZARRAY:
+               case MONO_TYPE_ARRAY:
+               case MONO_TYPE_STRING:
+                       cinfo->ret.reg = ppc_r3;
+                       break;
+               case MONO_TYPE_U8:
+               case MONO_TYPE_I8:
+                       cinfo->ret.reg = ppc_r3;
+                       break;
+               case MONO_TYPE_R4:
+               case MONO_TYPE_R8:
+                       cinfo->ret.reg = ppc_f1;
+                       cinfo->ret.regtype = 2;
+                       break;
+               case MONO_TYPE_VALUETYPE:
+                       if (sig->ret->data.klass->enumtype) {
+                               simpletype = sig->ret->data.klass->enum_basetype->type;
+                               goto enum_retvalue;
+                       }
+                       break;
+               case MONO_TYPE_VOID:
+                       break;
+               default:
+                       g_error ("Can't handle as return value 0x%x", sig->ret->type);
+               }
+       }
+
+       /* align stack size to 16 */
+       DEBUG (printf ("      stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
+       stack_size = (stack_size + 15) & ~15;
+
+       cinfo->stack_usage = stack_size;
+       return cinfo;
+}
+
+
+/*
+ * Set var information according to the calling convention. ppc version.
+ * The locals var stuff should most likely be split in another method.
+ */
+void
+mono_arch_allocate_vars (MonoCompile *m)
+{
+       MonoMethodSignature *sig;
+       MonoMethodHeader *header;
+       MonoInst *inst;
+       int i, offset, size, align, curinst;
+
+       header = ((MonoMethodNormal *)m->method)->header;
+
+       sig = m->method->signature;
+       
+       offset = 0;
+       curinst = 0;
+       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+               m->ret->opcode = OP_REGVAR;
+               m->ret->inst_c0 = ppc_r3;
+       } else {
+               /* FIXME: handle long and FP values */
+               switch (sig->ret->type) {
+               case MONO_TYPE_VOID:
+                       break;
+               default:
+                       m->ret->opcode = OP_REGVAR;
+                       m->ret->inst_c0 = ppc_r3;
+                       break;
+               }
+       }
+       /* local vars are at a positive offset from the stack pointer */
+       /* 
+        * also note that if the function uses alloca, we use ppc_r31
+        * to point at the local variables.
+        */
+       offset = 12; /* linkage area */
+       /* align the offset to 16 bytes: not sure this is needed here  */
+       offset += 16 - 1;
+       offset &= ~(16 - 1);
+
+       /* add parameter area size for called functions */
+       offset += m->param_area;
+       offset += 16 - 1;
+       offset &= ~(16 - 1);
+
+       /* FIXME: check how to handle this stuff... reserve space to save LMF and caller saved registers */
+       offset += sizeof (MonoLMF);
+
+#if 0
+       /* this stuff should not be needed on ppc and the new jit,
+        * because a call on ppc to the handlers doesn't change the 
+        * stack pointer and the jist doesn't manipulate the stack pointer
+        * for operations involving valuetypes.
+        */
+       /* reserve space to store the esp */
+       offset += sizeof (gpointer);
+
+       /* this is a global constant */
+       mono_exc_esp_offset = offset;
+#endif
+
+       curinst = m->locals_start;
+       for (i = curinst; i < m->num_varinfo; ++i) {
+               inst = m->varinfo [i];
+               if (inst->opcode == OP_REGVAR)
+                       continue;
+
+               /* inst->unused indicates native sized value types, this is used by the
+               * pinvoke wrappers when they call functions returning structure */
+               if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype))
+                       size = mono_class_native_size (inst->inst_vtype->data.klass, &align);
+               else
+                       size = mono_type_size (inst->inst_vtype, &align);
+
+               offset += align - 1;
+               offset &= ~(align - 1);
+               inst->inst_offset = offset;
+               inst->opcode = OP_REGOFFSET;
+               inst->inst_basereg = ppc_sp;
+               offset += size;
+               //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
+       }
+
+       curinst = 0;
+       if (sig->hasthis) {
+               inst = m->varinfo [curinst];
+               if (inst->opcode != OP_REGVAR) {
+                       inst->opcode = OP_REGOFFSET;
+                       inst->inst_basereg = ppc_sp;
+                       offset += sizeof (gpointer) - 1;
+                       offset &= ~(sizeof (gpointer) - 1);
+                       inst->inst_offset = offset;
+                       offset += sizeof (gpointer);
+               }
+               curinst++;
+       }
+
+       for (i = 0; i < sig->param_count; ++i) {
+               inst = m->varinfo [curinst];
+               if (inst->opcode != OP_REGVAR) {
+                       inst->opcode = OP_REGOFFSET;
+                       inst->inst_basereg = ppc_sp;
+                       size = mono_type_size (sig->params [i], &align);
+                       offset += align - 1;
+                       offset &= ~(align - 1);
+                       inst->inst_offset = offset;
+                       offset += size;
+               }
+               curinst++;
+       }
+
+       /* align the offset to 16 bytes */
+       offset += 16 - 1;
+       offset &= ~(16 - 1);
+
+       /* change sign? */
+       m->stack_offset = offset;
+
+}
+
+/* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
+ * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info 
+ */
+
+/* 
+ * take the arguments and generate the arch-specific
+ * instructions to properly call the function in call.
+ * This includes pushing, moving arguments to the right register
+ * etc.
+ * Issue: who does the spilling if needed, and when?
+ */
+MonoCallInst*
+mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
+       MonoInst *arg, *in, **new_args;
+       MonoMethodSignature *sig;
+       int i, n, type;
+       MonoType *ptype;
+       CallInfo *cinfo;
+       ArgInfo *ainfo;
+
+       sig = call->signature;
+       n = sig->param_count + sig->hasthis;
+       new_args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
+       
+       cinfo = calculate_sizes (sig, sig->pinvoke);
+
+       for (i = 0; i < n; ++i) {
+               ainfo = cinfo->args + i;
+               if (is_virtual && i == 0) {
+                       /* the argument will be attached to the call instrucion */
+                       new_args [n] = arg = NULL;
+                       in = call->args [i];
+               } else {
+                       MONO_INST_NEW (cfg, arg, OP_OUTARG);
+                       in = call->args [i];
+                       arg->cil_code = in->cil_code;
+                       arg->inst_left = in;
+                       arg->type = in->type;
+                       new_args [i] = arg;
+                       if (ainfo->regtype == 0) {
+                               arg->unused = ainfo->reg;
+                               call->used_iregs |= 1 << ainfo->reg;
+                       } else if (ainfo->regtype == 1) {
+                               g_assert_not_reached ();
+                       } else if (ainfo->regtype == 2) {
+                               arg->opcode = OP_OUTARG_R8;
+                               arg->unused = ainfo->reg;
+                               call->used_fregs |= 1 << ainfo->reg;
+                       } else {
+                               g_assert_not_reached ();
+                       }
+               }
+       }
+       call->args = new_args;
+       call->stack_usage = cinfo->stack_usage;
+       cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage);
+       cfg->flags |= MONO_CFG_HAS_CALLS;
+       /* 
+        * should set more info in call, such as the stack space
+        * used by the args that needs to be added back to esp
+        */
+
+       g_free (cinfo);
+       return call;
+}
+
+/*
+ * Allow tracing to work with this interface (with an optional argument)
+ */
+
+/*
+ * This may be needed on some archs or for debugging support.
+ */
+void
+mono_arch_instrument_mem_needs (MonoMethod *method, int *stack, int *code)
+{
+       /* no stack room needed now (may be needed for FASTCALL-trace support) */
+       *stack = 0;
+       /* split prolog-epilog requirements? */
+       *code = 50; /* max bytes needed: check this number */
+}
+
+void*
+mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
+{
+       guchar *code = p;
+#if 0
+       /* if some args are passed in registers, we need to save them here */
+       x86_push_reg (code, X86_EBP);
+       x86_push_imm (code, cfg->method);
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func);
+       x86_call_code (code, 0);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
+#endif
+       return code;
+}
+
+enum {
+       SAVE_NONE,
+       SAVE_STRUCT,
+       SAVE_ONE,
+       SAVE_TWO,
+       SAVE_FP
+};
+
+void*
+mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
+{
+       guchar *code = p;
+       int arg_size = 0, save_mode = SAVE_NONE;
+       MonoMethod *method = cfg->method;
+       int rtype = method->signature->ret->type;
+       
+handle_enum:
+       switch (rtype) {
+       case MONO_TYPE_VOID:
+               /* special case string .ctor icall */
+               if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
+                       save_mode = SAVE_ONE;
+               else
+                       save_mode = SAVE_NONE;
+               break;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               save_mode = SAVE_TWO;
+               break;
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+               save_mode = SAVE_FP;
+               break;
+       case MONO_TYPE_VALUETYPE:
+               if (method->signature->ret->data.klass->enumtype) {
+                       rtype = method->signature->ret->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               }
+               save_mode = SAVE_STRUCT;
+               break;
+       default:
+               save_mode = SAVE_ONE;
+               break;
+       }
+
+       switch (save_mode) {
+       case SAVE_TWO:
+               //x86_push_reg (code, X86_EDX);
+               //x86_push_reg (code, X86_EAX);
+               if (enable_arguments) {
+                       //x86_push_reg (code, X86_EDX);
+                       //x86_push_reg (code, X86_EAX);
+                       arg_size = 8;
+               }
+               break;
+       case SAVE_ONE:
+               //x86_push_reg (code, X86_EAX);
+               if (enable_arguments) {
+                       //x86_push_reg (code, X86_EAX);
+                       arg_size = 4;
+               }
+               break;
+       case SAVE_FP:
+               //x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+               ///x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE);
+               if (enable_arguments) {
+                       //x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+                       //x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE);
+                       arg_size = 8;
+               }
+               break;
+       case SAVE_STRUCT:
+               if (enable_arguments) {
+                       //x86_push_membase (code, X86_EBP, 8);
+                       arg_size = 4;
+               }
+               break;
+       case SAVE_NONE:
+       default:
+               break;
+       }
+
+       /*x86_push_imm (code, method);
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func);
+       x86_call_code (code, 0);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, arg_size + 4);
+       */
+
+       switch (save_mode) {
+       case SAVE_TWO:
+               //x86_pop_reg (code, X86_EAX);
+               //x86_pop_reg (code, X86_EDX);
+               break;
+       case SAVE_ONE:
+               //x86_pop_reg (code, X86_EAX);
+               break;
+       case SAVE_FP:
+               //x86_fld_membase (code, X86_ESP, 0, TRUE);
+               //x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
+               break;
+       case SAVE_NONE:
+       default:
+               break;
+       }
+
+       return code;
+}
+
+#define EMIT_COND_BRANCH(ins,cond) \
+if (ins->flags & MONO_INST_BRLABEL) { \
+        if (ins->inst_i0->inst_c0) { \
+               ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffff);      \
+        } else { \
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
+               ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], 0);       \
+        } \
+} else { \
+        if (0 && ins->inst_true_bb->native_offset) { \
+               ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffff); \
+        } else { \
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
+               ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], 0);       \
+        } \
+}
+
+/* emit an exception if condition is fail */
+#define EMIT_COND_SYSTEM_EXCEPTION(cond,signed,exc_name)            \
+        do {                                                        \
+               mono_add_patch_info (cfg, code - cfg->native_code,   \
+                                   MONO_PATCH_INFO_EXC, exc_name);  \
+               x86_branch32 (code, cond, 0, signed);               \
+       } while (0); 
+
+#define EMIT_FPCOMPARE(code) do { \
+       x86_fcompp (code); \
+       x86_fnstsw (code); \
+       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); \
+} while (0); 
+
+static void
+peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins, *last_ins = NULL;
+       ins = bb->code;
+
+       while (ins) {
+
+               switch (ins->opcode) {
+               case OP_MUL_IMM: 
+                       /* remove unnecessary multiplication with 1 */
+                       if (ins->inst_imm == 1) {
+                               if (ins->dreg != ins->sreg1) {
+                                       ins->opcode = OP_MOVE;
+                               } else {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               }
+                       }
+                       break;
+               case OP_LOAD_MEMBASE:
+               case OP_LOADI4_MEMBASE:
+                       /* 
+                        * OP_STORE_MEMBASE_REG reg, offset(basereg) 
+                        * OP_LOAD_MEMBASE offset(basereg), reg
+                        */
+                       if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG 
+                                        || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
+                           ins->inst_basereg == last_ins->inst_destbasereg &&
+                           ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+
+                       /* 
+                        * Note: reg1 must be different from the basereg in the second load
+                        * OP_LOAD_MEMBASE offset(basereg), reg1
+                        * OP_LOAD_MEMBASE offset(basereg), reg2
+                        * -->
+                        * OP_LOAD_MEMBASE offset(basereg), reg1
+                        * OP_MOVE reg1, reg2
+                        */
+                       } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
+                                          || last_ins->opcode == OP_LOAD_MEMBASE) &&
+                             ins->inst_basereg != last_ins->dreg &&
+                             ins->inst_basereg == last_ins->inst_basereg &&
+                             ins->inst_offset == last_ins->inst_offset) {
+
+                               if (ins->dreg == last_ins->dreg) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->dreg;
+                               }
+
+                               //g_assert_not_reached ();
+
+#if 0
+                       /* 
+                        * OP_STORE_MEMBASE_IMM imm, offset(basereg) 
+                        * OP_LOAD_MEMBASE offset(basereg), reg
+                        * -->
+                        * OP_STORE_MEMBASE_IMM imm, offset(basereg) 
+                        * OP_ICONST reg, imm
+                        */
+                       } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
+                                               || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
+                                  ins->inst_basereg == last_ins->inst_destbasereg &&
+                                  ins->inst_offset == last_ins->inst_offset) {
+                               //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                               ins->opcode = OP_ICONST;
+                               ins->inst_c0 = last_ins->inst_imm;
+                               g_assert_not_reached (); // check this rule
+#endif
+                       }
+                       break;
+               case OP_LOADU1_MEMBASE:
+               case OP_LOADI1_MEMBASE:
+                       if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
+                                       ins->inst_basereg == last_ins->inst_destbasereg &&
+                                       ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+                       }
+                       break;
+               case OP_LOADU2_MEMBASE:
+               case OP_LOADI2_MEMBASE:
+                       if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
+                                       ins->inst_basereg == last_ins->inst_destbasereg &&
+                                       ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+                       }
+                       break;
+               case CEE_CONV_I4:
+               case CEE_CONV_U4:
+               case OP_MOVE:
+                       /* 
+                        * OP_MOVE reg, reg 
+                        */
+                       if (ins->dreg == ins->sreg1) {
+                               if (last_ins)
+                                       last_ins->next = ins->next;                             
+                               ins = ins->next;
+                               continue;
+                       }
+                       /* 
+                        * OP_MOVE sreg, dreg 
+                        * OP_MOVE dreg, sreg
+                        */
+                       if (last_ins && last_ins->opcode == OP_MOVE &&
+                           ins->sreg1 == last_ins->dreg &&
+                           ins->dreg == last_ins->sreg1) {
+                               last_ins->next = ins->next;                             
+                               ins = ins->next;                                
+                               continue;
+                       }
+                       break;
+               }
+               last_ins = ins;
+               ins = ins->next;
+       }
+       bb->last_ins = last_ins;
+}
+
+/* 
+ * the branch_b0_table should maintain the order of these
+ * opcodes.
+case CEE_BEQ:
+case CEE_BGE:
+case CEE_BGT:
+case CEE_BLE:
+case CEE_BLT:
+case CEE_BNE_UN:
+case CEE_BGE_UN:
+case CEE_BGT_UN:
+case CEE_BLE_UN:
+case CEE_BLT_UN:
+ */
+static const guchar 
+branch_b0_table [] = {
+       PPC_BR_TRUE, 
+       PPC_BR_FALSE, 
+       PPC_BR_TRUE, 
+       PPC_BR_FALSE, 
+       PPC_BR_TRUE, 
+       
+       PPC_BR_FALSE, 
+       PPC_BR_FALSE, 
+       PPC_BR_TRUE, 
+       PPC_BR_FALSE,
+       PPC_BR_TRUE
+};
+
+static const guchar 
+branch_b1_table [] = {
+       PPC_BR_EQ, 
+       PPC_BR_LT, 
+       PPC_BR_GT, 
+       PPC_BR_GT,
+       PPC_BR_LT, 
+       
+       PPC_BR_EQ, 
+       PPC_BR_LT, 
+       PPC_BR_GT, 
+       PPC_BR_GT,
+       PPC_BR_LT 
+};
+
+#undef DEBUG
+#define DEBUG(a) if (cfg->verbose_level > 1) a
+//#define DEBUG(a)
+#define reg_is_freeable(r) ((r) >= 3 && (r) <= 10)
+
+typedef struct {
+       int born_in;
+       int killed_in;
+       int last_use;
+       int prev_use;
+} RegTrack;
+
+static const char*const * ins_spec = ppcg4;
+
+static void
+print_ins (int i, MonoInst *ins)
+{
+       const char *spec = ins_spec [ins->opcode];
+       g_print ("\t%-2d %s", i, mono_inst_name (ins->opcode));
+       if (spec [MONO_INST_DEST]) {
+               if (ins->dreg >= MONO_MAX_IREGS)
+                       g_print (" R%d <-", ins->dreg);
+               else
+                       g_print (" %s <-", mono_arch_regname (ins->dreg));
+       }
+       if (spec [MONO_INST_SRC1]) {
+               if (ins->sreg1 >= MONO_MAX_IREGS)
+                       g_print (" R%d", ins->sreg1);
+               else
+                       g_print (" %s", mono_arch_regname (ins->sreg1));
+       }
+       if (spec [MONO_INST_SRC2]) {
+               if (ins->sreg2 >= MONO_MAX_IREGS)
+                       g_print (" R%d", ins->sreg2);
+               else
+                       g_print (" %s", mono_arch_regname (ins->sreg2));
+       }
+       if (spec [MONO_INST_CLOB])
+               g_print (" clobbers: %c", spec [MONO_INST_CLOB]);
+       g_print ("\n");
+}
+
+static void
+print_regtrack (RegTrack *t, int num)
+{
+       int i;
+       char buf [32];
+       const char *r;
+       
+       for (i = 0; i < num; ++i) {
+               if (!t [i].born_in)
+                       continue;
+               if (i >= MONO_MAX_IREGS) {
+                       g_snprintf (buf, sizeof(buf), "R%d", i);
+                       r = buf;
+               } else
+                       r = mono_arch_regname (i);
+               g_print ("liveness: %s [%d - %d]\n", r, t [i].born_in, t[i].last_use);
+       }
+}
+
+typedef struct InstList InstList;
+
+struct InstList {
+       InstList *prev;
+       InstList *next;
+       MonoInst *data;
+};
+
+static inline InstList*
+inst_list_prepend (MonoMemPool *pool, InstList *list, MonoInst *data)
+{
+       InstList *item = mono_mempool_alloc (pool, sizeof (InstList));
+       item->data = data;
+       item->prev = NULL;
+       item->next = list;
+       if (list)
+               list->prev = item;
+       return item;
+}
+
+/*
+ * Force the spilling of the variable in the symbolic register 'reg'.
+ */
+static int
+get_register_force_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, int reg)
+{
+       MonoInst *load;
+       int i, sel, spill;
+       
+       sel = cfg->rs->iassign [reg];
+       /*i = cfg->rs->isymbolic [sel];
+       g_assert (i == reg);*/
+       i = reg;
+       spill = ++cfg->spill_count;
+       cfg->rs->iassign [i] = -spill - 1;
+       mono_regstate_free_int (cfg->rs, sel);
+       /* we need to create a spill var and insert a load to sel after the current instruction */
+       MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE);
+       load->dreg = sel;
+       load->inst_basereg = ppc_sp;
+       load->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (item->prev) {
+               while (ins->next != item->prev->data)
+                       ins = ins->next;
+       }
+       load->next = ins->next;
+       ins->next = load;
+       DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%sp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel)));
+       i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+       g_assert (i == sel);
+
+       return sel;
+}
+
+static int
+get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32 regmask, int reg)
+{
+       MonoInst *load;
+       int i, sel, spill;
+
+       DEBUG (g_print ("start regmask to assign R%d: 0x%08x (R%d <- R%d R%d)\n", reg, regmask, ins->dreg, ins->sreg1, ins->sreg2));
+       /* exclude the registers in the current instruction */
+       if (reg != ins->sreg1 && (reg_is_freeable (ins->sreg1) || (ins->sreg1 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg1] >= 0))) {
+               if (ins->sreg1 >= MONO_MAX_IREGS)
+                       regmask &= ~ (1 << cfg->rs->iassign [ins->sreg1]);
+               else
+                       regmask &= ~ (1 << ins->sreg1);
+               DEBUG (g_print ("excluding sreg1 %s\n", mono_arch_regname (ins->sreg1)));
+       }
+       if (reg != ins->sreg2 && (reg_is_freeable (ins->sreg2) || (ins->sreg2 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg2] >= 0))) {
+               if (ins->sreg2 >= MONO_MAX_IREGS)
+                       regmask &= ~ (1 << cfg->rs->iassign [ins->sreg2]);
+               else
+                       regmask &= ~ (1 << ins->sreg2);
+               DEBUG (g_print ("excluding sreg2 %s %d\n", mono_arch_regname (ins->sreg2), ins->sreg2));
+       }
+       if (reg != ins->dreg && reg_is_freeable (ins->dreg)) {
+               regmask &= ~ (1 << ins->dreg);
+               DEBUG (g_print ("excluding dreg %s\n", mono_arch_regname (ins->dreg)));
+       }
+
+       DEBUG (g_print ("available regmask: 0x%08x\n", regmask));
+       g_assert (regmask); /* need at least a register we can free */
+       sel = -1;
+       /* we should track prev_use and spill the register that's farther */
+       for (i = 0; i < MONO_MAX_IREGS; ++i) {
+               if (regmask & (1 << i)) {
+                       sel = i;
+                       DEBUG (g_print ("selected register %s has assignment %d\n", mono_arch_regname (sel), cfg->rs->iassign [sel]));
+                       break;
+               }
+       }
+       i = cfg->rs->isymbolic [sel];
+       spill = ++cfg->spill_count;
+       cfg->rs->iassign [i] = -spill - 1;
+       mono_regstate_free_int (cfg->rs, sel);
+       /* we need to create a spill var and insert a load to sel after the current instruction */
+       MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE);
+       load->dreg = sel;
+       load->inst_basereg = ppc_sp;
+       load->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (item->prev) {
+               while (ins->next != item->prev->data)
+                       ins = ins->next;
+       }
+       load->next = ins->next;
+       ins->next = load;
+       DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%sp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel)));
+       i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+       g_assert (i == sel);
+       
+       return sel;
+}
+
+static MonoInst*
+create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins)
+{
+       MonoInst *copy;
+       MONO_INST_NEW (cfg, copy, OP_MOVE);
+       copy->dreg = dest;
+       copy->sreg1 = src;
+       if (ins) {
+               copy->next = ins->next;
+               ins->next = copy;
+       }
+       DEBUG (g_print ("\tforced copy from %s to %s\n", mono_arch_regname (src), mono_arch_regname (dest)));
+       return copy;
+}
+
+static MonoInst*
+create_spilled_store (MonoCompile *cfg, int spill, int reg, int prev_reg, MonoInst *ins)
+{
+       MonoInst *store;
+       MONO_INST_NEW (cfg, store, OP_STORE_MEMBASE_REG);
+       store->sreg1 = reg;
+       store->inst_destbasereg = ppc_r1;
+       store->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (ins) {
+               store->next = ins->next;
+               ins->next = store;
+       }
+       DEBUG (g_print ("SPILLED STORE (%d at 0x%08x(%%sp)) R%d (from %s)\n", spill, store->inst_offset, prev_reg, mono_arch_regname (reg)));
+       return store;
+}
+
+static void
+insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert)
+{
+       MonoInst *prev;
+       g_assert (item->next);
+       prev = item->next->data;
+
+       while (prev->next != ins)
+               prev = prev->next;
+       to_insert->next = ins;
+       prev->next = to_insert;
+       /* 
+        * needed otherwise in the next instruction we can add an ins to the 
+        * end and that would get past this instruction.
+        */
+       item->data = to_insert; 
+}
+
+static int
+alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, guint32 allow_mask)
+{
+       int val = cfg->rs->iassign [sym_reg];
+       if (val < 0) {
+               int spill = 0;
+               if (val < -1) {
+                       /* the register gets spilled after this inst */
+                       spill = -val -1;
+               }
+               val = mono_regstate_alloc_int (cfg->rs, allow_mask);
+               if (val < 0)
+                       val = get_register_spilling (cfg, curinst, ins, allow_mask, sym_reg);
+               cfg->rs->iassign [sym_reg] = val;
+               /* add option to store before the instruction for src registers */
+               if (spill)
+                       create_spilled_store (cfg, spill, val, sym_reg, ins);
+       }
+       cfg->rs->isymbolic [val] = sym_reg;
+       return val;
+}
+
+/* use ppc_r3-ppc_310 as temp registers */
+#define PPC_CALLER_REGS (0xf<<3)
+
+/*
+ * Local register allocation.
+ * We first scan the list of instructions and we save the liveness info of
+ * each register (when the register is first used, when it's value is set etc.).
+ * We also reverse the list of instructions (in the InstList list) because assigning
+ * registers backwards allows for more tricks to be used.
+ */
+void
+mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins;
+       MonoRegState *rs = cfg->rs;
+       int i, val, fpcount;
+       RegTrack *reginfo, *reginfof;
+       RegTrack *reginfo1, *reginfo2, *reginfod;
+       InstList *tmp, *reversed = NULL;
+       const char *spec;
+       guint32 src1_mask, src2_mask, dest_mask;
+
+       if (!bb->code)
+               return;
+       rs->next_vireg = bb->max_ireg;
+       rs->next_vfreg = bb->max_freg;
+       mono_regstate_assign (rs);
+       reginfo = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vireg);
+       reginfof = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vfreg);
+       rs->ifree_mask = PPC_CALLER_REGS;
+
+       ins = bb->code;
+       i = 1;
+       fpcount = 0; /* FIXME: track fp stack utilization */
+       DEBUG (g_print ("LOCAL regalloc: basic block: %d\n", bb->block_num));
+       /* forward pass on the instructions to collect register liveness info */
+       while (ins) {
+               spec = ins_spec [ins->opcode];
+               DEBUG (print_ins (i, ins));
+               if (spec [MONO_INST_SRC1]) {
+                       if (spec [MONO_INST_SRC1] == 'f')
+                               reginfo1 = reginfof;
+                       else
+                               reginfo1 = reginfo;
+                       reginfo1 [ins->sreg1].prev_use = reginfo1 [ins->sreg1].last_use;
+                       reginfo1 [ins->sreg1].last_use = i;
+               } else {
+                       ins->sreg1 = -1;
+               }
+               if (spec [MONO_INST_SRC2]) {
+                       if (spec [MONO_INST_SRC2] == 'f')
+                               reginfo2 = reginfof;
+                       else
+                               reginfo2 = reginfo;
+                       reginfo2 [ins->sreg2].prev_use = reginfo2 [ins->sreg2].last_use;
+                       reginfo2 [ins->sreg2].last_use = i;
+               } else {
+                       ins->sreg2 = -1;
+               }
+               if (spec [MONO_INST_DEST]) {
+                       if (spec [MONO_INST_DEST] == 'f')
+                               reginfod = reginfof;
+                       else
+                               reginfod = reginfo;
+                       if (spec [MONO_INST_DEST] != 'b') /* it's not just a base register */
+                               reginfod [ins->dreg].killed_in = i;
+                       reginfod [ins->dreg].prev_use = reginfod [ins->dreg].last_use;
+                       reginfod [ins->dreg].last_use = i;
+                       if (reginfod [ins->dreg].born_in == 0 || reginfod [ins->dreg].born_in > i)
+                               reginfod [ins->dreg].born_in = i;
+                       if (spec [MONO_INST_DEST] == 'l') {
+                               /* result in eax:edx, the virtual register is allocated sequentially */
+                               reginfod [ins->dreg + 1].prev_use = reginfod [ins->dreg + 1].last_use;
+                               reginfod [ins->dreg + 1].last_use = i;
+                               if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i)
+                                       reginfod [ins->dreg + 1].born_in = i;
+                       }
+               } else {
+                       ins->dreg = -1;
+               }
+               reversed = inst_list_prepend (cfg->mempool, reversed, ins);
+               ++i;
+               ins = ins->next;
+       }
+
+       DEBUG (print_regtrack (reginfo, rs->next_vireg));
+       DEBUG (print_regtrack (reginfof, rs->next_vfreg));
+       tmp = reversed;
+       while (tmp) {
+               int prev_dreg, prev_sreg1, prev_sreg2;
+               dest_mask = src1_mask = src2_mask = PPC_CALLER_REGS;
+               --i;
+               ins = tmp->data;
+               spec = ins_spec [ins->opcode];
+               DEBUG (g_print ("processing:"));
+               DEBUG (print_ins (i, ins));
+               /* update for use with FP regs... */
+               if (spec [MONO_INST_DEST] != 'f' && ins->dreg >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->dreg];
+                       prev_dreg = ins->dreg;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               val = mono_regstate_alloc_int (rs, dest_mask);
+                               if (val < 0)
+                                       val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg);
+                               rs->iassign [ins->dreg] = val;
+                               if (spill)
+                                       create_spilled_store (cfg, spill, val, prev_dreg, ins);
+                       }
+                       DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg));
+                       rs->isymbolic [val] = prev_dreg;
+                       ins->dreg = val;
+                       if (spec [MONO_INST_DEST] == 'l') {
+                               int hreg = prev_dreg + 1;
+                               val = rs->iassign [hreg];
+                               if (val < 0) {
+                                       int spill = 0;
+                                       if (val < -1) {
+                                               /* the register gets spilled after this inst */
+                                               spill = -val -1;
+                                       }
+                                       val = mono_regstate_alloc_int (rs, dest_mask);
+                                       if (val < 0)
+                                               val = get_register_spilling (cfg, tmp, ins, dest_mask, hreg);
+                                       rs->iassign [hreg] = val;
+                                       if (spill)
+                                               create_spilled_store (cfg, spill, val, hreg, ins);
+                               }
+                               DEBUG (g_print ("\tassigned hreg %s to dest R%d\n", mono_arch_regname (val), hreg));
+                               rs->isymbolic [val] = hreg;
+                               /* FIXME:? ins->dreg = val; */
+                               if (ins->dreg == ppc_r3) {
+                                       if (val != ppc_r4)
+                                               create_copy_ins (cfg, val, ppc_r4, ins);
+                               } else if (ins->dreg == ppc_r4) {
+                                       if (val == ppc_r3) {
+                                               /* swap */
+                                               g_assert_not_reached ();
+                                       } else {
+                                               /* two forced copies */
+                                               create_copy_ins (cfg, val, ppc_r4, ins);
+                                               create_copy_ins (cfg, ins->dreg, ppc_r3, ins);
+                                       }
+                               } else {
+                                       if (val == ppc_r4) {
+                                               create_copy_ins (cfg, ins->dreg, ppc_r3, ins);
+                                       } else {
+                                               /* two forced copies */
+                                               create_copy_ins (cfg, val, ppc_r4, ins);
+                                               create_copy_ins (cfg, ins->dreg, ppc_r3, ins);
+                                       }
+                               }
+                               if (reg_is_freeable (val) && hreg >= 0 && reginfo [hreg].born_in >= i) {
+                                       DEBUG (g_print ("\tfreeable %s (R%d)\n", mono_arch_regname (val), hreg));
+                                       mono_regstate_free_int (rs, val);
+                               }
+                       }
+               } else {
+                       prev_dreg = -1;
+               }
+               if (spec [MONO_INST_DEST] != 'f' && reg_is_freeable (ins->dreg) && prev_dreg >= 0 && reginfo [prev_dreg].born_in >= i) {
+                       DEBUG (g_print ("\tfreeable %s (R%d) (born in %d)\n", mono_arch_regname (ins->dreg), prev_dreg, reginfo [prev_dreg].born_in));
+                       mono_regstate_free_int (rs, ins->dreg);
+               }
+               if (spec [MONO_INST_SRC1] != 'f' && ins->sreg1 >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->sreg1];
+                       prev_sreg1 = ins->sreg1;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               if (0 && ins->opcode == OP_MOVE) {
+                                       /* 
+                                        * small optimization: the dest register is already allocated
+                                        * but the src one is not: we can simply assign the same register
+                                        * here and peephole will get rid of the instruction later.
+                                        * This optimization may interfere with the clobbering handling:
+                                        * it removes a mov operation that will be added again to handle clobbering.
+                                        * There are also some other issues that should with make testjit.
+                                        */
+                                       mono_regstate_alloc_int (rs, 1 << ins->dreg);
+                                       val = rs->iassign [ins->sreg1] = ins->dreg;
+                                       //g_assert (val >= 0);
+                                       DEBUG (g_print ("\tfast assigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1));
+                               } else {
+                                       //g_assert (val == -1); /* source cannot be spilled */
+                                       val = mono_regstate_alloc_int (rs, src1_mask);
+                                       if (val < 0)
+                                               val = get_register_spilling (cfg, tmp, ins, src1_mask, ins->sreg1);
+                                       rs->iassign [ins->sreg1] = val;
+                                       DEBUG (g_print ("\tassigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1));
+                               }
+                               if (spill) {
+                                       MonoInst *store = create_spilled_store (cfg, spill, val, prev_sreg1, NULL);
+                                       insert_before_ins (ins, tmp, store);
+                               }
+                       }
+                       rs->isymbolic [val] = prev_sreg1;
+                       ins->sreg1 = val;
+               } else {
+                       prev_sreg1 = -1;
+               }
+               if (spec [MONO_INST_SRC2] != 'f' && ins->sreg2 >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->sreg2];
+                       prev_sreg2 = ins->sreg2;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               val = mono_regstate_alloc_int (rs, src2_mask);
+                               if (val < 0)
+                                       val = get_register_spilling (cfg, tmp, ins, src2_mask, ins->sreg2);
+                               rs->iassign [ins->sreg2] = val;
+                               DEBUG (g_print ("\tassigned sreg2 %s to R%d\n", mono_arch_regname (val), ins->sreg2));
+                               if (spill)
+                                       create_spilled_store (cfg, spill, val, prev_sreg2, ins);
+                       }
+                       rs->isymbolic [val] = prev_sreg2;
+                       ins->sreg2 = val;
+               } else {
+                       prev_sreg2 = -1;
+               }
+
+               if (spec [MONO_INST_CLOB] == 'c') {
+                       int j, s;
+                       guint32 clob_mask = PPC_CALLER_REGS;
+                       for (j = 0; j < MONO_MAX_IREGS; ++j) {
+                               s = 1 << j;
+                               if ((clob_mask & s) && !(rs->ifree_mask & s) && j != ins->sreg1) {
+                                       //g_warning ("register %s busy at call site\n", mono_arch_regname (j));
+                               }
+                       }
+               }
+               /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) {
+                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg1)));
+                       mono_regstate_free_int (rs, ins->sreg1);
+               }
+               if (reg_is_freeable (ins->sreg2) && prev_sreg2 >= 0 && reginfo [prev_sreg2].born_in >= i) {
+                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2)));
+                       mono_regstate_free_int (rs, ins->sreg2);
+               }*/
+               
+               //DEBUG (print_ins (i, ins));
+               tmp = tmp->next;
+       }
+}
+
+static guchar*
+emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int size, gboolean is_signed)
+{
+       return code;
+}
+
+static unsigned char*
+mono_emit_stack_alloc (guchar *code, MonoInst* tree)
+{
+#if 0
+       int sreg = tree->sreg1;
+       x86_alu_reg_reg (code, X86_SUB, X86_ESP, tree->sreg1);
+       if (tree->flags & MONO_INST_INIT) {
+               int offset = 0;
+               if (tree->dreg != X86_EAX && sreg != X86_EAX) {
+                       x86_push_reg (code, X86_EAX);
+                       offset += 4;
+               }
+               if (tree->dreg != X86_ECX && sreg != X86_ECX) {
+                       x86_push_reg (code, X86_ECX);
+                       offset += 4;
+               }
+               if (tree->dreg != X86_EDI && sreg != X86_EDI) {
+                       x86_push_reg (code, X86_EDI);
+                       offset += 4;
+               }
+               
+               x86_shift_reg_imm (code, X86_SHR, sreg, 2);
+               if (sreg != X86_ECX)
+                       x86_mov_reg_reg (code, X86_ECX, sreg, 4);
+               x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
+                               
+               x86_lea_membase (code, X86_EDI, X86_ESP, offset);
+               x86_cld (code);
+               x86_prefix (code, X86_REP_PREFIX);
+               x86_stosl (code);
+               
+               if (tree->dreg != X86_EDI && sreg != X86_EDI)
+                       x86_pop_reg (code, X86_EDI);
+               if (tree->dreg != X86_ECX && sreg != X86_ECX)
+                       x86_pop_reg (code, X86_ECX);
+               if (tree->dreg != X86_EAX && sreg != X86_EAX)
+                       x86_pop_reg (code, X86_EAX);
+       }
+#endif
+       return code;
+}
+
+void
+ppc_patch (guchar *code, guchar *target)
+{
+       guint32 ins = *(guint32*)code;
+       guint32 prim = ins >> 26;
+
+//     g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
+       if (prim == 18) {
+               // absolute address
+               if (ins & 2) {
+                       guint32 li = (guint32)target;
+                       ins = prim << 26 | (ins & 3);
+                       ins |= li;
+                       // FIXME: assert the top bits of li are 0
+               } else {
+                       gint diff = target - code;
+                       ins = prim << 26 | (ins & 3);
+                       diff &= ~3;
+                       diff &= ~(63 << 26);
+                       ins |= diff;
+               }
+               *(guint32*)code = ins;
+       } else if (prim == 16) {
+               // absolute address
+               if (ins & 2) {
+                       guint32 li = (guint32)target;
+                       ins = (ins & 0xffff0000) | (ins & 3);
+                       li &= 0xffff;
+                       ins |= li;
+                       // FIXME: assert the top bits of li are 0
+               } else {
+                       gint diff = target - code;
+                       ins = (ins & 0xffff0000) | (ins & 3);
+                       diff &= 0xffff;
+                       ins |= diff;
+               }
+               *(guint32*)code = ins;
+       } else {
+               g_assert_not_reached ();
+       }
+//     g_print ("patched with 0x%08x\n", ins);
+}
+
+void
+mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins;
+       MonoCallInst *call;
+       guint offset;
+       guint8 *code = cfg->native_code + cfg->code_len;
+       MonoInst *last_ins = NULL;
+       guint last_offset = 0;
+       int max_len, cpos;
+
+       if (cfg->opt & MONO_OPT_PEEPHOLE)
+               peephole_pass (cfg, bb);
+
+#if 0
+       /* 
+        * various stratgies to align BBs. Using real loop detection or simply
+        * aligning every block leads to more consistent benchmark results,
+        * but usually slows down the code
+        * we should do the alignment outside this function or we should adjust
+        * bb->native offset as well or the code is effectively slowed down!
+        */
+       /* align all blocks */
+//     if ((pad = (cfg->code_len & (align - 1)))) {
+       /* poor man loop start detection */
+//     if (bb->code && bb->in_count && bb->in_bb [0]->cil_code > bb->cil_code && (pad = (cfg->code_len & (align - 1)))) {
+       /* consider real loop detection and nesting level */
+//     if (bb->loop_blocks && bb->nesting < 3 && (pad = (cfg->code_len & (align - 1)))) {
+       /* consider real loop detection */
+       if (/*bb->loop_blocks &&*/ (pad = (cfg->code_len & (align - 1)))) {
+               pad = align - pad;
+               x86_padding (code, pad);
+               cfg->code_len += pad;
+               bb->native_offset = cfg->code_len;
+       }
+#endif
+
+       if (cfg->verbose_level > 2)
+               g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
+
+       cpos = bb->max_offset;
+
+       if (mono_trace_coverage) {
+               MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
+               g_assert (!mono_compile_aot);
+               cpos += 6;
+               if (bb->cil_code)
+                       cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
+               /* this is not thread save, but good enough */
+               /* fixme: howto handle overflows? */
+               //x86_inc_mem (code, &cov->data [bb->dfn].count); 
+       }
+
+       ins = bb->code;
+       while (ins) {
+               offset = code - cfg->native_code;
+
+               max_len = ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN];
+
+               if (offset > (cfg->code_size - max_len - 16)) {
+                       cfg->code_size *= 2;
+                       cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+                       code = cfg->native_code + offset;
+               }
+       //      if (ins->cil_code)
+       //              g_print ("cil code\n");
+
+               switch (ins->opcode) {
+               case OP_STOREI1_MEMBASE_IMM:
+                       ppc_li (code, ppc_r11, ins->inst_imm);
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_stb (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_STOREI2_MEMBASE_IMM:
+                       ppc_li (code, ppc_r11, ins->inst_imm);
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_sth (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_STORE_MEMBASE_IMM:
+               case OP_STOREI4_MEMBASE_IMM:
+                       ppc_load (code, ppc_r11, ins->inst_imm);
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_stw (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_STOREI1_MEMBASE_REG:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_stb (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_STOREI2_MEMBASE_REG:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_sth (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_STORE_MEMBASE_REG:
+               case OP_STOREI4_MEMBASE_REG:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_stw (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case CEE_LDIND_I:
+               case CEE_LDIND_I4:
+               case CEE_LDIND_U4:
+                       g_assert_not_reached ();
+                       //x86_mov_reg_mem (code, ins->dreg, ins->inst_p0, 4);
+                       break;
+               case OP_LOADU4_MEM:
+                       g_assert_not_reached ();
+                       //x86_mov_reg_imm (code, ins->dreg, ins->inst_p0);
+                       //x86_mov_reg_membase (code, ins->dreg, ins->dreg, 0, 4);
+                       break;
+               case OP_LOAD_MEMBASE:
+               case OP_LOADI4_MEMBASE:
+               case OP_LOADU4_MEMBASE:
+                       if (ppc_is_imm16 (ins->inst_offset)) {
+                               ppc_lwz (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_offset);
+                               ppc_lwz (code, ins->dreg, 0, ppc_r11);
+                       }
+                       break;
+               case OP_LOADU1_MEMBASE:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_lbz (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       break;
+               case OP_LOADI1_MEMBASE:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       // FIXME: sign extend
+                       ppc_lbz (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       break;
+               case OP_LOADU2_MEMBASE:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_lhz (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       break;
+               case OP_LOADI2_MEMBASE:
+                       g_assert (ppc_is_imm16 (ins->inst_offset));
+                       ppc_lha (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
+                       break;
+               case CEE_CONV_I1:
+                       ppc_extsb (code, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_CONV_I2:
+                       ppc_extsh (code, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_CONV_U1:
+                       ppc_rlwinm (code, ins->dreg, ins->sreg1, 0, 24, 31);
+                       break;
+               case CEE_CONV_U2:
+                       ppc_rlwinm (code, ins->dreg, ins->sreg1, 0, 16, 31);
+                       break;
+               case OP_COMPARE:
+                       ppc_cmp (code, 0, 0, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_COMPARE_IMM:
+                       if (ppc_is_imm16 (ins->inst_imm)) {
+                               ppc_cmpi (code, 0, 0, ins->sreg1, (ins->inst_imm & 0xffff));
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_imm);
+                               ppc_cmp (code, 0, 0, ins->sreg1, ppc_r11);
+                       }
+                       break;
+               case OP_X86_TEST_NULL:
+                       ppc_cmpi (code, 0, 0, ins->sreg1, 0);
+                       break;
+               case CEE_BREAK:
+                       ppc_break (code);
+                       break;
+               case OP_ADDCC:
+                       ppc_addc (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case CEE_ADD:
+                       ppc_add (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_ADC:
+                       ppc_adde (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_ADD_IMM:
+                       if (ppc_is_imm16 (ins->inst_imm)) {
+                               ppc_addi (code, ins->dreg, ins->sreg1, ins->inst_imm);
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_imm);
+                               ppc_add (code, ins->dreg, ins->sreg1, ppc_r11);
+                       }
+                       break;
+               case OP_ADC_IMM:
+                       g_assert_not_reached ();
+                       //x86_alu_reg_imm (code, X86_ADC, ins->dreg, ins->inst_imm);
+                       break;
+               case OP_SUBCC:
+                       ppc_subfc (code, ins->dreg, ins->sreg2, ins->sreg1);
+                       break;
+               case CEE_SUB:
+                       ppc_subf (code, ins->dreg, ins->sreg2, ins->sreg1);
+                       break;
+               case OP_SBB:
+                       ppc_subfe (code, ins->dreg, ins->sreg2, ins->sreg1);
+                       break;
+               case OP_SUB_IMM:
+                       // we add the negated value
+                       g_assert (ppc_is_imm16 (-ins->inst_imm));
+                       ppc_addi (code, ins->dreg, ins->sreg1, -ins->inst_imm);
+                       break;
+               case OP_SBB_IMM:
+                       g_assert_not_reached ();
+                       //x86_alu_reg_imm (code, X86_SBB, ins->dreg, ins->inst_imm);
+                       break;
+               case CEE_AND:
+                       /* FIXME: the ppc macros as inconsistent here: put dest as the first arg! */
+                       ppc_and (code, ins->sreg1, ins->dreg, ins->sreg2);
+                       break;
+               case OP_AND_IMM:
+                       if (!(ins->inst_imm & 0xffff0000)) {
+                               ppc_andid (code, ins->sreg1, ins->dreg, ins->inst_imm);
+                       } else if (!(ins->inst_imm & 0xffff)) {
+                               ppc_andisd (code, ins->sreg1, ins->dreg, ((guint32)ins->inst_imm >> 16));
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_imm);
+                               ppc_and (code, ins->sreg1, ins->dreg, ins->sreg2);
+                       }
+                       break;
+               case CEE_DIV:
+                       ppc_divw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case CEE_DIV_UN:
+                       ppc_divwu (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_DIV_IMM:
+                       ppc_load (code, ppc_r11, ins->inst_imm);
+                       ppc_divw (code, ins->dreg, ins->sreg1, ppc_r11);
+                       break;
+               case CEE_REM:
+                       ppc_divw (code, ppc_r11, ins->sreg1, ins->sreg2);
+                       ppc_mullw (code, ppc_r11, ppc_r11, ins->sreg2);
+                       ppc_subf (code, ins->dreg, ppc_r11, ins->sreg1);
+                       break;
+               case CEE_REM_UN:
+                       ppc_divwu (code, ppc_r11, ins->sreg1, ins->sreg2);
+                       ppc_mullw (code, ppc_r11, ppc_r11, ins->sreg2);
+                       ppc_subf (code, ins->dreg, ppc_r11, ins->sreg1);
+                       break;
+               case OP_REM_IMM:
+                       ppc_load (code, ppc_r11, ins->inst_imm);
+                       ppc_divw (code, ins->dreg, ins->sreg1, ppc_r11);
+                       ppc_mullw (code, ins->dreg, ins->dreg, ppc_r11);
+                       ppc_subf (code, ins->dreg, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_OR:
+                       ppc_or (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_OR_IMM:
+                       if (!(ins->inst_imm & 0xffff0000)) {
+                               ppc_ori (code, ins->sreg1, ins->dreg, ins->inst_imm);
+                       } else if (!(ins->inst_imm & 0xffff)) {
+                               ppc_oris (code, ins->sreg1, ins->dreg, ((guint32)(ins->inst_imm) >> 16));
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_imm);
+                               ppc_or (code, ins->sreg1, ins->dreg, ins->sreg2);
+                       }
+                       break;
+               case CEE_XOR:
+                       ppc_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_XOR_IMM:
+                       if (!(ins->inst_imm & 0xffff0000)) {
+                               ppc_xori (code, ins->sreg1, ins->dreg, ins->inst_imm);
+                       } else if (!(ins->inst_imm & 0xffff)) {
+                               ppc_xoris (code, ins->sreg1, ins->dreg, ((guint32)(ins->inst_imm) >> 16));
+                       } else {
+                               ppc_load (code, ppc_r11, ins->inst_imm);
+                               ppc_xor (code, ins->sreg1, ins->dreg, ins->sreg2);
+                       }
+                       break;
+               case CEE_SHL:
+                       ppc_slw (code, ins->sreg1, ins->dreg, ins->sreg2);
+                       break;
+               case OP_SHL_IMM:
+                       ppc_rlwinm (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0xf), 0, (31 - (ins->inst_imm & 0xf)));
+                       //ppc_load (code, ppc_r11, ins->inst_imm);
+                       //ppc_slw (code, ins->sreg1, ins->dreg, ppc_r11);
+                       break;
+               case CEE_SHR:
+                       ppc_sraw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_SHR_IMM:
+                       // there is also ppc_srawi
+                       //ppc_load (code, ppc_r11, ins->inst_imm);
+                       //ppc_sraw (code, ins->dreg, ins->sreg1, ppc_r11);
+                       ppc_srawi (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
+                       break;
+               case OP_SHR_UN_IMM:
+                       ppc_load (code, ppc_r11, ins->inst_imm);
+                       ppc_srw (code, ins->dreg, ins->sreg1, ppc_r11);
+                       //ppc_rlwinm (code, ins->dreg, ins->sreg1, (32 - (ins->inst_imm & 0xf)), (ins->inst_imm & 0xf), 31);
+                       break;
+               case CEE_SHR_UN:
+                       ppc_srw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case CEE_NOT:
+                       ppc_not (code, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_NEG:
+                       ppc_neg (code, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_MUL:
+                       ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_MUL_IMM:
+                       ppc_load (code, ppc_r11, ins->inst_imm);
+                       ppc_mullw (code, ins->dreg, ins->sreg1, ppc_r11);
+                       break;
+               case CEE_MUL_OVF:
+                       ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       //g_assert_not_reached ();
+                       //x86_imul_reg_reg (code, ins->sreg1, ins->sreg2);
+                       //EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException");
+                       break;
+               case CEE_MUL_OVF_UN:
+                       ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       //FIXME: g_assert_not_reached ();
+                       break;
+               case OP_ICONST:
+               case OP_SETREGIMM:
+                       ppc_load (code, ins->dreg, ins->inst_c0);
+                       break;
+               /*case OP_CLASS:
+                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_CLASS, (gpointer)ins->inst_c0);
+                       ppc_load (code, ins->dreg, 0xff00ff00);
+                       break;
+               case OP_IMAGE:
+                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_IMAGE, (gpointer)ins->inst_c0);
+                       ppc_load (code, ins->dreg, 0xff00ff00);
+                       break;*/
+               case CEE_CONV_I4:
+               case CEE_CONV_U4:
+               case OP_MOVE:
+               case OP_SETREG:
+                       ppc_mr (code, ins->dreg, ins->sreg1);
+                       break;
+               case CEE_JMP:
+                       g_assert_not_reached ();
+                       break;
+               case OP_CHECK_THIS:
+                       /* ensure ins->sreg1 is not NULL */
+                       g_assert_not_reached ();
+                       //x86_alu_membase_imm (code, X86_CMP, ins->sreg1, 0, 0);
+                       break;
+               case OP_FCALL:
+               case OP_LCALL:
+               case OP_VCALL:
+               case OP_VOIDCALL:
+               case CEE_CALL:
+                       call = (MonoCallInst*)ins;
+                       if (ins->flags & MONO_INST_HAS_METHOD)
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
+                       else
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
+                       ppc_bl (code, 0);
+                       break;
+               case OP_FCALL_REG:
+               case OP_LCALL_REG:
+               case OP_VCALL_REG:
+               case OP_VOIDCALL_REG:
+               case OP_CALL_REG:
+                       ppc_mtlr (code, ins->sreg1);
+                       ppc_blrl (code);
+                       break;
+               case OP_FCALL_MEMBASE:
+               case OP_LCALL_MEMBASE:
+               case OP_VCALL_MEMBASE:
+               case OP_VOIDCALL_MEMBASE:
+               case OP_CALL_MEMBASE:
+                       ppc_lwz (code, ppc_r0, ins->inst_offset, ins->sreg1);
+                       ppc_mtlr (code, ppc_r0);
+                       ppc_blrl (code);
+                       break;
+               case OP_OUTARG:
+                       g_assert_not_reached ();
+                       break;
+               case OP_LOCALLOC:
+                       /* keep alignment */
+#define MONO_FRAME_ALIGNMENT 16
+                       g_assert_not_reached ();
+                       break;
+               case CEE_RET:
+                       ppc_blr (code);
+                       break;
+               case CEE_THROW: {
+                       ppc_mr (code, ppc_r3, ins->sreg1);
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+                                            (gpointer)"throw_exception");
+                       ppc_bl (code, 0);
+                       break;
+               }
+               case OP_ENDFILTER:
+                       if (ins->sreg1 != ppc_r3)
+                               ppc_mr (code, ppc_r3, ins->sreg1);
+                       ppc_blr (code);
+                       break;
+               case CEE_ENDFINALLY:
+                       ppc_blr (code);
+                       break;
+               case OP_HANDLER: 
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
+                       ppc_bl (code, 0);
+                       break;
+               case OP_LABEL:
+                       ins->inst_c0 = code - cfg->native_code;
+                       break;
+               case CEE_BR:
+                       //g_print ("target: %p, next: %p, curr: %p, last: %p\n", ins->inst_target_bb, bb->next_bb, ins, bb->last_ins);
+                       //if ((ins->inst_target_bb == bb->next_bb) && ins == bb->last_ins)
+                       //break;
+                       if (ins->flags & MONO_INST_BRLABEL) {
+                               /*if (ins->inst_i0->inst_c0) {
+                                       ppc_b (code, 0);
+                                       //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
+                               } else*/ {
+                                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
+                                       ppc_b (code, 0);
+                               }
+                       } else {
+                               /*if (ins->inst_target_bb->native_offset) {
+                                       ppc_b (code, 0);
+                                       //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset); 
+                               } else*/ {
+                                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
+                                       ppc_b (code, 0);
+                               } 
+                       }
+                       break;
+               case OP_BR_REG:
+                       ppc_mtctr (code, ins->sreg1);
+                       ppc_bcctr (code, 20, 0);
+                       break;
+               case OP_CEQ:
+                       ppc_li (code, ins->dreg, 0);
+                       ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 2);
+                       ppc_li (code, ins->dreg, 1);
+                       break;
+               case OP_CLT:
+               case OP_CLT_UN:
+                       ppc_li (code, ins->dreg, 1);
+                       ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2);
+                       ppc_li (code, ins->dreg, 0);
+                       break;
+               case OP_CGT:
+               case OP_CGT_UN:
+                       ppc_li (code, ins->dreg, 1);
+                       ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2);
+                       ppc_li (code, ins->dreg, 0);
+                       break;
+               case OP_COND_EXC_EQ:
+               case OP_COND_EXC_NE_UN:
+               case OP_COND_EXC_LT:
+               case OP_COND_EXC_LT_UN:
+               case OP_COND_EXC_GT:
+               case OP_COND_EXC_GT_UN:
+               case OP_COND_EXC_GE:
+               case OP_COND_EXC_GE_UN:
+               case OP_COND_EXC_LE:
+               case OP_COND_EXC_LE_UN:
+               case OP_COND_EXC_OV:
+               case OP_COND_EXC_NO:
+               case OP_COND_EXC_C:
+               case OP_COND_EXC_NC:
+                       //EMIT_COND_SYSTEM_EXCEPTION (branch_cc_table [ins->opcode - OP_COND_EXC_EQ], 
+                       //                          (ins->opcode < OP_COND_EXC_NE_UN), ins->inst_p1);
+                       break;
+               case CEE_BEQ:
+               case CEE_BNE_UN:
+               case CEE_BLT:
+               case CEE_BLT_UN:
+               case CEE_BGT:
+               case CEE_BGT_UN:
+               case CEE_BGE:
+               case CEE_BGE_UN:
+               case CEE_BLE:
+               case CEE_BLE_UN:
+                       EMIT_COND_BRANCH (ins, ins->opcode - CEE_BEQ);
+                       break;
+
+               /* floating point opcodes */
+               case OP_R8CONST:
+                       ppc_load (code, ppc_r11, ins->inst_p0);
+                       ppc_lfd (code, ins->dreg, 0, ppc_r11);
+                       break;
+               case OP_R4CONST:
+                       ppc_load (code, ppc_r11, ins->inst_p0);
+                       ppc_lfs (code, ins->dreg, 0, ppc_r11);
+                       break;
+               case OP_STORER8_MEMBASE_REG:
+                       ppc_stfd (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_LOADR8_MEMBASE:
+                       ppc_lfd (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       break;
+               case OP_STORER4_MEMBASE_REG:
+                       ppc_stfs (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg);
+                       break;
+               case OP_LOADR4_MEMBASE:
+                       ppc_lfs (code, ins->dreg, ins->inst_offset, ins->inst_basereg);
+                       break;
+               case CEE_CONV_R4: /* FIXME: change precision */
+               case CEE_CONV_R8:
+                       g_assert_not_reached ();
+                       x86_push_reg (code, ins->sreg1);
+                       x86_fild_membase (code, X86_ESP, 0, FALSE);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+                       break;
+               case OP_X86_FP_LOAD_I8:
+                       g_assert_not_reached ();
+                       x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, TRUE);
+                       break;
+               case OP_X86_FP_LOAD_I4:
+                       g_assert_not_reached ();
+                       x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, FALSE);
+                       break;
+               case OP_FCONV_TO_I1:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 1, TRUE);
+                       break;
+               case OP_FCONV_TO_U1:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 1, FALSE);
+                       break;
+               case OP_FCONV_TO_I2:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 2, TRUE);
+                       break;
+               case OP_FCONV_TO_U2:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 2, FALSE);
+                       break;
+               case OP_FCONV_TO_I4:
+               case OP_FCONV_TO_I:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 4, TRUE);
+                       break;
+               case OP_FCONV_TO_U4:
+               case OP_FCONV_TO_U:
+                       g_assert_not_reached ();
+                       code = emit_float_to_int (cfg, code, ins->dreg, 4, FALSE);
+                       break;
+               case OP_FCONV_TO_I8:
+               case OP_FCONV_TO_U8:
+                       g_assert_not_reached ();
+                       /*x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
+                       x86_fnstcw_membase(code, X86_ESP, 0);
+                       x86_mov_reg_membase (code, ins->inst_dreg_low, X86_ESP, 0, 2);
+                       x86_alu_reg_imm (code, X86_OR, ins->inst_dreg_low, 0xc00);
+                       x86_mov_membase_reg (code, X86_ESP, 2, ins->inst_dreg_low, 2);
+                       x86_fldcw_membase (code, X86_ESP, 2);
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+                       x86_fist_pop_membase (code, X86_ESP, 0, TRUE);
+                       x86_pop_reg (code, ins->inst_dreg_low);
+                       x86_pop_reg (code, ins->inst_dreg_high);
+                       x86_fldcw_membase (code, X86_ESP, 0);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);*/
+                       break;
+               case OP_LCONV_TO_R_UN: { 
+#if 0
+                       static guint8 mn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x40 };
+                       guint8 *br;
+
+                       /* load 64bit integer to FP stack */
+                       x86_push_imm (code, 0);
+                       x86_push_reg (code, ins->sreg2);
+                       x86_push_reg (code, ins->sreg1);
+                       x86_fild_membase (code, X86_ESP, 0, TRUE);
+                       /* store as 80bit FP value */
+                       x86_fst80_membase (code, X86_ESP, 0);
+                       
+                       /* test if lreg is negative */
+                       x86_test_reg_reg (code, ins->sreg2, ins->sreg2);
+                       br = code; x86_branch8 (code, X86_CC_GEZ, 0, TRUE);
+       
+                       /* add correction constant mn */
+                       x86_fld80_mem (code, mn);
+                       x86_fld80_membase (code, X86_ESP, 0);
+                       x86_fp_op_reg (code, X86_FADD, 1, TRUE);
+                       x86_fst80_membase (code, X86_ESP, 0);
+
+                       x86_patch (br, code);
+
+                       x86_fld80_membase (code, X86_ESP, 0);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
+#endif
+                       g_assert_not_reached ();
+                       break;
+               }
+               case OP_LCONV_TO_OVF_I: {
+#if 0
+                       guint8 *br [3], *label [1];
+
+                       /* 
+                        * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
+                        */
+                       x86_test_reg_reg (code, ins->sreg1, ins->sreg1);
+
+                       /* If the low word top bit is set, see if we are negative */
+                       br [0] = code; x86_branch8 (code, X86_CC_LT, 0, TRUE);
+                       /* We are not negative (no top bit set, check for our top word to be zero */
+                       x86_test_reg_reg (code, ins->sreg2, ins->sreg2);
+                       br [1] = code; x86_branch8 (code, X86_CC_EQ, 0, TRUE);
+                       label [0] = code;
+
+                       /* throw exception */
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException");
+                       x86_jump32 (code, 0);
+       
+                       x86_patch (br [0], code);
+                       /* our top bit is set, check that top word is 0xfffffff */
+                       x86_alu_reg_imm (code, X86_CMP, ins->sreg2, 0xffffffff);
+               
+                       x86_patch (br [1], code);
+                       /* nope, emit exception */
+                       br [2] = code; x86_branch8 (code, X86_CC_NE, 0, TRUE);
+                       x86_patch (br [2], label [0]);
+
+                       if (ins->dreg != ins->sreg1)
+                               x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4);
+#endif
+                       g_assert_not_reached ();
+                       break;
+               }
+               case OP_FADD:
+                       ppc_fadd (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_FSUB:
+                       ppc_fsub (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;          
+               case OP_FMUL:
+                       ppc_fmul (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;          
+               case OP_FDIV:
+                       ppc_fdiv (code, ins->dreg, ins->sreg1, ins->sreg2);
+                       break;          
+               case OP_FNEG:
+                       ppc_fneg (code, ins->dreg, ins->sreg1);
+                       break;          
+               case OP_FREM:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FCOMPARE:
+                       g_assert_not_reached ();
+                       /* this overwrites EAX */
+                       EMIT_FPCOMPARE(code);
+                       break;
+               case OP_FCEQ:
+                       g_assert_not_reached ();
+                       /*if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000);
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);*/
+                       break;
+               case OP_FCLT:
+               case OP_FCLT_UN:
+                       g_assert_not_reached ();
+                       /*if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);*/
+                       break;
+               case OP_FCGT:
+               case OP_FCGT_UN:
+                       g_assert_not_reached ();
+                       /*if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);*/
+                       break;
+               case OP_FBEQ:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FBNE_UN:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FBLT:
+               case OP_FBLT_UN:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FBGT:
+               case OP_FBGT_UN:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FBGE:
+               case OP_FBGE_UN:
+                       g_assert_not_reached ();
+                       break;
+               case OP_FBLE:
+               case OP_FBLE_UN:
+                       g_assert_not_reached ();
+                       break;
+               case CEE_CKFINITE: {
+                       g_assert_not_reached ();
+                       x86_push_reg (code, X86_EAX);
+                       x86_fxam (code);
+                       x86_fnstsw (code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4100);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       x86_pop_reg (code, X86_EAX);
+                       EMIT_COND_SYSTEM_EXCEPTION (X86_CC_EQ, FALSE, "ArithmeticException");
+                       break;
+               }
+               default:
+                       g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
+                       g_assert_not_reached ();
+               }
+
+               if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
+                       g_warning ("wrong maximal instruction length of instruction %s (exptected %d, got %d)",
+                                  mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
+                       g_assert_not_reached ();
+               }
+              
+               cpos += max_len;
+
+               last_ins = ins;
+               last_offset = offset;
+               
+               ins = ins->next;
+       }
+
+       cfg->code_len = code - cfg->native_code;
+}
+
+void
+mono_arch_register_lowlevel_calls (void)
+{
+       mono_register_jit_icall (enter_method, "mono_enter_method", NULL, TRUE);
+       mono_register_jit_icall (leave_method, "mono_leave_method", NULL, TRUE);
+}
+
+void
+mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji)
+{
+       MonoJumpInfo *patch_info;
+
+       for (patch_info = ji; patch_info; patch_info = patch_info->next) {
+               unsigned char *ip = patch_info->ip.i + code;
+               const unsigned char *target = NULL;
+
+               switch (patch_info->type) {
+               case MONO_PATCH_INFO_BB:
+                       target = patch_info->data.bb->native_offset + code;
+                       break;
+               case MONO_PATCH_INFO_ABS:
+                       target = patch_info->data.target;
+                       break;
+               case MONO_PATCH_INFO_LABEL:
+                       target = patch_info->data.inst->inst_c0 + code;
+                       break;
+               case MONO_PATCH_INFO_IP:
+                       *((gpointer *)(ip)) = ip;
+                       continue;
+               case MONO_PATCH_INFO_INTERNAL_METHOD:
+                       if (!strcmp (patch_info->data.name, "throw_exception")) {
+                               target = mono_arch_get_throw_exception ();
+                       } else if (!strcmp (patch_info->data.name, "throw_exception_by_name")) {
+                               target = mono_arch_get_throw_exception_by_name ();
+                       } else if (!strcmp (patch_info->data.name, "ldstr")) {
+                               target = (gpointer)mono_ldstr;
+                       } else if (!strcmp (patch_info->data.name, "domain_get")) {
+                               target = (gpointer)mono_domain_get;
+                       } else if (!strcmp (patch_info->data.name, "object_new")) {
+                               target = (gpointer)mono_object_new;
+                       } else if (!strcmp (patch_info->data.name, "get_lmf_addr")) {
+                               target = (gpointer)mono_get_lmf_addr;
+                       } else
+                               g_assert_not_reached ();
+                       break;
+               case MONO_PATCH_INFO_METHOD:
+                       if (patch_info->data.method == method) {
+                               target = code;
+                       } else {
+                               /* get the trampoline to the method from the domain */
+                               target = mono_arch_create_jit_trampoline (patch_info->data.method);
+                       }
+                       break;
+               case MONO_PATCH_INFO_SWITCH: {
+                       gpointer *table = (gpointer *)patch_info->data.target;
+                       int i;
+
+                       // FIXME: inspect code to get the register
+                       ppc_load (ip, ppc_r11, patch_info->data.target);
+                       //*((gconstpointer *)(ip + 2)) = patch_info->data.target;
+
+                       for (i = 0; i < patch_info->table_size; i++) {
+                               table [i] = (int)patch_info->data.table [i] + code;
+                       }
+                       /* we put into the table the absolute address, no need for ppc_patch in this case */
+                       continue;
+               }
+               case MONO_PATCH_INFO_CLASS:
+                       *((gconstpointer *)(ip + 1)) = patch_info->data.target;
+                       continue;
+               case MONO_PATCH_INFO_IMAGE:
+                       *((gconstpointer *)(ip + 1)) = patch_info->data.target;
+                       continue;
+               default:
+                       g_assert_not_reached ();
+               }
+               ppc_patch (ip, target);
+       }
+}
+
+int
+mono_arch_max_epilog_size (MonoCompile *cfg)
+{
+       int exc_count = 0, max_epilog_size = 16;
+       MonoJumpInfo *patch_info;
+       
+       if (cfg->method->save_lmf)
+               max_epilog_size += 128;
+       
+       if (mono_jit_trace_calls)
+               max_epilog_size += 50;
+
+       if (mono_jit_profile)
+               max_epilog_size += 50;
+
+       /* count the number of exception infos */
+     
+       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+               if (patch_info->type == MONO_PATCH_INFO_EXC)
+                       exc_count++;
+       }
+
+       /* 
+        * make sure we have enough space for exceptions
+        * 16 is the size of two push_imm instructions and a call
+        */
+       max_epilog_size += exc_count*16;
+
+       return max_epilog_size;
+}
+
+guint8 *
+mono_arch_emit_prolog (MonoCompile *cfg)
+{
+       MonoMethod *method = cfg->method;
+       MonoBasicBlock *bb;
+       MonoMethodSignature *sig;
+       MonoInst *inst;
+       int alloc_size, pos, max_offset, i;
+       guint8 *code;
+       CallInfo *cinfo;
+
+       cfg->code_size = 256;
+       code = cfg->native_code = g_malloc (cfg->code_size);
+
+       if (cfg->flags & MONO_CFG_HAS_CALLS) {
+               ppc_mflr (code, ppc_r0);
+               ppc_stw (code, ppc_r0, 8, ppc_sp);
+       }
+
+       alloc_size = cfg->stack_offset;
+       pos = 0;
+
+       if (method->save_lmf) {
+#if 0
+               pos += sizeof (MonoLMF);
+               
+               /* save the current IP */
+               mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL);
+               x86_push_imm (code, 0);
+
+               /* save all caller saved regs */
+               x86_push_reg (code, X86_EBX);
+               x86_push_reg (code, X86_EDI);
+               x86_push_reg (code, X86_ESI);
+               x86_push_reg (code, X86_EBP);
+
+               /* save method info */
+               x86_push_imm (code, method);
+       
+               /* get the address of lmf for the current thread */
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+                                    (gpointer)"get_lmf_addr");
+               x86_call_code (code, 0);
+
+               /* push lmf */
+               x86_push_reg (code, X86_EAX); 
+               /* push *lfm (previous_lmf) */
+               x86_push_membase (code, X86_EAX, 0);
+               /* *(lmf) = ESP */
+               x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
+#endif
+       } else {
+
+               for (i = 13; i < 32; ++i) {
+                       if (cfg->used_int_regs & (1 << i)) {
+                               pos += 4;
+                               ppc_stw (code, i, -pos, ppc_sp);
+                       }
+               }
+       }
+
+       alloc_size += pos;
+       // align to 16 bytes
+       if (alloc_size & (16 - 1))
+               alloc_size += 16 - (alloc_size & (16 - 1));
+
+       cfg->stack_usage = alloc_size;
+       if (alloc_size)
+               ppc_stwu (code, ppc_sp, -alloc_size, ppc_sp);
+
+        /* compute max_offset in order to use short forward jumps */
+       max_offset = 0;
+       if (cfg->opt & MONO_OPT_BRANCH) {
+               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+                       MonoInst *ins = bb->code;
+                       bb->max_offset = max_offset;
+
+                       if (mono_trace_coverage)
+                               max_offset += 6; 
+
+                       while (ins) {
+                               max_offset += ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN];
+                               ins = ins->next;
+                       }
+               }
+       }
+
+       if (mono_jit_trace_calls)
+               code = mono_arch_instrument_prolog (cfg, enter_method, code, TRUE);
+
+       /* load arguments allocated to register from the stack */
+       sig = method->signature;
+       pos = 0;
+
+       cinfo = calculate_sizes (sig, sig->pinvoke);
+
+       for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+               ArgInfo *ainfo = cinfo->args + i;
+               inst = cfg->varinfo [pos];
+               
+               if (inst->opcode == OP_REGVAR) {
+                       g_assert (!ainfo->regtype); // fine for now
+                       ppc_mr (code, inst->dreg, ainfo->reg);
+                       x86_mov_reg_membase (code, inst->dreg, X86_EBP, inst->inst_offset, 4);
+                       if (cfg->verbose_level > 2)
+                               g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
+               } else {
+                       /* the argument should be put on the stack: FIXME handle size != word  */
+                       ppc_stw (code, ainfo->reg, inst->inst_offset, inst->inst_basereg);
+               }
+               pos++;
+       }
+
+       cfg->code_len = code - cfg->native_code;
+
+       return code;
+}
+
+void
+mono_arch_emit_epilog (MonoCompile *cfg)
+{
+       MonoJumpInfo *patch_info;
+       MonoMethod *method = cfg->method;
+       int pos, i;
+       guint8 *code;
+
+       code = cfg->native_code + cfg->code_len;
+
+       if (mono_jit_trace_calls)
+               code = mono_arch_instrument_epilog (cfg, leave_method, code, TRUE);
+
+       
+       pos = 0;
+       
+       if (method->save_lmf) {
+               pos = -sizeof (MonoLMF);
+       }
+
+       if (method->save_lmf) {
+#if 0
+               /* ebx = previous_lmf */
+               x86_pop_reg (code, X86_EBX);
+               /* edi = lmf */
+               x86_pop_reg (code, X86_EDI);
+               /* *(lmf) = previous_lmf */
+               x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
+
+               /* discard method info */
+               x86_pop_reg (code, X86_ESI);
+
+               /* restore caller saved regs */
+               x86_pop_reg (code, X86_EBP);
+               x86_pop_reg (code, X86_ESI);
+               x86_pop_reg (code, X86_EDI);
+               x86_pop_reg (code, X86_EBX);
+#endif
+       }
+
+       if (cfg->flags & MONO_CFG_HAS_CALLS) {
+               ppc_lwz (code, ppc_r0, cfg->stack_usage + 8, ppc_sp);
+               ppc_mtlr (code, ppc_r0);
+       }
+       ppc_addic (code, ppc_sp, ppc_sp, cfg->stack_usage);
+       for (i = 13; i < 32; ++i) {
+               if (cfg->used_int_regs & (1 << i)) {
+                       pos += 4;
+                       ppc_lwz (code, i, -pos, ppc_sp);
+               }
+       }
+       ppc_blr (code);
+
+       /* add code to raise exceptions */
+       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+               switch (patch_info->type) {
+               case MONO_PATCH_INFO_EXC:
+                       /*x86_patch (patch_info->ip.i + cfg->native_code, code);
+                       x86_push_imm (code, patch_info->data.target);
+                       x86_push_imm (code, patch_info->ip.i + cfg->native_code);
+                       patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
+                       patch_info->data.name = "throw_exception_by_name";
+                       patch_info->ip.i = code - cfg->native_code;
+                       x86_jump_code (code, 0);*/
+                       break;
+               default:
+                       /* do nothing */
+                       break;
+               }
+       }
+
+       cfg->code_len = code - cfg->native_code;
+
+       g_assert (cfg->code_len < cfg->code_size);
+
+}
diff --git a/mono/mini/mini-ppc.h b/mono/mini/mini-ppc.h
new file mode 100644 (file)
index 0000000..31dbf56
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __MONO_MINI_X86_H__
+#define __MONO_MINI_X86_H__
+
+#include <mono/arch/x86/x86-codegen.h>
+#include <mono/arch/ppc/ppc-codegen.h>
+
+#define MONO_ARCH_FRAME_ALIGNMENT 4
+
+/* fixme: align to 16byte instead of 32byte (we align to 32byte to get 
+ * reproduceable results for benchmarks */
+#define MONO_ARCH_CODE_ALIGNMENT 32
+
+#define MONO_ARCH_BASEREG X86_EBP
+#define MONO_ARCH_RETREG1 ppc_r3
+#define MONO_ARCH_RETREG2 ppc_r4
+#define MONO_ARCH_EXC_REG ppc_r5
+
+struct MonoLMF {
+       gpointer    previous_lmf;
+       gpointer    lmf_addr;
+       MonoMethod *method;
+       guint32     ebp;
+       guint32     eip;
+};
+
+#endif /* __MONO_MINI_X86_H__ */  
diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c
new file mode 100644 (file)
index 0000000..34465f6
--- /dev/null
@@ -0,0 +1,3141 @@
+/*
+ * mini-x86.c: x86 backend for the Mono code generator
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include "mini.h"
+#include <string.h>
+
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini-x86.h"
+#include "inssel.h"
+#include "regset.h"
+#include "cpu-pentium.h"
+
+int mono_exc_esp_offset = 0;
+
+const char*
+mono_arch_regname (int reg) {
+       switch (reg) {
+       case X86_EAX: return "%eax";
+       case X86_EBX: return "%ebx";
+       case X86_ECX: return "%ecx";
+       case X86_EDX: return "%edx";
+       case X86_ESP: return "%esp";
+       case X86_EBP: return "%ebp";
+       case X86_EDI: return "%edi";
+       case X86_ESI: return "%esi";
+       }
+       return "unknown";
+}
+
+typedef struct {
+       guint16 size;
+       guint16 offset;
+       guint8  pad;
+} MonoJitArgumentInfo;
+
+/*
+ * arch_get_argument_info:
+ * @csig:  a method signature
+ * @param_count: the number of parameters to consider
+ * @arg_info: an array to store the result infos
+ *
+ * Gathers information on parameters such as size, alignment and
+ * padding. arg_info should be large enought to hold param_count + 1 entries. 
+ *
+ * Returns the size of the activation frame.
+ */
+static int
+arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
+{
+       int k, frame_size = 0;
+       int size, align, pad;
+       int offset = 8;
+
+       if (MONO_TYPE_ISSTRUCT (csig->ret)) { 
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].offset = offset;
+
+       if (csig->hasthis) {
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].size = frame_size;
+
+       for (k = 0; k < param_count; k++) {
+               
+               if (csig->pinvoke)
+                       size = mono_type_native_stack_size (csig->params [k], &align);
+               else
+                       size = mono_type_stack_size (csig->params [k], &align);
+
+               /* ignore alignment for now */
+               align = 1;
+
+               frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); 
+               arg_info [k].pad = pad;
+               frame_size += size;
+               arg_info [k + 1].pad = 0;
+               arg_info [k + 1].size = size;
+               offset += pad;
+               arg_info [k + 1].offset = offset;
+               offset += size;
+       }
+
+       align = MONO_ARCH_FRAME_ALIGNMENT;
+       frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
+       arg_info [k].pad = pad;
+
+       return frame_size;
+}
+
+static int indent_level = 0;
+
+static void indent (int diff) {
+       int v = indent_level;
+       while (v-- > 0) {
+               printf (". ");
+       }
+       indent_level += diff;
+}
+
+static void
+enter_method (MonoMethod *method, char *ebp)
+{
+       int i, j;
+       MonoClass *class;
+       MonoObject *o;
+       MonoJitArgumentInfo *arg_info;
+       MonoMethodSignature *sig;
+       char *fname;
+
+       fname = mono_method_full_name (method, TRUE);
+       indent (1);
+       printf ("ENTER: %s(", fname);
+       g_free (fname);
+       
+       if (((int)ebp & (MONO_ARCH_FRAME_ALIGNMENT - 1)) != 0) {
+               g_error ("unaligned stack detected (%p)", ebp);
+       }
+
+       sig = method->signature;
+
+       arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1));
+
+       arch_get_argument_info (sig, sig->param_count, arg_info);
+
+       if (MONO_TYPE_ISSTRUCT (method->signature->ret)) {
+               g_assert (!method->signature->ret->byref);
+
+               printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8)));
+       }
+
+       if (method->signature->hasthis) {
+               gpointer *this = (gpointer *)(ebp + arg_info [0].offset);
+               if (method->klass->valuetype) {
+                       printf ("value:%p, ", *this);
+               } else {
+                       o = *((MonoObject **)this);
+
+                       if (o) {
+                               class = o->vtable->klass;
+
+                               if (class == mono_defaults.string_class) {
+                                       printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
+                               } else {
+                                       printf ("this:%p[%s.%s], ", o, class->name_space, class->name);
+                               }
+                       } else 
+                               printf ("this:NULL, ");
+               }
+       }
+
+       for (i = 0; i < method->signature->param_count; ++i) {
+               gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset);
+               int size = arg_info [i + 1].size;
+
+               MonoType *type = method->signature->params [i];
+               
+               if (type->byref) {
+                       printf ("[BYREF:%p], ", *cpos); 
+               } else switch (type->type) {
+                       
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+                       printf ("%p, ", (gpointer)*((int *)(cpos)));
+                       break;
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+                       printf ("%d, ", *((int *)(cpos)));
+                       break;
+               case MONO_TYPE_STRING: {
+                       MonoString *s = *((MonoString **)cpos);
+                       if (s) {
+                               g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
+                               printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s));
+                       } else 
+                               printf ("[STRING:null], ");
+                       break;
+               }
+               case MONO_TYPE_CLASS:
+               case MONO_TYPE_OBJECT: {
+                       o = *((MonoObject **)cpos);
+                       if (o) {
+                               class = o->vtable->klass;
+                   
+                               if (class == mono_defaults.string_class) {
+                                       printf ("[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
+                               } else if (class == mono_defaults.int32_class) {
+                                       printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject)));
+                               } else
+                                       printf ("[%s.%s:%p], ", class->name_space, class->name, o);
+                       } else {
+                               printf ("%p, ", *((gpointer *)(cpos)));                         
+                       }
+                       break;
+               }
+               case MONO_TYPE_PTR:
+               case MONO_TYPE_FNPTR:
+               case MONO_TYPE_ARRAY:
+               case MONO_TYPE_SZARRAY:
+                       printf ("%p, ", *((gpointer *)(cpos)));
+                       break;
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+                       printf ("0x%016llx, ", *((gint64 *)(cpos)));
+                       break;
+               case MONO_TYPE_R4:
+                       printf ("%f, ", *((float *)(cpos)));
+                       break;
+               case MONO_TYPE_R8:
+                       printf ("%f, ", *((double *)(cpos)));
+                       break;
+               case MONO_TYPE_VALUETYPE: 
+                       printf ("[");
+                       for (j = 0; j < size; j++)
+                               printf ("%02x,", *((guint8*)cpos +j));
+                       printf ("], ");
+                       break;
+               default:
+                       printf ("XX, ");
+               }
+       }
+
+       printf (")\n");
+}
+
+static void
+leave_method (MonoMethod *method, ...)
+{
+       MonoType *type;
+       char *fname;
+       va_list ap;
+
+       va_start(ap, method);
+
+       fname = mono_method_full_name (method, TRUE);
+       indent (-1);
+       printf ("LEAVE: %s", fname);
+       g_free (fname);
+
+       type = method->signature->ret;
+
+handle_enum:
+       switch (type->type) {
+       case MONO_TYPE_VOID:
+               break;
+       case MONO_TYPE_BOOLEAN: {
+               int eax = va_arg (ap, int);
+               if (eax)
+                       printf ("TRUE:%d", eax);
+               else 
+                       printf ("FALSE");
+                       
+               break;
+       }
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U: {
+               int eax = va_arg (ap, int);
+               printf ("EAX=%d", eax);
+               break;
+       }
+       case MONO_TYPE_STRING: {
+               MonoString *s = va_arg (ap, MonoString *);
+;
+               if (s) {
+                       g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
+                       printf ("[STRING:%p:%s]", s, mono_string_to_utf8 (s));
+               } else 
+                       printf ("[STRING:null], ");
+               break;
+       }
+       case MONO_TYPE_CLASS: 
+       case MONO_TYPE_OBJECT: {
+               MonoObject *o = va_arg (ap, MonoObject *);
+
+               if (o) {
+                       if (o->vtable->klass == mono_defaults.boolean_class) {
+                               printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
+                       } else if  (o->vtable->klass == mono_defaults.int32_class) {
+                               printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject))));    
+                       } else if  (o->vtable->klass == mono_defaults.int64_class) {
+                               printf ("[INT64:%p:%lld]", o, *((gint64 *)((char *)o + sizeof (MonoObject))));  
+                       } else
+                               printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o);
+               } else
+                       printf ("[OBJECT:%p]", o);
+              
+               break;
+       }
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_SZARRAY: {
+               gpointer p = va_arg (ap, gpointer);
+               printf ("EAX=%p", p);
+               break;
+       }
+       case MONO_TYPE_I8: {
+               gint64 l =  va_arg (ap, gint64);
+               printf ("EAX/EDX=0x%16llx", l);
+               break;
+       }
+       case MONO_TYPE_U8: {
+               gint64 l =  va_arg (ap, gint64);
+               printf ("EAX/EDX=0x%16llx", l);
+               break;
+       }
+       case MONO_TYPE_R8: {
+               double f = va_arg (ap, double);
+               printf ("FP=%f\n", f);
+               break;
+       }
+       case MONO_TYPE_VALUETYPE: 
+               if (type->data.klass->enumtype) {
+                       type = type->data.klass->enum_basetype;
+                       goto handle_enum;
+               } else {
+                       guint8 *p = va_arg (ap, gpointer);
+                       int j, size, align;
+                       size = mono_type_size (type, &align);
+                       printf ("[");
+                       for (j = 0; p && j < size; j++)
+                               printf ("%02x,", p [j]);
+                       printf ("]");
+               }
+               break;
+       default:
+               printf ("(unknown return type %x)", method->signature->ret->type);
+       }
+
+       printf ("\n");
+}
+
+static const guchar cpuid_impl [] = {
+       0x55,                           /* push   %ebp */
+       0x89, 0xe5,                     /* mov    %esp,%ebp */
+       0x53,                           /* push   %ebx */
+       0x8b, 0x45, 0x08,               /* mov    0x8(%ebp),%eax */
+       0x0f, 0xa2,                     /* cpuid   */
+       0x50,                           /* push   %eax */
+       0x8b, 0x45, 0x10,               /* mov    0x10(%ebp),%eax */
+       0x89, 0x18,                     /* mov    %ebx,(%eax) */
+       0x8b, 0x45, 0x14,               /* mov    0x14(%ebp),%eax */
+       0x89, 0x08,                     /* mov    %ecx,(%eax) */
+       0x8b, 0x45, 0x18,               /* mov    0x18(%ebp),%eax */
+       0x89, 0x10,                     /* mov    %edx,(%eax) */
+       0x58,                           /* pop    %eax */
+       0x8b, 0x55, 0x0c,               /* mov    0xc(%ebp),%edx */
+       0x89, 0x02,                     /* mov    %eax,(%edx) */
+       0x5b,                           /* pop    %ebx */
+       0xc9,                           /* leave   */
+       0xc3,                           /* ret     */
+};
+
+typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx);
+
+static int 
+cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx)
+{
+       int have_cpuid = 0;
+       __asm__  __volatile__ (
+               "pushfl\n"
+               "popl %%eax\n"
+               "movl %%eax, %%edx\n"
+               "xorl $0x200000, %%eax\n"
+               "pushl %%eax\n"
+               "popfl\n"
+               "pushfl\n"
+               "popl %%eax\n"
+               "xorl %%edx, %%eax\n"
+               "andl $0x200000, %%eax\n"
+               "movl %%eax, %0"
+               : "=r" (have_cpuid)
+               :
+               : "%eax", "%edx"
+       );
+
+       if (have_cpuid) {
+               CpuidFunc func = (CpuidFunc)cpuid_impl;
+               func (id, p_eax, p_ebx, p_ecx, p_edx);
+               /*
+                * We use this approach because of issues with gcc and pic code, see:
+                * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329
+               __asm__ __volatile__ ("cpuid"
+                       : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx)
+                       : "a" (id));
+               */
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * This function returns the optimizations supported on this cpu.
+ */
+guint32
+mono_arch_cpu_optimizazions (void)
+{
+       int eax, ebx, ecx, edx;
+       guint32 opts = 0;
+
+       /* Feature Flags function, flags returned in EDX. */
+       if (cpuid (1, &eax, &ebx, &ecx, &edx)) {
+               if (edx & (1 << 15)) {
+                       opts |= MONO_OPT_CMOV;
+                       if (edx & 1)
+                               opts |= MONO_OPT_FCMOV;
+               }
+       }
+       return opts;
+}
+
+static gboolean
+is_regsize_var (MonoType *t) {
+       if (t->byref)
+               return TRUE;
+       switch (t->type) {
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+               return TRUE;
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:
+               return FALSE;
+       case MONO_TYPE_VALUETYPE:
+               if (t->data.klass->enumtype)
+                       return is_regsize_var (t->data.klass->enum_basetype);
+               return FALSE;
+       }
+       return FALSE;
+}
+
+GList *
+mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
+{
+       GList *vars = NULL;
+       int i;
+
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoInst *ins = cfg->varinfo [i];
+               MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+               /* unused vars */
+               if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos)
+                       continue;
+
+               if ((ins->flags & (MONO_INST_IS_DEAD|MONO_INST_VOLATILE|MONO_INST_INDIRECT)) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
+                       continue;
+
+               /* we can only allocate 32 bit values */
+               if (is_regsize_var (ins->inst_vtype)) {
+                       g_assert (MONO_VARINFO (cfg, i)->reg == -1);
+                       g_assert (i == vmv->idx);
+                       vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
+               }
+       }
+
+       return vars;
+}
+
+GList *
+mono_arch_get_global_int_regs (MonoCompile *cfg)
+{
+       GList *regs = NULL;
+
+       /* we can use 3 registers for global allocation */
+       regs = g_list_prepend (regs, (gpointer)X86_EBX);
+       regs = g_list_prepend (regs, (gpointer)X86_ESI);
+       regs = g_list_prepend (regs, (gpointer)X86_EDI);
+
+       return regs;
+}
+/*
+ * Set var information according to the calling convention. X86 version.
+ * The locals var stuff should most likely be split in another method.
+ */
+void
+mono_arch_allocate_vars (MonoCompile *m)
+{
+       MonoMethodSignature *sig;
+       MonoMethodHeader *header;
+       MonoInst *inst;
+       int i, offset, size, align, curinst;
+
+       header = ((MonoMethodNormal *)m->method)->header;
+
+       sig = m->method->signature;
+       
+       offset = 8;
+       curinst = 0;
+       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+               m->ret->opcode = OP_REGOFFSET;
+               m->ret->inst_basereg = X86_EBP;
+               m->ret->inst_offset = offset;
+               offset += sizeof (gpointer);
+       } else {
+               /* FIXME: handle long and FP values */
+               switch (sig->ret->type) {
+               case MONO_TYPE_VOID:
+                       break;
+               default:
+                       m->ret->opcode = OP_REGVAR;
+                       m->ret->inst_c0 = X86_EAX;
+                       break;
+               }
+       }
+       if (sig->hasthis) {
+               inst = m->varinfo [curinst];
+               if (inst->opcode != OP_REGVAR) {
+                       inst->opcode = OP_REGOFFSET;
+                       inst->inst_basereg = X86_EBP;
+               }
+               inst->inst_offset = offset;
+               offset += sizeof (gpointer);
+               curinst++;
+       }
+
+       for (i = 0; i < sig->param_count; ++i) {
+               inst = m->varinfo [curinst];
+               if (inst->opcode != OP_REGVAR) {
+                       inst->opcode = OP_REGOFFSET;
+                       inst->inst_basereg = X86_EBP;
+               }
+               inst->inst_offset = offset;
+               size = mono_type_size (sig->params [i], &align);
+               size += 4 - 1;
+               size &= ~(4 - 1);
+               offset += size;
+               curinst++;
+       }
+
+       offset = 0;
+       /* reserve space to save LMF and caller saved registers */
+       offset += sizeof (MonoLMF);
+
+       /* reserve space to store the esp */
+       offset += sizeof (gpointer);
+
+       /* this is a global constant */
+       mono_exc_esp_offset = -offset;
+
+       for (i = curinst; i < m->num_varinfo; ++i) {
+               inst = m->varinfo [i];
+
+               if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
+                       continue;
+
+               /* inst->unused indicates native sized value types, this is used by the
+               * pinvoke wrappers when they call functions returning structure */
+               if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype))
+                       size = mono_class_native_size (inst->inst_vtype->data.klass, &align);
+               else
+                       size = mono_type_size (inst->inst_vtype, &align);
+
+               offset += size;
+               offset += align - 1;
+               offset &= ~(align - 1);
+               inst->opcode = OP_REGOFFSET;
+               inst->inst_basereg = X86_EBP;
+               inst->inst_offset = -offset;
+               //g_print ("allocating local %d to %d\n", i, -offset);
+       }
+       offset += 3;
+       offset &= ~3;
+
+
+       /* change sign? */
+       m->stack_offset = -offset;
+}
+
+/* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
+ * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info 
+ */
+
+/* 
+ * take the arguments and generate the arch-specific
+ * instructions to properly call the function in call.
+ * This includes pushing, moving arguments to the right register
+ * etc.
+ * Issue: who does the spilling if needed, and when?
+ */
+MonoCallInst*
+mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
+       MonoInst *arg, *in, **rev_args;
+       MonoMethodSignature *sig;
+       int i, n, stack_size, type;
+       MonoType *ptype;
+
+       sig = call->signature;
+       n = sig->param_count + sig->hasthis;
+       rev_args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
+       
+       if (sig->ret && (sig->ret->type == MONO_TYPE_I8 || sig->ret->type == MONO_TYPE_U8)) {
+               //g_warning ("long value returned");
+       }
+       if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret))
+               stack_size = 4;
+       else
+               stack_size = 0;
+       for (i = 0; i < n; ++i) {
+               if (is_virtual && i == 0) {
+                       /* the argument will be attached to the call instrucion */
+                       rev_args [n - 1] = arg = NULL;
+                       in = call->args [i];
+                       stack_size += 4;
+               } else {
+                       MONO_INST_NEW (cfg, arg, OP_OUTARG);
+                       in = call->args [i];
+                       arg->cil_code = in->cil_code;
+                       arg->inst_left = in;
+                       arg->type = in->type;
+                       rev_args [n - i - 1] = arg;
+                       if (i >= sig->hasthis) {
+                               ptype = sig->params [i - sig->hasthis];
+                               if (ptype->byref)
+                                       type = MONO_TYPE_U;
+                               else
+                                       type = ptype->type;
+handle_enum:
+                               /* FIXME: validate arguments... */
+                               switch (type) {
+                               case MONO_TYPE_I:
+                               case MONO_TYPE_U:
+                               case MONO_TYPE_BOOLEAN:
+                               case MONO_TYPE_CHAR:
+                               case MONO_TYPE_I1:
+                               case MONO_TYPE_U1:
+                               case MONO_TYPE_I2:
+                               case MONO_TYPE_U2:
+                               case MONO_TYPE_I4:
+                               case MONO_TYPE_U4:
+                               case MONO_TYPE_STRING:
+                               case MONO_TYPE_CLASS:
+                               case MONO_TYPE_OBJECT:
+                               case MONO_TYPE_PTR:
+                               case MONO_TYPE_FNPTR:
+                               case MONO_TYPE_ARRAY:
+                               case MONO_TYPE_SZARRAY:
+                                       stack_size += 4;
+                                       break;
+                               case MONO_TYPE_I8:
+                               case MONO_TYPE_U8:
+                                       stack_size += 8;
+                                       break;
+                               case MONO_TYPE_R4:
+                                       stack_size += 4;
+                                       arg->opcode = OP_OUTARG_R4;
+                                       break;
+                               case MONO_TYPE_R8:
+                                       stack_size += 8;
+                                       arg->opcode = OP_OUTARG_R8;
+                                       break;
+                               case MONO_TYPE_VALUETYPE:
+                                       if (MONO_TYPE_ISSTRUCT (ptype)) {
+                                               int size;
+                                               if (sig->pinvoke) 
+                                                       size = mono_type_native_stack_size (&in->klass->byval_arg, NULL);
+                                               else 
+                                                       size = mono_type_stack_size (&in->klass->byval_arg, NULL);
+                                               stack_size += size;
+                                               arg->opcode = OP_OUTARG_VT;
+                                               arg->klass = in->klass;
+                                               arg->unused = sig->pinvoke;
+                                               arg->inst_imm = size; 
+                                       } else {
+                                               type = ptype->data.klass->enum_basetype->type;
+                                               goto handle_enum;
+                                       }
+                                       break;
+                               default:
+                                       g_warning ("unknown type 0x%02x\n", type);
+                                       g_assert_not_reached ();
+                               }
+                       } else {
+                               /* the this argument */
+                               stack_size += 4;
+                       }
+               }
+       }
+       /* they need to be pushed in reverse order */
+       call->args = rev_args;
+       call->stack_usage = stack_size;
+       /* 
+        * should set more info in call, such as the stack space
+        * used by the args that needs to be added back to esp
+        */
+
+       return call;
+}
+
+/*
+ * Allow tracing to work with this interface (with an optional argument)
+ */
+
+/*
+ * This may be needed on some archs or for debugging support.
+ */
+void
+mono_arch_instrument_mem_needs (MonoMethod *method, int *stack, int *code)
+{
+       /* no stack room needed now (may be needed for FASTCALL-trace support) */
+       *stack = 0;
+       /* split prolog-epilog requirements? */
+       *code = 50; /* max bytes needed: check this number */
+}
+
+void*
+mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
+{
+       guchar *code = p;
+
+       /* if some args are passed in registers, we need to save them here */
+       x86_push_reg (code, X86_EBP);
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_METHODCONST, cfg->method);
+       x86_push_imm (code, cfg->method);
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func);
+       x86_call_code (code, 0);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
+
+       return code;
+}
+
+enum {
+       SAVE_NONE,
+       SAVE_STRUCT,
+       SAVE_EAX,
+       SAVE_EAX_EDX,
+       SAVE_FP
+};
+
+void*
+mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
+{
+       guchar *code = p;
+       int arg_size = 0, save_mode = SAVE_NONE;
+       MonoMethod *method = cfg->method;
+       int rtype = method->signature->ret->type;
+       
+handle_enum:
+       switch (rtype) {
+       case MONO_TYPE_VOID:
+               /* special case string .ctor icall */
+               if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
+                       save_mode = SAVE_EAX;
+               else
+                       save_mode = SAVE_NONE;
+               break;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               save_mode = SAVE_EAX_EDX;
+               break;
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+               save_mode = SAVE_FP;
+               break;
+       case MONO_TYPE_VALUETYPE:
+               if (method->signature->ret->data.klass->enumtype) {
+                       rtype = method->signature->ret->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               }
+               save_mode = SAVE_STRUCT;
+               break;
+       default:
+               save_mode = SAVE_EAX;
+               break;
+       }
+
+       switch (save_mode) {
+       case SAVE_EAX_EDX:
+               x86_push_reg (code, X86_EDX);
+               x86_push_reg (code, X86_EAX);
+               if (enable_arguments) {
+                       x86_push_reg (code, X86_EDX);
+                       x86_push_reg (code, X86_EAX);
+                       arg_size = 8;
+               }
+               break;
+       case SAVE_EAX:
+               x86_push_reg (code, X86_EAX);
+               if (enable_arguments) {
+                       x86_push_reg (code, X86_EAX);
+                       arg_size = 4;
+               }
+               break;
+       case SAVE_FP:
+               x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+               x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE);
+               if (enable_arguments) {
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+                       x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE);
+                       arg_size = 8;
+               }
+               break;
+       case SAVE_STRUCT:
+               if (enable_arguments) {
+                       x86_push_membase (code, X86_EBP, 8);
+                       arg_size = 4;
+               }
+               break;
+       case SAVE_NONE:
+       default:
+               break;
+       }
+
+
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_METHODCONST, method);
+       x86_push_imm (code, method);
+       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func);
+       x86_call_code (code, 0);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, arg_size + 4);
+
+       switch (save_mode) {
+       case SAVE_EAX_EDX:
+               x86_pop_reg (code, X86_EAX);
+               x86_pop_reg (code, X86_EDX);
+               break;
+       case SAVE_EAX:
+               x86_pop_reg (code, X86_EAX);
+               break;
+       case SAVE_FP:
+               x86_fld_membase (code, X86_ESP, 0, TRUE);
+               x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
+               break;
+       case SAVE_NONE:
+       default:
+               break;
+       }
+
+       return code;
+}
+
+#define EMIT_COND_BRANCH(ins,cond,sign) \
+if (ins->flags & MONO_INST_BRLABEL) { \
+        if (ins->inst_i0->inst_c0) { \
+               x86_branch (code, cond, cfg->native_code + ins->inst_i0->inst_c0, sign); \
+        } else { \
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
+               x86_branch32 (code, cond, 0, sign); \
+        } \
+} else { \
+        if (ins->inst_true_bb->native_offset) { \
+               x86_branch (code, cond, cfg->native_code + ins->inst_true_bb->native_offset, sign); \
+        } else { \
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
+               if ((cfg->opt & MONO_OPT_BRANCH) && \
+                    x86_is_imm8 (ins->inst_true_bb->max_offset - cpos)) \
+                       x86_branch8 (code, cond, 0, sign); \
+                else \
+                       x86_branch32 (code, cond, 0, sign); \
+        } \
+}
+
+/* emit an exception if condition is fail */
+#define EMIT_COND_SYSTEM_EXCEPTION(cond,signed,exc_name)            \
+        do {                                                        \
+               mono_add_patch_info (cfg, code - cfg->native_code,   \
+                                   MONO_PATCH_INFO_EXC, exc_name);  \
+               x86_branch32 (code, cond, 0, signed);               \
+       } while (0); 
+
+#define EMIT_FPCOMPARE(code) do { \
+       x86_fcompp (code); \
+       x86_fnstsw (code); \
+} while (0); 
+
+static void
+peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins, *last_ins = NULL;
+       ins = bb->code;
+
+       while (ins) {
+
+               switch (ins->opcode) {
+               case OP_ICONST:
+                       /* reg = 0 -> XOR (reg, reg) */
+                       /* XOR sets cflags on x86, so we cant do it always */
+                       if (ins->inst_c0 == 0 && ins->next &&
+                           (ins->next->opcode == CEE_BR)) { 
+                               ins->opcode = CEE_XOR;
+                               ins->sreg1 = ins->dreg;
+                               ins->sreg2 = ins->dreg;
+                       }
+                       break;
+               case OP_MUL_IMM: 
+                       /* remove unnecessary multiplication with 1 */
+                       if (ins->inst_imm == 1) {
+                               if (ins->dreg != ins->sreg1) {
+                                       ins->opcode = OP_MOVE;
+                               } else {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               }
+                       }
+                       break;
+               case OP_COMPARE_IMM:
+                       /* OP_COMPARE_IMM (reg, 0) --> OP_X86_TEST_NULL (reg) */
+                       if (ins->inst_imm == 0 && ins->next &&
+                           (ins->next->opcode == CEE_BEQ || ins->next->opcode == CEE_BNE_UN ||
+                            ins->next->opcode == OP_CEQ)) {
+                               ins->opcode = OP_X86_TEST_NULL;
+                       }     
+                       break;
+               case OP_LOAD_MEMBASE:
+               case OP_LOADI4_MEMBASE:
+                       /* 
+                        * OP_STORE_MEMBASE_REG reg, offset(basereg) 
+                        * OP_LOAD_MEMBASE offset(basereg), reg
+                        */
+                       if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG 
+                                        || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
+                           ins->inst_basereg == last_ins->inst_destbasereg &&
+                           ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+
+                       /* 
+                        * Note: reg1 must be different from the basereg in the second load
+                        * OP_LOAD_MEMBASE offset(basereg), reg1
+                        * OP_LOAD_MEMBASE offset(basereg), reg2
+                        * -->
+                        * OP_LOAD_MEMBASE offset(basereg), reg1
+                        * OP_MOVE reg1, reg2
+                        */
+                       } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
+                                          || last_ins->opcode == OP_LOAD_MEMBASE) &&
+                             ins->inst_basereg != last_ins->dreg &&
+                             ins->inst_basereg == last_ins->inst_basereg &&
+                             ins->inst_offset == last_ins->inst_offset) {
+
+                               if (ins->dreg == last_ins->dreg) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->dreg;
+                               }
+
+                               //g_assert_not_reached ();
+
+#if 0
+                       /* 
+                        * OP_STORE_MEMBASE_IMM imm, offset(basereg) 
+                        * OP_LOAD_MEMBASE offset(basereg), reg
+                        * -->
+                        * OP_STORE_MEMBASE_IMM imm, offset(basereg) 
+                        * OP_ICONST reg, imm
+                        */
+                       } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
+                                               || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
+                                  ins->inst_basereg == last_ins->inst_destbasereg &&
+                                  ins->inst_offset == last_ins->inst_offset) {
+                               //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                               ins->opcode = OP_ICONST;
+                               ins->inst_c0 = last_ins->inst_imm;
+                               g_assert_not_reached (); // check this rule
+#endif
+                       }
+                       break;
+               case OP_LOADU1_MEMBASE:
+               case OP_LOADI1_MEMBASE:
+                 /*
+                  * FIXME: Missing explanation
+                  */
+                       if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
+                                       ins->inst_basereg == last_ins->inst_destbasereg &&
+                                       ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+                       }
+                       break;
+               case OP_LOADU2_MEMBASE:
+               case OP_LOADI2_MEMBASE:
+                 /*
+                  * FIXME: Missing explanation
+                  */
+                       if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
+                                       ins->inst_basereg == last_ins->inst_destbasereg &&
+                                       ins->inst_offset == last_ins->inst_offset) {
+                               if (ins->dreg == last_ins->sreg1) {
+                                       last_ins->next = ins->next;                             
+                                       ins = ins->next;                                
+                                       continue;
+                               } else {
+                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = last_ins->sreg1;
+                               }
+                       }
+                       break;
+               case CEE_CONV_I4:
+               case CEE_CONV_U4:
+               case OP_MOVE:
+                       /* 
+                        * OP_MOVE reg, reg 
+                        */
+                       if (ins->dreg == ins->sreg1) {
+                               if (last_ins)
+                                       last_ins->next = ins->next;                             
+                               ins = ins->next;
+                               continue;
+                       }
+                       /* 
+                        * OP_MOVE sreg, dreg 
+                        * OP_MOVE dreg, sreg
+                        */
+                       if (last_ins && last_ins->opcode == OP_MOVE &&
+                           ins->sreg1 == last_ins->dreg &&
+                           ins->dreg == last_ins->sreg1) {
+                               last_ins->next = ins->next;                             
+                               ins = ins->next;                                
+                               continue;
+                       }
+                       break;
+               }
+               last_ins = ins;
+               ins = ins->next;
+       }
+       bb->last_ins = last_ins;
+}
+
+static const int 
+branch_cc_table [] = {
+       X86_CC_EQ, X86_CC_GE, X86_CC_GT, X86_CC_LE, X86_CC_LT,
+       X86_CC_NE, X86_CC_GE, X86_CC_GT, X86_CC_LE, X86_CC_LT,
+       X86_CC_O, X86_CC_NO, X86_CC_C, X86_CC_NC
+};
+
+#define DEBUG(a) if (cfg->verbose_level > 1) a
+//#define DEBUG(a)
+#define reg_is_freeable(r) ((r) >= 0 && (r) <= 7 && X86_IS_CALLEE ((r)))
+
+typedef struct {
+       int born_in;
+       int killed_in;
+       int last_use;
+       int prev_use;
+} RegTrack;
+
+static const char*const * ins_spec = pentium;
+
+static void
+print_ins (int i, MonoInst *ins)
+{
+       const char *spec = ins_spec [ins->opcode];
+       g_print ("\t%-2d %s", i, mono_inst_name (ins->opcode));
+       if (spec [MONO_INST_DEST]) {
+               if (ins->dreg >= MONO_MAX_IREGS)
+                       g_print (" R%d <-", ins->dreg);
+               else
+                       g_print (" %s <-", mono_arch_regname (ins->dreg));
+       }
+       if (spec [MONO_INST_SRC1]) {
+               if (ins->sreg1 >= MONO_MAX_IREGS)
+                       g_print (" R%d", ins->sreg1);
+               else
+                       g_print (" %s", mono_arch_regname (ins->sreg1));
+       }
+       if (spec [MONO_INST_SRC2]) {
+               if (ins->sreg2 >= MONO_MAX_IREGS)
+                       g_print (" R%d", ins->sreg2);
+               else
+                       g_print (" %s", mono_arch_regname (ins->sreg2));
+       }
+       if (spec [MONO_INST_CLOB])
+               g_print (" clobbers: %c", spec [MONO_INST_CLOB]);
+       g_print ("\n");
+}
+
+static void
+print_regtrack (RegTrack *t, int num)
+{
+       int i;
+       char buf [32];
+       const char *r;
+       
+       for (i = 0; i < num; ++i) {
+               if (!t [i].born_in)
+                       continue;
+               if (i >= MONO_MAX_IREGS) {
+                       g_snprintf (buf, sizeof(buf), "R%d", i);
+                       r = buf;
+               } else
+                       r = mono_arch_regname (i);
+               g_print ("liveness: %s [%d - %d]\n", r, t [i].born_in, t[i].last_use);
+       }
+}
+
+typedef struct InstList InstList;
+
+struct InstList {
+       InstList *prev;
+       InstList *next;
+       MonoInst *data;
+};
+
+static inline InstList*
+inst_list_prepend (MonoMemPool *pool, InstList *list, MonoInst *data)
+{
+       InstList *item = mono_mempool_alloc (pool, sizeof (InstList));
+       item->data = data;
+       item->prev = NULL;
+       item->next = list;
+       if (list)
+               list->prev = item;
+       return item;
+}
+
+/*
+ * Force the spilling of the variable in the symbolic register 'reg'.
+ */
+static int
+get_register_force_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, int reg)
+{
+       MonoInst *load;
+       int i, sel, spill;
+       
+       sel = cfg->rs->iassign [reg];
+       /*i = cfg->rs->isymbolic [sel];
+       g_assert (i == reg);*/
+       i = reg;
+       spill = ++cfg->spill_count;
+       cfg->rs->iassign [i] = -spill - 1;
+       mono_regstate_free_int (cfg->rs, sel);
+       /* we need to create a spill var and insert a load to sel after the current instruction */
+       MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE);
+       load->dreg = sel;
+       load->inst_basereg = X86_EBP;
+       load->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (item->prev) {
+               while (ins->next != item->prev->data)
+                       ins = ins->next;
+       }
+       load->next = ins->next;
+       ins->next = load;
+       DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%ebp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel)));
+       i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+       g_assert (i == sel);
+
+       return sel;
+}
+
+static int
+get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32 regmask, int reg)
+{
+       MonoInst *load;
+       int i, sel, spill;
+
+       DEBUG (g_print ("start regmask to assign R%d: 0x%08x (R%d <- R%d R%d)\n", reg, regmask, ins->dreg, ins->sreg1, ins->sreg2));
+       /* exclude the registers in the current instruction */
+       if (reg != ins->sreg1 && (reg_is_freeable (ins->sreg1) || (ins->sreg1 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg1] >= 0))) {
+               if (ins->sreg1 >= MONO_MAX_IREGS)
+                       regmask &= ~ (1 << cfg->rs->iassign [ins->sreg1]);
+               else
+                       regmask &= ~ (1 << ins->sreg1);
+               DEBUG (g_print ("excluding sreg1 %s\n", mono_arch_regname (ins->sreg1)));
+       }
+       if (reg != ins->sreg2 && (reg_is_freeable (ins->sreg2) || (ins->sreg2 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg2] >= 0))) {
+               if (ins->sreg2 >= MONO_MAX_IREGS)
+                       regmask &= ~ (1 << cfg->rs->iassign [ins->sreg2]);
+               else
+                       regmask &= ~ (1 << ins->sreg2);
+               DEBUG (g_print ("excluding sreg2 %s %d\n", mono_arch_regname (ins->sreg2), ins->sreg2));
+       }
+       if (reg != ins->dreg && reg_is_freeable (ins->dreg)) {
+               regmask &= ~ (1 << ins->dreg);
+               DEBUG (g_print ("excluding dreg %s\n", mono_arch_regname (ins->dreg)));
+       }
+
+       DEBUG (g_print ("available regmask: 0x%08x\n", regmask));
+       g_assert (regmask); /* need at least a register we can free */
+       sel = -1;
+       /* we should track prev_use and spill the register that's farther */
+       for (i = 0; i < MONO_MAX_IREGS; ++i) {
+               if (regmask & (1 << i)) {
+                       sel = i;
+                       DEBUG (g_print ("selected register %s has assignment %d\n", mono_arch_regname (sel), cfg->rs->iassign [sel]));
+                       break;
+               }
+       }
+       i = cfg->rs->isymbolic [sel];
+       spill = ++cfg->spill_count;
+       cfg->rs->iassign [i] = -spill - 1;
+       mono_regstate_free_int (cfg->rs, sel);
+       /* we need to create a spill var and insert a load to sel after the current instruction */
+       MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE);
+       load->dreg = sel;
+       load->inst_basereg = X86_EBP;
+       load->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (item->prev) {
+               while (ins->next != item->prev->data)
+                       ins = ins->next;
+       }
+       load->next = ins->next;
+       ins->next = load;
+       DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%ebp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel)));
+       i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+       g_assert (i == sel);
+       
+       return sel;
+}
+
+static MonoInst*
+create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins)
+{
+       MonoInst *copy;
+       MONO_INST_NEW (cfg, copy, OP_MOVE);
+       copy->dreg = dest;
+       copy->sreg1 = src;
+       if (ins) {
+               copy->next = ins->next;
+               ins->next = copy;
+       }
+       DEBUG (g_print ("\tforced copy from %s to %s\n", mono_arch_regname (src), mono_arch_regname (dest)));
+       return copy;
+}
+
+static MonoInst*
+create_spilled_store (MonoCompile *cfg, int spill, int reg, int prev_reg, MonoInst *ins)
+{
+       MonoInst *store;
+       MONO_INST_NEW (cfg, store, OP_STORE_MEMBASE_REG);
+       store->sreg1 = reg;
+       store->inst_destbasereg = X86_EBP;
+       store->inst_offset = mono_spillvar_offset (cfg, spill);
+       if (ins) {
+               store->next = ins->next;
+               ins->next = store;
+       }
+       DEBUG (g_print ("SPILLED STORE (%d at 0x%08x(%%ebp)) R%d (from %s)\n", spill, store->inst_offset, prev_reg, mono_arch_regname (reg)));
+       return store;
+}
+
+static void
+insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert)
+{
+       MonoInst *prev;
+       if (item->next) {
+               prev = item->next->data;
+
+               while (prev->next != ins)
+                       prev = prev->next;
+               to_insert->next = ins;
+               prev->next = to_insert;
+       } else {
+               to_insert->next = ins;
+       }
+       /* 
+        * needed otherwise in the next instruction we can add an ins to the 
+        * end and that would get past this instruction.
+        */
+       item->data = to_insert; 
+}
+
+#if  0
+static int
+alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, guint32 allow_mask)
+{
+       int val = cfg->rs->iassign [sym_reg];
+       if (val < 0) {
+               int spill = 0;
+               if (val < -1) {
+                       /* the register gets spilled after this inst */
+                       spill = -val -1;
+               }
+               val = mono_regstate_alloc_int (cfg->rs, allow_mask);
+               if (val < 0)
+                       val = get_register_spilling (cfg, curinst, ins, allow_mask, sym_reg);
+               cfg->rs->iassign [sym_reg] = val;
+               /* add option to store before the instruction for src registers */
+               if (spill)
+                       create_spilled_store (cfg, spill, val, sym_reg, ins);
+       }
+       cfg->rs->isymbolic [val] = sym_reg;
+       return val;
+}
+#endif
+
+#include "cprop.c"
+
+/*
+ * Local register allocation.
+ * We first scan the list of instructions and we save the liveness info of
+ * each register (when the register is first used, when it's value is set etc.).
+ * We also reverse the list of instructions (in the InstList list) because assigning
+ * registers backwards allows for more tricks to be used.
+ */
+void
+mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins;
+       MonoRegState *rs = cfg->rs;
+       int i, val, fpcount;
+       RegTrack *reginfo, *reginfof;
+       RegTrack *reginfo1, *reginfo2, *reginfod;
+       InstList *tmp, *reversed = NULL;
+       const char *spec;
+       guint32 src1_mask, src2_mask, dest_mask;
+
+       if (!bb->code)
+               return;
+       rs->next_vireg = bb->max_ireg;
+       rs->next_vfreg = bb->max_freg;
+       mono_regstate_assign (rs);
+       reginfo = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vireg);
+       reginfof = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vfreg);
+       rs->ifree_mask = X86_CALLEE_REGS;
+
+       ins = bb->code;
+
+       if (cfg->opt & MONO_OPT_COPYPROP)
+               local_copy_prop (cfg, ins);
+       
+       i = 1;
+       fpcount = 0; /* FIXME: track fp stack utilization */
+       DEBUG (g_print ("LOCAL regalloc: basic block: %d\n", bb->block_num));
+       /* forward pass on the instructions to collect register liveness info */
+       while (ins) {
+               spec = ins_spec [ins->opcode];
+               DEBUG (print_ins (i, ins));
+               if (spec [MONO_INST_SRC1]) {
+                       if (spec [MONO_INST_SRC1] == 'f')
+                               reginfo1 = reginfof;
+                       else
+                               reginfo1 = reginfo;
+                       reginfo1 [ins->sreg1].prev_use = reginfo1 [ins->sreg1].last_use;
+                       reginfo1 [ins->sreg1].last_use = i;
+               } else {
+                       ins->sreg1 = -1;
+               }
+               if (spec [MONO_INST_SRC2]) {
+                       if (spec [MONO_INST_SRC2] == 'f')
+                               reginfo2 = reginfof;
+                       else
+                               reginfo2 = reginfo;
+                       reginfo2 [ins->sreg2].prev_use = reginfo2 [ins->sreg2].last_use;
+                       reginfo2 [ins->sreg2].last_use = i;
+               } else {
+                       ins->sreg2 = -1;
+               }
+               if (spec [MONO_INST_DEST]) {
+                       if (spec [MONO_INST_DEST] == 'f')
+                               reginfod = reginfof;
+                       else
+                               reginfod = reginfo;
+                       if (spec [MONO_INST_DEST] != 'b') /* it's not just a base register */
+                               reginfod [ins->dreg].killed_in = i;
+                       reginfod [ins->dreg].prev_use = reginfod [ins->dreg].last_use;
+                       reginfod [ins->dreg].last_use = i;
+                       if (reginfod [ins->dreg].born_in == 0 || reginfod [ins->dreg].born_in > i)
+                               reginfod [ins->dreg].born_in = i;
+                       if (spec [MONO_INST_DEST] == 'l') {
+                               /* result in eax:edx, the virtual register is allocated sequentially */
+                               reginfod [ins->dreg + 1].prev_use = reginfod [ins->dreg + 1].last_use;
+                               reginfod [ins->dreg + 1].last_use = i;
+                               if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i)
+                                       reginfod [ins->dreg + 1].born_in = i;
+                       }
+               } else {
+                       ins->dreg = -1;
+               }
+               reversed = inst_list_prepend (cfg->mempool, reversed, ins);
+               ++i;
+               ins = ins->next;
+       }
+
+       DEBUG (print_regtrack (reginfo, rs->next_vireg));
+       DEBUG (print_regtrack (reginfof, rs->next_vfreg));
+       tmp = reversed;
+       while (tmp) {
+               int prev_dreg, prev_sreg1, prev_sreg2;
+               dest_mask = src1_mask = src2_mask = X86_CALLEE_REGS;
+               --i;
+               ins = tmp->data;
+               spec = ins_spec [ins->opcode];
+               DEBUG (g_print ("processing:"));
+               DEBUG (print_ins (i, ins));
+               if (spec [MONO_INST_CLOB] == 's') {
+                       if (rs->ifree_mask & (1 << X86_ECX)) {
+                               DEBUG (g_print ("\tshortcut assignment of R%d to ECX\n", ins->sreg2));
+                               rs->iassign [ins->sreg2] = X86_ECX;
+                               rs->isymbolic [X86_ECX] = ins->sreg2;
+                               ins->sreg2 = X86_ECX;
+                               rs->ifree_mask &= ~ (1 << X86_ECX);
+                       } else {
+                               int need_ecx_spill = TRUE;
+                               /* 
+                                * we first check if src1/dreg is already assigned a register
+                                * and then we force a spill of the var assigned to ECX.
+                                */
+                               /* the destination register can't be ECX */
+                               dest_mask &= ~ (1 << X86_ECX);
+                               src1_mask &= ~ (1 << X86_ECX);
+                               val = rs->iassign [ins->dreg];
+                               /* 
+                                * the destination register is already assigned to ECX:
+                                * we need to allocate another register for it and then
+                                * copy from this to ECX.
+                                */
+                               if (val == X86_ECX && ins->dreg != ins->sreg2) {
+                                       int new_dest = mono_regstate_alloc_int (rs, dest_mask);
+                                       if (new_dest < 0)
+                                               new_dest = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg);
+                                       g_assert (new_dest >= 0);
+                                       ins->dreg = new_dest;
+                                       create_copy_ins (cfg, X86_ECX, new_dest, ins);
+                                       need_ecx_spill = FALSE;
+                                       /*DEBUG (g_print ("\tforced spill of R%d\n", ins->dreg));
+                                       val = get_register_force_spilling (cfg, tmp, ins, ins->dreg);
+                                       rs->iassign [ins->dreg] = val;
+                                       rs->isymbolic [val] = prev_dreg;
+                                       ins->dreg = val;*/
+                               }
+                               val = rs->iassign [ins->sreg1];
+                               if (val == X86_ECX) {
+                                       g_assert_not_reached ();
+                               } else if (val >= 0) {
+                                       /* 
+                                        * the first src reg was already assigned to a register,
+                                        * we need to copy it to the dest register because the 
+                                        * shift instruction clobbers the first operand.
+                                        */
+                                       MonoInst *copy = create_copy_ins (cfg, ins->dreg, val, NULL);
+                                       insert_before_ins (ins, tmp, copy);
+                               }
+                               val = rs->iassign [ins->sreg2];
+                               if (val >= 0 && val != X86_ECX) {
+                                       MonoInst *move = create_copy_ins (cfg, X86_ECX, val, NULL);
+                                       DEBUG (g_print ("\tmoved arg from R%d (%d) to ECX\n", val, ins->sreg2));
+                                       move->next = ins;
+                                       g_assert_not_reached ();
+                                       /* FIXME: where is move connected to the instruction list? */
+                                       //tmp->prev->data->next = move;
+                               }
+                               if (need_ecx_spill && !(rs->ifree_mask & (1 << X86_ECX))) {
+                                       DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_ECX]));
+                                       get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_ECX]);
+                                       mono_regstate_free_int (rs, X86_ECX);
+                               }
+                               /* force-set sreg2 */
+                               rs->iassign [ins->sreg2] = X86_ECX;
+                               rs->isymbolic [X86_ECX] = ins->sreg2;
+                               ins->sreg2 = X86_ECX;
+                               rs->ifree_mask &= ~ (1 << X86_ECX);
+                       }
+               } else if (spec [MONO_INST_CLOB] == 'd') { /* division */
+                       int dest_reg = X86_EAX;
+                       if (spec [MONO_INST_DEST] == 'd')
+                               dest_reg = X86_EDX; /* reminder */
+                       val = rs->iassign [ins->dreg];
+                       if (0 && val >= 0 && val != dest_reg && !(rs->ifree_mask & (1 << dest_reg))) {
+                               DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [dest_reg]));
+                               get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [dest_reg]);
+                               mono_regstate_free_int (rs, dest_reg);
+                       }
+                       if (val < 0) {
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       int spill = -val -1;
+                                       dest_mask = 1 << (dest_reg == X86_EAX? X86_EDX: X86_EAX);
+                                       prev_dreg = ins->dreg;
+                                       val = mono_regstate_alloc_int (rs, dest_mask);
+                                       if (val < 0)
+                                               val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg);
+                                       rs->iassign [ins->dreg] = val;
+                                       if (spill)
+                                               create_spilled_store (cfg, spill, val, prev_dreg, ins);
+                                       DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg));
+                                       rs->isymbolic [val] = prev_dreg;
+                                       ins->dreg = val;
+                                       if (val != dest_reg) { /* force a copy */
+                                               create_copy_ins (cfg, val, dest_reg, ins);
+                                       }
+                               } else {
+                                       DEBUG (g_print ("\tshortcut assignment of R%d to %s\n", ins->dreg, mono_arch_regname (dest_reg)));
+                                       rs->iassign [ins->dreg] = dest_reg;
+                                       rs->isymbolic [dest_reg] = ins->dreg;
+                                       ins->dreg = dest_reg;
+                                       rs->ifree_mask &= ~ (1 << dest_reg);
+                               }
+                       } else {
+                               //DEBUG (g_print ("dest reg in div assigned: %s\n", mono_arch_regname (val)));
+                               if (val != dest_reg) { /* force a copy */
+                                       create_copy_ins (cfg, val, dest_reg, ins);
+                               }
+                       }
+                       src1_mask = 1 << X86_EAX;
+                       src2_mask = 1 << X86_ECX;
+               }
+               if (spec [MONO_INST_DEST] == 'l') {
+                       if (!(rs->ifree_mask & (1 << X86_EAX))) {
+                               DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_EAX]));
+                               get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_EAX]);
+                               mono_regstate_free_int (rs, X86_EAX);
+                       }
+                       if (!(rs->ifree_mask & (1 << X86_EDX))) {
+                               DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_EDX]));
+                               get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_EDX]);
+                               mono_regstate_free_int (rs, X86_EDX);
+                       }
+               }
+               /* update for use with FP regs... */
+               if (spec [MONO_INST_DEST] != 'f' && ins->dreg >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->dreg];
+                       prev_dreg = ins->dreg;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               val = mono_regstate_alloc_int (rs, dest_mask);
+                               if (val < 0)
+                                       val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg);
+                               rs->iassign [ins->dreg] = val;
+                               if (spill)
+                                       create_spilled_store (cfg, spill, val, prev_dreg, ins);
+                       }
+                       DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg));
+                       rs->isymbolic [val] = prev_dreg;
+                       ins->dreg = val;
+                       if (spec [MONO_INST_DEST] == 'l') {
+                               int hreg = prev_dreg + 1;
+                               val = rs->iassign [hreg];
+                               if (val < 0) {
+                                       int spill = 0;
+                                       if (val < -1) {
+                                               /* the register gets spilled after this inst */
+                                               spill = -val -1;
+                                       }
+                                       val = mono_regstate_alloc_int (rs, dest_mask);
+                                       if (val < 0)
+                                               val = get_register_spilling (cfg, tmp, ins, dest_mask, hreg);
+                                       rs->iassign [hreg] = val;
+                                       if (spill)
+                                               create_spilled_store (cfg, spill, val, hreg, ins);
+                               }
+                               DEBUG (g_print ("\tassigned hreg %s to dest R%d\n", mono_arch_regname (val), hreg));
+                               rs->isymbolic [val] = hreg;
+                               /* FIXME:? ins->dreg = val; */
+                               if (ins->dreg == X86_EAX) {
+                                       if (val != X86_EDX)
+                                               create_copy_ins (cfg, val, X86_EDX, ins);
+                               } else if (ins->dreg == X86_EDX) {
+                                       if (val == X86_EAX) {
+                                               /* swap */
+                                               g_assert_not_reached ();
+                                       } else {
+                                               /* two forced copies */
+                                               create_copy_ins (cfg, val, X86_EDX, ins);
+                                               create_copy_ins (cfg, ins->dreg, X86_EAX, ins);
+                                       }
+                               } else {
+                                       if (val == X86_EDX) {
+                                               create_copy_ins (cfg, ins->dreg, X86_EAX, ins);
+                                       } else {
+                                               /* two forced copies */
+                                               create_copy_ins (cfg, val, X86_EDX, ins);
+                                               create_copy_ins (cfg, ins->dreg, X86_EAX, ins);
+                                       }
+                               }
+                               if (reg_is_freeable (val) && hreg >= 0 && reginfo [hreg].born_in >= i) {
+                                       DEBUG (g_print ("\tfreeable %s (R%d)\n", mono_arch_regname (val), hreg));
+                                       mono_regstate_free_int (rs, val);
+                               }
+                       } else if (spec [MONO_INST_DEST] == 'a' && ins->dreg != X86_EAX && spec [MONO_INST_CLOB] != 'd') {
+                               /* this instruction only outputs to EAX, need to copy */
+                               create_copy_ins (cfg, ins->dreg, X86_EAX, ins);
+                       } else if (spec [MONO_INST_DEST] == 'd' && ins->dreg != X86_EDX && spec [MONO_INST_CLOB] != 'd') {
+                               create_copy_ins (cfg, ins->dreg, X86_EDX, ins);
+                       }
+               } else {
+                       prev_dreg = -1;
+               }
+               if (spec [MONO_INST_DEST] != 'f' && reg_is_freeable (ins->dreg) && prev_dreg >= 0 && reginfo [prev_dreg].born_in >= i) {
+                       DEBUG (g_print ("\tfreeable %s (R%d) (born in %d)\n", mono_arch_regname (ins->dreg), prev_dreg, reginfo [prev_dreg].born_in));
+                       mono_regstate_free_int (rs, ins->dreg);
+               }
+               if (spec [MONO_INST_SRC1] != 'f' && ins->sreg1 >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->sreg1];
+                       prev_sreg1 = ins->sreg1;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               if (0 && ins->opcode == OP_MOVE) {
+                                       /* 
+                                        * small optimization: the dest register is already allocated
+                                        * but the src one is not: we can simply assign the same register
+                                        * here and peephole will get rid of the instruction later.
+                                        * This optimization may interfere with the clobbering handling:
+                                        * it removes a mov operation that will be added again to handle clobbering.
+                                        * There are also some other issues that should with make testjit.
+                                        */
+                                       mono_regstate_alloc_int (rs, 1 << ins->dreg);
+                                       val = rs->iassign [ins->sreg1] = ins->dreg;
+                                       //g_assert (val >= 0);
+                                       DEBUG (g_print ("\tfast assigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1));
+                               } else {
+                                       //g_assert (val == -1); /* source cannot be spilled */
+                                       val = mono_regstate_alloc_int (rs, src1_mask);
+                                       if (val < 0)
+                                               val = get_register_spilling (cfg, tmp, ins, src1_mask, ins->sreg1);
+                                       rs->iassign [ins->sreg1] = val;
+                                       DEBUG (g_print ("\tassigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1));
+                               }
+                               if (spill) {
+                                       MonoInst *store = create_spilled_store (cfg, spill, val, prev_sreg1, NULL);
+                                       insert_before_ins (ins, tmp, store);
+                               }
+                       }
+                       rs->isymbolic [val] = prev_sreg1;
+                       ins->sreg1 = val;
+               } else {
+                       prev_sreg1 = -1;
+               }
+               /* handle clobbering of sreg1 */
+               if ((spec [MONO_INST_CLOB] == '1' || spec [MONO_INST_CLOB] == 's') && ins->dreg != ins->sreg1) {
+                       MonoInst *copy = create_copy_ins (cfg, ins->dreg, ins->sreg1, NULL);
+                       DEBUG (g_print ("\tneed to copy sreg1 %s to dreg %s\n", mono_arch_regname (ins->sreg1), mono_arch_regname (ins->dreg)));
+                       if (ins->sreg2 == -1 || spec [MONO_INST_CLOB] == 's') {
+                               /* note: the copy is inserted before the current instruction! */
+                               insert_before_ins (ins, tmp, copy);
+                               /* we set sreg1 to dest as well */
+                               prev_sreg1 = ins->sreg1 = ins->dreg;
+                       } else {
+                               /* inserted after the operation */
+                               copy->next = ins->next;
+                               ins->next = copy;
+                       }
+               }
+               if (spec [MONO_INST_SRC2] != 'f' && ins->sreg2 >= MONO_MAX_IREGS) {
+                       val = rs->iassign [ins->sreg2];
+                       prev_sreg2 = ins->sreg2;
+                       if (val < 0) {
+                               int spill = 0;
+                               if (val < -1) {
+                                       /* the register gets spilled after this inst */
+                                       spill = -val -1;
+                               }
+                               val = mono_regstate_alloc_int (rs, src2_mask);
+                               if (val < 0)
+                                       val = get_register_spilling (cfg, tmp, ins, src2_mask, ins->sreg2);
+                               rs->iassign [ins->sreg2] = val;
+                               DEBUG (g_print ("\tassigned sreg2 %s to R%d\n", mono_arch_regname (val), ins->sreg2));
+                               if (spill)
+                                       create_spilled_store (cfg, spill, val, prev_sreg2, ins);
+                       }
+                       rs->isymbolic [val] = prev_sreg2;
+                       ins->sreg2 = val;
+                       if (spec [MONO_INST_CLOB] == 's' && ins->sreg2 != X86_ECX) {
+                               DEBUG (g_print ("\tassigned sreg2 %s to R%d, but ECX is needed (R%d)\n", mono_arch_regname (val), ins->sreg2, rs->iassign [X86_ECX]));
+                       }
+               } else {
+                       prev_sreg2 = -1;
+               }
+
+               if (spec [MONO_INST_CLOB] == 'c') {
+                       int j, s;
+                       guint32 clob_mask = X86_CALLEE_REGS;
+                       for (j = 0; j < MONO_MAX_IREGS; ++j) {
+                               s = 1 << j;
+                               if ((clob_mask & s) && !(rs->ifree_mask & s) && j != ins->sreg1) {
+                                       //g_warning ("register %s busy at call site\n", mono_arch_regname (j));
+                               }
+                       }
+               }
+               /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) {
+                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg1)));
+                       mono_regstate_free_int (rs, ins->sreg1);
+               }
+               if (reg_is_freeable (ins->sreg2) && prev_sreg2 >= 0 && reginfo [prev_sreg2].born_in >= i) {
+                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2)));
+                       mono_regstate_free_int (rs, ins->sreg2);
+               }*/
+               
+               //DEBUG (print_ins (i, ins));
+               /* this may result from a insert_before call */
+               if (!tmp->next)
+                       bb->code = tmp->data;
+               tmp = tmp->next;
+       }
+}
+
+static unsigned char*
+emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int size, gboolean is_signed)
+{
+       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
+       x86_fnstcw_membase(code, X86_ESP, 0);
+       x86_mov_reg_membase (code, dreg, X86_ESP, 0, 2);
+       x86_alu_reg_imm (code, X86_OR, dreg, 0xc00);
+       x86_mov_membase_reg (code, X86_ESP, 2, dreg, 2);
+       x86_fldcw_membase (code, X86_ESP, 2);
+       if (size == 8) {
+               x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+               x86_fist_pop_membase (code, X86_ESP, 0, TRUE);
+               x86_pop_reg (code, dreg);
+               /* FIXME: need the high register 
+                * x86_pop_reg (code, dreg_high);
+                */
+       } else {
+               x86_push_reg (code, X86_EAX); // SP = SP - 4
+               x86_fist_pop_membase (code, X86_ESP, 0, FALSE);
+               x86_pop_reg (code, dreg);
+       }
+       x86_fldcw_membase (code, X86_ESP, 0);
+       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+
+       if (size == 1)
+               x86_widen_reg (code, dreg, dreg, is_signed, FALSE);
+       else if (size == 2)
+               x86_widen_reg (code, dreg, dreg, is_signed, TRUE);
+       return code;
+}
+
+static unsigned char*
+mono_emit_stack_alloc (guchar *code, MonoInst* tree)
+{
+       int sreg = tree->sreg1;
+#ifdef PLATFORM_WIN32
+       guint8* br[5];
+
+       /*
+        * Under Windows:
+        * If requested stack size is larger than one page,
+        * perform stack-touch operation
+        */
+       /*
+        * Generate stack probe code.
+        * Under Windows, it is necessary to allocate one page at a time,
+        * "touching" stack after each successful sub-allocation. This is
+        * because of the way stack growth is implemented - there is a
+        * guard page before the lowest stack page that is currently commited.
+        * Stack normally grows sequentially so OS traps access to the
+        * guard page and commits more pages when needed.
+        */
+       x86_test_reg_imm (code, sreg, ~0xFFF);
+       br[0] = code; x86_branch8 (code, X86_CC_Z, 0, FALSE);
+
+       br[2] = code; /* loop */
+       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x1000);
+       x86_test_membase_reg (code, X86_ESP, 0, X86_ESP);
+       x86_alu_reg_imm (code, X86_SUB, sreg, 0x1000);
+       x86_alu_reg_imm (code, X86_CMP, sreg, 0x1000);
+       br[3] = code; x86_branch8 (code, X86_CC_AE, 0, FALSE);
+       x86_patch (br[3], br[2]);
+       x86_test_reg_reg (code, sreg, sreg);
+       br[4] = code; x86_branch8 (code, X86_CC_Z, 0, FALSE);
+       x86_alu_reg_reg (code, X86_SUB, X86_ESP, sreg);
+
+       br[1] = code; x86_jump8 (code, 0);
+
+       x86_patch (br[0], code);
+       x86_alu_reg_reg (code, X86_SUB, X86_ESP, sreg);
+       x86_patch (br[1], code);
+       x86_patch (br[4], code);
+#else /* PLATFORM_WIN32 */
+       x86_alu_reg_reg (code, X86_SUB, X86_ESP, tree->sreg1);
+#endif
+       if (tree->flags & MONO_INST_INIT) {
+               int offset = 0;
+               if (tree->dreg != X86_EAX && sreg != X86_EAX) {
+                       x86_push_reg (code, X86_EAX);
+                       offset += 4;
+               }
+               if (tree->dreg != X86_ECX && sreg != X86_ECX) {
+                       x86_push_reg (code, X86_ECX);
+                       offset += 4;
+               }
+               if (tree->dreg != X86_EDI && sreg != X86_EDI) {
+                       x86_push_reg (code, X86_EDI);
+                       offset += 4;
+               }
+               
+               x86_shift_reg_imm (code, X86_SHR, sreg, 2);
+               if (sreg != X86_ECX)
+                       x86_mov_reg_reg (code, X86_ECX, sreg, 4);
+               x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
+                               
+               x86_lea_membase (code, X86_EDI, X86_ESP, offset);
+               x86_cld (code);
+               x86_prefix (code, X86_REP_PREFIX);
+               x86_stosl (code);
+               
+               if (tree->dreg != X86_EDI && sreg != X86_EDI)
+                       x86_pop_reg (code, X86_EDI);
+               if (tree->dreg != X86_ECX && sreg != X86_ECX)
+                       x86_pop_reg (code, X86_ECX);
+               if (tree->dreg != X86_EAX && sreg != X86_EAX)
+                       x86_pop_reg (code, X86_EAX);
+       }
+       return code;
+}
+
+#define REAL_PRINT_REG(text,reg) \
+mono_assert (reg >= 0); \
+x86_push_reg (code, X86_EAX); \
+x86_push_reg (code, X86_EDX); \
+x86_push_reg (code, X86_ECX); \
+x86_push_reg (code, reg); \
+x86_push_imm (code, reg); \
+x86_push_imm (code, text " %d %p\n"); \
+x86_mov_reg_imm (code, X86_EAX, printf); \
+x86_call_reg (code, X86_EAX); \
+x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3*4); \
+x86_pop_reg (code, X86_ECX); \
+x86_pop_reg (code, X86_EDX); \
+x86_pop_reg (code, X86_EAX);
+
+void
+mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+       MonoInst *ins;
+       MonoCallInst *call;
+       guint offset;
+       guint8 *code = cfg->native_code + cfg->code_len;
+       MonoInst *last_ins = NULL;
+       guint last_offset = 0;
+       int max_len, cpos;
+
+       if (cfg->opt & MONO_OPT_PEEPHOLE)
+               peephole_pass (cfg, bb);
+
+#if 0
+       /* 
+        * various stratgies to align BBs. Using real loop detection or simply
+        * aligning every block leads to more consistent benchmark results,
+        * but usually slows down the code
+        * we should do the alignment outside this function or we should adjust
+        * bb->native offset as well or the code is effectively slowed down!
+        */
+       /* align all blocks */
+//     if ((pad = (cfg->code_len & (align - 1)))) {
+       /* poor man loop start detection */
+//     if (bb->code && bb->in_count && bb->in_bb [0]->cil_code > bb->cil_code && (pad = (cfg->code_len & (align - 1)))) {
+       /* consider real loop detection and nesting level */
+//     if (bb->loop_blocks && bb->nesting < 3 && (pad = (cfg->code_len & (align - 1)))) {
+       /* consider real loop detection */
+       if (bb->loop_blocks && (pad = (cfg->code_len & (align - 1)))) {
+               pad = align - pad;
+               x86_padding (code, pad);
+               cfg->code_len += pad;
+               bb->native_offset = cfg->code_len;
+       }
+#endif
+
+       if (cfg->verbose_level > 2)
+               g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
+
+       cpos = bb->max_offset;
+
+       if (mono_trace_coverage) {
+               MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
+               g_assert (!mono_compile_aot);
+               cpos += 6;
+
+               // fixme: make this work with inlining
+               g_assert_not_reached ();
+               //if (bb->cil_code)
+               //cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
+               /* this is not thread save, but good enough */
+               /* fixme: howto handle overflows? */
+               x86_inc_mem (code, &cov->data [bb->dfn].count); 
+       }
+
+       offset = code - cfg->native_code;
+
+       ins = bb->code;
+       while (ins) {
+               offset = code - cfg->native_code;
+
+               max_len = ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN];
+
+               if (offset > (cfg->code_size - max_len - 16)) {
+                       cfg->code_size *= 2;
+                       cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+                       code = cfg->native_code + offset;
+                       mono_jit_stats.code_reallocs++;
+               }
+
+               mono_debug_record_line_number (cfg, ins, offset);
+
+               switch (ins->opcode) {
+               case OP_STOREI1_MEMBASE_IMM:
+                       x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 1);
+                       break;
+               case OP_STOREI2_MEMBASE_IMM:
+                       x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 2);
+                       break;
+               case OP_STORE_MEMBASE_IMM:
+               case OP_STOREI4_MEMBASE_IMM:
+                       x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 4);
+                       break;
+               case OP_STOREI1_MEMBASE_REG:
+                       x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 1);
+                       break;
+               case OP_STOREI2_MEMBASE_REG:
+                       x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 2);
+                       break;
+               case OP_STORE_MEMBASE_REG:
+               case OP_STOREI4_MEMBASE_REG:
+                       x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 4);
+                       break;
+               case CEE_LDIND_I:
+               case CEE_LDIND_I4:
+               case CEE_LDIND_U4:
+                       x86_mov_reg_mem (code, ins->dreg, ins->inst_p0, 4);
+                       break;
+               case OP_LOADU4_MEM:
+                       x86_mov_reg_imm (code, ins->dreg, ins->inst_p0);
+                       x86_mov_reg_membase (code, ins->dreg, ins->dreg, 0, 4);
+                       break;
+               case OP_LOAD_MEMBASE:
+               case OP_LOADI4_MEMBASE:
+               case OP_LOADU4_MEMBASE:
+                       x86_mov_reg_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, 4);
+                       break;
+               case OP_LOADU1_MEMBASE:
+                       x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, FALSE, FALSE);
+                       break;
+               case OP_LOADI1_MEMBASE:
+                       x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, TRUE, FALSE);
+                       break;
+               case OP_LOADU2_MEMBASE:
+                       x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, FALSE, TRUE);
+                       break;
+               case OP_LOADI2_MEMBASE:
+                       x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, TRUE, TRUE);
+                       break;
+               case CEE_CONV_I1:
+                       x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, FALSE);
+                       break;
+               case CEE_CONV_I2:
+                       x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, TRUE);
+                       break;
+               case CEE_CONV_U1:
+                       x86_widen_reg (code, ins->dreg, ins->sreg1, FALSE, FALSE);
+                       break;
+               case CEE_CONV_U2:
+                       x86_widen_reg (code, ins->dreg, ins->sreg1, FALSE, TRUE);
+                       break;
+               case OP_COMPARE:
+                       x86_alu_reg_reg (code, X86_CMP, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_COMPARE_IMM:
+                       x86_alu_reg_imm (code, X86_CMP, ins->sreg1, ins->inst_imm);
+                       break;
+               case OP_X86_COMPARE_MEMBASE_REG:
+                       x86_alu_membase_reg (code, X86_CMP, ins->inst_basereg, ins->inst_offset, ins->sreg2);
+                       break;
+               case OP_X86_COMPARE_MEMBASE_IMM:
+                       x86_alu_membase_imm (code, X86_CMP, ins->inst_basereg, ins->inst_offset, ins->inst_imm);
+                       break;
+               case OP_X86_COMPARE_REG_MEMBASE:
+                       x86_alu_reg_membase (code, X86_CMP, ins->sreg1, ins->sreg2, ins->inst_offset);
+                       break;
+               case OP_X86_TEST_NULL:
+                       x86_test_reg_reg (code, ins->sreg1, ins->sreg1);
+                       break;
+               case OP_X86_ADD_MEMBASE_IMM:
+                       x86_alu_membase_imm (code, X86_ADD, ins->inst_basereg, ins->inst_offset, ins->inst_imm);
+                       break;
+               case OP_X86_SUB_MEMBASE_IMM:
+                       x86_alu_membase_imm (code, X86_SUB, ins->inst_basereg, ins->inst_offset, ins->inst_imm);
+                       break;
+               case OP_X86_INC_MEMBASE:
+                       x86_inc_membase (code, ins->inst_basereg, ins->inst_offset);
+                       break;
+               case OP_X86_INC_REG:
+                       x86_inc_reg (code, ins->dreg);
+                       break;
+               case OP_X86_DEC_MEMBASE:
+                       x86_dec_membase (code, ins->inst_basereg, ins->inst_offset);
+                       break;
+               case OP_X86_DEC_REG:
+                       x86_dec_reg (code, ins->dreg);
+                       break;
+               case CEE_BREAK:
+                       x86_breakpoint (code);
+                       break;
+               case OP_ADDCC:
+               case CEE_ADD:
+                       x86_alu_reg_reg (code, X86_ADD, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_ADC:
+                       x86_alu_reg_reg (code, X86_ADC, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_ADD_IMM:
+                       x86_alu_reg_imm (code, X86_ADD, ins->dreg, ins->inst_imm);
+                       break;
+               case OP_ADC_IMM:
+                       x86_alu_reg_imm (code, X86_ADC, ins->dreg, ins->inst_imm);
+                       break;
+               case OP_SUBCC:
+               case CEE_SUB:
+                       x86_alu_reg_reg (code, X86_SUB, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_SBB:
+                       x86_alu_reg_reg (code, X86_SBB, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_SUB_IMM:
+                       x86_alu_reg_imm (code, X86_SUB, ins->dreg, ins->inst_imm);
+                       break;
+               case OP_SBB_IMM:
+                       x86_alu_reg_imm (code, X86_SBB, ins->dreg, ins->inst_imm);
+                       break;
+               case CEE_AND:
+                       x86_alu_reg_reg (code, X86_AND, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_AND_IMM:
+                       x86_alu_reg_imm (code, X86_AND, ins->sreg1, ins->inst_imm);
+                       break;
+               case CEE_DIV:
+                       x86_cdq (code);
+                       x86_div_reg (code, ins->sreg2, TRUE);
+                       break;
+               case CEE_DIV_UN:
+                       x86_alu_reg_reg (code, X86_XOR, X86_EDX, X86_EDX);
+                       x86_div_reg (code, ins->sreg2, FALSE);
+                       break;
+               case OP_DIV_IMM:
+                       x86_mov_reg_imm (code, ins->sreg2, ins->inst_imm);
+                       x86_cdq (code);
+                       x86_div_reg (code, ins->sreg2, TRUE);
+                       break;
+               case CEE_REM:
+                       x86_cdq (code);
+                       x86_div_reg (code, ins->sreg2, TRUE);
+                       break;
+               case CEE_REM_UN:
+                       x86_alu_reg_reg (code, X86_XOR, X86_EDX, X86_EDX);
+                       x86_div_reg (code, ins->sreg2, FALSE);
+                       break;
+               case OP_REM_IMM:
+                       x86_mov_reg_imm (code, ins->sreg2, ins->inst_imm);
+                       x86_cdq (code);
+                       x86_div_reg (code, ins->sreg2, TRUE);
+                       break;
+               case CEE_OR:
+                       x86_alu_reg_reg (code, X86_OR, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_OR_IMM:
+                       x86_alu_reg_imm (code, X86_OR, ins->sreg1, ins->inst_imm);
+                       break;
+               case CEE_XOR:
+                       x86_alu_reg_reg (code, X86_XOR, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_XOR_IMM:
+                       x86_alu_reg_imm (code, X86_XOR, ins->sreg1, ins->inst_imm);
+                       break;
+               case CEE_SHL:
+                       g_assert (ins->sreg2 == X86_ECX);
+                       x86_shift_reg (code, X86_SHL, ins->dreg);
+                       break;
+               case CEE_SHR:
+                       g_assert (ins->sreg2 == X86_ECX);
+                       x86_shift_reg (code, X86_SAR, ins->dreg);
+                       break;
+               case OP_SHR_IMM:
+                       x86_shift_reg_imm (code, X86_SAR, ins->dreg, ins->inst_imm);
+                       break;
+               case OP_SHR_UN_IMM:
+                       x86_shift_reg_imm (code, X86_SHR, ins->dreg, ins->inst_imm);
+                       break;
+               case CEE_SHR_UN:
+                       g_assert (ins->sreg2 == X86_ECX);
+                       x86_shift_reg (code, X86_SHR, ins->dreg);
+                       break;
+               case OP_SHL_IMM:
+                       x86_shift_reg_imm (code, X86_SHL, ins->dreg, ins->inst_imm);
+                       break;
+               case CEE_NOT:
+                       x86_not_reg (code, ins->sreg1);
+                       break;
+               case CEE_NEG:
+                       x86_neg_reg (code, ins->sreg1);
+                       break;
+               case CEE_MUL:
+                       x86_imul_reg_reg (code, ins->sreg1, ins->sreg2);
+                       break;
+               case OP_MUL_IMM:
+                       x86_imul_reg_reg_imm (code, ins->dreg, ins->sreg1, ins->inst_imm);
+                       break;
+               case CEE_MUL_OVF:
+                       x86_imul_reg_reg (code, ins->sreg1, ins->sreg2);
+                       EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException");
+                       break;
+               case CEE_MUL_OVF_UN: {
+                       /* the mul operation and the exception check should most likely be split */
+                       int non_eax_reg, saved_eax = FALSE, saved_edx = FALSE;
+                       /*g_assert (ins->sreg2 == X86_EAX);
+                       g_assert (ins->dreg == X86_EAX);*/
+                       if (ins->sreg2 == X86_EAX) {
+                               non_eax_reg = ins->sreg1;
+                       } else if (ins->sreg1 == X86_EAX) {
+                               non_eax_reg = ins->sreg2;
+                       } else {
+                               /* no need to save since we're going to store to it anyway */
+                               if (ins->dreg != X86_EAX) {
+                                       saved_eax = TRUE;
+                                       x86_push_reg (code, X86_EAX);
+                               }
+                               x86_mov_reg_reg (code, X86_EAX, ins->sreg1, 4);
+                               non_eax_reg = ins->sreg2;
+                       }
+                       if (ins->dreg == X86_EDX) {
+                               if (!saved_eax) {
+                                       saved_eax = TRUE;
+                                       x86_push_reg (code, X86_EAX);
+                               }
+                       } else if (ins->dreg != X86_EAX) {
+                               saved_edx = TRUE;
+                               x86_push_reg (code, X86_EDX);
+                       }
+                       x86_mul_reg (code, non_eax_reg, FALSE);
+                       /* save before the check since pop and mov don't change the flags */
+                       if (saved_edx)
+                               x86_pop_reg (code, X86_EDX);
+                       if (saved_eax)
+                               x86_pop_reg (code, X86_EAX);
+                       if (ins->dreg != X86_EAX)
+                               x86_mov_reg_reg (code, ins->dreg, X86_EAX, 4);
+                       EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException");
+                       break;
+               }
+               case OP_ICONST:
+                       x86_mov_reg_imm (code, ins->dreg, ins->inst_c0);
+                       break;
+               case OP_AOTCONST:
+                       mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
+                       x86_mov_reg_imm (code, ins->dreg, 0);
+                       break;
+               case CEE_CONV_I4:
+               case CEE_CONV_U4:
+               case OP_MOVE:
+                       x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4);
+                       break;
+               case CEE_JMP: {
+                       /*
+                        * Note: this 'frame destruction' logic is useful for tail calls, too.
+                        */
+                       int pos = -4;
+                       if (cfg->used_int_regs & (1 << X86_EBX)) {
+                               x86_mov_reg_membase (code, X86_EBX, X86_EBP, pos, 4);
+                               pos -= 4;
+                       }
+                       if (cfg->used_int_regs & (1 << X86_EDI)) {
+                               x86_mov_reg_membase (code, X86_EDI, X86_EBP, pos, 4);
+                               pos -= 4;
+                       }
+                       if (cfg->used_int_regs & (1 << X86_ESI)) {
+                               x86_mov_reg_membase (code, X86_ESI, X86_EBP, pos, 4);
+                               pos -= 4;
+                       }
+                       /* restore ESP/EBP */
+                       x86_leave (code);
+                       offset = code - cfg->native_code;
+                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, ins->inst_p0);
+                       x86_jump32 (code, 0);
+                       break;
+               }
+               case OP_CHECK_THIS:
+                       /* ensure ins->sreg1 is not NULL */
+                       x86_alu_membase_imm (code, X86_CMP, ins->sreg1, 0, 0);
+                       break;
+               case OP_FCALL:
+               case OP_LCALL:
+               case OP_VCALL:
+               case OP_VOIDCALL:
+               case CEE_CALL:
+                       call = (MonoCallInst*)ins;
+                       if (ins->flags & MONO_INST_HAS_METHOD)
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
+                       else {
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
+                       }
+                       x86_call_code (code, 0);
+                       if (call->stack_usage)
+                               x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage);
+                       break;
+               case OP_FCALL_REG:
+               case OP_LCALL_REG:
+               case OP_VCALL_REG:
+               case OP_VOIDCALL_REG:
+               case OP_CALL_REG:
+                       call = (MonoCallInst*)ins;
+                       x86_call_reg (code, ins->sreg1);
+                       if (call->stack_usage)
+                               x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage);
+                       break;
+               case OP_FCALL_MEMBASE:
+               case OP_LCALL_MEMBASE:
+               case OP_VCALL_MEMBASE:
+               case OP_VOIDCALL_MEMBASE:
+               case OP_CALL_MEMBASE:
+                       call = (MonoCallInst*)ins;
+                       x86_call_membase (code, ins->sreg1, ins->inst_offset);
+                       if (call->stack_usage)
+                               x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage);
+                       break;
+               case OP_OUTARG:
+               case OP_X86_PUSH:
+                       x86_push_reg (code, ins->sreg1);
+                       break;
+               case OP_X86_PUSH_IMM:
+                       x86_push_imm (code, ins->inst_imm);
+                       break;
+               case OP_X86_PUSH_MEMBASE:
+                       x86_push_membase (code, ins->inst_basereg, ins->inst_offset);
+                       break;
+               case OP_X86_PUSH_OBJ: 
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, ins->inst_imm);
+                       x86_push_reg (code, X86_EDI);
+                       x86_push_reg (code, X86_ESI);
+                       x86_push_reg (code, X86_ECX);
+                       if (ins->inst_offset)
+                               x86_lea_membase (code, X86_ESI, ins->inst_basereg, ins->inst_offset);
+                       else
+                               x86_mov_reg_reg (code, X86_ESI, ins->inst_basereg, 4);
+                       x86_lea_membase (code, X86_EDI, X86_ESP, 12);
+                       x86_mov_reg_imm (code, X86_ECX, (ins->inst_imm >> 2));
+                       x86_cld (code);
+                       x86_prefix (code, X86_REP_PREFIX);
+                       x86_movsd (code);
+                       x86_pop_reg (code, X86_ECX);
+                       x86_pop_reg (code, X86_ESI);
+                       x86_pop_reg (code, X86_EDI);
+                       break;
+               case OP_X86_LEA:
+                       x86_lea_memindex (code, ins->dreg, ins->sreg1, ins->inst_imm, ins->sreg2, ins->unused);
+                       break;
+               case OP_X86_XCHG:
+                       x86_xchg_reg_reg (code, ins->sreg1, ins->sreg2, 4);
+                       break;
+               case OP_LOCALLOC:
+                       /* keep alignment */
+                       x86_alu_reg_imm (code, X86_ADD, ins->sreg1, MONO_ARCH_FRAME_ALIGNMENT - 1);
+                       x86_alu_reg_imm (code, X86_AND, ins->sreg1, ~(MONO_ARCH_FRAME_ALIGNMENT - 1));
+                       code = mono_emit_stack_alloc (code, ins);
+                       x86_mov_reg_reg (code, ins->dreg, X86_ESP, 4);
+                       break;
+               case CEE_RET:
+                       x86_ret (code);
+                       break;
+               case CEE_THROW: {
+                       x86_push_reg (code, ins->sreg1);
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+                                            (gpointer)"mono_arch_throw_exception");
+                       x86_call_code (code, 0);
+                       break;
+               }
+               case OP_ENDFILTER:
+                       if (ins->sreg1 != X86_EAX)
+                               x86_mov_reg_reg (code, X86_EAX, ins->sreg1, 4);
+                       x86_mov_reg_membase (code, X86_ESP, X86_EBP, mono_exc_esp_offset, 4);
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
+                       x86_ret (code);
+                       break;
+               case CEE_ENDFINALLY:
+                       /* 
+                        * restore ESP - which can be modified when we allocate value types in the filter
+                        */
+                       x86_mov_reg_membase (code, X86_ESP, X86_EBP, mono_exc_esp_offset, 4);
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
+                       x86_ret (code);
+                       break;
+               case OP_HANDLER: 
+                       x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
+                       x86_call_imm (code, 0);
+                       break;
+               case OP_LABEL:
+                       ins->inst_c0 = code - cfg->native_code;
+                       break;
+               case CEE_BR:
+                       //g_print ("target: %p, next: %p, curr: %p, last: %p\n", ins->inst_target_bb, bb->next_bb, ins, bb->last_ins);
+                       //if ((ins->inst_target_bb == bb->next_bb) && ins == bb->last_ins)
+                       //break;
+                       if (ins->flags & MONO_INST_BRLABEL) {
+                               if (ins->inst_i0->inst_c0) {
+                                       x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
+                               } else {
+                                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
+                                       x86_jump32 (code, 0);
+                               }
+                       } else {
+                               if (ins->inst_target_bb->native_offset) {
+                                       x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset); 
+                               } else {
+                                       mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
+                                       if ((cfg->opt & MONO_OPT_BRANCH) &&
+                                           x86_is_imm8 (ins->inst_target_bb->max_offset - cpos))
+                                               x86_jump8 (code, 0);
+                                       else 
+                                               x86_jump32 (code, 0);
+                               } 
+                       }
+                       break;
+               case OP_BR_REG:
+                       x86_jump_reg (code, ins->sreg1);
+                       break;
+               case OP_CEQ:
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+                       break;
+               case OP_CLT:
+                       x86_set_reg (code, X86_CC_LT, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+                       break;
+               case OP_CLT_UN:
+                       x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+                       break;
+               case OP_CGT:
+                       x86_set_reg (code, X86_CC_GT, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+                       break;
+               case OP_CGT_UN:
+                       x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+                       break;
+               case OP_COND_EXC_EQ:
+               case OP_COND_EXC_NE_UN:
+               case OP_COND_EXC_LT:
+               case OP_COND_EXC_LT_UN:
+               case OP_COND_EXC_GT:
+               case OP_COND_EXC_GT_UN:
+               case OP_COND_EXC_GE:
+               case OP_COND_EXC_GE_UN:
+               case OP_COND_EXC_LE:
+               case OP_COND_EXC_LE_UN:
+               case OP_COND_EXC_OV:
+               case OP_COND_EXC_NO:
+               case OP_COND_EXC_C:
+               case OP_COND_EXC_NC:
+                       EMIT_COND_SYSTEM_EXCEPTION (branch_cc_table [ins->opcode - OP_COND_EXC_EQ], 
+                                                   (ins->opcode < OP_COND_EXC_NE_UN), ins->inst_p1);
+                       break;
+               case CEE_BEQ:
+               case CEE_BNE_UN:
+               case CEE_BLT:
+               case CEE_BLT_UN:
+               case CEE_BGT:
+               case CEE_BGT_UN:
+               case CEE_BGE:
+               case CEE_BGE_UN:
+               case CEE_BLE:
+               case CEE_BLE_UN:
+                       EMIT_COND_BRANCH (ins, branch_cc_table [ins->opcode - CEE_BEQ], (ins->opcode < CEE_BNE_UN));
+                       break;
+
+               /* floating point opcodes */
+               case OP_R8CONST: {
+                       double d = *(double *)ins->inst_p0;
+
+                       if (d == 0.0) {
+                               x86_fldz (code);
+                       } else if (d == 1.0) {
+                               x86_fld1 (code);
+                       } else {
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_R8, ins->inst_p0);
+                               x86_fld (code, NULL, TRUE);
+                       }
+                       break;
+               }
+               case OP_R4CONST: {
+                       float f = *(float *)ins->inst_p0;
+
+                       if (f == 0.0) {
+                               x86_fldz (code);
+                       } else if (f == 1.0) {
+                               x86_fld1 (code);
+                       } else {
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_R4, ins->inst_p0);
+                               x86_fld (code, NULL, FALSE);
+                       }
+                       break;
+               }
+               case OP_STORER8_MEMBASE_REG:
+                       x86_fst_membase (code, ins->inst_destbasereg, ins->inst_offset, TRUE, TRUE);
+                       break;
+               case OP_LOADR8_MEMBASE:
+                       x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, TRUE);
+                       break;
+               case OP_STORER4_MEMBASE_REG:
+                       x86_fst_membase (code, ins->inst_destbasereg, ins->inst_offset, FALSE, TRUE);
+                       break;
+               case OP_LOADR4_MEMBASE:
+                       x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, FALSE);
+                       break;
+               case CEE_CONV_R4: /* FIXME: change precision */
+               case CEE_CONV_R8:
+                       x86_push_reg (code, ins->sreg1);
+                       x86_fild_membase (code, X86_ESP, 0, FALSE);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+                       break;
+               case OP_X86_FP_LOAD_I8:
+                       x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, TRUE);
+                       break;
+               case OP_X86_FP_LOAD_I4:
+                       x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, FALSE);
+                       break;
+               case OP_FCONV_TO_I1:
+                       code = emit_float_to_int (cfg, code, ins->dreg, 1, TRUE);
+                       break;
+               case OP_FCONV_TO_U1:
+                       code = emit_float_to_int (cfg, code, ins->dreg, 1, FALSE);
+                       break;
+               case OP_FCONV_TO_I2:
+                       code = emit_float_to_int (cfg, code, ins->dreg, 2, TRUE);
+                       break;
+               case OP_FCONV_TO_U2:
+                       code = emit_float_to_int (cfg, code, ins->dreg, 2, FALSE);
+                       break;
+               case OP_FCONV_TO_I4:
+               case OP_FCONV_TO_I:
+                       code = emit_float_to_int (cfg, code, ins->dreg, 4, TRUE);
+                       break;
+               case OP_FCONV_TO_I8:
+                       /* we defined this instruction to output only to eax:edx */
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
+                       x86_fnstcw_membase(code, X86_ESP, 0);
+                       x86_mov_reg_membase (code, X86_EAX, X86_ESP, 0, 2);
+                       x86_alu_reg_imm (code, X86_OR, X86_EAX, 0xc00);
+                       x86_mov_membase_reg (code, X86_ESP, 2, X86_EAX, 2);
+                       x86_fldcw_membase (code, X86_ESP, 2);
+                       x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
+                       x86_fist_pop_membase (code, X86_ESP, 0, TRUE);
+                       x86_pop_reg (code, X86_EAX);
+                       x86_pop_reg (code, X86_EDX);
+                       x86_fldcw_membase (code, X86_ESP, 0);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+                       break;
+               case OP_LCONV_TO_R_UN: { 
+                       static guint8 mn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x40 };
+                       guint8 *br;
+
+                       /* load 64bit integer to FP stack */
+                       x86_push_imm (code, 0);
+                       x86_push_reg (code, ins->sreg2);
+                       x86_push_reg (code, ins->sreg1);
+                       x86_fild_membase (code, X86_ESP, 0, TRUE);
+                       /* store as 80bit FP value */
+                       x86_fst80_membase (code, X86_ESP, 0);
+                       
+                       /* test if lreg is negative */
+                       x86_test_reg_reg (code, ins->sreg2, ins->sreg2);
+                       br = code; x86_branch8 (code, X86_CC_GEZ, 0, TRUE);
+       
+                       /* add correction constant mn */
+                       x86_fld80_mem (code, mn);
+                       x86_fld80_membase (code, X86_ESP, 0);
+                       x86_fp_op_reg (code, X86_FADD, 1, TRUE);
+                       x86_fst80_membase (code, X86_ESP, 0);
+
+                       x86_patch (br, code);
+
+                       x86_fld80_membase (code, X86_ESP, 0);
+                       x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
+
+                       break;
+               }
+               case OP_LCONV_TO_OVF_I: {
+                       guint8 *br [3], *label [1];
+
+                       /* 
+                        * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
+                        */
+                       x86_test_reg_reg (code, ins->sreg1, ins->sreg1);
+
+                       /* If the low word top bit is set, see if we are negative */
+                       br [0] = code; x86_branch8 (code, X86_CC_LT, 0, TRUE);
+                       /* We are not negative (no top bit set, check for our top word to be zero */
+                       x86_test_reg_reg (code, ins->sreg2, ins->sreg2);
+                       br [1] = code; x86_branch8 (code, X86_CC_EQ, 0, TRUE);
+                       label [0] = code;
+
+                       /* throw exception */
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException");
+                       x86_jump32 (code, 0);
+       
+                       x86_patch (br [0], code);
+                       /* our top bit is set, check that top word is 0xfffffff */
+                       x86_alu_reg_imm (code, X86_CMP, ins->sreg2, 0xffffffff);
+               
+                       x86_patch (br [1], code);
+                       /* nope, emit exception */
+                       br [2] = code; x86_branch8 (code, X86_CC_NE, 0, TRUE);
+                       x86_patch (br [2], label [0]);
+
+                       if (ins->dreg != ins->sreg1)
+                               x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4);
+                       break;
+               }
+               case OP_FADD:
+                       x86_fp_op_reg (code, X86_FADD, 1, TRUE);
+                       break;
+               case OP_FSUB:
+                       x86_fp_op_reg (code, X86_FSUB, 1, TRUE);
+                       break;          
+               case OP_FMUL:
+                       x86_fp_op_reg (code, X86_FMUL, 1, TRUE);
+                       break;          
+               case OP_FDIV:
+                       x86_fp_op_reg (code, X86_FDIV, 1, TRUE);
+                       break;          
+               case OP_FNEG:
+                       x86_fchs (code);
+                       break;          
+               case OP_SIN:
+                       x86_fsin (code);
+                       break;          
+               case OP_COS:
+                       x86_fcos (code);
+                       break;          
+               case OP_ABS:
+                       x86_fabs (code);
+                       break;          
+               case OP_TAN:
+                       x86_fptan (code);
+                       break;          
+               case OP_ATAN:
+                       x86_fpatan (code);
+                       break;          
+               case OP_SQRT:
+                       x86_fsqrt (code);
+                       break;          
+               case OP_X86_FPOP:
+                       x86_fstp (code, 0);
+                       break;          
+               case OP_FREM: {
+                       guint8 *l1, *l2;
+
+                       x86_push_reg (code, X86_EAX);
+                       /* we need to exchange ST(0) with ST(1) */
+                       x86_fxch (code, 1);
+
+                       /* this requires a loop, because fprem1 somtimes 
+                        * returns a partial remainder */
+                       l1 = code;
+                       x86_fprem (code);
+                       x86_fnstsw (code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x0400);
+                       l2 = code + 2;
+                       x86_branch8 (code, X86_CC_NE, l1 - l2, FALSE);
+
+                       /* pop result */
+                       x86_fstp (code, 1);
+
+                       x86_pop_reg (code, X86_EAX);
+                       break;
+               }
+               case OP_FCOMPARE:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               x86_fcomip (code, 1);
+                               x86_fstp (code, 0);
+                               break;
+                       }
+                       /* this overwrites EAX */
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500);
+                       break;
+               case OP_FCEQ:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               /* zeroing the register at the start results in 
+                                * shorter and faster code (we can also remove the widening op)
+                                */
+                               guchar *unordered_check;
+                               x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg);
+                               x86_fcomip (code, 1);
+                               x86_fstp (code, 0);
+                               unordered_check = code;
+                               x86_branch8 (code, X86_CC_P, 0, FALSE);
+                               x86_set_reg (code, X86_CC_EQ, ins->dreg, FALSE);
+                               x86_patch (unordered_check, code);
+                               break;
+                       }
+                       if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000);
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);
+                       break;
+               case OP_FCLT:
+               case OP_FCLT_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               /* zeroing the register at the start results in 
+                                * shorter and faster code (we can also remove the widening op)
+                                */
+                               x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg);
+                               x86_fcomip (code, 1);
+                               x86_fstp (code, 0);
+                               if (ins->opcode == OP_FCLT_UN) {
+                                       guchar *unordered_check = code;
+                                       guchar *jump_to_end;
+                                       x86_branch8 (code, X86_CC_P, 0, FALSE);
+                                       x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE);
+                                       jump_to_end = code;
+                                       x86_jump8 (code, 0);
+                                       x86_patch (unordered_check, code);
+                                       x86_inc_reg (code, ins->dreg);
+                                       x86_patch (jump_to_end, code);
+                               } else {
+                                       x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE);
+                               }
+                               break;
+                       }
+                       if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500);
+                       if (ins->opcode == OP_FCLT_UN) {
+                               guchar *is_not_zero_check, *end_jump;
+                               is_not_zero_check = code;
+                               x86_branch8 (code, X86_CC_NZ, 0, TRUE);
+                               end_jump = code;
+                               x86_jump8 (code, 0);
+                               x86_patch (is_not_zero_check, code);
+                               x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500);
+
+                               x86_patch (end_jump, code);
+                       }
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);
+                       break;
+               case OP_FCGT:
+               case OP_FCGT_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               /* zeroing the register at the start results in 
+                                * shorter and faster code (we can also remove the widening op)
+                                */
+                               guchar *unordered_check;
+                               x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg);
+                               x86_fcomip (code, 1);
+                               x86_fstp (code, 0);
+                               if (ins->opcode == OP_FCGT) {
+                                       unordered_check = code;
+                                       x86_branch8 (code, X86_CC_P, 0, FALSE);
+                                       x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE);
+                                       x86_patch (unordered_check, code);
+                               } else {
+                                       x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE);
+                               }
+                               break;
+                       }
+                       if (ins->dreg != X86_EAX) 
+                               x86_push_reg (code, X86_EAX);
+
+                       EMIT_FPCOMPARE(code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       if (ins->opcode == OP_FCGT_UN) {
+                               guchar *is_not_zero_check, *end_jump;
+                               is_not_zero_check = code;
+                               x86_branch8 (code, X86_CC_NZ, 0, TRUE);
+                               end_jump = code;
+                               x86_jump8 (code, 0);
+                               x86_patch (is_not_zero_check, code);
+                               x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500);
+
+                               x86_patch (end_jump, code);
+                       }
+                       x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE);
+                       x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE);
+
+                       if (ins->dreg != X86_EAX) 
+                               x86_pop_reg (code, X86_EAX);
+                       break;
+               case OP_FBEQ:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE);
+                               break;
+                       }
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000);
+                       EMIT_COND_BRANCH (ins, X86_CC_EQ, TRUE);
+                       break;
+               case OP_FBNE_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_P, FALSE);
+                               EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE);
+                               break;
+                       }
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000);
+                       EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE);
+                       break;
+               case OP_FBLT:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_GT, FALSE);
+                               break;
+                       }
+                       EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE);
+                       break;
+               case OP_FBLT_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_P, FALSE);
+                               EMIT_COND_BRANCH (ins, X86_CC_GT, FALSE);
+                               break;
+                       }
+                       if (ins->opcode == OP_FBLT_UN) {
+                               guchar *is_not_zero_check, *end_jump;
+                               is_not_zero_check = code;
+                               x86_branch8 (code, X86_CC_NZ, 0, TRUE);
+                               end_jump = code;
+                               x86_jump8 (code, 0);
+                               x86_patch (is_not_zero_check, code);
+                               x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500);
+
+                               x86_patch (end_jump, code);
+                       }
+                       EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE);
+                       break;
+               case OP_FBGT:
+               case OP_FBGT_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_LT, FALSE);
+                               break;
+                       }
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       if (ins->opcode == OP_FBGT_UN) {
+                               guchar *is_not_zero_check, *end_jump;
+                               is_not_zero_check = code;
+                               x86_branch8 (code, X86_CC_NZ, 0, TRUE);
+                               end_jump = code;
+                               x86_jump8 (code, 0);
+                               x86_patch (is_not_zero_check, code);
+                               x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500);
+
+                               x86_patch (end_jump, code);
+                       }
+                       EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE);
+                       break;
+               case OP_FBGE:
+               case OP_FBGE_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_LE, FALSE);
+                               break;
+                       }
+                       EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE);
+                       break;
+               case OP_FBLE:
+               case OP_FBLE_UN:
+                       if (cfg->opt & MONO_OPT_FCMOV) {
+                               EMIT_COND_BRANCH (ins, X86_CC_P, FALSE);
+                               EMIT_COND_BRANCH (ins, X86_CC_GE, FALSE);
+                               break;
+                       }
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE);
+                       break;
+               case CEE_CKFINITE: {
+                       x86_push_reg (code, X86_EAX);
+                       x86_fxam (code);
+                       x86_fnstsw (code);
+                       x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4100);
+                       x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100);
+                       x86_pop_reg (code, X86_EAX);
+                       EMIT_COND_SYSTEM_EXCEPTION (X86_CC_EQ, FALSE, "ArithmeticException");
+                       break;
+               }
+               default:
+                       g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
+                       g_assert_not_reached ();
+               }
+
+               if ((code - cfg->native_code - offset) > max_len) {
+                       g_warning ("wrong maximal instruction length of instruction %s (exptected %d, got %d)",
+                                  mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
+                       g_assert_not_reached ();
+               }
+              
+               cpos += max_len;
+
+               last_ins = ins;
+               last_offset = offset;
+               
+               ins = ins->next;
+       }
+
+       cfg->code_len = code - cfg->native_code;
+}
+
+void
+mono_arch_register_lowlevel_calls (void)
+{
+       mono_register_jit_icall (enter_method, "mono_enter_method", NULL, TRUE);
+       mono_register_jit_icall (leave_method, "mono_leave_method", NULL, TRUE);
+}
+
+void
+mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji)
+{
+       MonoJumpInfo *patch_info;
+
+       for (patch_info = ji; patch_info; patch_info = patch_info->next) {
+               unsigned char *ip = patch_info->ip.i + code;
+               const unsigned char *target = NULL;
+
+               switch (patch_info->type) {
+               case MONO_PATCH_INFO_BB:
+                       target = patch_info->data.bb->native_offset + code;
+                       break;
+               case MONO_PATCH_INFO_ABS:
+                       target = patch_info->data.target;
+                       break;
+               case MONO_PATCH_INFO_LABEL:
+                       target = patch_info->data.inst->inst_c0 + code;
+                       break;
+               case MONO_PATCH_INFO_IP:
+                       *((gpointer *)(ip)) = ip;
+                       continue;
+               case MONO_PATCH_INFO_INTERNAL_METHOD: {
+                       MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name);
+                       if (!mi) {
+                               g_warning ("unknown MONO_PATCH_INFO_INTERNAL_METHOD %s", patch_info->data.name);
+                               g_assert_not_reached ();
+                       }
+                       target = mi->wrapper;
+                       break;
+               }
+               case MONO_PATCH_INFO_METHOD:
+                       if (patch_info->data.method == method) {
+                               target = code;
+                       } else {
+                               /* get the trampoline to the method from the domain */
+                               target = mono_arch_create_jit_trampoline (patch_info->data.method);
+                       }
+                       break;
+               case MONO_PATCH_INFO_SWITCH: {
+                       gpointer *table = (gpointer *)patch_info->data.target;
+                       int i;
+
+                       *((gconstpointer *)(ip + 2)) = patch_info->data.target;
+
+                       for (i = 0; i < patch_info->table_size; i++) {
+                               table [i] = (int)patch_info->data.table [i] + code;
+                       }
+                       /* we put into the table the absolute address, no need fo x86_patch in this case */
+                       continue;
+               }
+               case MONO_PATCH_INFO_METHODCONST:
+               case MONO_PATCH_INFO_CLASS:
+               case MONO_PATCH_INFO_IMAGE:
+               case MONO_PATCH_INFO_FIELD:
+                       *((gconstpointer *)(ip + 1)) = patch_info->data.target;
+                       continue;
+               case MONO_PATCH_INFO_R4:
+               case MONO_PATCH_INFO_R8:
+                       *((gconstpointer *)(ip + 2)) = patch_info->data.target;
+                       continue;
+               default:
+                       g_assert_not_reached ();
+               }
+               x86_patch (ip, target);
+       }
+}
+
+int
+mono_arch_max_epilog_size (MonoCompile *cfg)
+{
+       int exc_count = 0, max_epilog_size = 16;
+       MonoJumpInfo *patch_info;
+       
+       if (cfg->method->save_lmf)
+               max_epilog_size += 128;
+       
+       if (mono_jit_trace_calls)
+               max_epilog_size += 50;
+
+       if (mono_jit_profile)
+               max_epilog_size += 50;
+
+       /* count the number of exception infos */
+     
+       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+               if (patch_info->type == MONO_PATCH_INFO_EXC)
+                       exc_count++;
+       }
+
+       /* 
+        * make sure we have enough space for exceptions
+        * 16 is the size of two push_imm instructions and a call
+        */
+       max_epilog_size += exc_count*16;
+
+       return max_epilog_size;
+}
+
+guint8 *
+mono_arch_emit_prolog (MonoCompile *cfg)
+{
+       MonoMethod *method = cfg->method;
+       MonoBasicBlock *bb;
+       MonoMethodSignature *sig;
+       MonoInst *inst;
+       int alloc_size, pos, max_offset, i;
+       guint8 *code;
+
+       cfg->code_size = 256;
+       code = cfg->native_code = g_malloc (cfg->code_size);
+
+       x86_push_reg (code, X86_EBP);
+       x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
+
+       alloc_size = - cfg->stack_offset;
+       pos = 0;
+
+       if (method->save_lmf) {
+               pos += sizeof (MonoLMF);
+               
+               /* save the current IP */
+               mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL);
+               x86_push_imm (code, 0);
+
+               /* save all caller saved regs */
+               x86_push_reg (code, X86_EBX);
+               x86_push_reg (code, X86_EDI);
+               x86_push_reg (code, X86_ESI);
+               x86_push_reg (code, X86_EBP);
+
+               /* save method info */
+               x86_push_imm (code, method);
+       
+               /* get the address of lmf for the current thread */
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+                                    (gpointer)"mono_get_lmf_addr");
+               x86_call_code (code, 0);
+
+               /* push lmf */
+               x86_push_reg (code, X86_EAX); 
+               /* push *lfm (previous_lmf) */
+               x86_push_membase (code, X86_EAX, 0);
+               /* *(lmf) = ESP */
+               x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
+       } else {
+
+               if (cfg->used_int_regs & (1 << X86_EBX)) {
+                       x86_push_reg (code, X86_EBX);
+                       pos += 4;
+               }
+
+               if (cfg->used_int_regs & (1 << X86_EDI)) {
+                       x86_push_reg (code, X86_EDI);
+                       pos += 4;
+               }
+
+               if (cfg->used_int_regs & (1 << X86_ESI)) {
+                       x86_push_reg (code, X86_ESI);
+                       pos += 4;
+               }
+       }
+
+       alloc_size -= pos;
+
+       if (alloc_size)
+               x86_alu_reg_imm (code, X86_SUB, X86_ESP, alloc_size);
+
+        /* compute max_offset in order to use short forward jumps */
+       max_offset = 0;
+       if (cfg->opt & MONO_OPT_BRANCH) {
+               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+                       MonoInst *ins = bb->code;
+                       bb->max_offset = max_offset;
+
+                       if (mono_trace_coverage)
+                               max_offset += 6; 
+
+                       while (ins) {
+                               max_offset += ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN];
+                               ins = ins->next;
+                       }
+               }
+       }
+
+       if (mono_jit_trace_calls)
+               code = mono_arch_instrument_prolog (cfg, enter_method, code, TRUE);
+
+       /* load arguments allocated to register from the stack */
+       sig = method->signature;
+       pos = 0;
+
+       for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+               inst = cfg->varinfo [pos];
+               if (inst->opcode == OP_REGVAR) {
+                       x86_mov_reg_membase (code, inst->dreg, X86_EBP, inst->inst_offset, 4);
+                       if (cfg->verbose_level > 2)
+                               g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
+               }
+               pos++;
+       }
+
+       cfg->code_len = code - cfg->native_code;
+
+       return code;
+}
+
+void
+mono_arch_emit_epilog (MonoCompile *cfg)
+{
+       MonoJumpInfo *patch_info;
+       MonoMethod *method = cfg->method;
+       int pos;
+       guint8 *code;
+
+       code = cfg->native_code + cfg->code_len;
+
+       if (mono_jit_trace_calls)
+               code = mono_arch_instrument_epilog (cfg, leave_method, code, TRUE);
+
+       
+       pos = 0;
+       
+       if (method->save_lmf) {
+               pos = -sizeof (MonoLMF);
+       } else {
+               if (cfg->used_int_regs & (1 << X86_EBX)) {
+                       pos -= 4;
+               }
+               if (cfg->used_int_regs & (1 << X86_EDI)) {
+                       pos -= 4;
+               }
+               if (cfg->used_int_regs & (1 << X86_ESI)) {
+                       pos -= 4;
+               }
+       }
+
+       if (pos)
+               x86_lea_membase (code, X86_ESP, X86_EBP, pos);
+       
+       if (method->save_lmf) {
+               /* ebx = previous_lmf */
+               x86_pop_reg (code, X86_EBX);
+               /* edi = lmf */
+               x86_pop_reg (code, X86_EDI);
+               /* *(lmf) = previous_lmf */
+               x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
+
+               /* discard method info */
+               x86_pop_reg (code, X86_ESI);
+
+               /* restore caller saved regs */
+               x86_pop_reg (code, X86_EBP);
+               x86_pop_reg (code, X86_ESI);
+               x86_pop_reg (code, X86_EDI);
+               x86_pop_reg (code, X86_EBX);
+
+       } else {
+
+               if (cfg->used_int_regs & (1 << X86_ESI)) {
+                       x86_pop_reg (code, X86_ESI);
+               }
+               if (cfg->used_int_regs & (1 << X86_EDI)) {
+                       x86_pop_reg (code, X86_EDI);
+               }
+               if (cfg->used_int_regs & (1 << X86_EBX)) {
+                       x86_pop_reg (code, X86_EBX);
+               }
+       }
+
+       x86_leave (code);
+       x86_ret (code);
+
+       /* add code to raise exceptions */
+       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+               switch (patch_info->type) {
+               case MONO_PATCH_INFO_EXC:
+                       x86_patch (patch_info->ip.i + cfg->native_code, code);
+                       x86_push_imm (code, patch_info->data.target);
+                       x86_push_imm (code, patch_info->ip.i + cfg->native_code);
+                       patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
+                       patch_info->data.name = "mono_arch_throw_exception_by_name";
+                       patch_info->ip.i = code - cfg->native_code;
+                       x86_jump_code (code, 0);
+                       break;
+               default:
+                       /* do nothing */
+                       break;
+               }
+       }
+
+       cfg->code_len = code - cfg->native_code;
+
+       g_assert (cfg->code_len < cfg->code_size);
+
+}
diff --git a/mono/mini/mini-x86.h b/mono/mini/mini-x86.h
new file mode 100644 (file)
index 0000000..b99ef7e
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __MONO_MINI_X86_H__
+#define __MONO_MINI_X86_H__
+
+#include <mono/arch/x86/x86-codegen.h>
+
+#define MONO_ARCH_FRAME_ALIGNMENT 4
+
+/* fixme: align to 16byte instead of 32byte (we align to 32byte to get 
+ * reproduceable results for benchmarks */
+#define MONO_ARCH_CODE_ALIGNMENT 32
+
+#define MONO_ARCH_BASEREG X86_EBP
+#define MONO_ARCH_RETREG1 X86_EAX
+#define MONO_ARCH_RETREG2 X86_EDX
+#define MONO_ARCH_EXC_REG X86_ECX
+
+#define MONO_ARCH_ENCODE_LREG(r1,r2) (r1 | (r2<<3))
+
+#define inst_dreg_low dreg&7 
+#define inst_dreg_high dreg>>3
+#define inst_sreg1_low sreg1&7 
+#define inst_sreg1_high sreg1>>3
+#define inst_sreg2_low sreg2&7 
+#define inst_sreg2_high sreg2>>3
+
+struct MonoLMF {
+       gpointer    previous_lmf;
+       gpointer    lmf_addr;
+       MonoMethod *method;
+       guint32     ebp;
+       guint32     esi;
+       guint32     edi;
+       guint32     ebx;
+       guint32     eip;
+};
+
+#endif /* __MONO_MINI_X86_H__ */  
diff --git a/mono/mini/mini.c b/mono/mini/mini.c
new file mode 100644 (file)
index 0000000..076e90b
--- /dev/null
@@ -0,0 +1,6182 @@
+/*
+ * mini.c: The new Mono code generator.
+ *
+ * Author:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/loader.h>
+#include <mono/metadata/cil-coff.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/exception.h>
+#include <mono/metadata/opcodes.h>
+#include <mono/metadata/mono-endian.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/threads.h>
+#include <mono/metadata/marshal.h>
+#include <mono/metadata/socket-io.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/io-layer/io-layer.h>
+#include "mono/metadata/profiler.h"
+#include <mono/metadata/profiler-private.h>
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/environment.h>
+
+#include "mini.h"
+#include <string.h>
+#include <ctype.h>
+#include "inssel.h"
+#include "debug.h"
+
+#include "jit-icalls.c"
+
+#define MONO_CHECK_THIS(ins) (cfg->method->signature->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0)
+
+static gpointer mono_jit_compile_method (MonoMethod *method);
+
+static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, 
+                         const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native);
+
+extern guint8 mono_burg_arity [];
+/* helper methods signature */
+static MonoMethodSignature *helper_sig_long_long_long = NULL;
+static MonoMethodSignature *helper_sig_long_long_int = NULL;
+static MonoMethodSignature *helper_sig_newarr = NULL;
+static MonoMethodSignature *helper_sig_ldstr = NULL;
+static MonoMethodSignature *helper_sig_domain_get = NULL;
+static MonoMethodSignature *helper_sig_object_new = NULL;
+static MonoMethodSignature *helper_sig_compile = NULL;
+static MonoMethodSignature *helper_sig_compile_virt = NULL;
+static MonoMethodSignature *helper_sig_obj_ptr = NULL;
+static MonoMethodSignature *helper_sig_ptr_void = NULL;
+static MonoMethodSignature *helper_sig_void_ptr = NULL;
+static MonoMethodSignature *helper_sig_void_obj = NULL;
+static MonoMethodSignature *helper_sig_void_ptr_ptr = NULL;
+static MonoMethodSignature *helper_sig_void_ptr_ptr_ptr = NULL;
+static MonoMethodSignature *helper_sig_ptr_ptr_ptr = NULL;
+static MonoMethodSignature *helper_sig_ptr_obj = NULL;
+static MonoMethodSignature *helper_sig_initobj = NULL;
+static MonoMethodSignature *helper_sig_memcpy = NULL;
+static MonoMethodSignature *helper_sig_memset = NULL;
+static MonoMethodSignature *helper_sig_ulong_double = NULL;
+static MonoMethodSignature *helper_sig_long_double = NULL;
+static MonoMethodSignature *helper_sig_uint_double = NULL;
+static MonoMethodSignature *helper_sig_int_double = NULL;
+
+static guint32 default_opt = MONO_OPT_PEEPHOLE;
+
+guint32 mono_jit_tls_id = 0;
+gboolean mono_jit_trace_calls = FALSE;
+gboolean mono_break_on_exc = FALSE;
+gboolean mono_compile_aot = FALSE;
+gboolean mono_trace_coverage = FALSE;
+gboolean mono_jit_profile = FALSE;
+MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
+
+CRITICAL_SECTION *metadata_section = NULL;
+
+static int mini_verbose = 0;
+
+#ifdef MONO_USE_EXC_TABLES
+static gboolean
+mono_type_blittable (MonoType *type)
+{
+       if (type->byref)
+               return FALSE;
+
+       switch (type->type){
+       case MONO_TYPE_VOID:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+               return TRUE;
+       case MONO_TYPE_VALUETYPE:
+       case MONO_TYPE_CLASS:
+               return type->data.klass->blittable;
+               break;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+gboolean
+mono_method_blittable (MonoMethod *method)
+{
+       MonoMethodSignature *sig;
+       int i;
+
+       if (!method->addr)
+               return FALSE;
+
+       if (!mono_arch_has_unwind_info (method->addr)) {
+               return FALSE;
+       }
+
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
+               return TRUE;
+
+       sig = method->signature;
+
+       if (!mono_type_blittable (sig->ret))
+               return FALSE;
+
+       for (i = 0; i < sig->param_count; i++)
+               if (!mono_type_blittable (sig->params [i]))
+                       return FALSE;
+
+       return TRUE;
+}
+#endif
+
+#if 0
+/* debug function */
+static void
+print_method_from_ip (void *ip)
+{
+       MonoJitInfo *ji;
+       char *method;
+       
+       ji = mono_jit_info_table_find (mono_domain_get (), ip);
+       if (!ji) {
+               g_print ("No method at %p\n", ip);
+               return;
+       }
+       method = mono_method_full_name (ji->method, TRUE);
+       g_print ("IP at offset 0x%x of method %s (%p %p)\n", (char*)ip - (char*)ji->code_start, method, ji->code_start, (char*)ji->code_start + ji->code_size);
+       g_free (method);
+
+}
+#endif
+
+#define MONO_INIT_VARINFO(vi,id) do { \
+       (vi)->range.first_use.pos.bid = 0xffff; \
+       (vi)->reg = -1; \
+        (vi)->idx = (id); \
+} while (0)
+
+/*
+ * Basic blocks have two numeric identifiers:
+ * dfn: Depth First Number
+ * block_num: unique ID assigned at bblock creation
+ */
+#define NEW_BBLOCK(cfg) (mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)))
+#define ADD_BBLOCK(cfg,bbhash,b) do {  \
+               g_hash_table_insert (bbhash, (b)->cil_code, (b));       \
+               (b)->block_num = cfg->num_bblocks++;    \
+               (b)->real_offset = real_offset; \
+       } while (0)
+
+#define GET_BBLOCK(cfg,bbhash,tblock,ip) do {  \
+               (tblock) = g_hash_table_lookup (bbhash, (ip));  \
+               if (!(tblock)) {        \
+                       if ((ip) >= end || (ip) < header->code) goto unverified; \
+                       (tblock) = NEW_BBLOCK (cfg);    \
+                       (tblock)->cil_code = (ip);      \
+                       ADD_BBLOCK (cfg, (bbhash), (tblock));   \
+               }       \
+               (tblock)->real_offset = real_offset; \
+       } while (0)
+
+#define CHECK_BBLOCK(target,ip,tblock) do {    \
+               if ((target) < (ip) && !(tblock)->code) {       \
+                       bb_recheck = g_list_prepend (bb_recheck, (tblock));     \
+                       if (cfg->verbose_level > 2) g_print ("queued block %d for check at IL%04x from IL%04x\n", (tblock)->block_num, (target) - header->code, (ip) - header->code);   \
+               }       \
+       } while (0)
+
+#define NEW_ICONST(cfg,dest,val) do {  \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = OP_ICONST;     \
+               (dest)->inst_c0 = (val);        \
+               (dest)->type = STACK_I4;        \
+       } while (0)
+
+/* FIXME: have a different definition of NEW_PCONST for 64 bit systems */
+#define NEW_PCONST(cfg,dest,val) do {  \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = OP_ICONST;     \
+               (dest)->inst_p0 = (val);        \
+               (dest)->type = STACK_PTR;       \
+       } while (0)
+
+#define NEW_CLASSCONST(cfg,dest,val) do {      \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST;    \
+               (dest)->inst_p0 = (val);        \
+               (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_CLASS; \
+               (dest)->type = STACK_PTR;       \
+       } while (0)
+
+#define NEW_IMAGECONST(cfg,dest,val) do {      \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST;    \
+               (dest)->inst_p0 = (val);        \
+               (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_IMAGE; \
+               (dest)->type = STACK_PTR;       \
+       } while (0)
+
+#define NEW_FIELDCONST(cfg,dest,field) do {    \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST;    \
+               (dest)->inst_p0 = (field);      \
+               (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_FIELD; \
+               (dest)->type = STACK_PTR;       \
+       } while (0)
+
+#define NEW_METHODCONST(cfg,dest,val) do {     \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST;    \
+               (dest)->inst_p0 = (val);        \
+               (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_METHODCONST; \
+               (dest)->type = STACK_PTR;       \
+       } while (0)
+
+#define NEW_DOMAINCONST(cfg,dest) do { \
+               if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) { \
+                       NEW_TEMPLOAD (cfg, dest, mono_get_domainvar (cfg)->inst_c0); \
+               } else { \
+                       NEW_PCONST (cfg, dest, (cfg)->domain); \
+               } \
+       } while (0)
+
+#define GET_VARINFO_INST(cfg,num) ((cfg)->varinfo [(num)]->inst)
+
+#define NEW_ARGLOAD(cfg,dest,num) do { \
+                if (arg_array [(num)]->opcode == OP_ICONST) (dest) = arg_array [(num)]; else { \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_LOAD; \
+               (dest)->inst_i0 = arg_array [(num)];    \
+               (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype);      \
+               type_to_eval_stack_type (param_types [(num)], (dest));  \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       }} while (0)
+
+#define NEW_LOCLOAD(cfg,dest,num) do { \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_LOAD; \
+               (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)];       \
+               (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype);      \
+               type_to_eval_stack_type (header->locals [(num)], (dest));       \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       } while (0)
+
+#define NEW_LOCLOADA(cfg,dest,num) do {        \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_MAYBE_LOAD;   \
+               (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)];       \
+               (dest)->inst_i0->flags |= MONO_INST_INDIRECT;   \
+               (dest)->opcode = OP_LDADDR;     \
+               (dest)->type = STACK_MP;        \
+               (dest)->klass = (dest)->inst_i0->klass; \
+                (cfg)->disable_ssa = TRUE; \
+       } while (0)
+
+#define NEW_RETLOADA(cfg,dest) do {    \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_MAYBE_LOAD;   \
+               (dest)->inst_i0 = (cfg)->ret;   \
+               (dest)->inst_i0->flags |= MONO_INST_INDIRECT;   \
+               (dest)->opcode = CEE_LDIND_I;   \
+               (dest)->type = STACK_MP;        \
+               (dest)->klass = (dest)->inst_i0->klass; \
+                (cfg)->disable_ssa = TRUE; \
+       } while (0)
+
+#define NEW_ARGLOADA(cfg,dest,num) do {        \
+                if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_MAYBE_LOAD;   \
+               (dest)->inst_i0 = arg_array [(num)];    \
+               (dest)->inst_i0->flags |= MONO_INST_INDIRECT;   \
+               (dest)->opcode = OP_LDADDR;     \
+               (dest)->type = STACK_MP;        \
+               (dest)->klass = (dest)->inst_i0->klass; \
+                (cfg)->disable_ssa = TRUE; \
+       } while (0)
+
+#define NEW_TEMPLOAD(cfg,dest,num) do {        \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_LOAD; \
+               (dest)->inst_i0 = (cfg)->varinfo [(num)];       \
+               (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype);      \
+               type_to_eval_stack_type ((dest)->inst_i0->inst_vtype, (dest));  \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       } while (0)
+
+#define NEW_TEMPLOADA(cfg,dest,num) do {       \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_MAYBE_LOAD;   \
+               (dest)->inst_i0 = (cfg)->varinfo [(num)];       \
+               (dest)->inst_i0->flags |= MONO_INST_INDIRECT;   \
+               (dest)->opcode = OP_LDADDR;     \
+               (dest)->type = STACK_MP;        \
+               (dest)->klass = (dest)->inst_i0->klass; \
+                (cfg)->disable_ssa = TRUE; \
+       } while (0)
+
+
+#define NEW_INDLOAD(cfg,dest,addr,vtype) do {  \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->inst_left = addr;       \
+               (dest)->opcode = mono_type_to_ldind (vtype);    \
+               type_to_eval_stack_type (vtype, (dest));        \
+               /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/     \
+       } while (0)
+
+#define NEW_INDSTORE(cfg,dest,addr,value,vtype) do {   \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->inst_i0 = addr; \
+               (dest)->opcode = mono_type_to_stind (vtype);    \
+               (dest)->inst_i1 = (value);      \
+               /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/     \
+       } while (0)
+
+#define NEW_TEMPSTORE(cfg,dest,num,inst) do {  \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->ssa_op = MONO_SSA_STORE;        \
+               (dest)->inst_i0 = (cfg)->varinfo [(num)];       \
+               (dest)->opcode = mono_type_to_stind ((dest)->inst_i0->inst_vtype);      \
+               (dest)->inst_i1 = (inst);       \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       } while (0)
+
+#define NEW_LOCSTORE(cfg,dest,num,inst) do {   \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_type_to_stind (header->locals [(num)]);   \
+               (dest)->ssa_op = MONO_SSA_STORE;        \
+               (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)];       \
+               (dest)->inst_i1 = (inst);       \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       } while (0)
+
+#define NEW_ARGSTORE(cfg,dest,num,inst) do {   \
+                if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = mono_type_to_stind (param_types [(num)]);      \
+               (dest)->ssa_op = MONO_SSA_STORE;        \
+               (dest)->inst_i0 = arg_array [(num)];    \
+               (dest)->inst_i1 = (inst);       \
+               (dest)->klass = (dest)->inst_i0->klass; \
+       } while (0)
+
+#define ADD_BINOP(op) do {     \
+               MONO_INST_NEW (cfg, ins, (op)); \
+               ins->cil_code = ip;     \
+               sp -= 2;        \
+               ins->inst_i0 = sp [0];  \
+               ins->inst_i1 = sp [1];  \
+               *sp++ = ins;    \
+               type_from_op (ins);     \
+               CHECK_TYPE (ins);       \
+       } while (0)
+
+#define ADD_UNOP(op) do {      \
+               MONO_INST_NEW (cfg, ins, (op)); \
+               ins->cil_code = ip;     \
+               sp--;   \
+               ins->inst_i0 = sp [0];  \
+               *sp++ = ins;    \
+               type_from_op (ins);     \
+               CHECK_TYPE (ins);       \
+       } while (0)
+
+#define ADD_BINCOND(next_block) do {   \
+               MonoInst *cmp;  \
+               MONO_INST_NEW(cfg, cmp, OP_COMPARE);    \
+               sp -= 2;                \
+               cmp->inst_i0 = sp [0];  \
+               cmp->inst_i1 = sp [1];  \
+               cmp->cil_code = ins->cil_code;  \
+               type_from_op (cmp);     \
+               CHECK_TYPE (cmp);       \
+               ins->inst_i0 = cmp;     \
+               MONO_ADD_INS (bblock, ins);     \
+               ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);      \
+               GET_BBLOCK (cfg, bbhash, tblock, target);               \
+               link_bblock (cfg, bblock, tblock);      \
+               ins->inst_true_bb = tblock;     \
+               CHECK_BBLOCK (target, ip, tblock);      \
+               if ((next_block)) {     \
+                       link_bblock (cfg, bblock, (next_block));        \
+                       ins->inst_false_bb = (next_block);      \
+                       start_new_bblock = 1;   \
+               } else {        \
+                       GET_BBLOCK (cfg, bbhash, tblock, ip);           \
+                       link_bblock (cfg, bblock, tblock);      \
+                       ins->inst_false_bb = tblock;    \
+                       start_new_bblock = 2;   \
+               }       \
+       } while (0)
+
+/* FIXME: handle float, long ... */
+#define ADD_UNCOND(istrue) do {        \
+               MonoInst *cmp;  \
+               MONO_INST_NEW(cfg, cmp, OP_COMPARE);    \
+               sp--;           \
+               cmp->inst_i0 = sp [0];  \
+                switch (cmp->inst_i0->type) { \
+               case STACK_I8: \
+                       cmp->inst_i1 = zero_int64; break; \
+               case STACK_R8: \
+                       cmp->inst_i1 = zero_r8; break; \
+               case STACK_PTR: \
+               case STACK_MP: \
+                       cmp->inst_i1 = zero_ptr; break; \
+               case STACK_OBJ: \
+                       cmp->inst_i1 = zero_obj; break; \
+               default: \
+                       cmp->inst_i1 = zero_int32;  \
+               }  \
+               cmp->cil_code = ins->cil_code;  \
+               type_from_op (cmp);     \
+               CHECK_TYPE (cmp);       \
+               ins->inst_i0 = cmp;     \
+               ins->opcode = (istrue)? CEE_BNE_UN: CEE_BEQ;    \
+               MONO_ADD_INS (bblock, ins);     \
+               ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);      \
+               GET_BBLOCK (cfg, bbhash, tblock, target);               \
+               link_bblock (cfg, bblock, tblock);      \
+               ins->inst_true_bb = tblock;     \
+               CHECK_BBLOCK (target, ip, tblock);      \
+               GET_BBLOCK (cfg, bbhash, tblock, ip);           \
+               link_bblock (cfg, bblock, tblock);      \
+               ins->inst_false_bb = tblock;    \
+               start_new_bblock = 2;   \
+       } while (0)
+
+#define NEW_LDELEMA(cfg,dest,sp,k) do {        \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = CEE_LDELEMA;   \
+               (dest)->inst_left = (sp) [0];   \
+               (dest)->inst_right = (sp) [1];  \
+               (dest)->type = STACK_MP;        \
+               (dest)->klass = (k);    \
+       } while (0)
+
+static GHashTable *coverage_hash = NULL;
+
+MonoCoverageInfo *
+mono_allocate_coverage_info (MonoMethod *method, int size)
+{
+       MonoCoverageInfo *res;
+
+       if (!coverage_hash)
+               coverage_hash = g_hash_table_new (NULL, NULL);
+
+       res = g_malloc0 (sizeof (MonoCoverageInfo) + sizeof (int) * size * 2);
+
+       res->entries = size;
+
+       g_hash_table_insert (coverage_hash, method, res);
+
+       return res;
+}
+
+MonoCoverageInfo *
+mono_get_coverage_info (MonoMethod *method)
+{
+       if (!coverage_hash)
+               return NULL;
+
+       return g_hash_table_lookup (coverage_hash, method);
+}
+               
+#if 0
+static gint
+compare_bblock (gconstpointer a, gconstpointer b)
+{
+       const MonoBasicBlock *b1 = a;
+       const MonoBasicBlock *b2 = b;
+
+       return b2->cil_code - b1->cil_code;
+}
+#endif
+
+/* *
+ * link_bblock: Links two basic blocks
+ *
+ * links two basic blocks in the control flow graph, the 'from'
+ * argument is the starting block and the 'to' argument is the block
+ * the control flow ends to after 'from'.
+ */
+static void
+link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
+{
+       MonoBasicBlock **newa;
+       int i, found;
+
+#if 0
+       if (from->cil_code) {
+               if (to->cil_code)
+                       g_print ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
+               else
+                       g_print ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
+       } else {
+               if (to->cil_code)
+                       g_print ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
+               else
+                       g_print ("edge from entry to exit\n");
+       }
+#endif
+       found = FALSE;
+       for (i = 0; i < from->out_count; ++i) {
+               if (to == from->out_bb [i]) {
+                       found = TRUE;
+                       break;
+               }
+       }
+       if (!found) {
+               newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
+               for (i = 0; i < from->out_count; ++i) {
+                       newa [i] = from->out_bb [i];
+               }
+               newa [i] = to;
+               from->out_count++;
+               from->out_bb = newa;
+       }
+
+       found = FALSE;
+       for (i = 0; i < to->in_count; ++i) {
+               if (from == to->in_bb [i]) {
+                       found = TRUE;
+                       break;
+               }
+       }
+       if (!found) {
+               newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
+               for (i = 0; i < to->in_count; ++i) {
+                       newa [i] = to->in_bb [i];
+               }
+               newa [i] = from;
+               to->in_count++;
+               to->in_bb = newa;
+       }
+}
+
+/*
+ * We mark each basic block with a region ID. We use that to avoid BB
+ * optimizations when blocks are in different regions. 
+ */
+static int
+mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths)
+{
+       MonoMethod *method = cfg->method;
+       MonoMethodHeader *header = ((MonoMethodNormal *)method)->header;
+       MonoExceptionClause *clause;
+       int i;
+
+       /* first search for handlers and filters */
+       for (i = 0; i < header->num_clauses; ++i) {
+               clause = &header->clauses [i];
+               if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->token_or_filter) &&
+                   (offset < (clause->token_or_filter + filter_lengths [i])))
+                       return (i << 8) | 128 | clause->flags;
+                          
+               if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
+                       return (i << 8) | 64 | clause->flags;
+               }
+       }
+
+       /* search the try blocks */
+       for (i = 0; i < header->num_clauses; ++i) {
+               clause = &header->clauses [i];
+               if (MONO_OFFSET_IN_CLAUSE (clause, offset))
+                       return (i << 8) | clause->flags;
+       }
+
+       return -1;
+}
+
+static MonoBasicBlock *
+mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
+{
+       MonoMethod *method = cfg->method;
+       MonoMethodHeader *header = ((MonoMethodNormal *)method)->header;
+       MonoExceptionClause *clause;
+       MonoBasicBlock *handler;
+       int i;
+
+       for (i = 0; i < header->num_clauses; ++i) {
+               clause = &header->clauses [i];
+               if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) && 
+                   (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
+                       if (clause->flags & type) {
+                               handler = g_hash_table_lookup (cfg->bb_hash, header->code + clause->handler_offset);
+                               g_assert (handler);
+                               return handler;
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+static void
+df_visit (MonoBasicBlock *start, int *dfn, MonoBasicBlock **array)
+{
+       int i;
+
+       array [*dfn] = start;
+       /*g_print ("visit %d at %p\n", *dfn, start->cil_code);*/
+       for (i = 0; i < start->out_count; ++i) {
+               if (start->out_bb [i]->dfn)
+                       continue;
+               (*dfn)++;
+               start->out_bb [i]->dfn = *dfn;
+               start->out_bb [i]->df_parent = start;
+               array [*dfn] = start->out_bb [i];
+               df_visit (start->out_bb [i], dfn, array);
+       }
+}
+
+typedef struct {
+       const guchar *code;
+       MonoBasicBlock *best;
+} PrevStruct;
+
+static void
+previous_foreach (gconstpointer key, gpointer val, gpointer data)
+{
+       PrevStruct *p = data;
+       MonoBasicBlock *bb = val;
+       //printf ("FIDPREV %d %p  %p %p %p %p %d %d %d\n", bb->block_num, p->code, bb, p->best, bb->cil_code, p->best->cil_code,
+       //bb->method == p->best->method, bb->cil_code < p->code, bb->cil_code > p->best->cil_code);
+
+       if (bb->cil_code && bb->cil_code < p->code && bb->cil_code > p->best->cil_code)
+               p->best = bb;
+}
+
+static MonoBasicBlock*
+find_previous (GHashTable *bb_hash, MonoBasicBlock *start, const guchar *code) {
+       PrevStruct p;
+
+       p.code = code;
+       p.best = start;
+
+       g_hash_table_foreach (bb_hash, (GHFunc)previous_foreach, &p);
+       return p.best;
+}
+
+static void
+split_bblock (MonoCompile *cfg, MonoBasicBlock *first, MonoBasicBlock *second) {
+       int i, j;
+       MonoInst *inst;
+       MonoBasicBlock *bb;
+
+       if (second->code)
+               return;
+       
+       /* 
+        * FIXME: take into account all the details:
+        * second may have been the target of more than one bblock
+        */
+       second->out_count = first->out_count;
+       second->out_bb = first->out_bb;
+
+       for (i = 0; i < first->out_count; ++i) {
+               bb = first->out_bb [i];
+               for (j = 0; j < bb->in_count; ++j) {
+                       if (bb->in_bb [j] == first)
+                               bb->in_bb [j] = second;
+               }
+       }
+
+       first->out_count = 0;
+       first->out_bb = NULL;
+       link_bblock (cfg, first, second);
+
+       second->last_ins = first->last_ins;
+
+       /*g_print ("start search at %p for %p\n", first->cil_code, second->cil_code);*/
+       for (inst = first->code; inst && inst->next; inst = inst->next) {
+               /*char *code = mono_disasm_code_one (NULL, cfg->method, inst->next->cil_code, NULL);
+               g_print ("found %p: %s", inst->next->cil_code, code);
+               g_free (code);*/
+               if (inst->cil_code < second->cil_code && inst->next->cil_code >= second->cil_code) {
+                       second->code = inst->next;
+                       inst->next = NULL;
+                       first->last_ins = inst;
+                       second->next_bb = first->next_bb;
+                       first->next_bb = second;
+                       return;
+               }
+       }
+       if (!second->code) {
+               g_warning ("bblock split failed in %s::%s\n", cfg->method->klass->name, cfg->method->name);
+               //G_BREAKPOINT ();
+       }
+}
+
+guint
+mono_type_to_ldind (MonoType *type)
+{
+       int t = type->type;
+
+       if (type->byref)
+               return CEE_LDIND_I;
+
+handle_enum:
+       switch (t) {
+       case MONO_TYPE_I1:
+               return CEE_LDIND_I1;
+       case MONO_TYPE_U1:
+       case MONO_TYPE_BOOLEAN:
+               return CEE_LDIND_U1;
+       case MONO_TYPE_I2:
+               return CEE_LDIND_I2;
+       case MONO_TYPE_U2:
+       case MONO_TYPE_CHAR:
+               return CEE_LDIND_U2;
+       case MONO_TYPE_I4:
+               return CEE_LDIND_I4;
+       case MONO_TYPE_U4:
+               return CEE_LDIND_U4;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+               return CEE_LDIND_I;
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:    
+               return CEE_LDIND_REF;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               return CEE_LDIND_I8;
+       case MONO_TYPE_R4:
+               return CEE_LDIND_R4;
+       case MONO_TYPE_R8:
+               return CEE_LDIND_R8;
+       case MONO_TYPE_VALUETYPE:
+               if (type->data.klass->enumtype) {
+                       t = type->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               }
+               return CEE_LDOBJ;
+       default:
+               g_error ("unknown type 0x%02x in type_to_ldind", type->type);
+       }
+       return -1;
+}
+
+guint
+mono_type_to_stind (MonoType *type)
+{
+       int t = type->type;
+
+       if (type->byref)
+               return CEE_STIND_I;
+
+handle_enum:
+       switch (t) {
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_BOOLEAN:
+               return CEE_STIND_I1;
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_CHAR:
+               return CEE_STIND_I2;
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+               return CEE_STIND_I4;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+               return CEE_STIND_I;
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:    
+               return CEE_STIND_REF;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               return CEE_STIND_I8;
+       case MONO_TYPE_R4:
+               return CEE_STIND_R4;
+       case MONO_TYPE_R8:
+               return CEE_STIND_R8;
+       case MONO_TYPE_VALUETYPE:
+               if (type->data.klass->enumtype) {
+                       t = type->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               }
+               return CEE_STOBJ;
+               /* fail right now */
+       default:
+               g_error ("unknown type %02x in type_to_stind", type->type);
+       }
+       return -1;
+}
+
+/*
+ * Returns the type used in the eval stack when @type is loaded.
+ * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
+ */
+static void
+type_to_eval_stack_type (MonoType *type, MonoInst *inst) {
+       int t = type->type;
+
+       if (type->byref) {
+               inst->type = STACK_MP;
+               return;
+       }
+
+handle_enum:
+       switch (t) {
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+               inst->type = STACK_I4;
+               return;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+               inst->type = STACK_PTR;
+               return;
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:    
+               inst->type = STACK_OBJ;
+               return;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               inst->type = STACK_I8;
+               return;
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+               inst->type = STACK_R8;
+               return;
+       case MONO_TYPE_VALUETYPE:
+               if (type->data.klass->enumtype) {
+                       t = type->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               } else {
+                       inst->klass = type->data.klass;
+                       inst->type = STACK_VTYPE;
+                       return;
+               }
+       default:
+               g_error ("unknown type 0x%02x in eval stack type", type->type);
+       }
+}
+
+/*
+ * The following tables are used to quickly validate the IL code in type_from_op ().
+ */
+static const char
+bin_num_table [STACK_MAX] [STACK_MAX] = {
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_I4,  STACK_INV, STACK_PTR, STACK_INV, STACK_MP,  STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_I8,  STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP,  STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8,  STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_MP,  STACK_INV, STACK_MP,  STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
+};
+
+static const char 
+neg_table [] = {
+       STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV
+};
+
+/* reduce the size of this table */
+static const char
+bin_int_table [STACK_MAX] [STACK_MAX] = {
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_I4,  STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_I8,  STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
+};
+
+static const char
+bin_comp_table [STACK_MAX] [STACK_MAX] = {
+       {0},
+       {0, 1, 0, 1, 0, 0, 4, 0},
+       {0, 0, 1, 0, 0, 0, 0, 0},
+       {0, 1, 0, 1, 0, 2, 0, 0},
+       {0, 0, 0, 0, 1, 0, 0, 0},
+       {0, 0, 0, 2, 0, 1, 0, 0},
+       {0, 4, 0, 0, 0, 0, 3, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+/* reduce the size of this table */
+static const char
+shift_table [STACK_MAX] [STACK_MAX] = {
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_I4,  STACK_INV, STACK_I4,  STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_I8,  STACK_INV, STACK_I8,  STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
+       {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
+};
+
+/*
+ * Tables to map from the non-specific opcode to the matching
+ * type-specific opcode.
+ */
+/* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
+static const guint16
+binops_op_map [STACK_MAX] = {
+       0, 0, OP_LADD-CEE_ADD, OP_PADD-CEE_ADD, OP_FADD-CEE_ADD, 0
+};
+
+/* handles from CEE_NEG to CEE_CONV_U8 */
+static const guint16
+unops_op_map [STACK_MAX] = {
+       0, 0, OP_LNEG-CEE_NEG, OP_PNEG-CEE_NEG, OP_FNEG-CEE_NEG, 0
+};
+
+/* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
+static const guint16
+ovfops_op_map [STACK_MAX] = {
+       0, 0, OP_LCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_FCONV_TO_U2-CEE_CONV_U2, 0
+};
+
+/* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
+static const guint16
+ovf2ops_op_map [STACK_MAX] = {
+       0, 0, OP_LCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_FCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, 0
+};
+
+/* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
+static const guint16
+ovf3ops_op_map [STACK_MAX] = {
+       0, 0, OP_LCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_FCONV_TO_OVF_I1-CEE_CONV_OVF_I1, 0
+};
+
+/* handles from CEE_CEQ to CEE_CLT_UN */
+static const guint16
+ceqops_op_map [STACK_MAX] = {
+       0, 0, OP_LCEQ-CEE_CEQ, OP_PCEQ-CEE_CEQ, OP_FCEQ-CEE_CEQ, 0
+};
+
+/*
+ * Sets ins->type (the type on the eval stack) according to the
+ * type of the opcode and the arguments to it.
+ * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
+ *
+ * FIXME: this function sets ins->type unconditionally in some cases, but
+ * it should set it to invalid for some types (a conv.x on an object)
+ */
+static void
+type_from_op (MonoInst *ins) {
+       switch (ins->opcode) {
+       /* binops */
+       case CEE_ADD:
+       case CEE_SUB:
+       case CEE_MUL:
+       case CEE_DIV:
+       case CEE_REM:
+               /* FIXME: check unverifiable args for STACK_MP */
+               ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type];
+               ins->opcode += binops_op_map [ins->type];
+               return;
+       case CEE_DIV_UN:
+       case CEE_REM_UN:
+       case CEE_AND:
+       case CEE_OR:
+       case CEE_XOR:
+               ins->type = bin_int_table [ins->inst_i0->type] [ins->inst_i1->type];
+               ins->opcode += binops_op_map [ins->type];
+               return;
+       case CEE_SHL:
+       case CEE_SHR:
+       case CEE_SHR_UN:
+               ins->type = shift_table [ins->inst_i0->type] [ins->inst_i1->type];
+               ins->opcode += binops_op_map [ins->type];
+               return;
+       case OP_COMPARE:
+               /* FIXME: handle some specifics with ins->next->type */
+               ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV;
+               return;
+       case 256+CEE_CEQ:
+       case 256+CEE_CGT:
+       case 256+CEE_CGT_UN:
+       case 256+CEE_CLT:
+       case 256+CEE_CLT_UN:
+               ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV;
+               ins->opcode += ceqops_op_map [ins->inst_i0->type];
+               return;
+       /* unops */
+       case CEE_NEG:
+               ins->type = neg_table [ins->inst_i0->type];
+               ins->opcode += unops_op_map [ins->type];
+               return;
+       case CEE_NOT:
+               if (ins->inst_i0->type >= STACK_I4 && ins->inst_i0->type <= STACK_PTR)
+                       ins->type = ins->inst_i0->type;
+               else
+                       ins->type = STACK_INV;
+               ins->opcode += unops_op_map [ins->type];
+               return;
+       case CEE_CONV_I1:
+       case CEE_CONV_I2:
+       case CEE_CONV_I4:
+       case CEE_CONV_U4:
+               ins->type = STACK_I4;
+               ins->opcode += unops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_R_UN:
+               ins->type = STACK_R8;
+               switch (ins->inst_i0->type) {
+               case STACK_I4:
+               case STACK_PTR:
+                       break;
+               case STACK_I8:
+                       ins->opcode = OP_LCONV_TO_R_UN; 
+                       break;
+               }
+               return;
+       case CEE_CONV_OVF_I1:
+       case CEE_CONV_OVF_U1:
+       case CEE_CONV_OVF_I2:
+       case CEE_CONV_OVF_U2:
+       case CEE_CONV_OVF_I4:
+       case CEE_CONV_OVF_U4:
+               ins->type = STACK_I4;
+               ins->opcode += ovf3ops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_OVF_I_UN:
+       case CEE_CONV_OVF_U_UN:
+               ins->type = STACK_PTR;
+               ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_OVF_I1_UN:
+       case CEE_CONV_OVF_I2_UN:
+       case CEE_CONV_OVF_I4_UN:
+       case CEE_CONV_OVF_U1_UN:
+       case CEE_CONV_OVF_U2_UN:
+       case CEE_CONV_OVF_U4_UN:
+               ins->type = STACK_I4;
+               ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_U:
+               ins->type = STACK_PTR;
+               switch (ins->inst_i0->type) {
+               case STACK_I4:
+               case STACK_PTR:
+               case STACK_MP:
+                       break;
+               case STACK_I8:
+                       ins->opcode = OP_LCONV_TO_U;
+                       break;
+               case STACK_R8:
+                       ins->opcode = OP_FCONV_TO_U;
+                       break;
+               }
+               return;
+       case CEE_CONV_I8:
+       case CEE_CONV_U8:
+               ins->type = STACK_I8;
+               ins->opcode += unops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_OVF_I8:
+       case CEE_CONV_OVF_U8:
+               ins->type = STACK_I8;
+               ins->opcode += ovf3ops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_OVF_U8_UN:
+       case CEE_CONV_OVF_I8_UN:
+               ins->type = STACK_I8;
+               ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CONV_R4:
+       case CEE_CONV_R8:
+               ins->type = STACK_R8;
+               ins->opcode += unops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_CKFINITE:
+               ins->type = STACK_R8;           
+               return;
+       case CEE_CONV_U2:
+       case CEE_CONV_U1:
+               ins->type = STACK_I4;
+               ins->opcode += ovfops_op_map [ins->inst_i0->type];
+               break;
+       case CEE_CONV_I:
+       case CEE_CONV_OVF_I:
+       case CEE_CONV_OVF_U:
+               ins->type = STACK_PTR;
+               ins->opcode += ovfops_op_map [ins->inst_i0->type];
+               return;
+       case CEE_ADD_OVF:
+       case CEE_ADD_OVF_UN:
+       case CEE_MUL_OVF:
+       case CEE_MUL_OVF_UN:
+       case CEE_SUB_OVF:
+       case CEE_SUB_OVF_UN:
+               ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type];
+               ins->opcode += ovfops_op_map [ins->inst_i0->type];
+               return;
+       default:
+               g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
+               break;
+       }
+}
+
+static const char 
+ldind_type [] = {
+       STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_MP, STACK_R8, STACK_R8, STACK_OBJ
+};
+
+/* map ldelem.x to the matching ldind.x opcode */
+static const guchar
+ldelem_to_ldind [] = {
+       CEE_LDIND_I1,
+       CEE_LDIND_U1,
+       CEE_LDIND_I2,
+       CEE_LDIND_U2,
+       CEE_LDIND_I4,
+       CEE_LDIND_U4,
+       CEE_LDIND_I8,
+       CEE_LDIND_I,
+       CEE_LDIND_R4,
+       CEE_LDIND_R8,
+       CEE_LDIND_REF
+};
+
+/* map stelem.x to the matching stind.x opcode */
+static const guchar
+stelem_to_stind [] = {
+       CEE_STIND_I,
+       CEE_STIND_I1,
+       CEE_STIND_I2,
+       CEE_STIND_I4,
+       CEE_STIND_I8,
+       CEE_STIND_R4,
+       CEE_STIND_R8,
+       CEE_STIND_REF
+};
+
+#if 0
+
+static const char
+param_table [STACK_MAX] [STACK_MAX] = {
+       {0},
+};
+
+static int
+check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) {
+       int i;
+
+       if (sig->hasthis) {
+               switch (args->type) {
+               case STACK_I4:
+               case STACK_I8:
+               case STACK_R8:
+               case STACK_VTYPE:
+               case STACK_INV:
+                       return 0;
+               }
+               args++;
+       }
+       for (i = 0; i < sig->param_count; ++i) {
+               switch (args [i].type) {
+               case STACK_INV:
+                       return 0;
+               case STACK_MP:
+                       if (!sig->params [i]->byref)
+                               return 0;
+                       continue;
+               case STACK_OBJ:
+                       if (sig->params [i]->byref)
+                               return 0;
+                       switch (sig->params [i]->type) {
+                       case MONO_TYPE_CLASS:
+                       case MONO_TYPE_STRING:
+                       case MONO_TYPE_OBJECT:
+                       case MONO_TYPE_SZARRAY:
+                       case MONO_TYPE_ARRAY:
+                               break;
+                       default:
+                               return 0;
+                       }
+                       continue;
+               case STACK_R8:
+                       if (sig->params [i]->byref)
+                               return 0;
+                       if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
+                               return 0;
+                       continue;
+               case STACK_PTR:
+               case STACK_I4:
+               case STACK_I8:
+               case STACK_VTYPE:
+                       break;
+               }
+               /*if (!param_table [args [i].type] [sig->params [i]->type])
+                       return 0;*/
+       }
+       return 1;
+}
+#endif
+
+/*
+ * When we need a pointer to the current domain many times in a method, we
+ * call mono_domain_get() once and we store the result in a local variable.
+ * This function returns the variable that represents the MonoDomain*.
+ */
+inline static MonoInst *
+mono_get_domainvar (MonoCompile *cfg)
+{
+       if (!cfg->domainvar)
+               cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+       return cfg->domainvar;
+}
+
+static void
+realloc_var_info (MonoCompile *cfg, int count)
+{
+       gpointer data;
+       int num = cfg->varinfo_count;
+
+       g_assert (count > num);
+
+       cfg->varinfo_count = count;
+
+       data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * cfg->varinfo_count);
+       if (num)
+               memcpy (data, cfg->varinfo, sizeof (MonoInst*) * num);
+       cfg->varinfo = (MonoInst **)data;
+
+       data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar*) * cfg->varinfo_count);
+       if (num)
+               memcpy (data, cfg->vars, sizeof (MonoMethodVar*) * num);
+       cfg->vars = (MonoMethodVar **)data;             
+}
+
+MonoInst*
+mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode)
+{
+       MonoInst *inst;
+       int num = cfg->num_varinfo;
+
+       if ((num + 1) >= cfg->varinfo_count)
+               realloc_var_info (cfg, cfg->varinfo_count + 16);
+
+       mono_jit_stats.allocate_var++;
+
+       MONO_INST_NEW (cfg, inst, opcode);
+       inst->inst_c0 = num;
+       inst->inst_vtype = type;
+       inst->klass = mono_class_from_mono_type (type);
+       /* if set to 1 the variable is native */
+       inst->unused = 0;
+
+       cfg->varinfo [num] = inst;
+
+       cfg->vars [num] = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar));
+       MONO_INIT_VARINFO (cfg->vars [num], num);
+
+       cfg->num_varinfo++;
+       //g_print ("created temp %d of type %s\n", num, mono_type_get_name (type));
+       return inst;
+}
+
+static MonoType*
+type_from_stack_type (MonoInst *ins) {
+       switch (ins->type) {
+       case STACK_I4: return &mono_defaults.int32_class->byval_arg;
+       case STACK_I8: return &mono_defaults.int64_class->byval_arg;
+       case STACK_PTR: return &mono_defaults.int_class->byval_arg;
+       case STACK_R8: return &mono_defaults.double_class->byval_arg;
+       case STACK_MP: return &mono_defaults.int_class->byval_arg;
+       case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
+       case STACK_VTYPE: return &ins->klass->byval_arg;
+       default:
+               g_error ("stack type %d to montype not handled\n", ins->type);
+       }
+       return NULL;
+}
+
+static MonoClass*
+array_access_to_klass (int opcode)
+{
+       switch (opcode) {
+       case CEE_LDELEM_U1:
+               return mono_defaults.byte_class;
+       case CEE_LDELEM_U2:
+               return mono_defaults.uint16_class;
+       case CEE_LDELEM_I:
+       case CEE_STELEM_I:
+               return mono_defaults.int_class;
+       case CEE_LDELEM_I1:
+       case CEE_STELEM_I1:
+               return mono_defaults.sbyte_class;
+       case CEE_LDELEM_I2:
+       case CEE_STELEM_I2:
+               return mono_defaults.int16_class;
+       case CEE_LDELEM_I4:
+       case CEE_STELEM_I4:
+               return mono_defaults.int32_class;
+       case CEE_LDELEM_U4:
+               return mono_defaults.uint32_class;
+       case CEE_LDELEM_I8:
+       case CEE_STELEM_I8:
+               return mono_defaults.int64_class;
+       case CEE_LDELEM_R4:
+       case CEE_STELEM_R4:
+               return mono_defaults.single_class;
+       case CEE_LDELEM_R8:
+       case CEE_STELEM_R8:
+               return mono_defaults.double_class;
+       case CEE_LDELEM_REF:
+       case CEE_STELEM_REF:
+               return mono_defaults.object_class;
+       default:
+               g_assert_not_reached ();
+       }
+       return NULL;
+}
+
+static void
+mono_add_ins_to_end (MonoBasicBlock *bb, MonoInst *inst)
+{
+       MonoInst *prev;
+       if (!bb->code) {
+               MONO_ADD_INS (bb, inst);
+               return;
+       }
+       switch (bb->last_ins->opcode) {
+       case CEE_BEQ:
+       case CEE_BGE:
+       case CEE_BGT:
+       case CEE_BLE:
+       case CEE_BLT:
+       case CEE_BNE_UN:
+       case CEE_BGE_UN:
+       case CEE_BGT_UN:
+       case CEE_BLE_UN:
+       case CEE_BLT_UN:
+       case CEE_BR:
+               prev = bb->code;
+               while (prev->next && prev->next != bb->last_ins)
+                       prev = prev->next;
+               if (prev == bb->code) {
+                       if (bb->last_ins == bb->code) {
+                               inst->next = bb->code;
+                               bb->code = inst;
+                       } else {
+                               inst->next = prev->next;
+                               prev->next = inst;
+                       }
+               } else {
+                       inst->next = bb->last_ins;
+                       prev->next = inst;
+               }
+               break;
+       //      g_warning ("handle conditional jump in add_ins_to_end ()\n");
+       default:
+               MONO_ADD_INS (bb, inst);
+               break;
+       }
+}
+
+void
+mono_add_varcopy_to_end (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest)
+{
+       MonoInst *inst, *load;
+
+       NEW_TEMPLOAD (cfg, load, src);
+
+       NEW_TEMPSTORE (cfg, inst, dest, load);
+       if (inst->opcode == CEE_STOBJ) {
+               NEW_TEMPLOADA (cfg, inst, dest);
+               handle_stobj (cfg, bb, inst, load, NULL, inst->klass, TRUE, FALSE);
+       } else {
+               inst->cil_code = NULL;
+               mono_add_ins_to_end (bb, inst);
+       }
+}
+
+/*
+ * This function is called to handle items that are left on the evaluation stack
+ * at basic block boundaries. What happens is that we save the values to local variables
+ * and we reload them later when first entering the target basic block (with the
+ * handle_loaded_temps () function).
+ * A single joint point will use the same variables (stored in the array bb->out_stack or
+ * bb->in_stack, if the basic block is before or after the joint point).
+ */
+static int
+handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int count) {
+       int i;
+       MonoBasicBlock *outb;
+       MonoInst *inst, **locals;
+
+       if (!count)
+               return 0;
+       if (cfg->verbose_level > 3)
+               g_print ("%d item(s) on exit from B%d\n", count, bb->block_num);
+       if (!bb->out_scount) {
+               int found = 0;
+               bb->out_scount = count;
+               //g_print ("bblock %d has out:", bb->block_num);
+               for (i = 0; i < bb->out_count; ++i) {
+                       outb = bb->out_bb [i];
+                       //g_print (" %d", outb->block_num);
+                       if (outb->in_stack) {
+                               found = 1;
+                               bb->out_stack = outb->in_stack;
+                               break;
+                       }
+               }
+               //g_print ("\n");
+               if (!found) {
+                       bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
+                       for (i = 0; i < count; ++i) {
+                               /* 
+                                * dietmar suggests that we can reuse temps already allocated 
+                                * for this purpouse, if they occupy the same stack slot and if 
+                                * they are of the same type.
+                                */
+                               bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
+                       }
+               }
+       }
+       locals = bb->out_stack;
+       for (i = 0; i < count; ++i) {
+               /* add store ops at the end of the bb, before the branch */
+               NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
+               if (inst->opcode == CEE_STOBJ) {
+                       NEW_TEMPLOADA (cfg, inst, locals [i]->inst_c0);
+                       handle_stobj (cfg, bb, inst, sp [i], sp [i]->cil_code, inst->klass, TRUE, FALSE);
+               } else {
+                       inst->cil_code = sp [i]->cil_code;
+                       mono_add_ins_to_end (bb, inst);
+               }
+               if (cfg->verbose_level > 3)
+                       g_print ("storing %d to temp %d\n", i, locals [i]->inst_c0);
+       }
+       
+       for (i = 0; i < bb->out_count; ++i) {
+               outb = bb->out_bb [i];
+               if (outb->in_scount)
+                       continue; /* check they are the same locals */
+               outb->in_scount = count;
+               outb->in_stack = locals;
+       }
+       return 0;
+}
+
+static int
+ret_type_to_call_opcode (MonoType *type, int calli, int virt)
+{
+       int t = type->type;
+
+       if (type->byref)
+               return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
+
+handle_enum:
+       switch (t) {
+       case MONO_TYPE_VOID:
+               return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALLVIRT: OP_VOIDCALL;
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+               return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+               return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:    
+               return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               return calli? OP_LCALL_REG: virt? OP_LCALLVIRT: OP_LCALL;
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+               return calli? OP_FCALL_REG: virt? OP_FCALLVIRT: OP_FCALL;
+       case MONO_TYPE_VALUETYPE:
+               if (type->data.klass->enumtype) {
+                       t = type->data.klass->enum_basetype->type;
+                       goto handle_enum;
+               } else
+                       return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL;
+       default:
+               g_error ("unknown type %02x in ret_type_to_call_opcode", type->type);
+       }
+       return -1;
+}
+
+void
+mono_create_jump_table (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks)
+{
+       MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo));
+       
+       ji->ip.label = label;
+       ji->type = MONO_PATCH_INFO_SWITCH;
+       ji->data.table = bbs;
+       ji->next = cfg->patch_info;
+       ji->table_size = num_blocks;
+       cfg->patch_info = ji;
+}
+
+/*
+ * When we add a tree of instructions, we need to ensure the instructions currently
+ * on the stack are executed before (like, if we load a value from a local).
+ * We ensure this by saving the currently loaded values to temps and rewriting the
+ * instructions to load the values.
+ * This is not done for opcodes that terminate a basic block (because it's handled already
+ * by handle_stack_args ()) and for opcodes that can't change values, like POP.
+ */
+static void
+handle_loaded_temps (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst **stack, MonoInst **sp)
+{
+       MonoInst *load, *store, *temp, *ins;
+
+       while (stack < sp) {
+               ins = *stack;
+               /* handle also other constants */
+               if (ins->opcode != OP_ICONST) {
+                       temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
+                       NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
+                       store->cil_code = ins->cil_code;
+                       if (store->opcode == CEE_STOBJ) {
+                               NEW_TEMPLOADA (cfg, store, temp->inst_c0);
+                               handle_stobj (cfg, bblock, store, ins, ins->cil_code, temp->klass, FALSE, FALSE);
+                       } else
+                               MONO_ADD_INS (bblock, store);
+                       NEW_TEMPLOAD (cfg, load, temp->inst_c0);
+                       load->cil_code = ins->cil_code;
+                       *stack = load;
+               }
+               stack++;
+       }
+}
+
+inline static int
+mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, MonoMethodSignature *sig, gboolean ret_object, 
+                const guint8 *ip, gboolean to_end)
+{
+       MonoInst *temp, *store, *ins = (MonoInst*)call;
+       MonoType *ret = sig->ret;
+
+       if (!MONO_TYPE_IS_VOID (ret) || ret_object) {
+               if (ret_object) {
+                       call->inst.type = STACK_OBJ;
+                       call->inst.opcode = CEE_CALL;
+                       temp = mono_compile_create_var (cfg, &mono_defaults.string_class->byval_arg, OP_LOCAL);
+               } else {
+                       type_to_eval_stack_type (ret, ins);
+                       temp = mono_compile_create_var (cfg, ret, OP_LOCAL);
+               }
+
+               if (MONO_TYPE_ISSTRUCT (ret)) {
+                       MonoInst *loada;
+
+                       /* we use this to allocate native sized structs */
+                       temp->unused = sig->pinvoke;
+
+                       NEW_TEMPLOADA (cfg, loada, temp->inst_c0);
+                       if (call->inst.opcode == OP_VCALL)
+                               ins->inst_left = loada;
+                       else
+                               ins->inst_right = loada; /* a virtual or indirect call */
+
+                       if (to_end)
+                               mono_add_ins_to_end (bblock, ins);
+                       else
+                               MONO_ADD_INS (bblock, ins);
+               } else {
+                       NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
+                       store->cil_code = ip;
+                       if (to_end)
+                               mono_add_ins_to_end (bblock, store);
+                       else
+                               MONO_ADD_INS (bblock, store);
+               }
+               return temp->inst_c0;
+       } else {
+               if (to_end)
+                       mono_add_ins_to_end (bblock, ins);
+               else
+                       MONO_ADD_INS (bblock, ins);
+               return -1;
+       }
+}
+
+inline static MonoCallInst *
+mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, 
+                    MonoInst **args, int calli, int virtual, const guint8 *ip, gboolean to_end)
+{
+       MonoCallInst *call;
+       int i;
+
+       MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (sig->ret, calli, virtual));
+       
+       call->inst.cil_code = ip;
+       call->args = args;
+       call->signature = sig;
+       call = mono_arch_call_opcode (cfg, bblock, call, virtual);
+
+       for (i = 0; i < (sig->param_count + sig->hasthis); ++i) {
+               if (call->args [i]) {
+                       if (to_end)
+                               mono_add_ins_to_end (bblock, call->args [i]);
+                       else
+                               MONO_ADD_INS (bblock, call->args [i]);
+               }
+       }
+       return call;
+}
+
+inline static int
+mono_emit_calli (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, 
+                MonoInst **args, MonoInst *addr, const guint8 *ip)
+{
+       MonoCallInst *call = mono_emit_call_args (cfg, bblock, sig, args, TRUE, FALSE, ip, FALSE);
+
+       call->inst.inst_i0 = addr;
+
+       return mono_spill_call (cfg, bblock, call, sig, FALSE, ip, FALSE);
+}
+
+static MonoCallInst*
+mono_emit_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig,
+                      MonoInst **args, const guint8 *ip, MonoInst *this)
+{
+       gboolean virtual = this != NULL;
+       MonoCallInst *call;
+
+       call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, virtual, ip, FALSE);
+
+       if (this && sig->hasthis && 
+           (method->klass->marshalbyref || method->klass == mono_defaults.object_class) && 
+           !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !MONO_CHECK_THIS (this)) {
+               call->method = mono_marshal_get_remoting_invoke_with_check (method);
+       } else {
+               call->method = method;
+       }
+       call->inst.flags |= MONO_INST_HAS_METHOD;
+       call->inst.inst_left = this;
+
+       return call;
+}
+
+inline static int
+mono_emit_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method,  
+                      MonoInst **args, const guint8 *ip, MonoInst *this)
+{
+       MonoCallInst *call = mono_emit_method_call (cfg, bblock, method, method->signature, args, ip, this);
+
+       return mono_spill_call (cfg, bblock, call, method->signature, method->string_ctor, ip, FALSE);
+}
+
+inline static int
+mono_emit_native_call (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoMethodSignature *sig,
+                      MonoInst **args, const guint8 *ip, gboolean to_end)
+{
+       MonoCallInst *call;
+
+       g_assert (sig);
+
+       call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, FALSE, ip, to_end);
+       call->fptr = func;
+       return mono_spill_call (cfg, bblock, call, sig, func == mono_array_new_va, ip, to_end);
+}
+
+inline static int
+mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoInst **args, const guint8 *ip)
+{
+       MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
+       
+       if (!info) {
+               g_warning ("unregistered JIT ICall");
+               g_assert_not_reached ();
+       }
+
+       return mono_emit_native_call (cfg, bblock, info->wrapper, info->sig, args, ip, FALSE);
+}
+
+static void
+mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info)
+{
+       MonoInst *ins, *temp = NULL, *store, *load;
+       int i, nargs;
+       MonoCallInst *call;
+
+       /*g_print ("emulating: ");
+       mono_print_tree (tree);
+       g_print ("\n");*/
+       MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (info->sig->ret, FALSE, FALSE));
+       ins = (MonoInst*)call;
+       
+       call->inst.cil_code = tree->cil_code;
+       call->args = iargs;
+       call->signature = info->sig;
+
+       call = mono_arch_call_opcode (cfg, cfg->cbb, call, FALSE);
+
+       if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
+               temp = mono_compile_create_var (cfg, info->sig->ret, OP_LOCAL);
+               NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
+               store->cil_code = tree->cil_code;
+       } else {
+               store = ins;
+       }
+
+       nargs = info->sig->param_count + info->sig->hasthis;
+
+       for (i = 1; i < nargs; i++) {
+               call->args [i - 1]->next = call->args [i];
+       }
+
+       if (nargs)
+               call->args [nargs - 1]->next = store;
+
+       if (cfg->prev_ins) {
+               store->next = cfg->prev_ins->next;
+               if (nargs)
+                       cfg->prev_ins->next =  call->args [0];
+               else
+                       cfg->prev_ins->next = store;
+       } else {
+               store->next = cfg->cbb->code;
+               if (nargs)              
+                       cfg->cbb->code = call->args [0];
+               else
+                       cfg->cbb->code = store;
+       }
+
+       
+       call->fptr = info->wrapper;
+
+       if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
+               NEW_TEMPLOAD (cfg, load, temp->inst_c0);
+               *tree = *load;
+       }
+}
+
+static MonoMethodSignature *
+mono_get_element_address_signature (int arity)
+{
+       static GHashTable *sighash = NULL;
+       MonoMethodSignature *res;
+       int i;
+
+       if (!sighash)
+               sighash = g_hash_table_new (NULL, NULL);
+
+
+       if ((res = g_hash_table_lookup (sighash, (gpointer)arity)))
+               return res;
+
+       res = mono_metadata_signature_alloc (mono_defaults.corlib, arity + 1);
+
+       res->params [0] = &mono_defaults.array_class->byval_arg; 
+       
+       for (i = 1; i <= arity; i++)
+               res->params [i] = &mono_defaults.int_class->byval_arg;
+
+       res->ret = &mono_defaults.int_class->byval_arg;
+
+       g_hash_table_insert (sighash, (gpointer)arity, res);
+
+       return res;
+}
+
+static void
+handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native) {
+       MonoInst *iargs [3];
+       int n;
+
+       g_assert (klass);
+       /*
+        * This check breaks with spilled vars... need to handle it during verification anyway.
+        * g_assert (klass && klass == src->klass && klass == dest->klass);
+        */
+
+       if (native)
+               n = mono_class_native_size (klass, NULL);
+       else
+               n = mono_class_value_size (klass, NULL);
+
+       iargs [0] = dest;
+       iargs [1] = src;
+       NEW_ICONST (cfg, iargs [2], n);
+
+       mono_emit_native_call (cfg, bblock, helper_memcpy, helper_sig_memcpy, iargs, ip, to_end);
+}
+
+static void
+handle_initobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, const guchar *ip, MonoClass *klass, MonoInst **stack_start, MonoInst **sp)
+{
+       MonoInst *iargs [2];
+       MonoInst *ins, *zero_int32;
+       int n;
+
+       NEW_ICONST (cfg, zero_int32, 0);
+
+       mono_class_init (klass);
+       n = mono_class_value_size (klass, NULL);
+       MONO_INST_NEW (cfg, ins, 0);
+       ins->cil_code = ip;
+       ins->inst_left = dest;
+       ins->inst_right = zero_int32;
+       switch (n) {
+       case 1:
+               ins->opcode = CEE_STIND_I1;
+               MONO_ADD_INS (bblock, ins);
+               break;
+       case 2:
+               ins->opcode = CEE_STIND_I2;
+               MONO_ADD_INS (bblock, ins);
+               break;
+       case 4:
+               ins->opcode = CEE_STIND_I4;
+               MONO_ADD_INS (bblock, ins);
+               break;
+       default:
+               handle_loaded_temps (cfg, bblock, stack_start, sp);
+               NEW_ICONST (cfg, ins, n);
+               iargs [0] = dest;
+               iargs [1] = ins;
+               mono_emit_jit_icall (cfg, bblock, helper_initobj, iargs, ip);
+               break;
+       }
+}
+
+#define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S))
+
+static gboolean
+mono_method_check_inlining (MonoMethod *method)
+{
+       MonoMethodHeader *header = ((MonoMethodNormal *)method)->header;
+       MonoMethodSignature *signature = method->signature;
+       int i;
+
+       /* fixme: we should inline wrappers */
+       if (method->wrapper_type != MONO_WRAPPER_NONE)
+               return FALSE;
+
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
+           (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
+           (method->klass->marshalbyref) ||
+           !header || header->num_clauses ||
+           /* fixme: why cant we inline valuetype returns? */
+           MONO_TYPE_ISSTRUCT (signature->ret))
+               return FALSE;
+
+       /* its not worth to inline methods with valuetype arguments?? */
+       for (i = 0; i < signature->param_count; i++) {
+               if (MONO_TYPE_ISSTRUCT (signature->params [i])) {
+                       return FALSE;
+               }
+       }
+       
+       //if (!MONO_TYPWE_IS_VOID (signature->ret)) return FALSE;
+
+       /* also consider num_locals? */
+       if (header->code_size < 20)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void
+mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, MonoInst **sp, MonoInst **args)
+{
+       MonoInst *store, *temp;
+       int i;
+
+       g_assert (!MONO_TYPE_ISSTRUCT (sig->ret));
+
+       if (!sig->hasthis && sig->param_count == 0) 
+               return;
+
+       if (sig->hasthis) {
+               if (sp [0]->opcode == OP_ICONST) {
+                       *args++ = sp [0];
+                       //printf ("REUSABLE CONST\n");
+               } else if (sp [0]->ssa_op == MONO_SSA_LOAD  && 
+                   (sp [0]->inst_i0->opcode == OP_LOCAL || sp [0]->inst_i0->opcode == OP_ARG)) {
+                       *args++ = sp [0]->inst_i0;
+                       //printf ("REUSABLE ARG\n");
+               } else {
+                       //printf ("NONREUSABLE ARG0 %s\n", mono_inst_name (sp [0]->opcode));
+                       temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL);
+                       *args++ = temp;
+                       NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp);
+                       store->cil_code = sp [0]->cil_code;
+                       MONO_ADD_INS (bblock, store);
+                       //printf ("SAVE THIS %s\n", mono_inst_name (store->opcode));
+               }
+               sp++;
+       }
+
+       for (i = 0; i < sig->param_count; ++i) {
+               if (sp [0]->opcode == OP_ICONST) {
+                       *args++ = sp [0];
+                       //printf ("REUSABLE CONST\n");
+               } else if (sp [0]->ssa_op == MONO_SSA_LOAD  && 
+                   (sp [0]->inst_i0->opcode == OP_LOCAL || sp [0]->inst_i0->opcode == OP_ARG)) {
+                       *args++ = sp [0]->inst_i0;
+                       //printf ("REUSABLE ARG\n");
+               } else {
+                       //printf ("NONREUSABLE ARG1 %s\n", mono_inst_name (sp [0]->opcode));
+                       temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL);
+                       *args++ = temp;
+                       NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp);
+                       store->cil_code = sp [0]->cil_code;
+                       if (store->opcode == CEE_STOBJ) {
+                               NEW_TEMPLOADA (cfg, store, temp->inst_c0);
+                               handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, temp->klass, FALSE, FALSE);
+                               //printf ("SAVE OBJARGS %s\n", mono_inst_name (store->opcode));
+                       } else {
+                               MONO_ADD_INS (bblock, store);
+                               //printf ("SAVE ARGS %s\n", mono_inst_name (store->opcode));
+                       } 
+               }
+               sp++;
+       }
+}
+
+/*
+ * Some of these comments may well be out-of-date.
+ * Design decisions: we do a single pass over the IL code (and we do bblock 
+ * splitting/merging in the few cases when it's required: a back jump to an IL
+ * address that was not already seen as bblock starting point).
+ * Code is validated as we go (full verification is still better left to metadata/verify.c).
+ * Complex operations are decomposed in simpler ones right away. We need to let the 
+ * arch-specific code peek and poke inside this process somehow (except when the 
+ * optimizations can take advantage of the full semantic info of coarse opcodes).
+ * All the opcodes of the form opcode.s are 'normalized' to opcode.
+ * MonoInst->opcode initially is the IL opcode or some simplification of that 
+ * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific 
+ * opcode with value bigger than OP_LAST.
+ * At this point the IR can be handed over to an interpreter, a dumb code generator
+ * or to the optimizing code generator that will translate it to SSA form.
+ *
+ * Profiling directed optimizations.
+ * We may compile by default with few or no optimizations and instrument the code
+ * or the user may indicate what methods to optimize the most either in a config file
+ * or through repeated runs where the compiler applies offline the optimizations to 
+ * each method and then decides if it was worth it.
+ *
+ * TODO:
+ * * consider using an array instead of an hash table (bb_hash)
+ */
+
+#define CHECK_TYPE(ins) if (!(ins)->type) goto unverified
+#define CHECK_STACK(num) if ((sp - stack_start) < (num)) goto unverified
+#define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) goto unverified
+
+/* offset from br.s -> br like opcodes */
+#define BIG_BRANCH_OFFSET 13
+
+/*
+ * mono_method_to_ir: translates IL into basic blocks containing trees
+ */
+static int
+mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, 
+                  int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, 
+                  guint inline_offset)
+{
+       MonoInst *zero_int32, *zero_int64, *zero_ptr, *zero_obj, *zero_r8;
+       MonoInst *ins, **sp, **stack_start;
+       MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
+       GHashTable *bbhash;
+       MonoMethod *cmethod;
+       MonoInst **arg_array;
+       MonoMethodHeader *header;
+       MonoImage *image;
+       guint32 token, ins_flag;
+       MonoClass *klass;
+       unsigned char *ip, *end, *target;
+       static double r8_0 = 0.0;
+       MonoMethodSignature *sig;
+       MonoType **param_types;
+       GList *bb_recheck = NULL, *tmp;
+       int i, n, start_new_bblock, align;
+       int num_calls = 0, inline_costs = 0;
+       int *filter_lengths = NULL;
+       guint real_offset;
+
+       image = method->klass->image;
+       header = ((MonoMethodNormal *)method)->header;
+       sig = method->signature;
+       ip = (unsigned char*)header->code;
+       end = ip + header->code_size;
+       mono_jit_stats.cil_code_size += header->code_size;
+
+       if (cfg->method == method) {
+               real_offset = 0;
+               bbhash = cfg->bb_hash;
+       } else {
+               real_offset = inline_offset;
+               bbhash = g_hash_table_new (g_direct_hash, NULL);
+       }
+
+       if (cfg->method == method) {
+
+               /* ENTRY BLOCK */
+               cfg->bb_entry = start_bblock = NEW_BBLOCK (cfg);
+               start_bblock->cil_code = NULL;
+               start_bblock->cil_length = 0;
+               start_bblock->block_num = cfg->num_bblocks++;
+
+               /* EXIT BLOCK */
+               cfg->bb_exit = end_bblock = NEW_BBLOCK (cfg);
+               end_bblock->cil_code = NULL;
+               end_bblock->cil_length = 0;
+               end_bblock->block_num = cfg->num_bblocks++;
+               g_assert (cfg->num_bblocks == 2);
+
+               arg_array = alloca (sizeof (MonoInst *) * (sig->hasthis + sig->param_count));
+               for (i = sig->hasthis + sig->param_count - 1; i >= 0; i--)
+                       arg_array [i] = cfg->varinfo [i];
+
+               if (mono_compile_aot) 
+                       cfg->opt |= MONO_OPT_SAHRED;
+
+               if (header->num_clauses) {
+                       int size = sizeof (int) * header->num_clauses;
+                       filter_lengths = alloca (size);
+                       memset (filter_lengths, 0, size);
+               }
+               /* handle exception clauses */
+               for (i = 0; i < header->num_clauses; ++i) {
+                       //unsigned char *p = ip;
+                       MonoExceptionClause *clause = &header->clauses [i];
+                       GET_BBLOCK (cfg, bbhash, tblock, ip + clause->try_offset);
+                       tblock->real_offset = clause->try_offset;
+                       GET_BBLOCK (cfg, bbhash, tblock, ip + clause->handler_offset);
+                       tblock->real_offset = clause->handler_offset;
+                       /*g_print ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len);
+                         while (p < end) {
+                         g_print ("%s", mono_disasm_code_one (NULL, method, p, &p));
+                         }*/
+                       /* catch and filter blocks get the exception object on the stack */
+                       if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
+                           clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
+                               /* mostly like handle_stack_args (), but just sets the input args */
+                               /* g_print ("handling clause at IL_%04x\n", clause->handler_offset); */
+                               if (!cfg->exvar) {
+                                       cfg->exvar = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
+                                       /* prevent it from being register allocated */
+                                       cfg->exvar->flags |= MONO_INST_INDIRECT;
+                               }
+                               tblock->in_scount = 1;
+                               tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
+                               tblock->in_stack [0] = cfg->exvar;
+                               if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
+                                       GET_BBLOCK (cfg, bbhash, tblock, ip + clause->token_or_filter);
+                                       tblock->real_offset = clause->token_or_filter;
+                                       tblock->in_scount = 1;
+                                       tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
+                                       tblock->in_stack [0] = cfg->exvar;
+                               }
+                       }
+               }
+
+       } else {
+               arg_array = alloca (sizeof (MonoInst *) * (sig->hasthis + sig->param_count));
+               mono_save_args (cfg, start_bblock, sig, inline_args, arg_array);
+       }
+
+       /* FIRST CODE BLOCK */
+       bblock = NEW_BBLOCK (cfg);
+       bblock->cil_code = ip;
+
+       ADD_BBLOCK (cfg, bbhash, bblock);
+
+       if (cfg->method == method) {
+               if (mono_method_has_breakpoint (method, FALSE)) {
+                       MONO_INST_NEW (cfg, ins, CEE_BREAK);
+                       MONO_ADD_INS (bblock, ins);
+               }
+       }
+       
+       if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SAHRED)))) {
+               /* we use a separate basic block for the initialization code */
+               cfg->bb_init = init_localsbb = NEW_BBLOCK (cfg);
+               init_localsbb->real_offset = real_offset;
+               start_bblock->next_bb = init_localsbb;
+               init_localsbb->next_bb = bblock;
+               link_bblock (cfg, start_bblock, init_localsbb);
+               link_bblock (cfg, init_localsbb, bblock);
+               init_localsbb->block_num = cfg->num_bblocks++;
+       } else {
+               start_bblock->next_bb = bblock;
+               link_bblock (cfg, start_bblock, bblock);
+       }
+
+       mono_debug_init_method (cfg, bblock);
+
+       param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * (sig->hasthis + sig->param_count));
+       if (sig->hasthis)
+               param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
+       for (n = 0; n < sig->param_count; ++n)
+               param_types [n + sig->hasthis] = sig->params [n];
+
+       /* do this somewhere outside - not here */
+       NEW_ICONST (cfg, zero_int32, 0);
+       NEW_ICONST (cfg, zero_int64, 0);
+       zero_int64->type = STACK_I8;
+       NEW_PCONST (cfg, zero_ptr, 0);
+       NEW_PCONST (cfg, zero_obj, 0);
+       zero_obj->type = STACK_OBJ;
+
+       MONO_INST_NEW (cfg, zero_r8, OP_R8CONST);
+       zero_r8->type = STACK_R8;
+       zero_r8->inst_p0 = &r8_0;
+
+       /* add a check for this != NULL to inlined methods */
+       if (cfg->method != method && sig->hasthis) {
+               MONO_INST_NEW (cfg, ins, OP_CHECK_THIS);
+               NEW_ARGLOAD (cfg, ins->inst_left, 0);
+               ins->cil_code = ip;
+               MONO_ADD_INS (bblock, ins);
+       }
+
+       /* we use a spare stack slot in SWITCH and NEWOBJ and others */
+       stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
+
+       ins_flag = 0;
+       start_new_bblock = 0;
+       while (ip < end) {
+
+               if (cfg->method == method)
+                       real_offset = ip - header->code;
+               else
+                       real_offset = inline_offset;
+
+               if (start_new_bblock) {
+                       bblock->cil_length = ip - bblock->cil_code;
+                       if (start_new_bblock == 2) {
+                               g_assert (ip == tblock->cil_code);
+                       } else {
+                               GET_BBLOCK (cfg, bbhash, tblock, ip);
+                       }
+                       bblock->next_bb = tblock;
+                       bblock = tblock;
+                       start_new_bblock = 0;
+                       for (i = 0; i < bblock->in_scount; ++i) {
+                               NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
+                               *sp++ = ins;
+                       }
+               } else {
+                       if ((tblock = g_hash_table_lookup (bbhash, ip)) && (tblock != bblock)) {
+                               link_bblock (cfg, bblock, tblock);
+                               if (sp != stack_start) {
+                                       handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                                       sp = stack_start;
+                               }
+                               bblock->next_bb = tblock;
+                               bblock = tblock;
+                               for (i = 0; i < bblock->in_scount; ++i) {
+                                       NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
+                                       *sp++ = ins;
+                               }
+                       }
+               }
+
+               if (cfg->verbose_level > 3)
+                       g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, sp-stack_start, mono_disasm_code_one (NULL, method, ip, NULL));
+
+               switch (*ip) {
+               case CEE_NOP:
+                       ++ip;
+                       break;
+               case CEE_BREAK:
+                       MONO_INST_NEW (cfg, ins, CEE_BREAK);
+                       ins->cil_code = ip++;
+                       MONO_ADD_INS (bblock, ins);
+                       break;
+               case CEE_LDARG_0:
+               case CEE_LDARG_1:
+               case CEE_LDARG_2:
+               case CEE_LDARG_3:
+                       CHECK_STACK_OVF (1);
+                       n = (*ip)-CEE_LDARG_0;
+                       NEW_ARGLOAD (cfg, ins, n);
+                       ins->cil_code = ip++;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDLOC_0:
+               case CEE_LDLOC_1:
+               case CEE_LDLOC_2:
+               case CEE_LDLOC_3:
+                       CHECK_STACK_OVF (1);
+                       n = (*ip)-CEE_LDLOC_0;
+                       NEW_LOCLOAD (cfg, ins, n);
+                       ins->cil_code = ip++;
+                       *sp++ = ins;
+                       break;
+               case CEE_STLOC_0:
+               case CEE_STLOC_1:
+               case CEE_STLOC_2:
+               case CEE_STLOC_3:
+                       CHECK_STACK (1);
+                       n = (*ip)-CEE_STLOC_0;
+                       --sp;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       NEW_LOCSTORE (cfg, ins, n, *sp);
+                       ins->cil_code = ip;
+                       if (ins->opcode == CEE_STOBJ) {
+                               NEW_LOCLOADA (cfg, ins, n);
+                               handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                       } else
+                               MONO_ADD_INS (bblock, ins);
+                       ++ip;
+                       inline_costs += 1;
+                       break;
+               case CEE_LDARG_S:
+                       CHECK_STACK_OVF (1);
+                       NEW_ARGLOAD (cfg, ins, ip [1]);
+                       ins->cil_code = ip;
+                       *sp++ = ins;
+                       ip += 2;
+                       break;
+               case CEE_LDARGA_S:
+                       CHECK_STACK_OVF (1);
+                       NEW_ARGLOADA (cfg, ins, ip [1]);
+                       ins->cil_code = ip;
+                       *sp++ = ins;
+                       ip += 2;
+                       break;
+               case CEE_STARG_S:
+                       CHECK_STACK (1);
+                       --sp;
+                       NEW_ARGSTORE (cfg, ins, ip [1], *sp);
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       ins->cil_code = ip;
+                       if (ins->opcode == CEE_STOBJ) {
+                               NEW_ARGLOADA (cfg, ins, ip [1]);
+                               handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                       } else
+                               MONO_ADD_INS (bblock, ins);
+                       ip += 2;
+                       break;
+               case CEE_LDLOC_S:
+                       CHECK_STACK_OVF (1);
+                       NEW_LOCLOAD (cfg, ins, ip [1]);
+                       ins->cil_code = ip;
+                       *sp++ = ins;
+                       ip += 2;
+                       break;
+               case CEE_LDLOCA_S:
+                       CHECK_STACK_OVF (1);
+                       NEW_LOCLOADA (cfg, ins, ip [1]);
+                       ins->cil_code = ip;
+                       *sp++ = ins;
+                       ip += 2;
+                       break;
+               case CEE_STLOC_S:
+                       CHECK_STACK (1);
+                       --sp;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       NEW_LOCSTORE (cfg, ins, ip [1], *sp);
+                       ins->cil_code = ip;
+                       if (ins->opcode == CEE_STOBJ) {
+                               NEW_LOCLOADA (cfg, ins, ip [1]);
+                               handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                       } else
+                               MONO_ADD_INS (bblock, ins);
+                       ip += 2;
+                       inline_costs += 1;
+                       break;
+               case CEE_LDNULL:
+                       CHECK_STACK_OVF (1);
+                       NEW_PCONST (cfg, ins, NULL);
+                       ins->cil_code = ip;
+                       ins->type = STACK_OBJ;
+                       ++ip;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_I4_M1:
+                       CHECK_STACK_OVF (1);
+                       NEW_ICONST (cfg, ins, -1);
+                       ins->cil_code = ip;
+                       ++ip;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_I4_0:
+               case CEE_LDC_I4_1:
+               case CEE_LDC_I4_2:
+               case CEE_LDC_I4_3:
+               case CEE_LDC_I4_4:
+               case CEE_LDC_I4_5:
+               case CEE_LDC_I4_6:
+               case CEE_LDC_I4_7:
+               case CEE_LDC_I4_8:
+                       CHECK_STACK_OVF (1);
+                       NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
+                       ins->cil_code = ip;
+                       ++ip;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_I4_S:
+                       CHECK_STACK_OVF (1);
+                       ++ip;
+                       NEW_ICONST (cfg, ins, *((signed char*)ip));
+                       ins->cil_code = ip;
+                       ++ip;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_I4:
+                       CHECK_STACK_OVF (1);
+                       NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
+                       ins->cil_code = ip;
+                       ip += 5;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_I8:
+                       CHECK_STACK_OVF (1);
+                       MONO_INST_NEW (cfg, ins, OP_I8CONST);
+                       ins->cil_code = ip;
+                       ins->type = STACK_I8;
+                       ++ip;
+                       ins->inst_l = (gint64)read64 (ip);
+                       ip += 8;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDC_R4: {
+                       float *f = g_malloc (sizeof (float));
+                       CHECK_STACK_OVF (1);
+                       MONO_INST_NEW (cfg, ins, OP_R4CONST);
+                       ins->type = STACK_R8;
+                       ++ip;
+                       readr4 (ip, f);
+                       ins->inst_p0 = f;
+                       ip += 4;
+                       *sp++ = ins;                    
+                       break;
+               }
+               case CEE_LDC_R8: {
+                       double *d = g_malloc (sizeof (double));
+                       CHECK_STACK_OVF (1);
+                       MONO_INST_NEW (cfg, ins, OP_R8CONST);
+                       ins->type = STACK_R8;
+                       ++ip;
+                       readr8 (ip, d);
+                       ins->inst_p0 = d;
+                       ip += 8;
+                       *sp++ = ins;                    
+                       break;
+               }
+               case CEE_DUP: {
+                       MonoInst *temp, *store;
+                       CHECK_STACK (1);
+                       CHECK_STACK_OVF (1);
+                       sp--;
+                       ins = *sp;
+               
+                       /* 
+                        * small optimization: if the loaded value was from a local already,
+                        * just load it twice.
+                        */
+                       if (ins->ssa_op == MONO_SSA_LOAD && 
+                           (ins->inst_i0->opcode == OP_LOCAL || ins->inst_i0->opcode == OP_ARG)) {
+                               sp++;
+                               MONO_INST_NEW (cfg, temp, 0);
+                               *temp = *ins;
+                               temp->cil_code = ip;
+                               *sp++ = temp;
+                       } else {
+                               temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
+                               temp->cil_code = ip;
+                               NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
+                               store->cil_code = ip;
+                               MONO_ADD_INS (bblock, store);
+                               NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
+                               *sp++ = ins;
+                               ins->cil_code = ip;
+                               NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
+                               *sp++ = ins;
+                               ins->cil_code = ip;
+                       }
+                       ++ip;
+                       inline_costs += 2;
+                       break;
+               }
+               case CEE_POP:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, CEE_POP);
+                       MONO_ADD_INS (bblock, ins);
+                       ins->cil_code = ip++;
+                       --sp;
+                       ins->inst_i0 = *sp;
+                       break;
+               case CEE_JMP:
+                       if (stack_start != sp)
+                               goto unverified;
+                       MONO_INST_NEW (cfg, ins, CEE_JMP);
+                       token = read32 (ip + 1);
+                       /* FIXME: check the signature matches */
+                       cmethod = mono_get_method (image, token, NULL);
+                       /*
+                        * The current magic trampoline can't handle this
+                        * apparently, so we compile the method right away.
+                        * Later, we may need to fix the trampoline or use a different one.
+                        */
+                       ins->inst_p0 = mono_compile_method (cmethod);
+                       MONO_ADD_INS (bblock, ins);
+                       ip += 5;
+                       start_new_bblock = 1;
+                       break;
+               case CEE_CALLI:
+               case CEE_CALL:
+               case CEE_CALLVIRT: {
+                       MonoInst *addr = NULL;
+                       MonoMethodSignature *fsig = NULL;
+                       MonoMethodHeader *cheader;
+                       int mop, temp, array_rank = 0;
+                       int virtual = *ip == CEE_CALLVIRT;
+
+                       token = read32 (ip + 1);
+
+                       if (*ip == CEE_CALLI) {
+                               cmethod = NULL;
+                               cheader = NULL;
+                               CHECK_STACK (1);
+                               --sp;
+                               addr = *sp;
+                               if (method->wrapper_type != MONO_WRAPPER_NONE)
+                                       fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
+                               else
+                                       fsig = mono_metadata_parse_signature (image, token);
+
+                               n = fsig->param_count + fsig->hasthis;
+
+                       } else {
+                               cmethod = mono_get_method (image, token, NULL);
+                               cheader = ((MonoMethodNormal *)cmethod)->header;
+
+                               if (!cmethod->klass->inited)
+                                       mono_class_init (cmethod->klass);
+
+                               if (cmethod->signature->pinvoke) {
+#ifdef MONO_USE_EXC_TABLES
+                                       if (mono_method_blittable (cmethod)) {
+                                               fsig = cmethod->signature;
+                                       } else {
+#endif
+                                               MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod);
+                                               fsig = wrapper->signature;
+#ifdef MONO_USE_EXC_TABLES
+                                       }
+#endif
+                               } else {
+                                       fsig = cmethod->signature;
+                               }
+
+                               n = fsig->param_count + fsig->hasthis;
+
+                               if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
+                                   cmethod->klass->parent == mono_defaults.array_class) {
+                                       array_rank = cmethod->klass->rank;
+                               }
+
+                               if (cmethod->string_ctor)
+                                       g_assert_not_reached ();
+
+                       }
+
+                       CHECK_STACK (n);
+
+                       //g_assert (!virtual || fsig->hasthis);
+
+                       sp -= n;
+
+                       if (cmethod && (mop = mono_find_method_opcode (cmethod))) {
+
+                               MONO_INST_NEW (cfg, ins, mop);
+                               ins->cil_code = ip;
+                               g_assert (n <= 2);
+
+                               if (fsig->param_count > 0) {
+                                       ins->inst_i0 = sp [0];
+                                       if (fsig->param_count > 1)
+                                               ins->inst_i1 = sp [1];
+                               }
+
+                               if (MONO_TYPE_IS_VOID (fsig->ret)) {
+                                       MONO_ADD_INS (bblock, ins);
+                               } else {
+                                       type_to_eval_stack_type (fsig->ret, ins);
+                                       *sp = ins;
+                                       sp++;
+                               }
+
+                               ip += 5;
+                               break;
+                       }
+
+                       if ((cfg->opt & MONO_OPT_INLINE) && 
+                           (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) && 
+                           cmethod && cheader && mono_method_check_inlining (cmethod) &&
+                           method != cmethod && !g_list_find (dont_inline, cmethod)) {
+                               MonoInst *rvar = NULL;
+                               MonoBasicBlock *ebblock, *sbblock;
+                               int costs, new_locals_offset;
+                               
+                               if (cfg->verbose_level > 2)
+                                       g_print ("INLINE START %p %s\n", cmethod,  mono_method_full_name (cmethod, TRUE));
+
+                               mono_jit_stats.inlineable_methods++;
+
+                               /* allocate space to store the return value */
+                               if (!MONO_TYPE_IS_VOID (fsig->ret)) 
+                                       rvar =  mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
+
+                               /* allocate local variables */
+                               new_locals_offset = cfg->num_varinfo;
+                               for (i = 0; i < cheader->num_locals; ++i)
+                                       mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
+                               
+                               /* allocate starte and end blocks */
+                               sbblock = NEW_BBLOCK (cfg);
+                               sbblock->block_num = cfg->num_bblocks++;
+                               sbblock->real_offset = real_offset;
+
+                               ebblock = NEW_BBLOCK (cfg);
+                               ebblock->block_num = cfg->num_bblocks++;
+                               ebblock->real_offset = real_offset;
+                               
+                               dont_inline = g_list_prepend (dont_inline, method);
+                               costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset);
+                               dont_inline = g_list_remove (dont_inline, method);
+                               
+                               if (costs >= 0 && costs < 60) {
+
+                                       mono_jit_stats.inlined_methods++;
+
+                                       /* always add some code to avoid block split failures */
+                                       MONO_INST_NEW (cfg, ins, CEE_NOP);
+                                       MONO_ADD_INS (bblock, ins);
+                                       ins->cil_code = ip;
+
+                                       ip += 5;
+                                       real_offset += 5;
+
+                                       bblock->next_bb = sbblock;
+                                       link_bblock (cfg, bblock, sbblock);
+
+                                       GET_BBLOCK (cfg, bbhash, bblock, ip);
+                                       ebblock->next_bb = bblock;
+                                       link_bblock (cfg, ebblock, bblock);
+
+                                       if (rvar) {
+                                               NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
+                                               *sp++ = ins;
+                                       }
+                                       if (cfg->verbose_level > 2)
+                                               g_print ("INLINE END %s\n", mono_method_full_name (cmethod, TRUE));
+                                       
+                                       // { static int c = 0; printf ("ICOUNT %d %d %s\n", c++, costs, mono_method_full_name (cmethod, TRUE)); }
+
+                                       inline_costs += costs;
+                                       break;
+                               } else {
+
+                                       if (cfg->verbose_level > 2)
+                                               g_print ("INLINE ABORTED %s\n", mono_method_full_name (cmethod, TRUE));
+
+                               }
+                       }
+                       
+                       inline_costs += 10 * num_calls++;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+
+                       /* tail recursion elimination */
+                       if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == cfg->method && ip [5] == CEE_RET) {
+                               gboolean has_vtargs = FALSE;
+                               int i;
+                               
+                               /* keep it simple */
+                               for (i =  fsig->param_count - 1; i >= 0; i--) {
+                                       if (MONO_TYPE_ISSTRUCT (cmethod->signature->params [i])) 
+                                               has_vtargs = TRUE;
+                               }
+
+                               if (!has_vtargs) {
+                                       for (i = 0; i < n; ++i) {
+                                               NEW_ARGSTORE (cfg, ins, i, sp [i]);
+                                               ins->cil_code = ip;
+                                               MONO_ADD_INS (bblock, ins);
+                                       }
+                                       MONO_INST_NEW (cfg, ins, CEE_BR);
+                                       ins->cil_code = ip;
+                                       MONO_ADD_INS (bblock, ins);
+                                       tblock = start_bblock->out_bb [0];
+                                       link_bblock (cfg, bblock, tblock);
+                                       ins->inst_target_bb = tblock;
+                                       start_new_bblock = 1;
+                                       ip += 5;
+                                       
+                                       if (!MONO_TYPE_IS_VOID (fsig->ret)) {
+                                               /* just create a dummy - the value is never used */
+                                               ins = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
+                                               NEW_TEMPLOAD (cfg, *sp, ins->inst_c0);
+                                               sp++;
+                                       }
+
+                                       break;
+                               }
+                       }
+
+                       if (*ip == CEE_CALLI) {
+
+                               if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) {
+                                       NEW_TEMPLOAD (cfg, *sp, temp);
+                                       sp++;
+                               }
+                                       
+                       } else if (array_rank) {
+                               MonoMethodSignature *esig;
+                               MonoInst *addr;
+
+                               if (strcmp (cmethod->name, "Set") == 0) { /* array Set */ 
+                                       esig = mono_get_element_address_signature (fsig->param_count - 1);
+                                       
+                                       temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE);
+                                       NEW_TEMPLOAD (cfg, addr, temp);
+                                       NEW_INDSTORE (cfg, ins, addr, sp [fsig->param_count], fsig->params [fsig->param_count - 1]);
+                                       ins->cil_code = ip;
+                                       if (ins->opcode == CEE_STOBJ) {
+                                               handle_stobj (cfg, bblock, addr, sp [fsig->param_count], ip, mono_class_from_mono_type (fsig->params [fsig->param_count-1]), FALSE, FALSE);
+                                       } else {
+                                               MONO_ADD_INS (bblock, ins);
+                                       }
+
+                               } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
+                                       esig = mono_get_element_address_signature (fsig->param_count);
+
+                                       temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE);
+                                       NEW_TEMPLOAD (cfg, addr, temp);
+                                       NEW_INDLOAD (cfg, ins, addr, fsig->ret);
+                                       ins->cil_code = ip;
+
+                                       *sp++ = ins;
+                               } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
+                                       /* implement me */
+                                       esig = mono_get_element_address_signature (fsig->param_count);
+
+                                       temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE);
+                                       NEW_TEMPLOAD (cfg, *sp, temp);
+                                       sp++;
+                               } else {
+                                       g_assert_not_reached ();
+                               }
+
+                       } else {
+                               if (0 && CODE_IS_STLOC (ip + 5) && (!MONO_TYPE_ISSTRUCT (fsig->ret)) && (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor)) {
+                                       /* no need to spill */
+                                       ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL);
+                                       *sp++ = ins;
+                               } else {
+                                       if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, virtual ? sp [0] : NULL)) != -1) {
+                                               NEW_TEMPLOAD (cfg, *sp, temp);
+                                               sp++;
+                                       }
+                               }
+                       }
+
+                       ip += 5;
+                       break;
+               }
+               case CEE_RET:
+                       if (cfg->method != method) {
+                               /* return from inlined methode */
+                               if (return_var) {
+                                       MonoInst *store;
+                                       CHECK_STACK (1);
+                                       --sp;
+                                       //g_assert (returnvar != -1);
+                                       NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
+                                       store->cil_code = sp [0]->cil_code;
+                                       if (store->opcode == CEE_STOBJ) {
+                                               g_assert_not_reached ();
+                                               NEW_TEMPLOADA (cfg, store, return_var->inst_c0);
+                                               handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, return_var->klass, FALSE, FALSE);
+                                       } else
+                                               MONO_ADD_INS (bblock, store);
+                               } 
+                       } else {
+                               if (cfg->ret) {
+                                       g_assert (!return_var);
+                                       CHECK_STACK (1);
+                                       --sp;
+                                       MONO_INST_NEW (cfg, ins, CEE_NOP);
+                                       ins->opcode = mono_type_to_stind (method->signature->ret);
+                                       if (ins->opcode == CEE_STOBJ) {
+                                               NEW_RETLOADA (cfg, ins);
+                                               handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                                       } else {
+                                               ins->opcode = OP_SETRET;
+                                               ins->cil_code = ip;
+                                               ins->inst_i0 = *sp;;
+                                               ins->inst_i1 = NULL;
+                                               MONO_ADD_INS (bblock, ins);
+                                       }
+                               }
+                       }
+                       if (sp != stack_start)
+                               goto unverified;
+                       MONO_INST_NEW (cfg, ins, CEE_BR);
+                       ins->cil_code = ip++;
+                       ins->inst_target_bb = end_bblock;
+                       MONO_ADD_INS (bblock, ins);
+                       link_bblock (cfg, bblock, end_bblock);
+                       start_new_bblock = 1;
+                       break;
+               case CEE_BR_S:
+                       MONO_INST_NEW (cfg, ins, CEE_BR);
+                       ins->cil_code = ip++;
+                       MONO_ADD_INS (bblock, ins);
+                       target = ip + 1 + (signed char)(*ip);
+                       ++ip;
+                       GET_BBLOCK (cfg, bbhash, tblock, target);
+                       link_bblock (cfg, bblock, tblock);
+                       CHECK_BBLOCK (target, ip, tblock);
+                       ins->inst_target_bb = tblock;
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       start_new_bblock = 1;
+                       inline_costs += 10;
+                       break;
+               case CEE_BRFALSE_S:
+               case CEE_BRTRUE_S:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
+                       ins->cil_code = ip++;
+                       target = ip + 1 + *(signed char*)ip;
+                       ip++;
+                       ADD_UNCOND (ins->opcode == CEE_BRTRUE);
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       inline_costs += 10;
+                       break;
+               case CEE_BEQ_S:
+               case CEE_BGE_S:
+               case CEE_BGT_S:
+               case CEE_BLE_S:
+               case CEE_BLT_S:
+               case CEE_BNE_UN_S:
+               case CEE_BGE_UN_S:
+               case CEE_BGT_UN_S:
+               case CEE_BLE_UN_S:
+               case CEE_BLT_UN_S:
+                       CHECK_STACK (2);
+                       MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
+                       ins->cil_code = ip++;
+                       target = ip + 1 + *(signed char*)ip;
+                       ip++;
+                       ADD_BINCOND (NULL);
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       inline_costs += 10;
+                       break;
+               case CEE_BR:
+                       MONO_INST_NEW (cfg, ins, CEE_BR);
+                       ins->cil_code = ip++;
+                       MONO_ADD_INS (bblock, ins);
+                       target = ip + 4 + (gint32)read32(ip);
+                       ip += 4;
+                       GET_BBLOCK (cfg, bbhash, tblock, target);
+                       link_bblock (cfg, bblock, tblock);
+                       CHECK_BBLOCK (target, ip, tblock);
+                       ins->inst_target_bb = tblock;
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       start_new_bblock = 1;
+                       inline_costs += 10;
+                       break;
+               case CEE_BRFALSE:
+               case CEE_BRTRUE:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip++;
+                       target = ip + 4 + (gint32)read32(ip);
+                       ip += 4;
+                       ADD_UNCOND(ins->opcode == CEE_BRTRUE);
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       inline_costs += 10;
+                       break;
+               case CEE_BEQ:
+               case CEE_BGE:
+               case CEE_BGT:
+               case CEE_BLE:
+               case CEE_BLT:
+               case CEE_BNE_UN:
+               case CEE_BGE_UN:
+               case CEE_BGT_UN:
+               case CEE_BLE_UN:
+               case CEE_BLT_UN:
+                       CHECK_STACK (2);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip++;
+                       target = ip + 4 + (gint32)read32(ip);
+                       ip += 4;
+                       ADD_BINCOND(NULL);
+                       if (sp != stack_start) {
+                               handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+                               sp = stack_start;
+                       }
+                       inline_costs += 10;
+                       break;
+               case CEE_SWITCH:
+                       CHECK_STACK (1);
+                       n = read32 (ip + 1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       --sp;
+                       ins->inst_left = *sp;
+                       if (ins->inst_left->type != STACK_I4) goto unverified;
+                       ins->cil_code = ip;
+                       ip += 5;
+                       target = ip + n * sizeof (guint32);
+                       MONO_ADD_INS (bblock, ins);
+                       GET_BBLOCK (cfg, bbhash, tblock, target);
+                       link_bblock (cfg, bblock, tblock);
+                       ins->klass = GUINT_TO_POINTER (n);
+                       ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (n + 1));
+                       ins->inst_many_bb [n] = tblock;
+
+                       for (i = 0; i < n; ++i) {
+                               GET_BBLOCK (cfg, bbhash, tblock, target + (gint32)read32(ip));
+                               link_bblock (cfg, bblock, tblock);
+                               ins->inst_many_bb [i] = tblock;
+                               ip += 4;
+                       }
+                       /* FIXME: handle stack args */
+                       inline_costs += 20;
+                       break;
+               case CEE_LDIND_I1:
+               case CEE_LDIND_U1:
+               case CEE_LDIND_I2:
+               case CEE_LDIND_U2:
+               case CEE_LDIND_I4:
+               case CEE_LDIND_U4:
+               case CEE_LDIND_I8:
+               case CEE_LDIND_I:
+               case CEE_LDIND_R4:
+               case CEE_LDIND_R8:
+               case CEE_LDIND_REF:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip;
+                       --sp;
+                       ins->inst_i0 = *sp;
+                       *sp++ = ins;
+                       ins->type = ldind_type [*ip - CEE_LDIND_I1];
+                       ins->flags |= ins_flag;
+                       ins_flag = 0;
+                       ++ip;
+                       break;
+               case CEE_STIND_REF:
+               case CEE_STIND_I1:
+               case CEE_STIND_I2:
+               case CEE_STIND_I4:
+               case CEE_STIND_I8:
+               case CEE_STIND_R4:
+               case CEE_STIND_R8:
+                       CHECK_STACK (2);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip++;
+                       sp -= 2;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       MONO_ADD_INS (bblock, ins);
+                       ins->inst_i0 = sp [0];
+                       ins->inst_i1 = sp [1];
+                       ins->flags |= ins_flag;
+                       ins_flag = 0;
+                       inline_costs += 1;
+                       break;
+               case CEE_ADD:
+               case CEE_SUB:
+               case CEE_MUL:
+               case CEE_DIV:
+               case CEE_DIV_UN:
+               case CEE_REM:
+               case CEE_REM_UN:
+               case CEE_AND:
+               case CEE_OR:
+               case CEE_XOR:
+               case CEE_SHL:
+               case CEE_SHR:
+               case CEE_SHR_UN:
+                       CHECK_STACK (2);
+                       ADD_BINOP (*ip);
+                       ip++;
+                       break;
+               case CEE_NEG:
+               case CEE_NOT:
+               case CEE_CONV_I1:
+               case CEE_CONV_I2:
+               case CEE_CONV_I4:
+               case CEE_CONV_I8:
+               case CEE_CONV_R4:
+               case CEE_CONV_R8:
+               case CEE_CONV_U4:
+               case CEE_CONV_U8:
+               case CEE_CONV_OVF_I8:
+               case CEE_CONV_OVF_U8:
+               case CEE_CONV_R_UN:
+                       CHECK_STACK (1);
+                       ADD_UNOP (*ip);
+                       ip++;
+                       break;
+               case CEE_CONV_OVF_I4:
+               case CEE_CONV_OVF_I1:
+               case CEE_CONV_OVF_I2:
+               case CEE_CONV_OVF_I:
+               case CEE_CONV_OVF_U:
+                       CHECK_STACK (1);
+
+                       if (sp [-1]->type == STACK_R8) {
+                               ADD_UNOP (CEE_CONV_OVF_I8);
+                               ADD_UNOP (*ip);
+                       } else {
+                               ADD_UNOP (*ip);
+                       }
+
+                       ip++;
+                       break;
+               case CEE_CONV_OVF_U1:
+               case CEE_CONV_OVF_U2:
+               case CEE_CONV_OVF_U4:
+                       CHECK_STACK (1);
+
+                       if (sp [-1]->type == STACK_R8) {
+                               ADD_UNOP (CEE_CONV_OVF_U8);
+                               ADD_UNOP (*ip);
+                       } else {
+                               ADD_UNOP (*ip);
+                       }
+
+                       ip++;
+                       break;
+               case CEE_CONV_OVF_I1_UN:
+               case CEE_CONV_OVF_I2_UN:
+               case CEE_CONV_OVF_I4_UN:
+               case CEE_CONV_OVF_I8_UN:
+               case CEE_CONV_OVF_U1_UN:
+               case CEE_CONV_OVF_U2_UN:
+               case CEE_CONV_OVF_U4_UN:
+               case CEE_CONV_OVF_U8_UN:
+               case CEE_CONV_OVF_I_UN:
+               case CEE_CONV_OVF_U_UN:
+                       CHECK_STACK (1);
+                       ADD_UNOP (*ip);
+                       ip++;
+                       break;
+               case CEE_CPOBJ:
+                       g_error ("opcode 0x%02x not handled", *ip);
+                       break;
+               case CEE_LDOBJ: {
+                       MonoInst *iargs [3];
+                       CHECK_STACK (1);
+                       --sp;
+                       token = read32 (ip + 1);
+                       if (method->wrapper_type != MONO_WRAPPER_NONE)
+                               klass = mono_method_get_wrapper_data (method, token);
+                       else
+                               klass = mono_class_get (image, token);
+                       mono_class_init (klass);
+                       n = mono_class_value_size (klass, NULL);
+                       ins = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
+                       NEW_TEMPLOADA (cfg, iargs [0], ins->inst_c0);
+                       iargs [1] = *sp;
+                       NEW_ICONST (cfg, iargs [2], n);
+                       mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip);
+                       NEW_TEMPLOAD (cfg, *sp, ins->inst_c0);
+                       ++sp;
+                       ip += 5;
+                       inline_costs += 1;
+                       break;
+               }
+               case CEE_LDSTR:
+                       CHECK_STACK_OVF (1);
+                       n = read32 (ip + 1);
+
+                       if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) {
+                               int temp;
+                               MonoInst *iargs [3];
+                               NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0);
+                               NEW_IMAGECONST (cfg, iargs [1], image);
+                               NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_ldstr, iargs, ip);
+                               NEW_TEMPLOAD (cfg, *sp, temp);
+                       } else {
+                               NEW_PCONST (cfg, ins, NULL);
+                               ins->cil_code = ip;
+                               ins->type = STACK_OBJ;
+                               ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
+                               *sp = ins;
+                       }
+                       sp++;
+                       ip += 5;
+                       break;
+               case CEE_NEWOBJ: {
+                       MonoInst *iargs [2];
+                       int temp;
+
+                       token = read32 (ip + 1);
+                       if (method->wrapper_type != MONO_WRAPPER_NONE) {
+                               cmethod = mono_method_get_wrapper_data (method, token);
+                       } else
+                               cmethod = mono_get_method (image, token, NULL);
+
+                       mono_class_init (cmethod->klass);
+
+                       n = cmethod->signature->param_count;
+                       CHECK_STACK (n);
+
+                       /* move the args to allow room for 'this' in the first position */
+                       while (n--) {
+                               --sp;
+                               sp [1] = sp [0];
+                       }
+
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       
+
+                       if (cmethod->klass->parent == mono_defaults.array_class) {
+                               NEW_METHODCONST (cfg, *sp, cmethod);
+                               temp = mono_emit_native_call (cfg, bblock, mono_array_new_va, cmethod->signature, sp, ip, FALSE);
+
+                       } else if (cmethod->string_ctor) {
+                               /* we simply pass a null pointer */
+                               NEW_PCONST (cfg, *sp, NULL); 
+                               /* now call the string ctor */
+                               temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL);
+                       } else {
+                               if (cmethod->klass->valuetype) {
+                                       iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
+                                       temp = iargs [0]->inst_c0;
+                                       NEW_TEMPLOADA (cfg, *sp, temp);
+                               } else {
+                                       NEW_DOMAINCONST (cfg, iargs [0]);
+                                       NEW_CLASSCONST (cfg, iargs [1], cmethod->klass);
+
+                                       temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip);
+                                       NEW_TEMPLOAD (cfg, *sp, temp);
+                               }
+
+                               /* now call the actual ctor */
+                               mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL);
+                       }
+
+                       NEW_TEMPLOAD (cfg, *sp, temp);
+                       sp++;
+                       
+                       ip += 5;
+                       inline_costs += 5;
+                       break;
+               }
+               case CEE_ISINST:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       --sp;
+                       klass = mono_class_get (image, read32 (ip + 1));
+                       mono_class_init (klass);
+                       ins->type = STACK_OBJ;
+                       ins->inst_left = *sp;
+                       ins->inst_newa_class = klass;
+                       ins->cil_code = ip;
+                       ip += 5;
+                       *sp++ = ins;
+                       break;
+               case CEE_UNBOX: {
+                       MonoInst *add, *vtoffset;
+                       /* FIXME: need to check class: move to inssel.brg? */
+                       CHECK_STACK (1);
+                       --sp;
+                       token = read32 (ip + 1);
+                       if (method->wrapper_type != MONO_WRAPPER_NONE)
+                               klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
+                       else 
+                               klass = mono_class_get (image, token);
+                       mono_class_init (klass);
+                       MONO_INST_NEW (cfg, add, CEE_ADD);
+                       NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
+                       add->inst_left = *sp;
+                       add->inst_right = vtoffset;
+                       add->type = STACK_MP;
+                       *sp++ = add;
+                       ip += 5;
+                       inline_costs += 1;
+                       break;
+               }
+               case CEE_CASTCLASS:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       --sp;
+                       klass = mono_class_get (image, read32 (ip + 1));
+                       mono_class_init (klass);
+                       ins->type = STACK_OBJ;
+                       ins->inst_left = *sp;
+                       ins->klass = klass;
+                       ins->inst_newa_class = klass;
+                       ins->cil_code = ip;
+                       ip += 5;
+                       *sp++ = ins;
+                       break;
+               case CEE_THROW:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       --sp;
+                       ins->inst_left = *sp;
+                       ins->cil_code = ip++;
+                       MONO_ADD_INS (bblock, ins);
+                       sp = stack_start;
+                       start_new_bblock = 1;
+                       break;
+               case CEE_LDFLD:
+               case CEE_LDFLDA:
+               case CEE_STFLD: {
+                       MonoInst *offset_ins;
+                       MonoClassField *field;
+                       guint foffset;
+
+                       if (*ip == CEE_STFLD) {
+                               CHECK_STACK (2);
+                               sp -= 2;
+                       } else {
+                               CHECK_STACK (1);
+                               --sp;
+                       }
+                       // FIXME: enable this test later.
+                       //if (sp [0]->type != STACK_OBJ && sp [0]->type != STACK_MP)
+                       //      goto unverified;
+                       token = read32 (ip + 1);
+                       field = mono_field_from_token (image, token, &klass);
+                       mono_class_init (klass);
+                       foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
+                       /* FIXME: mark instructions for use in SSA */
+                       if (*ip == CEE_STFLD) {
+                               if (klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) {
+                                       /* fixme: we need to inline that call somehow */
+                                       MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type); 
+                                       MonoInst *iargs [5];
+                                       iargs [0] = sp [0];
+                                       NEW_CLASSCONST (cfg, iargs [1], klass);
+                                       NEW_FIELDCONST (cfg, iargs [2], field);
+                                       NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
+                                       iargs [4] = sp [1];
+                                       mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, iargs, ip, NULL);
+                               } else {
+                                       MonoInst *store;
+                                       NEW_ICONST (cfg, offset_ins, foffset);
+                                       MONO_INST_NEW (cfg, ins, CEE_ADD);
+                                       ins->cil_code = ip;
+                                       ins->inst_left = *sp;
+                                       ins->inst_right = offset_ins;
+                                       ins->type = STACK_MP;
+
+                                       MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type));
+                                       store->cil_code = ip;
+                                       store->inst_left = ins;
+                                       store->inst_right = sp [1];
+                                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                                       store->flags |= ins_flag;
+                                       ins_flag = 0;
+                                       if (store->opcode == CEE_STOBJ) {
+                                               handle_stobj (cfg, bblock, ins, sp [1], ip, 
+                                                             mono_class_from_mono_type (field->type), FALSE, FALSE);
+                                       } else
+                                               MONO_ADD_INS (bblock, store);
+                               }
+                       } else {
+                               if (klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) {
+                                       /* fixme: we need to inline that call somehow */
+                                       MonoMethod *ldfld_wrapper = mono_marshal_get_ldfld_wrapper (field->type); 
+                                       MonoInst *iargs [4];
+                                       int temp;
+                                       iargs [0] = sp [0];
+                                       NEW_CLASSCONST (cfg, iargs [1], klass);
+                                       NEW_FIELDCONST (cfg, iargs [2], field);
+                                       NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
+                                       temp = mono_emit_method_call_spilled (cfg, bblock, ldfld_wrapper, iargs, ip, NULL);
+                                       if (*ip == CEE_LDFLDA) {
+                                               /* not sure howto handle this */
+                                               NEW_TEMPLOADA (cfg, *sp, temp);
+                                       } else {
+                                               NEW_TEMPLOAD (cfg, *sp, temp);
+                                       }
+                                       sp++;
+                               } else {
+                                       NEW_ICONST (cfg, offset_ins, foffset);
+                                       MONO_INST_NEW (cfg, ins, CEE_ADD);
+                                       ins->cil_code = ip;
+                                       ins->inst_left = *sp;
+                                       ins->inst_right = offset_ins;
+                                       ins->type = STACK_MP;
+
+                                       if (*ip == CEE_LDFLDA) {
+                                               *sp++ = ins;
+                                       } else {
+                                               MonoInst *load;
+                                               MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type));
+                                               type_to_eval_stack_type (field->type, load);
+                                               load->cil_code = ip;
+                                               load->inst_left = ins;
+                                               load->flags |= ins_flag;
+                                               ins_flag = 0;
+                                               *sp++ = load;
+                                       }
+                               }
+                       }
+                       ip += 5;
+                       break;
+               }
+               case CEE_LDSFLD:
+               case CEE_LDSFLDA:
+               case CEE_STSFLD: {
+                       MonoClassField *field;
+                       MonoVTable *vtable;
+
+                       token = read32 (ip + 1);
+
+                       field = mono_field_from_token (image, token, &klass);
+                       mono_class_init (klass);
+
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                               
+                       if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) {
+                               int temp;
+                               MonoInst *iargs [2];
+                               g_assert (field->parent);
+                               NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0);
+                               NEW_FIELDCONST (cfg, iargs [1], field);
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_class_static_field_address, iargs, ip);
+                               NEW_TEMPLOAD (cfg, ins, temp);
+                       } else {
+                               vtable = mono_class_vtable (cfg->domain, klass);
+                               NEW_PCONST (cfg, ins, (char*)vtable->data + field->offset);
+                               ins->cil_code = ip;
+                       }
+
+                       /* FIXME: mark instructions for use in SSA */
+                       if (*ip == CEE_LDSFLDA) {
+                               *sp++ = ins;
+                       } else if (*ip == CEE_STSFLD) {
+                               MonoInst *store;
+                               CHECK_STACK (1);
+                               sp--;
+                               MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type));
+                               store->cil_code = ip;
+                               store->inst_left = ins;
+                               store->inst_right = sp [0];
+                               store->flags |= ins_flag;
+                               ins_flag = 0;
+
+                               if (store->opcode == CEE_STOBJ) {
+                                       handle_stobj (cfg, bblock, ins, sp [0], ip, mono_class_from_mono_type (field->type), FALSE, FALSE);
+                               } else
+                                       MONO_ADD_INS (bblock, store);
+                       } else {
+                               MonoInst *load;
+                               CHECK_STACK_OVF (1);
+                               MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type));
+                               type_to_eval_stack_type (field->type, load);
+                               load->cil_code = ip;
+                               load->inst_left = ins;
+                               *sp++ = load;
+                               load->flags |= ins_flag;
+                               ins_flag = 0;
+                       /* fixme: dont see the problem why this does not work */
+                               //cfg->disable_aot = TRUE;
+                       }
+                       ip += 5;
+                       break;
+               }
+               case CEE_STOBJ:
+                       CHECK_STACK (2);
+                       sp -= 2;
+                       token = read32 (ip + 1);
+                       if (method->wrapper_type != MONO_WRAPPER_NONE)
+                               klass = mono_method_get_wrapper_data (method, token);
+                       else
+                               klass = mono_class_get (image, token);
+                       mono_class_init (klass);
+                       handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE);
+                       ip += 5;
+                       inline_costs += 1;
+                       break;
+               case CEE_BOX: {
+                       MonoInst *iargs [2];
+                       MonoInst *load, *vtoffset, *add, *val, *vstore;
+                       int temp;
+                       CHECK_STACK (1);
+                       --sp;
+                       val = *sp;
+                       token = read32 (ip + 1);
+                       if (method->wrapper_type != MONO_WRAPPER_NONE)
+                               klass = mono_method_get_wrapper_data (method, token);
+                       else
+                               klass = mono_class_get (image, token);
+                       mono_class_init (klass);
+
+                       /* much like NEWOBJ */
+                       NEW_DOMAINCONST (cfg, iargs [0]);
+                       NEW_CLASSCONST (cfg, iargs [1], klass);
+                       
+                       temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip);
+                       NEW_TEMPLOAD (cfg, load, temp);
+                       NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
+                       MONO_INST_NEW (cfg, add, CEE_ADD);
+                       add->inst_left = load;
+                       add->inst_right = vtoffset;
+                       add->cil_code = ip;
+                       add->klass = klass;
+                       MONO_INST_NEW (cfg, vstore, CEE_STIND_I);
+                       vstore->opcode = mono_type_to_stind (&klass->byval_arg);
+                       vstore->cil_code = ip;
+                       vstore->inst_left = add;
+                       vstore->inst_right = val;
+
+                       if (vstore->opcode == CEE_STOBJ) {
+                               handle_stobj (cfg, bblock, add, val, ip, klass, FALSE, FALSE);
+                       } else
+                               MONO_ADD_INS (bblock, vstore);
+
+                       NEW_TEMPLOAD (cfg, load, temp);
+                       *sp++ = load;
+                       ip += 5;
+                       inline_costs += 1;
+                       break;
+               }
+               case CEE_NEWARR:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip;
+                       --sp;
+
+                       token = read32 (ip + 1);
+
+                       /* allocate the domainvar - becaus this is used in decompose_foreach */
+                       if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)
+                               mono_get_domainvar (cfg);
+                       
+                       if (method->wrapper_type != MONO_WRAPPER_NONE)
+                               klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
+                       else
+                               klass = mono_class_get (image, token);
+
+                       mono_class_init (klass);
+                       ins->inst_newa_class = klass;
+                       ins->inst_newa_len = *sp;
+                       ins->type = STACK_OBJ;
+                       ip += 5;
+                       *sp++ = ins;
+                       inline_costs += 1;
+                       break;
+               case CEE_LDLEN:
+                       CHECK_STACK (1);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       ins->cil_code = ip++;
+                       --sp;
+                       ins->inst_left = *sp;
+                       ins->type = STACK_PTR;
+                       *sp++ = ins;
+                       break;
+               case CEE_LDELEMA:
+                       CHECK_STACK (2);
+                       sp -= 2;
+                       klass = mono_class_get (image, read32 (ip + 1));
+                       mono_class_init (klass);
+                       NEW_LDELEMA (cfg, ins, sp, klass);
+                       ins->cil_code = ip;
+                       *sp++ = ins;
+                       ip += 5;
+                       break;
+               case CEE_LDELEM_I1:
+               case CEE_LDELEM_U1:
+               case CEE_LDELEM_I2:
+               case CEE_LDELEM_U2:
+               case CEE_LDELEM_I4:
+               case CEE_LDELEM_U4:
+               case CEE_LDELEM_I8:
+               case CEE_LDELEM_I:
+               case CEE_LDELEM_R4:
+               case CEE_LDELEM_R8:
+               case CEE_LDELEM_REF: {
+                       MonoInst *load;
+                       /*
+                        * translate to:
+                        * ldind.x (ldelema (array, index))
+                        * ldelema does the bounds check
+                        */
+                       CHECK_STACK (2);
+                       sp -= 2;
+                       klass = array_access_to_klass (*ip);
+                       NEW_LDELEMA (cfg, load, sp, klass);
+                       load->cil_code = ip;
+                       MONO_INST_NEW (cfg, ins, ldelem_to_ldind [*ip - CEE_LDELEM_I1]);
+                       ins->cil_code = ip;
+                       ins->inst_left = load;
+                       *sp++ = ins;
+                       ins->type = ldind_type [ins->opcode - CEE_LDIND_I1];
+                       ++ip;
+                       break;
+               }
+               case CEE_STELEM_I:
+               case CEE_STELEM_I1:
+               case CEE_STELEM_I2:
+               case CEE_STELEM_I4:
+               case CEE_STELEM_I8:
+               case CEE_STELEM_R4:
+               case CEE_STELEM_R8:
+               case CEE_STELEM_REF: {
+                       MonoInst *load;
+                       /*
+                        * translate to:
+                        * stind.x (ldelema (array, index), val)
+                        * ldelema does the bounds check
+                        */
+                       CHECK_STACK (3);
+                       sp -= 3;
+                       klass = array_access_to_klass (*ip);
+                       NEW_LDELEMA (cfg, load, sp, klass);
+                       load->cil_code = ip;
+                       MONO_INST_NEW (cfg, ins, stelem_to_stind [*ip - CEE_STELEM_I]);
+                       ins->cil_code = ip;
+                       ins->inst_left = load;
+                       ins->inst_right = sp [2];
+                       ++ip;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       MONO_ADD_INS (bblock, ins);
+                       /* FIXME: add the implicit STELEM_REF castclass */
+                       inline_costs += 1;
+                       cfg->disable_ssa = TRUE;
+                       break;
+               }
+               case CEE_CKFINITE: {
+                       MonoInst *store, *temp;
+                       CHECK_STACK (1);
+
+                       /* this instr. can throw exceptions as side effect,
+                        * so we cant eliminate dead code which contains CKFINITE opdodes.
+                        * Spilling to memory makes sure that we always perform
+                        * this check */
+
+                       
+                       MONO_INST_NEW (cfg, ins, CEE_CKFINITE);
+                       ins->cil_code = ip;
+                       ins->inst_left = sp [-1];
+                       temp = mono_compile_create_var (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL);
+
+                       NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
+                       store->cil_code = ip;
+                       MONO_ADD_INS (bblock, store);
+
+                       NEW_TEMPLOAD (cfg, sp [-1], temp->inst_c0);
+                      
+                       ++ip;
+                       break;
+               }
+               case CEE_REFANYVAL:
+               case CEE_MKREFANY:
+                       g_error ("opcode 0x%02x not handled", *ip);
+                       break;
+               case CEE_LDTOKEN: {
+                       gpointer handle;
+                       MonoClass *handle_class;
+
+                       CHECK_STACK_OVF (1);
+
+                       n = read32 (ip + 1);
+
+                       handle = mono_ldtoken (image, n, &handle_class);
+                       mono_class_init (handle_class);
+
+                       if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) {
+                               int temp;
+                               MonoInst *res, *store, *addr, *vtvar, *iargs [2];
+
+                               vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL); 
+
+                               NEW_IMAGECONST (cfg, iargs [0], image);
+                               NEW_ICONST (cfg, iargs [1], n);
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_ldtoken_wrapper, iargs, ip);
+                               NEW_TEMPLOAD (cfg, res, temp);
+                               NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
+                               NEW_INDSTORE (cfg, store, addr, res, &mono_defaults.int_class->byval_arg);
+                               MONO_ADD_INS (bblock, store);
+                               NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
+                       } else {
+                               if ((ip [5] == CEE_CALL) && (cmethod = mono_get_method (image, read32 (ip + 6), NULL)) &&
+                                               (cmethod->klass == mono_defaults.monotype_class->parent) &&
+                                               (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
+                                       MonoClass *tclass = mono_class_from_mono_type (handle);
+                                       mono_class_init (tclass);
+                                       NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle));
+                                       ins->type = STACK_OBJ;
+                                       ins->klass = cmethod->klass;
+                                       ip += 5;
+                               } else {
+                                       NEW_PCONST (cfg, ins, handle);
+                                       ins->type = STACK_VTYPE;
+                                       ins->klass = handle_class;
+                               }
+                       }
+
+                       *sp++ = ins;
+                       ip += 5;
+                       break;
+               }
+               case CEE_CONV_U2:
+               case CEE_CONV_U1:
+               case CEE_CONV_I:
+                       CHECK_STACK (1);
+                       ADD_UNOP (*ip);
+                       ip++;
+                       break;
+               case CEE_ADD_OVF:
+               case CEE_ADD_OVF_UN:
+               case CEE_MUL_OVF:
+               case CEE_MUL_OVF_UN:
+               case CEE_SUB_OVF:
+               case CEE_SUB_OVF_UN:
+                       CHECK_STACK (2);
+                       ADD_BINOP (*ip);
+                       ip++;
+                       break;
+               case CEE_ENDFINALLY:
+                       /* FIXME: check stack state */
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       MONO_ADD_INS (bblock, ins);
+                       ins->cil_code = ip++;
+                       start_new_bblock = 1;
+                       break;
+               case CEE_LEAVE:
+               case CEE_LEAVE_S:
+                       if (*ip == CEE_LEAVE) {
+                               target = ip + 5 + (gint32)read32(ip + 1);
+                       } else {
+                               target = ip + 2 + (signed char)(ip [1]);
+                       }
+
+                       /* empty the stack */
+                       while (sp != stack_start) {
+                               MONO_INST_NEW (cfg, ins, CEE_POP);
+                               ins->cil_code = ip;
+                               sp--;
+                               ins->inst_i0 = *sp;
+                               MONO_ADD_INS (bblock, ins);
+                       }
+
+                       /* fixme: call fault handler ? */
+
+                       if ((tblock = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
+                               link_bblock (cfg, bblock, tblock);
+                               MONO_INST_NEW (cfg, ins, OP_HANDLER);
+                               ins->cil_code = ip;
+                               ins->inst_target_bb = tblock;
+                               MONO_ADD_INS (bblock, ins);
+                       } 
+
+
+                       MONO_INST_NEW (cfg, ins, CEE_BR);
+                       ins->cil_code = ip;
+                       MONO_ADD_INS (bblock, ins);
+                       GET_BBLOCK (cfg, bbhash, tblock, target);
+                       link_bblock (cfg, bblock, tblock);
+                       CHECK_BBLOCK (target, ip, tblock);
+                       ins->inst_target_bb = tblock;
+                       start_new_bblock = 1;
+
+                       if (*ip == CEE_LEAVE)
+                               ip += 5;
+                       else
+                               ip += 2;
+
+
+                       break;
+               case CEE_STIND_I:
+                       CHECK_STACK (2);
+                       MONO_INST_NEW (cfg, ins, *ip);
+                       sp -= 2;
+                       handle_loaded_temps (cfg, bblock, stack_start, sp);
+                       MONO_ADD_INS (bblock, ins);
+                       ins->cil_code = ip++;
+                       ins->inst_i0 = sp [0];
+                       ins->inst_i1 = sp [1];
+                       inline_costs += 1;
+                       break;
+               case CEE_CONV_U:
+                       CHECK_STACK (1);
+                       ADD_UNOP (*ip);
+                       ip++;
+                       break;
+               /* trampoline mono specific opcodes */
+               case MONO_CUSTOM_PREFIX: {
+
+                       g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
+
+                       switch (ip [1]) {
+
+                       case CEE_MONO_FUNC1: {
+                               int temp;
+                               gpointer func = NULL;
+                               CHECK_STACK (1);
+                               sp--;
+
+                               switch (ip [2]) {
+                               case MONO_MARSHAL_CONV_STR_LPWSTR:
+                                       func = mono_string_to_utf16;
+                                       break;
+                               case MONO_MARSHAL_CONV_LPWSTR_STR:
+                                       func = mono_string_from_utf16;
+                                       break;
+                               case MONO_MARSHAL_CONV_LPSTR_STR:
+                                       func = mono_string_new_wrapper;
+                                       break;
+                               case MONO_MARSHAL_CONV_STR_LPTSTR:
+                               case MONO_MARSHAL_CONV_STR_LPSTR:
+                                       func = mono_string_to_utf8;
+                                       break;
+                               case MONO_MARSHAL_CONV_STR_BSTR:
+                                       func = mono_string_to_bstr;
+                                       break;
+                               case MONO_MARSHAL_CONV_STR_TBSTR:
+                               case MONO_MARSHAL_CONV_STR_ANSIBSTR:
+                                       func = mono_string_to_ansibstr;
+                                       break;
+                               case MONO_MARSHAL_CONV_SB_LPSTR:
+                                       func = mono_string_builder_to_utf8;
+                                       break;
+                               case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY:
+                                       func = mono_array_to_savearray;
+                                       break;
+                               case MONO_MARSHAL_CONV_ARRAY_LPARRAY:
+                                       func = mono_array_to_lparray;
+                                       break;
+                               case MONO_MARSHAL_CONV_DEL_FTN:
+                                       func = mono_delegate_to_ftnptr;
+                                       break;
+                               case MONO_MARSHAL_CONV_STRARRAY_STRLPARRAY:
+                                       func = mono_marshal_string_array;
+                                       break;
+                               default:
+                                       g_warning ("unknown conversion %d\n", ip [2]);
+                                       g_assert_not_reached ();
+                               }
+
+                               temp = mono_emit_jit_icall (cfg, bblock, func, sp, ip);
+                               NEW_TEMPLOAD (cfg, *sp, temp);
+                               sp++;
+
+                               ip += 3;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_PROC2: {
+                               gpointer func = NULL;
+                               CHECK_STACK (2);
+                               sp -= 2;
+
+                               switch (ip [2]) {
+                               case MONO_MARSHAL_CONV_LPSTR_SB:
+                                       func = mono_string_utf8_to_builder;
+                                       break;
+                               case MONO_MARSHAL_FREE_ARRAY:
+                                       func = mono_marshal_free_array;
+                                       break;
+                               default:
+                                       g_assert_not_reached ();
+                               }
+
+                               mono_emit_jit_icall (cfg, bblock, func, sp, ip);
+                               ip += 3;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_PROC3: {
+                               gpointer func = NULL;
+                               CHECK_STACK (3);
+                               sp -= 3;
+
+                               switch (ip [2]) {
+                               case MONO_MARSHAL_CONV_STR_BYVALSTR:
+                                       func = mono_string_to_byvalstr;
+                                       break;
+                               case MONO_MARSHAL_CONV_STR_BYVALWSTR:
+                                       func = mono_string_to_byvalwstr;
+                                       break;
+                               default:
+                                       g_assert_not_reached ();
+                               }
+
+                               mono_emit_jit_icall (cfg, bblock, func, sp, ip);
+                               ip += 3;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_FREE:
+                               CHECK_STACK (1);
+                               sp -= 1;
+                               mono_emit_jit_icall (cfg, bblock, g_free, sp, ip);
+                               ip += 2;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       case CEE_MONO_LDPTR:
+                               CHECK_STACK_OVF (1);
+                               token = read32 (ip + 2);
+                               NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
+                               ins->cil_code = ip;
+                               *sp++ = ins;
+                               ip += 6;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       case CEE_MONO_VTADDR:
+                               CHECK_STACK (1);
+                               --sp;
+                               MONO_INST_NEW (cfg, ins, OP_VTADDR);
+                               ins->cil_code = ip;
+                               ins->type = STACK_MP;
+                               ins->inst_left = *sp;
+                               *sp++ = ins;
+                               ip += 2;
+                               break;
+                       case CEE_MONO_NEWOBJ: {
+                               MonoInst *iargs [2];
+                               int temp;
+                               CHECK_STACK_OVF (1);
+                               token = read32 (ip + 2);
+                               klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
+                               mono_class_init (klass);
+                               NEW_DOMAINCONST (cfg, iargs [0]);
+                               NEW_CLASSCONST (cfg, iargs [1], klass);
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip);
+                               NEW_TEMPLOAD (cfg, *sp, temp);
+                               sp++;
+                               ip += 6;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_OBJADDR:
+                               CHECK_STACK (1);
+                               --sp;
+                               MONO_INST_NEW (cfg, ins, OP_OBJADDR);
+                               ins->cil_code = ip;
+                               ins->type = STACK_MP;
+                               ins->inst_left = *sp;
+                               *sp++ = ins;
+                               ip += 2;
+                               break;
+                       case CEE_MONO_LDNATIVEOBJ:
+                               CHECK_STACK (1);
+                               token = read32 (ip + 2);
+                               klass = mono_method_get_wrapper_data (method, token);
+                               g_assert (klass->valuetype);
+                               mono_class_init (klass);
+                               NEW_INDLOAD (cfg, ins, sp [-1], &klass->byval_arg);
+                               sp [-1] = ins;
+                               ip += 6;
+                               break;
+                       case CEE_MONO_RETOBJ:
+                               g_assert (cfg->ret);
+                               g_assert (method->signature->pinvoke); 
+                               CHECK_STACK (1);
+                               --sp;
+                               
+                               token = read32 (ip + 2);    
+                               klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
+
+                               NEW_RETLOADA (cfg, ins);
+                               handle_stobj (cfg, bblock, ins, *sp, ip, klass, FALSE, TRUE);
+                               
+                               if (sp != stack_start)
+                                       goto unverified;
+                               
+                               MONO_INST_NEW (cfg, ins, CEE_BR);
+                               ins->cil_code = ip;
+                               ins->inst_target_bb = end_bblock;
+                               MONO_ADD_INS (bblock, ins);
+                               link_bblock (cfg, bblock, end_bblock);
+                               start_new_bblock = 1;
+                               ip += 6;
+                               break;
+                       default:
+                               g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
+                               break;
+                       }
+                       break;
+               }
+               case CEE_PREFIX1: {
+                       switch (ip [1]) {
+                       case CEE_ARGLIST:
+                               g_error ("opcode 0xfe 0x%02x not handled", ip [1]);
+                               break;
+                       case CEE_CEQ:
+                       case CEE_CGT:
+                       case CEE_CGT_UN:
+                       case CEE_CLT:
+                       case CEE_CLT_UN: {
+                               MonoInst *cmp;
+                               CHECK_STACK (2);
+                               MONO_INST_NEW (cfg, cmp, 256 + ip [1]);
+                               MONO_INST_NEW (cfg, ins, cmp->opcode);
+                               sp -= 2;
+                               cmp->inst_i0 = sp [0];
+                               cmp->inst_i1 = sp [1];
+                               cmp->cil_code = ip;
+                               type_from_op (cmp);
+                               CHECK_TYPE (cmp);
+                               cmp->opcode = OP_COMPARE;
+                               ins->cil_code = ip;
+                               ins->type = STACK_I4;
+                               ins->inst_i0 = cmp;
+                               *sp++ = ins;
+                               ip += 2;
+                               break;
+                       }
+                       case CEE_LDFTN: {
+                               MonoInst *argconst;
+                               int temp;
+
+                               CHECK_STACK_OVF (1);
+                               n = read32 (ip + 2);
+                               if (method->wrapper_type != MONO_WRAPPER_NONE)
+                                       cmethod = mono_method_get_wrapper_data (method, n);
+                               else
+                                       cmethod = mono_get_method (image, n, NULL);
+                               
+                               mono_class_init (cmethod->klass);
+                               handle_loaded_temps (cfg, bblock, stack_start, sp);
+
+                               NEW_METHODCONST (cfg, argconst, cmethod);
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn, &argconst, ip);
+                               NEW_TEMPLOAD (cfg, *sp, temp);
+                               sp ++;
+                               
+                               ip += 6;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_LDVIRTFTN: {
+                               MonoInst *args [2];
+                               int temp;
+
+                               CHECK_STACK (1);
+                               n = read32 (ip + 2);
+                               if (method->wrapper_type != MONO_WRAPPER_NONE)
+                                       cmethod = mono_method_get_wrapper_data (method, n);
+                               else
+                                       cmethod = mono_get_method (image, n, NULL);
+
+                               mono_class_init (cmethod->klass);
+                               handle_loaded_temps (cfg, bblock, stack_start, sp);
+
+                               --sp;
+                               args [0] = *sp;
+                               NEW_METHODCONST (cfg, args [1], cmethod);
+                               temp = mono_emit_jit_icall (cfg, bblock, mono_ldvirtfn, args, ip);
+                               NEW_TEMPLOAD (cfg, *sp, temp);
+                               sp ++;
+
+                               ip += 6;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_LDARG:
+                               CHECK_STACK_OVF (1);
+                               NEW_ARGLOAD (cfg, ins, read16 (ip + 2));
+                               ins->cil_code = ip;
+                               *sp++ = ins;
+                               ip += 4;
+                               break;
+                       case CEE_LDARGA:
+                               CHECK_STACK_OVF (1);
+                               NEW_ARGLOADA (cfg, ins, read16 (ip + 2));
+                               ins->cil_code = ip;
+                               *sp++ = ins;
+                               ip += 4;
+                               break;
+                       case CEE_STARG:
+                               CHECK_STACK (1);
+                               --sp;
+                               handle_loaded_temps (cfg, bblock, stack_start, sp);
+                               n = read16 (ip + 2);
+                               NEW_ARGSTORE (cfg, ins, n, *sp);
+                               ins->cil_code = ip;
+                               if (ins->opcode == CEE_STOBJ) {
+                                       NEW_ARGLOADA (cfg, ins, n);
+                                       handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                               } else
+                                       MONO_ADD_INS (bblock, ins);
+                               ip += 4;
+                               break;
+                       case CEE_LDLOC:
+                               CHECK_STACK_OVF (1);
+                               NEW_LOCLOAD (cfg, ins, read16 (ip + 2));
+                               ins->cil_code = ip;
+                               *sp++ = ins;
+                               ip += 4;
+                               break;
+                       case CEE_LDLOCA:
+                               CHECK_STACK_OVF (1);
+                               NEW_LOCLOADA (cfg, ins, read16 (ip + 2));
+                               ins->cil_code = ip;
+                               *sp++ = ins;
+                               ip += 4;
+                               break;
+                       case CEE_STLOC:
+                               CHECK_STACK (1);
+                               --sp;
+                               n = read16 (ip + 2);
+                               handle_loaded_temps (cfg, bblock, stack_start, sp);
+                               NEW_LOCSTORE (cfg, ins, n, *sp);
+                               ins->cil_code = ip;
+                               if (ins->opcode == CEE_STOBJ) {
+                                       NEW_LOCLOADA (cfg, ins, n);
+                                       handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+                               } else
+                                       MONO_ADD_INS (bblock, ins);
+                               ip += 4;
+                               inline_costs += 1;
+                               break;
+                       case CEE_LOCALLOC:
+                               CHECK_STACK (1);
+                               --sp;
+                               if (sp != stack_start) 
+                                       goto unverified;
+                               MONO_INST_NEW (cfg, ins, 256 + ip [1]);
+                               ins->inst_left = *sp;
+                               ins->cil_code = ip;
+
+                               if (header->init_locals)
+                                       ins->flags |= MONO_INST_INIT;
+
+                               *sp++ = ins;
+                               ip += 2;
+                               /* FIXME: set init flag if locals init is set in this method */
+                               break;
+                       case CEE_ENDFILTER: {
+                               MonoExceptionClause *clause, *nearest;
+                               int cc, nearest_num;
+
+                               CHECK_STACK (1);
+                               --sp;
+                               if ((sp != stack_start) || (sp [0]->type != STACK_I4)) 
+                                       goto unverified;
+                               MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
+                               ins->inst_left = *sp;
+                               ins->cil_code = ip;
+                               MONO_ADD_INS (bblock, ins);
+                               start_new_bblock = 1;
+                               ip += 2;
+
+                               nearest = NULL;
+                               for (cc = 0; cc < header->num_clauses; ++cc) {
+                                       clause = &header->clauses [cc];
+                                       if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
+                                           (!nearest || (clause->token_or_filter > nearest->token_or_filter))) {
+                                               nearest = clause;
+                                               nearest_num = cc;
+                                       }
+                               }
+                               g_assert (nearest);
+                               filter_lengths [nearest_num] = (ip - header->code) -  nearest->token_or_filter;
+
+                               break;
+                       }
+                       case CEE_UNALIGNED_:
+                               ins_flag |= MONO_INST_UNALIGNED;
+                               ip += 3;
+                               break;
+                       case CEE_VOLATILE_:
+                               ins_flag |= MONO_INST_VOLATILE;
+                               ip += 2;
+                               break;
+                       case CEE_TAIL_:
+                               ins_flag |= MONO_INST_TAILCALL;
+                               ip += 2;
+                               break;
+                       case CEE_INITOBJ:
+                               CHECK_STACK (1);
+                               --sp;
+                               token = read32 (ip + 2);
+                               if (method->wrapper_type != MONO_WRAPPER_NONE)
+                                       klass = mono_method_get_wrapper_data (method, token);
+                               else
+                                       klass = mono_class_get (image, token);
+                               handle_initobj (cfg, bblock, *sp, NULL, klass, stack_start, sp);
+                               ip += 6;
+                               inline_costs += 1;
+                               break;
+                       case CEE_CPBLK:
+                       case CEE_INITBLK: {
+                               MonoInst *iargs [3];
+                               CHECK_STACK (3);
+                               sp -= 3;
+                               iargs [0] = sp [0];
+                               iargs [1] = sp [1];
+                               iargs [2] = sp [2];
+                               handle_loaded_temps (cfg, bblock, stack_start, sp);
+                               if (ip [1] == CEE_CPBLK) {
+                                       mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip);
+                               } else {
+                                       mono_emit_jit_icall (cfg, bblock, helper_memset, iargs, ip);
+                               }
+                               ip += 2;
+                               inline_costs += 1;
+                               break;
+                       }
+                       case CEE_RETHROW: {
+                               MonoInst *load;
+                               /* FIXME: check we are in a catch handler */
+                               NEW_TEMPLOAD (cfg, load, cfg->exvar->inst_c0);
+                               load->cil_code = ip;
+                               MONO_INST_NEW (cfg, ins, CEE_THROW);
+                               ins->inst_left = load;
+                               ins->cil_code = ip;
+                               MONO_ADD_INS (bblock, ins);
+                               sp = stack_start;
+                               start_new_bblock = 1;
+                               ip += 2;
+                               break;
+                       }
+                       case CEE_SIZEOF:
+                               CHECK_STACK_OVF (1);
+                               token = read32 (ip + 2);
+                               if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) {
+                                       MonoType *type = mono_type_create_from_typespec (image, token);
+                                       token = mono_type_size (type, &align);
+                                       mono_metadata_free_type (type);
+                               } else {
+                                       MonoClass *szclass = mono_class_get (image, token);
+                                       mono_class_init (szclass);
+                                       token = mono_class_value_size (szclass, &align);
+                               }
+                               NEW_ICONST (cfg, ins, token);
+                               ins->cil_code = ip;
+                               *sp++= ins;
+                               ip += 6;
+                               break;
+                       case CEE_REFANYTYPE:
+                               g_error ("opcode 0xfe 0x%02x not handled", ip [1]);
+                               break;
+                       default:
+                               g_error ("opcode 0xfe 0x%02x not handled", ip [1]);
+                       }
+                       break;
+               }
+               default:
+                       g_error ("opcode 0x%02x not handled", *ip);
+               }
+       }
+       if (start_new_bblock != 1)
+               goto unverified;
+
+       bblock->cil_length = ip - bblock->cil_code;
+       bblock->next_bb = end_bblock;
+       link_bblock (cfg, bblock, end_bblock);
+
+       if (cfg->method == method && cfg->domainvar) {
+               MonoCallInst *call;
+               MonoInst *store;
+
+               MONO_INST_NEW_CALL (cfg, call, CEE_CALL);
+               call->signature = helper_sig_domain_get;
+               call->inst.type = STACK_PTR;
+               call->fptr = mono_domain_get;
+               NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, (MonoInst*)call);
+               
+               MONO_ADD_INS (init_localsbb, store);
+       }
+
+       if (header->init_locals) {
+               MonoInst *store;
+               for (i = 0; i < header->num_locals; ++i) {
+                       int t = header->locals [i]->type;
+                       if (t == MONO_TYPE_VALUETYPE && header->locals [i]->data.klass->enumtype)
+                               t = header->locals [i]->data.klass->enum_basetype->type;
+                       /* FIXME: use initobj for valuetypes, handle pointers, long, float. */
+                       if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
+                               NEW_ICONST (cfg, ins, 0);
+                               NEW_LOCSTORE (cfg, store, i, ins);
+                               MONO_ADD_INS (init_localsbb, store);
+                       } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
+                               MONO_INST_NEW (cfg, ins, OP_I8CONST);
+                               ins->type = STACK_I8;
+                               ins->inst_l = 0;
+                               NEW_LOCSTORE (cfg, store, i, ins);
+                               MONO_ADD_INS (init_localsbb, store);
+                       } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
+                               MONO_INST_NEW (cfg, ins, OP_R8CONST);
+                               ins->type = STACK_R8;
+                               ins->inst_p0 = (void*)&r8_0;
+                               NEW_LOCSTORE (cfg, store, i, ins);
+                               MONO_ADD_INS (init_localsbb, store);
+                       } else if (t == MONO_TYPE_VALUETYPE) {
+                               NEW_LOCLOADA (cfg, ins, i);
+                               handle_initobj (cfg, init_localsbb, ins, NULL, mono_class_from_mono_type (header->locals [i]), NULL, NULL);
+                               break;
+                       } else {
+                               NEW_PCONST (cfg, ins, NULL);
+                               NEW_LOCSTORE (cfg, store, i, ins);
+                               MONO_ADD_INS (init_localsbb, store);
+                       }
+               }
+       }
+
+       
+       /* resolve backward branches in the middle of an existing basic block */
+       for (tmp = bb_recheck; tmp; tmp = tmp->next) {
+               bblock = tmp->data;
+               /*g_print ("need recheck in %s at IL_%04x\n", method->name, bblock->cil_code - header->code);*/
+               tblock = find_previous (bbhash, start_bblock, bblock->cil_code);
+               if (tblock != start_bblock) {
+                       int l;
+                       split_bblock (cfg, tblock, bblock);
+                       l = bblock->cil_code - header->code;
+                       bblock->cil_length = tblock->cil_length - l;
+                       tblock->cil_length = l;
+               } else {
+                       g_print ("recheck failed.\n");
+               }
+       }
+
+       /* we compute regions here, because the length of filter clauses is not known in advance.
+       * It is computed in the CEE_ENDFILTER case in the above switch statement*/
+       if (cfg->method == method) {
+               MonoBasicBlock *bb;
+               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+                       bb->region = mono_find_block_region (cfg, bb->real_offset, filter_lengths);
+                       if (cfg->verbose_level > 2)
+                               g_print ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
+               }
+       } else {
+               g_hash_table_destroy (bbhash);
+       }
+
+       return inline_costs;
+
+ inline_failure:
+       if (cfg->method != method) 
+               g_hash_table_destroy (bbhash);
+       return -1;
+
+ unverified:
+       if (cfg->method != method) 
+               g_hash_table_destroy (bbhash);
+       g_error ("Invalid IL code at IL%04x in %s: %s\n", ip - header->code, 
+                mono_method_full_name (method, TRUE), mono_disasm_code_one (NULL, method, ip, NULL));
+       return -1;
+}
+
+void
+mono_print_tree (MonoInst *tree) {
+       int arity;
+
+       if (!tree)
+               return;
+
+       arity = mono_burg_arity [tree->opcode];
+
+       printf (" %s%s", arity?"(":"",  mono_inst_name (tree->opcode));
+
+       switch (tree->opcode) {
+       case OP_ICONST:
+               printf ("[%d]", tree->inst_c0);
+               break;
+       case OP_I8CONST:
+               printf ("[%lld]", tree->inst_l);
+               break;
+       case OP_R8CONST:
+               printf ("[%f]", *(double*)tree->inst_p0);
+               break;
+       case OP_R4CONST:
+               printf ("[%f]", *(float*)tree->inst_p0);
+               break;
+       case OP_ARG:
+       case OP_LOCAL:
+               printf ("[%d]", tree->inst_c0);
+               break;
+       case OP_REGOFFSET:
+               printf ("[0x%x(%s)]", tree->inst_offset, mono_arch_regname (tree->inst_basereg));
+               break;
+       case OP_REGVAR:
+               printf ("[%s]", mono_arch_regname (tree->dreg));
+               break;
+       case CEE_NEWARR:
+               printf ("[%s]",  tree->inst_newa_class->name);
+               mono_print_tree (tree->inst_newa_len);
+               break;
+       case CEE_CALL:
+       case CEE_CALLVIRT:
+       case OP_FCALL:
+       case OP_FCALLVIRT:
+       case OP_LCALL:
+       case OP_LCALLVIRT:
+       case OP_VCALL:
+       case OP_VCALLVIRT:
+       case OP_VOIDCALL:
+       case OP_VOIDCALLVIRT: {
+               MonoCallInst *call = (MonoCallInst*)tree;
+               if (call->method)
+                       printf ("[%s]", call->method->name);
+               break;
+       }
+       case OP_PHI: {
+               int i;
+               printf ("[%d (", tree->inst_c0);
+               for (i = 0; i < tree->inst_phi_args [0]; i++) {
+                       if (i)
+                               printf (", ");
+                       printf ("%d", tree->inst_phi_args [i + 1]);
+               }
+               printf (")]");
+               break;
+       }
+       case OP_RENAME:
+       case OP_RETARG:
+       case CEE_NOP:
+       case CEE_JMP:
+       case CEE_BREAK:
+               break;
+       case CEE_BR:
+               printf ("[B%d]", tree->inst_target_bb->block_num);
+               break;
+       case CEE_SWITCH:
+       case CEE_ISINST:
+       case CEE_CASTCLASS:
+       case OP_OUTARG:
+       case OP_CALL_REG:
+       case OP_FCALL_REG:
+       case OP_LCALL_REG:
+       case OP_VCALL_REG:
+       case OP_VOIDCALL_REG:
+               mono_print_tree (tree->inst_left);
+               break;
+       case CEE_BNE_UN:
+       case CEE_BEQ:
+       case CEE_BLT:
+       case CEE_BLT_UN:
+       case CEE_BGT:
+       case CEE_BGT_UN:
+       case CEE_BGE:
+       case CEE_BGE_UN:
+       case CEE_BLE:
+       case CEE_BLE_UN:
+               printf ("[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num);
+               mono_print_tree (tree->inst_left);
+               break;
+       default:
+               if (arity) {
+                       mono_print_tree (tree->inst_left);
+                       if (arity > 1)
+                               mono_print_tree (tree->inst_right);
+               }
+               break;
+       }
+
+       if (arity)
+               printf (")");
+}
+
+static void
+create_helper_signature (void)
+{
+       /* FIXME: set call conv */
+       /* MonoArray * mono_array_new (MonoDomain *domain, MonoClass *klass, gint32 len) */
+       helper_sig_newarr = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+       helper_sig_newarr->params [0] = helper_sig_newarr->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_newarr->ret = &mono_defaults.object_class->byval_arg;
+       helper_sig_newarr->params [2] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_newarr->pinvoke = 1;
+
+       /* MonoObject * mono_object_new (MonoDomain *domain, MonoClass *klass) */
+       helper_sig_object_new = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_object_new->params [0] = helper_sig_object_new->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_object_new->ret = &mono_defaults.object_class->byval_arg;
+       helper_sig_object_new->pinvoke = 1;
+
+       /* void* mono_method_compile (MonoMethod*) */
+       helper_sig_compile = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_compile->params [0] = helper_sig_compile->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_compile->pinvoke = 1;
+
+       /* void* mono_ldvirtfn (MonoObject *, MonoMethod*) */
+       helper_sig_compile_virt = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_compile_virt->params [0] = &mono_defaults.object_class->byval_arg;
+       helper_sig_compile_virt->params [1] = helper_sig_compile_virt->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_compile_virt->pinvoke = 1;
+
+       /* MonoString* mono_ldstr (MonoDomain *domain, MonoImage *image, guint32 str_index) */
+       helper_sig_ldstr = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+       helper_sig_ldstr->params [0] = helper_sig_ldstr->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_ldstr->params [2] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_ldstr->ret = &mono_defaults.object_class->byval_arg;
+       helper_sig_ldstr->pinvoke = 1;
+
+       /* MonoDomain *mono_domain_get (void) */
+       helper_sig_domain_get = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
+       helper_sig_domain_get->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_domain_get->pinvoke = 1;
+
+       /* long amethod (long, long) */
+       helper_sig_long_long_long = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_long_long_long->params [0] = helper_sig_long_long_long->params [1] = 
+               &mono_defaults.int64_class->byval_arg;
+       helper_sig_long_long_long->ret = &mono_defaults.int64_class->byval_arg;
+       helper_sig_long_long_long->pinvoke = 1;
+
+       /* object  amethod (intptr) */
+       helper_sig_obj_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_obj_ptr->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_obj_ptr->ret = &mono_defaults.object_class->byval_arg;
+       helper_sig_obj_ptr->pinvoke = 1;
+
+       /* void amethod (intptr) */
+       helper_sig_void_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_void_ptr->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_void_ptr->pinvoke = 1;
+
+       /* void amethod (MonoObject *obj) */
+       helper_sig_void_obj = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_void_obj->params [0] = &mono_defaults.object_class->byval_arg;
+       helper_sig_void_obj->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_void_obj->pinvoke = 1;
+
+       /* intptr amethod (void) */
+       helper_sig_ptr_void = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
+       helper_sig_ptr_void->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_ptr_void->pinvoke = 1;
+
+       /* void  amethod (intptr, intptr) */
+       helper_sig_void_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_void_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr_ptr->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_void_ptr_ptr->pinvoke = 1;
+
+       /* void  amethod (intptr, intptr, intptr) */
+       helper_sig_void_ptr_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+       helper_sig_void_ptr_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr_ptr_ptr->params [2] = &mono_defaults.int_class->byval_arg;
+       helper_sig_void_ptr_ptr_ptr->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_void_ptr_ptr_ptr->pinvoke = 1;
+
+       /* intptr  amethod (intptr, intptr) */
+       helper_sig_ptr_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_ptr_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_ptr_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_ptr_ptr_ptr->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_ptr_ptr_ptr->pinvoke = 1;
+
+       /* IntPtr  amethod (object) */
+       helper_sig_ptr_obj = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_ptr_obj->params [0] = &mono_defaults.object_class->byval_arg;
+       helper_sig_ptr_obj->ret = &mono_defaults.int_class->byval_arg;
+       helper_sig_ptr_obj->pinvoke = 1;
+
+       /* long amethod (long, guint32) */
+       helper_sig_long_long_int = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_long_long_int->params [0] = &mono_defaults.int64_class->byval_arg;
+       helper_sig_long_long_int->params [1] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_long_long_int->ret = &mono_defaults.int64_class->byval_arg;
+       helper_sig_long_long_int->pinvoke = 1;
+
+       /* ulong amethod (double) */
+       helper_sig_ulong_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_ulong_double->params [0] = &mono_defaults.double_class->byval_arg;
+       helper_sig_ulong_double->ret = &mono_defaults.uint64_class->byval_arg;
+       helper_sig_ulong_double->pinvoke = 1;
+
+       /* long amethod (double) */
+       helper_sig_long_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_long_double->params [0] = &mono_defaults.double_class->byval_arg;
+       helper_sig_long_double->ret = &mono_defaults.int64_class->byval_arg;
+       helper_sig_long_double->pinvoke = 1;
+
+       /* uint amethod (double) */
+       helper_sig_uint_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_uint_double->params [0] = &mono_defaults.double_class->byval_arg;
+       helper_sig_uint_double->ret = &mono_defaults.uint32_class->byval_arg;
+       helper_sig_uint_double->pinvoke = 1;
+
+       /* int amethod (double) */
+       helper_sig_int_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
+       helper_sig_int_double->params [0] = &mono_defaults.double_class->byval_arg;
+       helper_sig_int_double->ret = &mono_defaults.int32_class->byval_arg;
+       helper_sig_int_double->pinvoke = 1;
+
+       /* void  initobj (intptr, int size) */
+       helper_sig_initobj = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+       helper_sig_initobj->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_initobj->params [1] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_initobj->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_initobj->pinvoke = 1;
+
+       /* void  memcpy (intptr, intptr, int size) */
+       helper_sig_memcpy = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+       helper_sig_memcpy->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_memcpy->params [1] = &mono_defaults.int_class->byval_arg;
+       helper_sig_memcpy->params [2] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_memcpy->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_memcpy->pinvoke = 1;
+
+       /* void  memset (intptr, int val, int size) */
+       helper_sig_memset = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+       helper_sig_memset->params [0] = &mono_defaults.int_class->byval_arg;
+       helper_sig_memset->params [1] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_memset->params [2] = &mono_defaults.int32_class->byval_arg;
+       helper_sig_memset->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_memset->pinvoke = 1;
+}
+
+static GHashTable *method_opcode_hash = NULL;
+
+static void
+mono_register_method_opcode (MonoMethod *method, int opcode)
+{
+
+       if (!method_opcode_hash)
+               method_opcode_hash = g_hash_table_new (NULL, NULL);
+
+       g_hash_table_insert (method_opcode_hash, method, (gpointer)opcode);
+
+}
+
+int
+mono_find_method_opcode (MonoMethod *method)
+{
+       g_assert (method_opcode_hash);
+
+       return (int)g_hash_table_lookup (method_opcode_hash, method);
+}
+
+static GHashTable *jit_icall_hash_name = NULL;
+static GHashTable *jit_icall_hash_addr = NULL;
+
+MonoJitICallInfo *
+mono_find_jit_icall_by_name (const char *name)
+{
+       g_assert (jit_icall_hash_name);
+
+       //printf ("lookup addr %s %p\n", name, g_hash_table_lookup (jit_icall_hash_name, name));
+       return g_hash_table_lookup (jit_icall_hash_name, name);
+}
+
+MonoJitICallInfo *
+mono_find_jit_icall_by_addr (gconstpointer addr)
+{
+       g_assert (jit_icall_hash_addr);
+
+       return g_hash_table_lookup (jit_icall_hash_addr, (gpointer)addr);
+}
+
+MonoJitICallInfo *
+mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save)
+{
+       MonoJitICallInfo *info;
+       MonoMethod *wrapper;
+       char *n;
+       
+       g_assert (func);
+       g_assert (name);
+
+       if (!jit_icall_hash_name) {
+               jit_icall_hash_name = g_hash_table_new (g_str_hash, g_str_equal);
+               jit_icall_hash_addr = g_hash_table_new (NULL, NULL);
+       }
+
+       if (g_hash_table_lookup (jit_icall_hash_name, name)) {
+               g_warning ("jit icall already defined \"%s\"\n", name);
+               g_assert_not_reached ();
+       }
+
+       info = g_new (MonoJitICallInfo, 1);
+       
+       info->name = g_strdup (name);
+       info->func = func;
+       info->sig = sig;
+               
+       if (is_save
+#ifdef MONO_USE_EXC_TABLES
+           || mono_arch_has_unwind_info (func)
+#endif
+           ) {
+               info->wrapper = func;
+       } else {
+               g_assert (sig);
+               n = g_strdup_printf ("__icall_wrapper_%s", name);       
+               wrapper = mono_marshal_get_icall_wrapper (sig, n, func);
+               info->wrapper = mono_jit_compile_method (wrapper);
+               g_free (n);
+       }
+
+       g_hash_table_insert (jit_icall_hash_name, info->name, info);
+       g_hash_table_insert (jit_icall_hash_addr, (gpointer)func, info);
+       if (func != info->wrapper)
+               g_hash_table_insert (jit_icall_hash_addr, (gpointer)info->wrapper, info);
+
+       return info;
+}
+
+static GHashTable *emul_opcode_hash = NULL;
+
+static MonoJitICallInfo *
+mono_find_jit_opcode_emulation (int opcode)
+{
+       if  (emul_opcode_hash)
+               return g_hash_table_lookup (emul_opcode_hash, (gpointer)opcode);
+       else
+               return NULL;
+}
+
+void
+mono_register_opcode_emulation (int opcode, MonoMethodSignature *sig, gpointer func)
+{
+       MonoJitICallInfo *info;
+       char *name;
+
+       if (!emul_opcode_hash)
+               emul_opcode_hash = g_hash_table_new (NULL, NULL);
+
+       g_assert (!sig->hasthis);
+       g_assert (sig->param_count < 3);
+
+       name = g_strdup_printf ("__emulate_%s",  mono_inst_name (opcode));
+
+       info = mono_register_jit_icall (func, name, sig, FALSE);
+
+       g_free (name);
+
+       g_hash_table_insert (emul_opcode_hash, (gpointer)opcode, info);
+}
+
+static void
+decompose_foreach (MonoInst *tree, gpointer data) 
+{
+       static MonoJitICallInfo *newarr_info = NULL;
+
+       switch (tree->opcode) {
+       case CEE_NEWARR: {
+               MonoCompile *cfg = data;
+               MonoInst *iargs [3];
+
+               NEW_DOMAINCONST (cfg, iargs [0]);
+               NEW_CLASSCONST (cfg, iargs [1], tree->inst_newa_class);
+               iargs [2] = tree->inst_newa_len;
+
+               if (!newarr_info) {
+                       newarr_info =  mono_find_jit_icall_by_addr (mono_array_new);
+                       g_assert (newarr_info);
+               }
+
+               mono_emulate_opcode (cfg, tree, iargs, newarr_info);
+               break;
+       }
+
+       default:
+               break;
+       }
+}
+
+void
+mono_inst_foreach (MonoInst *tree, MonoInstFunc func, gpointer data) {
+
+       switch (mono_burg_arity [tree->opcode]) {
+       case 0: break;
+       case 1: 
+               mono_inst_foreach (tree->inst_left, func, data);
+               break;
+       case 2: 
+               mono_inst_foreach (tree->inst_left, func, data);
+               mono_inst_foreach (tree->inst_right, func, data);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       func (tree, data);
+}
+
+#if 0
+static void
+mono_print_bb_code (MonoBasicBlock *bb) {
+       if (bb->code) {
+               MonoInst *c = bb->code;
+               while (c) {
+                       mono_print_tree (c);
+                       g_print ("\n");
+                       c = c->next;
+               }
+       }
+}
+#endif
+
+static void
+print_dfn (MonoCompile *cfg) {
+       int i, j;
+       char *code;
+       MonoBasicBlock *bb;
+
+       g_print ("IR code for method %s\n", mono_method_full_name (cfg->method, TRUE));
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               bb = cfg->bblocks [i];
+               if (bb->cil_code) {
+                       char* code1, *code2;
+                       code1 = mono_disasm_code_one (NULL, cfg->method, bb->cil_code, NULL);
+                       if (bb->last_ins->cil_code)
+                               code2 = mono_disasm_code_one (NULL, cfg->method, bb->last_ins->cil_code, NULL);
+                       else
+                               code2 = g_strdup ("");
+
+                       code1 [strlen (code1) - 1] = 0;
+                       code = g_strdup_printf ("%s -> %s", code1, code2);
+                       g_free (code1);
+                       g_free (code2);
+               } else
+                       code = g_strdup ("\n");
+               g_print ("\nBB%d DFN%d (len: %d): %s", bb->block_num, i, bb->cil_length, code);
+               if (bb->code) {
+                       MonoInst *c = bb->code;
+                       while (c) {
+                               mono_print_tree (c);
+                               g_print ("\n");
+                               c = c->next;
+                       }
+               } else {
+
+               }
+
+               g_print ("\tprev:");
+               for (j = 0; j < bb->in_count; ++j) {
+                       g_print (" BB%d", bb->in_bb [j]->block_num);
+               }
+               g_print ("\t\tsucc:");
+               for (j = 0; j < bb->out_count; ++j) {
+                       g_print (" BB%d", bb->out_bb [j]->block_num);
+               }
+               g_print ("\n\tidom: BB%d\n", bb->idom? bb->idom->block_num: -1);
+
+               if (bb->idom)
+                       g_assert (mono_bitset_test_fast (bb->dominators, bb->idom->dfn));
+
+               if (bb->dominators)
+                       mono_blockset_print (cfg, bb->dominators, "\tdominators", bb->idom? bb->idom->dfn: -1);
+               if (bb->dfrontier)
+                       mono_blockset_print (cfg, bb->dfrontier, "\tdfrontier", -1);
+               g_free (code);
+       }
+
+       g_print ("\n");
+}
+
+/*
+ * returns the offset used by spillvar. It allocates a new
+ * spill variable if necessary. 
+ */
+int
+mono_spillvar_offset (MonoCompile *cfg, int spillvar)
+{
+       MonoSpillInfo **si, *info;
+       int i = 0;
+
+       si = &cfg->spill_info; 
+       
+       while (i <= spillvar) {
+
+               if (!*si) {
+                       *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo));
+                       info->next = NULL;
+                       cfg->stack_offset -= sizeof (gpointer);
+                       info->offset = cfg->stack_offset;
+               }
+
+               if (i == spillvar)
+                       return (*si)->offset;
+
+               i++;
+               si = &(*si)->next;
+       }
+
+       g_assert_not_reached ();
+       return 0;
+}
+
+void
+mono_bblock_add_inst (MonoBasicBlock *bb, MonoInst *inst)
+{
+       inst->next = NULL;
+       if (bb->last_ins) {
+               g_assert (bb->code);
+               bb->last_ins->next = inst;
+               bb->last_ins = inst;
+       } else {
+               bb->last_ins = bb->code = inst;
+       }
+}
+
+void
+mono_destroy_compile (MonoCompile *cfg)
+{
+
+       //mono_mempool_stats (cfg->mempool);
+       g_hash_table_destroy (cfg->bb_hash);
+       mono_mempool_destroy (cfg->mempool);
+       g_free (cfg);
+}
+
+gpointer 
+mono_get_lmf_addr (void)
+{
+       MonoJitTlsData *jit_tls;        
+
+       if ((jit_tls = TlsGetValue (mono_jit_tls_id)))
+               return &jit_tls->lmf;
+
+       g_assert_not_reached ();
+       return NULL;
+}
+
+/**
+ * mono_thread_abort:
+ * @obj: exception object
+ *
+ * abort the thread, print exception information and stack trace
+ */
+static void
+mono_thread_abort (MonoObject *obj)
+{
+       MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+       
+       g_free (jit_tls);
+
+       ExitThread (-1);
+}
+
+static void
+mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func)
+{
+       MonoJitTlsData *jit_tls;
+       MonoLMF *lmf;
+
+       jit_tls = g_new0 (MonoJitTlsData, 1);
+
+       TlsSetValue (mono_jit_tls_id, jit_tls);
+
+       jit_tls->abort_func = mono_thread_abort;
+       jit_tls->end_of_stack = stack_start;
+
+       lmf = g_new0 (MonoLMF, 1);
+       lmf->ebp = -1;
+
+       jit_tls->lmf = lmf;
+}
+
+void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL;
+
+static void
+mono_thread_abort_dummy (MonoObject *obj)
+{
+  if (mono_thread_attach_aborted_cb)
+    mono_thread_attach_aborted_cb (obj);
+  else
+    mono_thread_abort (obj);
+}
+
+static void
+mono_thread_attach_cb (guint32 tid, gpointer stack_start)
+{
+       MonoJitTlsData *jit_tls;
+       MonoLMF *lmf;
+
+       jit_tls = g_new0 (MonoJitTlsData, 1);
+
+       TlsSetValue (mono_jit_tls_id, jit_tls);
+
+       jit_tls->abort_func = mono_thread_abort_dummy;
+       jit_tls->end_of_stack = stack_start;
+
+       lmf = g_new0 (MonoLMF, 1);
+       lmf->ebp = -1;
+
+       jit_tls->lmf = lmf;
+}
+
+void
+mono_add_patch_info (MonoCompile *cfg, int ip, MonoJumpInfoType type, gconstpointer target)
+{
+       MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo));
+
+       ji->ip.i = ip;
+       ji->type = type;
+       ji->data.target = target;
+       ji->next = cfg->patch_info;
+
+       cfg->patch_info = ji;
+}
+
+void
+mono_remove_patch_info (MonoCompile *cfg, int ip)
+{
+       MonoJumpInfo **ji = &cfg->patch_info;
+
+       while (*ji) {
+               if ((*ji)->ip.i == ip)
+                       *ji = (*ji)->next;
+               else
+                       ji = &((*ji)->next);
+       }
+}
+
+static void
+dec_foreach (MonoInst *tree, MonoCompile *cfg) {
+       MonoJitICallInfo *info;
+
+       switch (mono_burg_arity [tree->opcode]) {
+       case 0: break;
+       case 1: 
+               dec_foreach (tree->inst_left, cfg);
+
+               if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
+                       MonoInst *iargs [2];
+               
+                       iargs [0] = tree->inst_left;
+
+                       mono_emulate_opcode (cfg, tree, iargs, info);
+                       return;
+               }
+
+               break;
+       case 2: 
+               if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
+                       MonoInst *iargs [2];
+               
+                       iargs [0] = tree->inst_i0;
+                       iargs [1] = tree->inst_i1;
+               
+                       mono_emulate_opcode (cfg, tree, iargs, info);
+
+                       dec_foreach (iargs [0], cfg);
+                       dec_foreach (iargs [1], cfg);
+                       return;
+               } else {
+                       dec_foreach (tree->inst_left, cfg);
+                       dec_foreach (tree->inst_right, cfg);
+               }
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       decompose_foreach (tree, cfg);
+}
+
+static void
+decompose_pass (MonoCompile *cfg) {
+       MonoBasicBlock *bb;
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *tree;
+               cfg->cbb = bb;
+               cfg->prev_ins = NULL;
+               for (tree = cfg->cbb->code; tree; tree = tree->next) {
+                       dec_foreach (tree, cfg);
+                       cfg->prev_ins = tree;
+               }
+       }
+}
+
+static void
+nullify_basic_block (MonoBasicBlock *bb) 
+{
+       bb->in_count = 0;
+       bb->out_count = 0;
+       bb->in_bb = NULL;
+       bb->out_bb = NULL;
+       bb->next_bb = NULL;
+       bb->code = bb->last_ins = NULL;
+}
+
+static void 
+replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig,  MonoBasicBlock *repl)
+{
+       int i, j;
+
+       for (i = 0; i < bb->out_count; i++) {
+               MonoBasicBlock *ob = bb->out_bb [i];
+               for (j = 0; j < ob->in_count; j++) {
+                       if (ob->in_bb [j] == orig)
+                               ob->in_bb [j] = repl;
+               }
+       }
+
+}
+
+static void
+merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) 
+{
+       bb->out_count = bbn->out_count;
+       bb->out_bb = bbn->out_bb;
+
+       replace_basic_block (bb, bbn, bb);
+
+       if (bb->last_ins) {
+               if (bbn->code) {
+                       bb->last_ins->next = bbn->code;
+                       bb->last_ins = bbn->last_ins;
+               }
+       } else {
+               bb->code = bbn->code;
+               bb->last_ins = bbn->last_ins;
+       }
+       bb->next_bb = bbn->next_bb;
+       nullify_basic_block (bbn);
+}
+
+static void
+optimize_branches (MonoCompile *cfg) {
+       int changed = FALSE;
+       MonoBasicBlock *bb, *bbn;
+
+       do {
+               changed = FALSE;
+
+               /* we skip the entry block (exit is handled specially instead ) */
+               for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
+
+                       if (bb->out_count == 1) {
+                               bbn = bb->out_bb [0];
+
+                               if (bb->region == bbn->region && bb->next_bb == bbn) {
+                               /* the block are in sequence anyway ... */
+
+                                       /* 
+                                        * miguel: I do not understand what the test below does, could we
+                                        * use a macro, or a comment here?  opcode > CEE_BEQ && <= BLT_UN
+                                        *
+                                        * It could also test for bb->last_in only once, and the value
+                                        * could be cached (last_ins->opcode)
+                                        */
+                                       if (bb->last_ins && (bb->last_ins->opcode == CEE_BR || (
+                                               (bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN)))) {
+                                               bb->last_ins->opcode = CEE_NOP;
+                                               changed = TRUE;
+                                               if (cfg->verbose_level > 2)
+                                                       g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num);
+                                       }
+                                       /* fixme: this causes problems with inlining */
+                                       if (bbn->in_count == 1) {
+
+                                               if (bbn != cfg->bb_exit) {
+                                                       if (cfg->verbose_level > 2)
+                                                               g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num);
+                                                       merge_basic_blocks (bb, bbn);
+                                                       changed = TRUE;
+                                               }
+
+                                               //mono_print_bb_code (bb);
+                                       }
+                               } else {
+                                       if (bb->last_ins && bb->last_ins->opcode == CEE_BR) {
+                                               bbn = bb->last_ins->inst_target_bb;
+                                               if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) {
+                                                       /*
+                                                       if (cfg->verbose_level > 2)
+                                                               g_print ("in %s branch to branch triggered %d -> %d\n", cfg->method->name, bb->block_num, bbn->block_num);
+                                                       bb->out_bb [0] = bb->last_ins->inst_target_bb = bbn->code->inst_target_bb;
+                                                       changed = TRUE;*/
+                                               }
+                                       }
+                               }
+                       } else if (bb->out_count == 2) {
+                               /* fixme: this does not correctly unlink the blocks, so we get serious problems in idom code */
+                               if (0 && bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN) {
+                                       bbn = bb->last_ins->inst_true_bb;
+                                       if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) {
+                                               if (cfg->verbose_level > 2)
+                                                       g_print ("cbranch to branch triggered %d -> %d (0x%02x)\n", bb->block_num, 
+                                                                bbn->block_num, bbn->code->opcode);
+                                                
+                                               if (bb->out_bb [0] == bbn) {
+                                                       bb->out_bb [0] = bbn->code->inst_target_bb;
+                                               } else if (bb->out_bb [1] == bbn) {
+                                                       bb->out_bb [1] = bbn->code->inst_target_bb;
+                                               }
+                                               bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
+                                               changed = TRUE;
+                                       }
+                               }
+                       }
+               }
+       } while (changed);
+}
+
+static void
+mono_compile_create_vars (MonoCompile *cfg)
+{
+       MonoMethodSignature *sig;
+       MonoMethodHeader *header;
+       int i;
+
+       header = ((MonoMethodNormal *)cfg->method)->header;
+
+       sig = cfg->method->signature;
+       
+       if (!MONO_TYPE_IS_VOID (sig->ret)) {
+               cfg->ret = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
+               cfg->ret->opcode = OP_RETARG;
+               cfg->ret->inst_vtype = sig->ret;
+               cfg->ret->klass = mono_class_from_mono_type (sig->ret);
+       }
+
+       if (sig->hasthis)
+               mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG);
+
+       for (i = 0; i < sig->param_count; ++i)
+               mono_compile_create_var (cfg, sig->params [i], OP_ARG);
+
+       cfg->locals_start = cfg->num_varinfo;
+
+       for (i = 0; i < header->num_locals; ++i)
+               mono_compile_create_var (cfg, header->locals [i], OP_LOCAL);
+}
+
+#if 0
+static void
+mono_print_code (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *tree = bb->code;      
+
+               if (!tree)
+                       continue;
+               
+               g_print ("CODE BLOCK %d (nesting %d):\n", bb->block_num, bb->nesting);
+
+               for (; tree; tree = tree->next) {
+                       mono_print_tree (tree);
+                       g_print ("\n");
+               }
+
+               if (bb->last_ins)
+                       bb->last_ins->next = NULL;
+       }
+}
+#endif
+
+extern const char * const mono_burg_rule_string [];
+
+static void
+emit_state (MonoCompile *cfg, MBState *state, int goal)
+{
+       MBState *kids [10];
+       int ern = mono_burg_rule (state, goal);
+       const guint16 *nts = mono_burg_nts [ern];
+       MBEmitFunc emit;
+
+       //g_print ("rule: %s\n", mono_burg_rule_string [ern]);
+       switch (goal) {
+       case MB_NTERM_reg:
+               //if (state->reg2)
+               //      state->reg1 = state->reg2; /* chain rule */
+               //else
+               state->reg1 = mono_regstate_next_int (cfg->rs);
+               //g_print ("alloc symbolic R%d (reg2: R%d) in block %d\n", state->reg1, state->reg2, cfg->cbb->block_num);
+               break;
+       case MB_NTERM_lreg:
+               state->reg1 = mono_regstate_next_int (cfg->rs);
+               state->reg2 = mono_regstate_next_int (cfg->rs);
+               break;
+       case MB_NTERM_freg:
+               state->reg1 = mono_regstate_next_float (cfg->rs);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+       if (nts [0]) {
+               mono_burg_kids (state, ern, kids);
+
+               emit_state (cfg, kids [0], nts [0]);
+               if (nts [1]) {
+                       emit_state (cfg, kids [1], nts [1]);
+                       if (nts [2]) {
+                               g_assert (!nts [3]);
+                               emit_state (cfg, kids [2], nts [2]);
+                       }
+               }
+       }
+
+//     g_print ("emit: %s (%p)\n", mono_burg_rule_string [ern], state);
+       if ((emit = mono_burg_func [ern]))
+               emit (state, state->tree, cfg); 
+}
+
+#define DEBUG_SELECTION
+
+static void 
+mini_select_instructions (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       
+       cfg->state_pool = mono_mempool_new ();
+       cfg->rs = mono_regstate_new ();
+
+#ifdef DEBUG_SELECTION
+       if (cfg->verbose_level >= 4) {
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *tree = bb->code;      
+               g_print ("DUMP BLOCK %d:\n", bb->block_num);
+               if (!tree)
+                       continue;
+               for (; tree; tree = tree->next) {
+                       mono_print_tree (tree);
+                       g_print ("\n");
+               }
+       }
+       }
+#endif
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *tree = bb->code, *next;       
+               MBState *mbstate;
+
+               if (!tree)
+                       continue;
+               bb->code = NULL;
+               bb->last_ins = NULL;
+               
+               cfg->cbb = bb;
+               mono_regstate_reset (cfg->rs);
+
+#ifdef DEBUG_SELECTION
+               if (cfg->verbose_level >= 3)
+                       g_print ("LABEL BLOCK %d:\n", bb->block_num);
+#endif
+               for (; tree; tree = next) {
+                       next = tree->next;
+#ifdef DEBUG_SELECTION
+                       if (cfg->verbose_level >= 3) {
+                               mono_print_tree (tree);
+                               g_print ("\n");
+                       }
+#endif
+
+                       if (!(mbstate = mono_burg_label (tree, cfg))) {
+                               g_warning ("unabled to label tree %p", tree);
+                               mono_print_tree (tree);
+                               g_print ("\n");                         
+                               g_assert_not_reached ();
+                       }
+                       emit_state (cfg, mbstate, MB_NTERM_stmt);
+               }
+               bb->max_ireg = cfg->rs->next_vireg;
+               bb->max_freg = cfg->rs->next_vfreg;
+
+               if (bb->last_ins)
+                       bb->last_ins->next = NULL;
+
+               mono_mempool_empty (cfg->state_pool); 
+       }
+       mono_mempool_destroy (cfg->state_pool); 
+}
+
+void
+mono_codegen (MonoCompile *cfg)
+{
+       MonoJumpInfo *patch_info;
+       MonoBasicBlock *bb;
+       int i, max_epilog_size;
+       guint8 *code;
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               cfg->spill_count = 0;
+               /* we reuse dfn here */
+               /* bb->dfn = bb_count++; */
+               mono_arch_local_regalloc (cfg, bb);
+       }
+
+       if (mono_trace_coverage)
+               mono_allocate_coverage_info (cfg->method, cfg->num_bblocks);
+
+       code = mono_arch_emit_prolog (cfg);
+
+       if (mono_jit_profile)
+               code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE);
+
+       cfg->code_len = code - cfg->native_code;
+       cfg->prolog_end = cfg->code_len;
+
+       mono_debug_open_method (cfg);
+            
+       /* emit code all basic blocks */
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               bb->native_offset = cfg->code_len;
+               mono_arch_output_basic_block (cfg, bb);
+       }
+       cfg->bb_exit->native_offset = cfg->code_len;
+
+       code = cfg->native_code + cfg->code_len;
+
+       max_epilog_size = mono_arch_max_epilog_size (cfg);
+
+       /* we always allocate code in cfg->domain->code_mp to increase locality */
+       cfg->code_size = cfg->code_len + max_epilog_size;
+       /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */
+       code = mono_mempool_alloc (cfg->domain->code_mp, cfg->code_size);
+       memcpy (code, cfg->native_code, cfg->code_len);
+       g_free (cfg->native_code);
+       cfg->native_code = code;
+       code = cfg->native_code + cfg->code_len;
+  
+       /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */
+
+       cfg->epilog_begin = cfg->code_len;
+       
+       if (mono_jit_profile)
+               code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE);
+
+       cfg->code_len = code - cfg->native_code;
+
+       mono_arch_emit_epilog (cfg);
+
+       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+               switch (patch_info->type) {
+               case MONO_PATCH_INFO_ABS: {
+                       MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target);
+                       if (info) {
+                               //printf ("TEST %s %p\n", info->name, patch_info->data.target);
+                               patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
+                               patch_info->data.name = info->name;
+                       }
+                       break;
+               }
+               case MONO_PATCH_INFO_SWITCH: {
+                       gpointer *table = g_new (gpointer, patch_info->table_size);
+                       patch_info->ip.i = patch_info->ip.label->inst_c0;
+                       for (i = 0; i < patch_info->table_size; i++) {
+                               table [i] = (gpointer)patch_info->data.table [i]->native_offset;
+                       }
+                       patch_info->data.target = table;
+                       break;
+               }
+               default:
+                       /* do nothing */
+                       break;
+               }
+       }
+       
+       if (cfg->verbose_level > 1)
+               g_print ("Method %s::%s emmitted at %p to %p\n", cfg->method->klass->name, 
+                        cfg->method->name, cfg->native_code, cfg->native_code + cfg->code_len);
+
+       mono_arch_patch_code (cfg->method, cfg->domain, cfg->native_code, cfg->patch_info);
+
+       mono_debug_close_method (cfg);
+}
+
+static void
+mono_cprop_copy_values (MonoCompile *cfg, MonoInst *tree, MonoInst **acp)
+{
+       MonoInst *cp;
+       int arity;
+
+       if (tree->ssa_op == MONO_SSA_LOAD && (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG) && 
+           (cp = acp [tree->inst_i0->inst_c0]) && !tree->inst_i0->flags) {
+
+               if (cp->opcode == OP_ICONST) {
+                       if (cfg->opt & MONO_OPT_CONSPROP) {
+                               //{ static int c = 0; printf ("CCOPY %d %d %s\n", c++, cp->inst_c0, mono_method_full_name (cfg->method, TRUE)); }
+                               *tree = *cp;
+                       }
+               } else {
+                       if (tree->inst_i0->inst_vtype->type == cp->inst_vtype->type) {
+                               if (cfg->opt & MONO_OPT_COPYPROP) {
+                                       //{ static int c = 0; printf ("VCOPY %d\n", ++c); }
+                                       tree->inst_i0 = cp;
+                               } 
+                       }
+               } 
+       } else {
+               arity = mono_burg_arity [tree->opcode];
+
+               if (arity) {
+                       mono_cprop_copy_values (cfg, tree->inst_i0, acp);
+                       if (cfg->opt & MONO_OPT_CFOLD)
+                               mono_constant_fold_inst (tree, NULL); 
+                       if (arity > 1) {
+                               mono_cprop_copy_values (cfg, tree->inst_i1, acp);
+                               if (cfg->opt & MONO_OPT_CFOLD)
+                                       mono_constant_fold_inst (tree, NULL); 
+                       }
+                       mono_constant_fold_inst (tree, NULL); 
+               }
+       }
+}
+
+static void
+mono_cprop_invalidate_values (MonoInst *tree, MonoInst **acp, int acp_size)
+{
+       int arity;
+
+       switch (tree->opcode) {
+       case CEE_STIND_I:
+       case CEE_STIND_I1:
+       case CEE_STIND_I2:
+       case CEE_STIND_I4:
+       case CEE_STIND_REF:
+       case CEE_STIND_I8:
+       case CEE_STIND_R4:
+       case CEE_STIND_R8:
+       case CEE_STOBJ:
+               if (tree->ssa_op == MONO_SSA_NOP) {
+                       memset (acp, 0, sizeof (MonoInst *) * acp_size);
+                       return;
+               }
+
+               break;
+       case CEE_CALL:
+       case OP_CALL_REG:
+       case CEE_CALLVIRT:
+       case OP_LCALL_REG:
+       case OP_LCALLVIRT:
+       case OP_LCALL:
+       case OP_FCALL_REG:
+       case OP_FCALLVIRT:
+       case OP_FCALL:
+       case OP_VCALL_REG:
+       case OP_VCALLVIRT:
+       case OP_VCALL:
+       case OP_VOIDCALL_REG:
+       case OP_VOIDCALLVIRT:
+       case OP_VOIDCALL: {
+               MonoCallInst *call = (MonoCallInst *)tree;
+               MonoMethodSignature *sig = call->signature;
+               int i, byref = FALSE;
+
+               for (i = 0; i < sig->param_count; i++) {
+                       if (sig->params [i]->byref) {
+                               byref = TRUE;
+                               break;
+                       }
+               }
+
+               if (byref)
+                       memset (acp, 0, sizeof (MonoInst *) * acp_size);
+
+               return;
+       }
+       default:
+               break;
+       }
+
+       arity = mono_burg_arity [tree->opcode];
+
+       switch (arity) {
+       case 0:
+               break;
+       case 1:
+               mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size);
+               break;
+       case 2:
+               mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size);
+               mono_cprop_invalidate_values (tree->inst_i1, acp, acp_size);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+mono_local_cprop_bb (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size)
+{
+       MonoInst *tree = bb->code;      
+       int i;
+
+       if (!tree)
+               return;
+
+       for (; tree; tree = tree->next) {
+
+               mono_cprop_copy_values (cfg, tree, acp);
+
+               mono_cprop_invalidate_values (tree, acp, acp_size);
+
+               if (tree->ssa_op == MONO_SSA_STORE  && 
+                   (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG)) {
+                       MonoInst *i1 = tree->inst_i1;
+
+                       acp [tree->inst_i0->inst_c0] = NULL;
+
+                       for (i = 0; i < acp_size; i++) {
+                               if (acp [i] && acp [i]->opcode != OP_ICONST && 
+                                   acp [i]->inst_c0 == tree->inst_i0->inst_c0) {
+                                       acp [i] = NULL;
+                               }
+                       }
+
+                       if (i1->opcode == OP_ICONST) {
+                               acp [tree->inst_i0->inst_c0] = i1;
+                               //printf ("DEF1 BB%d %d\n", bb->block_num,tree->inst_i0->inst_c0);
+                       }
+                       if (i1->ssa_op == MONO_SSA_LOAD && 
+                           (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG) &&
+                           (i1->inst_i0->inst_c0 != tree->inst_i0->inst_c0)) {
+                               acp [tree->inst_i0->inst_c0] = i1->inst_i0;
+                               //printf ("DEF2 BB%d %d %d\n", bb->block_num,tree->inst_i0->inst_c0,i1->inst_i0->inst_c0);
+                       }
+               }
+
+               /*
+                 if (tree->opcode == CEE_BEQ) {
+                 g_assert (tree->inst_i0->opcode == OP_COMPARE);
+                 if (tree->inst_i0->inst_i0->opcode == OP_ICONST &&
+                 tree->inst_i0->inst_i1->opcode == OP_ICONST) {
+                 
+                 tree->opcode = CEE_BR;
+                 if (tree->inst_i0->inst_i0->opcode == tree->inst_i0->inst_i1->opcode) {
+                 tree->inst_target_bb = tree->inst_true_bb;
+                 } else {
+                 tree->inst_target_bb = tree->inst_false_bb;
+                 }
+                 }
+                 }
+               */
+       }
+}
+
+static void
+mono_local_cprop (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       MonoInst **acp;
+
+       acp = alloca (sizeof (MonoInst *) * cfg->num_varinfo);
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               memset (acp, 0, sizeof (MonoInst *) * cfg->num_varinfo);
+               mono_local_cprop_bb (cfg, bb, acp, cfg->num_varinfo);
+       }
+}
+
+MonoCompile*
+mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int parts)
+{
+       MonoMethodHeader *header = ((MonoMethodNormal *)method)->header;
+       guint8 *ip = (guint8 *)header->code;
+       MonoCompile *cfg;
+       MonoJitInfo *jinfo;
+       int dfn = 0, i, code_size_ratio;
+
+       mono_jit_stats.methods_compiled++;
+
+       cfg = g_new0 (MonoCompile, 1);
+       cfg->method = method;
+       cfg->mempool = mono_mempool_new ();
+       cfg->opt = opts;
+       cfg->bb_hash = g_hash_table_new (g_direct_hash, NULL);
+       cfg->domain = domain;
+       cfg->verbose_level = mini_verbose;
+
+       /*
+        * create MonoInst* which represents arguments and local variables
+        */
+       mono_compile_create_vars (cfg);
+
+       if (cfg->verbose_level > 2)
+               g_print ("converting method %s\n", mono_method_full_name (method, TRUE));
+
+       if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0)) < 0) {
+               mono_destroy_compile (cfg);
+               return NULL;
+       }
+
+       mono_jit_stats.basic_blocks += cfg->num_bblocks;
+       mono_jit_stats.max_basic_blocks = MAX (cfg->num_bblocks, mono_jit_stats.max_basic_blocks);
+
+       /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/
+
+       /* Depth-first ordering on basic blocks */
+       cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1));
+
+       if (cfg->opt & MONO_OPT_BRANCH)
+               optimize_branches (cfg);
+
+       df_visit (cfg->bb_entry, &dfn, cfg->bblocks);
+       if (cfg->num_bblocks != dfn + 1) {
+               if (cfg->verbose_level > 1)
+                       g_print ("unreachable code?\n");
+               cfg->num_bblocks = dfn + 1;
+       }
+
+       if (cfg->opt & MONO_OPT_LOOP) {
+               mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM);
+               mono_compute_natural_loops (cfg);
+       }
+
+
+       /* after method_to_ir */
+       if (parts == 1)
+               return cfg;
+
+//#define DEBUGSSA "logic_run"
+#define DEBUGSSA_CLASS "Tests"
+#ifdef DEBUGSSA
+
+
+       if (!header->num_clauses && !cfg->disable_ssa) {
+               mono_local_cprop (cfg);
+               mono_ssa_compute (cfg);
+       }
+#else 
+
+       /* fixme: add all optimizations which requires SSA */
+       if (cfg->opt & (MONO_OPT_DEADCE)) {
+               if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) {
+                       mono_local_cprop (cfg);
+                       mono_ssa_compute (cfg);
+
+                       if (cfg->verbose_level >= 2) {
+                               print_dfn (cfg);
+                       }
+               }
+       }
+#endif
+
+       /* after SSA translation */
+       if (parts == 2)
+               return cfg;
+
+       if ((cfg->opt & MONO_OPT_CONSPROP) ||  (cfg->opt & MONO_OPT_COPYPROP)) {
+               if (cfg->comp_done & MONO_COMP_SSA) {
+                       mono_ssa_cprop (cfg);
+               } else {
+                       mono_local_cprop (cfg);
+               }
+       }
+
+       if (cfg->comp_done & MONO_COMP_SSA) {                   
+               mono_ssa_deadce (cfg);
+
+               //mono_ssa_strength_reduction (cfg);
+
+               mono_ssa_remove (cfg);
+
+               if (cfg->opt & MONO_OPT_BRANCH)
+                       optimize_branches (cfg);
+       }
+
+       /* after SSA removal */
+       if (parts == 3)
+               return cfg;
+       
+       decompose_pass (cfg);
+
+       if (cfg->opt & MONO_OPT_LINEARS) {
+               GList *vars, *regs;
+
+               /* fixme: maybe we can avoid to compute livenesss here if already computed ? */
+               cfg->comp_done &= ~MONO_COMP_LIVENESS;
+               if (!(cfg->comp_done & MONO_COMP_LIVENESS))
+                       mono_analyze_liveness (cfg);
+               
+               if ((vars = mono_arch_get_allocatable_int_vars (cfg))) {
+                       regs = mono_arch_get_global_int_regs (cfg);
+                       mono_linear_scan (cfg, vars, regs, &cfg->used_int_regs);
+               }
+       }
+
+       //mono_print_code (cfg);
+       
+       //print_dfn (cfg);
+
+       /* variables are allocated after decompose, since decompose could create temps */
+       mono_arch_allocate_vars (cfg);
+
+       if (cfg->opt & MONO_OPT_CFOLD)
+               mono_constant_fold (cfg);
+
+       mini_select_instructions (cfg);
+
+       mono_codegen (cfg);
+       if (cfg->verbose_level >= 2) {
+               char *id =  mono_method_full_name (cfg->method, FALSE);
+               mono_disassemble_code (cfg->native_code, cfg->code_len, id + 3);
+               g_free (id);
+       }
+       
+       jinfo = mono_mempool_alloc0 (cfg->domain->mp, sizeof (MonoJitInfo));
+
+       jinfo = g_new0 (MonoJitInfo, 1);
+       jinfo->method = method;
+       jinfo->code_start = cfg->native_code;
+       jinfo->code_size = cfg->code_len;
+       jinfo->used_regs = cfg->used_int_regs;
+
+       if (header->num_clauses) {
+               int i;
+
+               jinfo->exvar_offset = cfg->exvar? cfg->exvar->inst_offset: 0;
+               jinfo->num_clauses = header->num_clauses;
+               jinfo->clauses = mono_mempool_alloc0 (cfg->domain->mp, 
+                       sizeof (MonoJitExceptionInfo) * header->num_clauses);
+
+               for (i = 0; i < header->num_clauses; i++) {
+                       MonoExceptionClause *ec = &header->clauses [i];
+                       MonoJitExceptionInfo *ei = &jinfo->clauses [i];
+                       MonoBasicBlock *tblock;
+
+                       ei->flags = ec->flags;
+
+                       if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
+                               tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->token_or_filter);
+                               g_assert (tblock);
+                               ei->data.filter = cfg->native_code + tblock->native_offset;
+                       } else {
+                               ei->data.token = ec->token_or_filter;
+                       }
+
+                       tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset);
+                       g_assert (tblock);
+                       ei->try_start = cfg->native_code + tblock->native_offset;
+                       tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset + ec->try_len);
+                       g_assert (tblock);
+                       ei->try_end = cfg->native_code + tblock->native_offset;
+                       tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->handler_offset);
+                       g_assert (tblock);
+                       ei->handler_start = cfg->native_code + tblock->native_offset;
+
+               }
+       }
+
+       mono_jit_info_table_add (cfg->domain, jinfo);
+
+       /* collect statistics */
+       mono_jit_stats.allocated_code_size += cfg->code_len;
+       code_size_ratio = cfg->code_len;
+       if (code_size_ratio > mono_jit_stats.biggest_method_size) {
+                       mono_jit_stats.biggest_method_size = code_size_ratio;
+                       mono_jit_stats.biggest_method = method;
+       }
+       code_size_ratio = (code_size_ratio * 100) / ((MonoMethodNormal *)method)->header->code_size;
+       if (code_size_ratio > mono_jit_stats.max_code_size_ratio) {
+               mono_jit_stats.max_code_size_ratio = code_size_ratio;
+               mono_jit_stats.max_ratio_method = method;
+       }
+       mono_jit_stats.native_code_size += cfg->code_len;
+
+       return cfg;
+}
+
+static gpointer
+mono_jit_compile_method (MonoMethod *method)
+{
+       /* FIXME: later copy the code from mono */
+       MonoDomain *target_domain, *domain = mono_domain_get ();
+       MonoCompile *cfg;
+       GHashTable *jit_code_hash;
+       gpointer code;
+
+       if (default_opt & MONO_OPT_SAHRED)
+               target_domain = mono_root_domain;
+       else 
+               target_domain = domain;
+
+       jit_code_hash = target_domain->jit_code_hash;
+
+       if ((code = g_hash_table_lookup (jit_code_hash, method))) {
+               mono_jit_stats.methods_lookups++;
+               return code;
+       }
+
+#ifdef MONO_USE_AOT_COMPILER
+       if (!mono_compile_aot) {
+               mono_class_init (method->klass);
+               if ((code = mono_aot_get_method (method))) {
+                       g_hash_table_insert (jit_code_hash, method, code);
+                       return code;
+               }
+       }
+#endif
+
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+               if (!method->info) {
+                       MonoMethod *nm;
+
+                       if (!method->addr && (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
+                               mono_lookup_pinvoke_call (method);
+#ifdef MONO_USE_EXC_TABLES
+                       if (mono_method_blittable (method)) {
+                               method->info = method->addr;
+                       } else {
+#endif
+                               nm = mono_marshal_get_native_wrapper (method);
+                               method->info = mono_compile_method (nm);
+
+                               //if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) 
+                               //mono_debug_add_wrapper (method, nm);
+#ifdef MONO_USE_EXC_TABLES
+                       }
+#endif
+               }
+               return method->info;
+       } else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
+               const char *name = method->name;
+               MonoMethod *nm;
+
+               if (method->klass->parent == mono_defaults.multicastdelegate_class) {
+                       if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
+                               /* FIXME: uhm, we need a wrapper to handle exceptions? */
+                               return (gpointer)mono_delegate_ctor;
+                       } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
+                               nm = mono_marshal_get_delegate_invoke (method);
+                               return mono_jit_compile_method (nm);
+                       } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
+                               nm = mono_marshal_get_delegate_begin_invoke (method);
+                               return mono_jit_compile_method (nm);
+                       } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
+                               nm = mono_marshal_get_delegate_end_invoke (method);
+                               return mono_jit_compile_method (nm);
+                       }
+               }
+               return NULL;
+       }
+
+       cfg = mini_method_compile (method, default_opt, target_domain, 0);
+       code = cfg->native_code;
+       mono_destroy_compile (cfg);
+
+       g_hash_table_insert (jit_code_hash, method, code);
+
+       /* make sure runtime_init is called */
+       mono_class_vtable (target_domain, method->klass);
+
+       return code;
+}
+
+/**
+ * mono_jit_runtime_invoke:
+ * @method: the method to invoke
+ * @obj: this pointer
+ * @params: array of parameter values.
+ * @exc: used to catch exceptions objects
+ */
+static MonoObject*
+mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
+{
+       MonoMethod *invoke;
+       MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc);
+       invoke = mono_marshal_get_runtime_invoke (method);
+       runtime_invoke = mono_jit_compile_method (invoke);      
+       return runtime_invoke (obj, params, exc);
+}
+
+#ifdef PLATFORM_WIN32
+#define GET_CONTEXT \
+       struct sigcontext *ctx = (struct sigcontext*)_dummy;
+#else
+#define GET_CONTEXT \
+       void **_p = (void **)&_dummy; \
+       struct sigcontext *ctx = (struct sigcontext *)++_p;
+#endif
+
+static void
+sigfpe_signal_handler (int _dummy)
+{
+       MonoException *exc;
+       GET_CONTEXT
+
+       exc = mono_get_exception_divide_by_zero ();
+       
+       mono_arch_handle_exception (ctx, exc, FALSE);
+}
+
+static void
+sigill_signal_handler (int _dummy)
+{
+       MonoException *exc;
+       GET_CONTEXT
+       exc = mono_get_exception_execution_engine ("SIGILL");
+       
+       mono_arch_handle_exception (ctx, exc, FALSE);
+}
+
+static void
+sigsegv_signal_handler (int _dummy)
+{
+       MonoException *exc;
+       GET_CONTEXT
+
+       exc = mono_get_exception_null_reference ();
+       
+       mono_arch_handle_exception (ctx, exc, FALSE);
+}
+
+static void
+sigusr1_signal_handler (int _dummy)
+{
+       MonoThread *thread;
+       GET_CONTEXT
+       
+       thread = mono_thread_current ();
+        
+       g_assert (thread->abort_exc);
+
+       mono_arch_handle_exception (ctx, thread->abort_exc, FALSE);
+}
+
+static void
+mono_runtime_install_handlers (void)
+{
+#ifndef PLATFORM_WIN32
+       struct sigaction sa;
+#endif
+
+#ifdef PLATFORM_WIN32
+       win32_seh_init();
+       win32_seh_set_handler(SIGFPE, sigfpe_signal_handler);
+       win32_seh_set_handler(SIGILL, sigill_signal_handler);
+       win32_seh_set_handler(SIGSEGV, sigsegv_signal_handler);
+#else /* !PLATFORM_WIN32 */
+
+       /* libpthreads has its own implementation of sigaction(),
+        * but it seems to work well with our current exception
+        * handlers. If not we must call syscall directly instead 
+        * of sigaction */
+       
+       /* catch SIGFPE */
+       sa.sa_handler = sigfpe_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = 0;
+       //g_assert (syscall (SYS_sigaction, SIGFPE, &sa, NULL) != -1);
+       g_assert (sigaction (SIGFPE, &sa, NULL) != -1);
+
+       /* catch SIGILL */
+       sa.sa_handler = sigill_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = 0;
+       //g_assert (syscall (SYS_sigaction, SIGILL, &sa, NULL) != -1);
+       g_assert (sigaction (SIGILL, &sa, NULL) != -1);
+
+       /* catch thread abort signal */
+       sa.sa_handler = sigusr1_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = 0;
+       //g_assert (syscall (SYS_sigaction, SIGILL, &sa, NULL) != -1);
+       g_assert (sigaction (mono_thread_get_abort_signal (), &sa, NULL) != -1);
+
+#if 1
+       /* catch SIGSEGV */
+       sa.sa_handler = sigsegv_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = 0;
+       //g_assert (syscall (SYS_sigaction, SIGSEGV, &sa, NULL) != -1);
+       g_assert (sigaction (SIGSEGV, &sa, NULL) != -1);
+#endif
+#endif /* PLATFORM_WIN32 */
+}
+
+/* mono_jit_create_remoting_trampoline:
+ * @method: pointer to the method info
+ *
+ * Creates a trampoline which calls the remoting functions. This
+ * is used in the vtable of transparent proxies.
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+static gpointer
+mono_jit_create_remoting_trampoline (MonoMethod *method)
+{
+       MonoMethod *nm;
+       guint8 *addr = NULL;
+
+       if (method->signature->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) {
+               nm = mono_marshal_get_remoting_invoke (method);
+               addr = mono_compile_method (nm);
+       } else {
+               addr = mono_compile_method (method);
+       }
+       return addr;
+}
+
+static MonoMethod *
+mono_find_unique_method (MonoClass *klass, const char *name, int param_count)
+{
+       MonoMethod *rval = NULL;
+       int i;
+
+       mono_class_init (klass);
+       for (i = 0; i < klass->method.count; ++i) {
+               if (!strcmp (name, klass->methods [i]->name) &&
+                   klass->methods [i]->signature->param_count == param_count) {
+                       g_assert (rval == NULL);
+                       rval = klass->methods [i];
+                       g_assert (!(rval->flags & METHOD_ATTRIBUTE_VIRTUAL));
+               }
+       }
+
+       return rval;
+}
+
+
+static CRITICAL_SECTION ms;
+
+MonoDomain *
+mini_init (const char *filename)
+{
+       MonoDomain *domain;
+       MonoMethod *m;
+       MonoMethodDesc *desc;
+       
+       metadata_section = &ms;
+       InitializeCriticalSection (metadata_section);
+
+       mono_jit_tls_id = TlsAlloc ();
+       mono_thread_start_cb (GetCurrentThreadId (), (gpointer)-1, NULL);
+
+       mono_burg_init ();
+
+       mono_runtime_install_handlers ();
+
+       mono_install_compile_method (mono_jit_compile_method);
+       mono_install_trampoline (mono_arch_create_jit_trampoline);
+       mono_install_remoting_trampoline (mono_jit_create_remoting_trampoline);
+       mono_install_runtime_invoke (mono_jit_runtime_invoke);
+       mono_install_handler (mono_arch_get_throw_exception ());
+       mono_install_stack_walk (mono_jit_walk_stack);
+       mono_install_get_config_dir ();
+
+       domain = mono_init (filename);
+       mono_init_icall ();
+
+       mono_add_internal_call ("System.Diagnostics.StackFrame::get_frame_info", 
+                               ves_icall_get_frame_info);
+       mono_add_internal_call ("System.Diagnostics.StackTrace::get_trace", 
+                               ves_icall_get_trace);
+       mono_add_internal_call ("Mono.Runtime::mono_runtime_install_handlers", 
+                               mono_runtime_install_handlers);
+
+
+       create_helper_signature ();
+
+       mono_arch_register_lowlevel_calls ();
+       mono_register_jit_icall (mono_profiler_method_enter, "mono_profiler_method_enter", NULL, TRUE);
+       mono_register_jit_icall (mono_profiler_method_leave, "mono_profiler_method_leave", NULL, TRUE);
+
+       mono_register_jit_icall (mono_get_lmf_addr, "mono_get_lmf_addr", helper_sig_ptr_void, TRUE);
+       mono_register_jit_icall (mono_domain_get, "mono_domain_get", helper_sig_domain_get, TRUE);
+
+       /* fixme: we cant hanlde vararg methods this way, because the signature is not constant */
+       //mono_register_jit_icall (ves_array_element_address, "ves_array_element_address", NULL);
+       //mono_register_jit_icall (mono_array_new_va, "mono_array_new_va", NULL);
+
+       mono_register_jit_icall (mono_arch_get_throw_exception (), "mono_arch_throw_exception", helper_sig_void_obj, TRUE);
+       mono_register_jit_icall (mono_arch_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", 
+                                helper_sig_void_ptr, TRUE);
+
+
+       g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sin", 1)));
+       mono_register_method_opcode (m, OP_SIN);
+       g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Cos", 1)));
+       mono_register_method_opcode (m, OP_COS);
+       g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Tan", 1)));
+       mono_register_method_opcode (m, OP_TAN);
+       g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Atan", 1)));
+       mono_register_method_opcode (m, OP_ATAN);
+       g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sqrt", 1)));
+       mono_register_method_opcode (m, OP_SQRT);
+       
+       g_assert ((desc = mono_method_desc_new ("System.Math:Abs(double)", 0)));
+       g_assert ((m = mono_method_desc_search_in_image (desc, mono_defaults.corlib)));
+       mono_register_method_opcode (m, OP_ABS);
+
+       /* 
+        * NOTE, NOTE, NOTE, NOTE:
+        * when adding emulation for some opcodes, remember to also add a dummy
+        * rule to the burg files, because we need the arity information to be correct.
+        */
+       mono_register_opcode_emulation (OP_LMUL, helper_sig_long_long_long, mono_llmult);
+       mono_register_opcode_emulation (OP_LMUL_OVF_UN, helper_sig_long_long_long, mono_llmult_ovf_un);
+       mono_register_opcode_emulation (OP_LMUL_OVF, helper_sig_long_long_long, mono_llmult_ovf);
+       mono_register_opcode_emulation (OP_LDIV, helper_sig_long_long_long, mono_lldiv);
+       mono_register_opcode_emulation (OP_LDIV_UN, helper_sig_long_long_long, mono_lldiv_un);
+       mono_register_opcode_emulation (OP_LREM, helper_sig_long_long_long, mono_llrem);
+       mono_register_opcode_emulation (OP_LREM_UN, helper_sig_long_long_long, mono_llrem_un);
+
+       mono_register_opcode_emulation (OP_LSHL, helper_sig_long_long_int, mono_lshl);
+       mono_register_opcode_emulation (OP_LSHR, helper_sig_long_long_int, mono_lshr);
+       mono_register_opcode_emulation (OP_LSHR_UN, helper_sig_long_long_int, mono_lshr_un);
+
+       mono_register_opcode_emulation (OP_FCONV_TO_U8, helper_sig_ulong_double, mono_fconv_u8);
+       mono_register_opcode_emulation (OP_FCONV_TO_U4, helper_sig_uint_double, mono_fconv_u4);
+       mono_register_opcode_emulation (OP_FCONV_TO_OVF_I8, helper_sig_long_double, mono_fconv_ovf_i8);
+       mono_register_opcode_emulation (OP_FCONV_TO_OVF_U8, helper_sig_ulong_double, mono_fconv_ovf_u8);
+
+#if SIZEOF_VOID_P == 4
+       mono_register_opcode_emulation (OP_FCONV_TO_U, helper_sig_uint_double, mono_fconv_u4);
+#else
+#warning "fixme: add opcode emulation"
+#endif
+
+       /* other jit icalls */
+       mono_register_jit_icall (mono_class_static_field_address , "mono_class_static_field_address", 
+                                helper_sig_ptr_ptr_ptr, FALSE);
+       mono_register_jit_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", helper_sig_ptr_ptr_ptr, FALSE);
+       mono_register_jit_icall (mono_ldstr, "mono_ldstr", helper_sig_ldstr, FALSE);
+       mono_register_jit_icall (helper_memcpy, "helper_memcpy", helper_sig_memcpy, FALSE);
+       mono_register_jit_icall (helper_memset, "helper_memset", helper_sig_memset, FALSE);
+       mono_register_jit_icall (helper_initobj, "helper_initobj", helper_sig_initobj, FALSE);
+       mono_register_jit_icall (mono_object_new, "mono_object_new", helper_sig_object_new, FALSE);
+       mono_register_jit_icall (mono_array_new, "mono_array_new", helper_sig_newarr, FALSE);
+       mono_register_jit_icall (mono_string_to_utf16, "mono_string_to_utf16", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_string_from_utf16, "mono_string_from_utf16", helper_sig_obj_ptr, FALSE);
+       mono_register_jit_icall (mono_string_new_wrapper, "mono_string_new_wrapper", helper_sig_obj_ptr, FALSE);
+       mono_register_jit_icall (mono_string_to_utf8, "mono_string_to_utf8", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_string_to_bstr, "mono_string_to_bstr", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_string_to_ansibstr, "mono_string_to_ansibstr", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_string_builder_to_utf8, "mono_string_builder_to_utf8", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_array_to_savearray, "mono_array_to_savearray", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_array_to_lparray, "mono_array_to_lparray", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_delegate_to_ftnptr, "mono_delegate_to_ftnptr", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_marshal_string_array, "mono_marshal_string_array", helper_sig_ptr_obj, FALSE);
+       mono_register_jit_icall (mono_string_utf8_to_builder, "mono_string_utf8_to_builder", helper_sig_void_ptr_ptr, FALSE);
+       mono_register_jit_icall (mono_marshal_free_array, "mono_marshal_free_array", helper_sig_void_ptr_ptr, FALSE);
+       mono_register_jit_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", helper_sig_void_ptr_ptr_ptr, FALSE);
+       mono_register_jit_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", helper_sig_void_ptr_ptr_ptr, FALSE);
+       mono_register_jit_icall (g_free, "g_free", helper_sig_void_ptr, FALSE);
+       mono_register_jit_icall (mono_ldftn, "mono_ldftn", helper_sig_compile, FALSE);
+       mono_register_jit_icall (mono_ldvirtfn, "mono_ldvirtfn", helper_sig_compile_virt, FALSE);
+
+       mono_runtime_init (domain, mono_thread_start_cb,
+                          mono_thread_attach_cb);
+
+       //mono_thread_attach (domain);
+       return domain;
+}
+
+MonoJitStats mono_jit_stats = {0};
+
+static void 
+print_jit_stats (void)
+{
+       if (mono_jit_stats.enabled) {
+               g_print ("Mono Jit statistics\n");
+               g_print ("Compiled methods:       %ld\n", mono_jit_stats.methods_compiled);
+               g_print ("Methods from AOT:       %ld\n", mono_jit_stats.methods_aot);
+               g_print ("Methods cache lookup:   %ld\n", mono_jit_stats.methods_lookups);
+               g_print ("Method trampolines:     %ld\n", mono_jit_stats.method_trampolines);
+               g_print ("Basic blocks:           %ld\n", mono_jit_stats.basic_blocks);
+               g_print ("Max basic blocks:       %ld\n", mono_jit_stats.max_basic_blocks);
+               g_print ("Allocated vars:         %ld\n", mono_jit_stats.allocate_var);
+               g_print ("Analyze stack repeat:   %ld\n", mono_jit_stats.analyze_stack_repeat);
+               g_print ("Compiled CIL code size: %ld\n", mono_jit_stats.cil_code_size);
+               g_print ("Native code size:       %ld\n", mono_jit_stats.native_code_size);
+               g_print ("Max code size ratio:    %.2f (%s::%s)\n", mono_jit_stats.max_code_size_ratio/100.0,
+                               mono_jit_stats.max_ratio_method->klass->name, mono_jit_stats.max_ratio_method->name);
+               g_print ("Biggest method:         %ld (%s::%s)\n", mono_jit_stats.biggest_method_size,
+                               mono_jit_stats.biggest_method->klass->name, mono_jit_stats.biggest_method->name);
+               g_print ("Code reallocs:          %ld\n", mono_jit_stats.code_reallocs);
+               g_print ("Allocated code size:    %ld\n", mono_jit_stats.allocated_code_size);
+               g_print ("Inlineable methods:     %ld\n", mono_jit_stats.inlineable_methods);
+               g_print ("Inlined methods:        %ld\n", mono_jit_stats.inlined_methods);
+               
+               g_print ("\nCreated object count:   %ld\n", mono_stats.new_object_count);
+               g_print ("Initialized classes:    %ld\n", mono_stats.initialized_class_count);
+               g_print ("Used classes:           %ld\n", mono_stats.used_class_count);
+               g_print ("Static data size:       %ld\n", mono_stats.class_static_data_size);
+               g_print ("VTable data size:       %ld\n", mono_stats.class_vtable_size);
+       }
+}
+
+void
+mini_cleanup (MonoDomain *domain)
+{
+       /* 
+        * mono_runtime_cleanup() needs to be called early since
+        * it needs the execution engine still fully working (it will
+        * wait for other threads to finish).
+        */
+       mono_runtime_cleanup (domain);
+
+       mono_domain_finalize (domain);
+
+       mono_profiler_shutdown ();
+
+       mono_debug_cleanup ();
+#ifdef PLATFORM_WIN32
+       win32_seh_cleanup();
+#endif
+
+       mono_domain_unload (domain, TRUE);
+
+       print_jit_stats ();
+       DeleteCriticalSection (metadata_section);
+}
+
+void
+mini_set_defaults (int verbose_level, guint32 opts)
+{
+       mini_verbose = verbose_level;
+       default_opt = opts;
+}
+
diff --git a/mono/mini/mini.h b/mono/mini/mini.h
new file mode 100644 (file)
index 0000000..889cc04
--- /dev/null
@@ -0,0 +1,662 @@
+#ifndef __MONO_MINI_H__
+#define __MONO_MINI_H__
+
+#include <glib.h>
+#include <signal.h>
+#include <mono/metadata/loader.h>
+#include <mono/metadata/mempool.h>
+#include <mono/utils/monobitset.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/opcodes.h>
+#include <mono/metadata/tabledefs.h>
+#include "regalloc.h"
+
+/* fixme: configure should set this */
+#define SIZEOF_VOID_P 4
+
+#define MONO_USE_AOT_COMPILER
+
+#if 1
+#define mono_bitset_test_fast(set,n) (((guint32*)set)[2+(n)/32] & (1 << ((n) % 32)))
+#else
+#define mono_bitset_test_fast(set,n) mono_bitset_test(set,n)
+#endif
+
+#if 0
+#define mono_bitset_foreach_bit(set,b,n) \
+       for (b = 0; b < n; b++)\
+               if (mono_bitset_test_fast(set,b))
+#define mono_bitset_foreach_bit_rev(set,b,n) \
+       for (b = n - 1; b >= 0; b--)\
+               if (mono_bitset_test_fast(set,b))
+#else
+#define mono_bitset_foreach_bit(set,b,n) \
+       for (b = mono_bitset_find_first (set, -1); b < n && b >= 0; b = mono_bitset_find_first (set, b))
+#define mono_bitset_foreach_bit_rev(set,b,n) \
+       for (b = mono_bitset_find_last (set, n - 1); b >= 0; b = b ? mono_bitset_find_last (set, b) : -1)
+#endif
+
+/*
+ * Pull the list of opcodes
+ */
+#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
+       a = i,
+
+enum {
+#include "mono/cil/opcode.def"
+       CEE_LASTOP
+};
+#undef OPDEF
+
+#define MONO_VARINFO(cfg,varnum) ((cfg)->vars [varnum])
+
+#define MONO_INST_NEW(cfg,dest,op) do {        \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = (op);  \
+       } while (0)
+
+#define MONO_INST_NEW_CALL(cfg,dest,op) do {   \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoCallInst));   \
+               (dest)->inst.opcode = (op);     \
+       } while (0)
+
+#define MONO_ADD_INS(b,inst) do {      \
+               if ((b)->last_ins) {    \
+                       (b)->last_ins->next = (inst);   \
+                       (b)->last_ins = (inst); \
+               } else {        \
+                       (b)->code = (b)->last_ins = (inst);     \
+               }       \
+       } while (0)
+
+typedef struct MonoInst MonoInst;
+typedef struct MonoCallInst MonoCallInst;
+typedef struct MonoEdge MonoEdge;
+typedef struct MonoMethodVar MonoMethodVar;
+typedef struct MonoBasicBlock MonoBasicBlock;
+typedef struct MonoLMF MonoLMF;
+typedef struct MonoSpillInfo MonoSpillInfo;
+
+extern guint32 mono_jit_tls_id;
+extern gboolean mono_jit_trace_calls;
+extern gboolean mono_break_on_exc;
+extern int mono_exc_esp_offset;
+extern gboolean mono_compile_aot;
+extern gboolean mono_trace_coverage;
+extern gboolean mono_jit_profile;
+
+extern CRITICAL_SECTION *metadata_section;
+
+struct MonoEdge {
+       MonoEdge *next;
+       MonoBasicBlock *bb;
+       /* add edge type? */
+};
+
+struct MonoSpillInfo {
+       MonoSpillInfo *next;
+       int offset;
+};
+
+/*
+ * The IR-level basic block.  
+ *
+ * A basic block can have multiple exits just fine, as long as the point of
+ * 'departure' is the last instruction in the basic block. Extended basic
+ * blocks, on the other hand, may have instructions that leave the block
+ * midstream. The important thing is that they cannot be _entered_
+ * midstream, ie, execution of a basic block (or extened bb) always start
+ * at the beginning of the block, never in the middle.
+ */
+struct MonoBasicBlock {
+       MonoInst *last_ins;
+
+       /* Points to the start of the CIL code that initiated this BB */
+       unsigned char* cil_code;
+
+       /* Length of the CIL block */
+       gint32 cil_length;
+
+       /* The address of the generated code, used for fixups */
+       int native_offset;
+       int max_offset;
+       
+       gint32 dfn;
+
+       /* unique block number identification */
+       gint32 block_num;
+
+       /* Visited and reachable flags */
+       guint32 flags;
+
+       /* Basic blocks: incoming and outgoing counts and pointers */
+       gint16 out_count, in_count;
+       MonoBasicBlock **in_bb;
+       MonoBasicBlock **out_bb;
+
+       /* the next basic block in the order it appears in IL */
+       MonoBasicBlock *next_bb;
+
+       /*
+        * Before instruction selection it is the first tree in the
+        * forest and the first item in the list of trees. After
+        * instruction selection it is the first instruction and the
+        * first item in the list of instructions.
+        */
+       MonoInst *code;
+
+       /*
+        * SSA and loop based flags
+        */
+       MonoBitSet *dominators;
+       MonoBitSet *dfrontier;
+       MonoBasicBlock *idom;
+       GList *dominated;
+       /* fast dominator algorithm */
+       MonoBasicBlock *df_parent, *ancestor, *child, *label;
+       MonoEdge *bucket;
+       int size, sdom, idomn;
+       
+       /* loop nesting and recognition */
+       GList *loop_blocks;
+       gint8  nesting;
+
+       /* use for liveness analysis */
+       MonoBitSet *gen_set;
+       MonoBitSet *kill_set;
+       MonoBitSet *live_in_set;
+       MonoBitSet *live_out_set;
+
+       /* fields to deal with non-empty stack slots at bb boundary */
+       guint16 out_scount, in_scount;
+       MonoInst **out_stack;
+       MonoInst **in_stack;
+
+       /* we use that to prevent merging of bblock covered by different clauses*/
+       guint real_offset;
+        guint region;
+
+       /* The current symbolic register number, used in local register allocation. */
+       guint16 max_ireg, max_freg;
+};
+
+/* BBlock flags */
+#define BB_VISITED 1
+#define BB_REACHABLE 2
+
+struct MonoInst {
+       union {
+               union {
+                       MonoInst *src;
+                       MonoMethodVar *var;
+                       gint32 const_val;
+                       gpointer p;
+                       MonoMethod *method;
+                       MonoMethodSignature *signature;
+                       MonoBasicBlock **many_blocks;
+                       MonoBasicBlock *target_block;
+                       MonoInst **args;
+                       MonoType *vtype;
+                       MonoClass *klass;
+                       int *phi_args;
+               } op [2];
+               gint64 i8const;
+               double r8const;
+       } data;
+       guint16 opcode;
+       guint8  type; /* stack type */
+       guint   ssa_op : 3;
+       guint8  flags  : 5;
+       
+       /* used by the register allocator */
+       gint16 dreg, sreg1, sreg2, unused;
+       
+       MonoInst *next;
+       MonoClass *klass;
+       const unsigned char* cil_code; /* for debugging and bblock splitting */
+};
+       
+struct MonoCallInst {
+       MonoInst inst;
+       MonoMethodSignature *signature;
+       MonoMethod *method;
+       MonoInst **args;
+       gconstpointer fptr;
+       guint stack_usage;
+       guint32 used_iregs;
+       guint32 used_fregs;
+};
+
+/* 
+ * flags for MonoInst
+ * Note: some of the values overlap, because they can't appear
+ * in the same MonoInst.
+ */
+enum {
+       MONO_INST_HAS_METHOD = 1,
+       /* temp local created by a DUP: used only within a BB */
+       MONO_INST_IS_TEMP    = 1,
+       MONO_INST_INIT       = 1, /* in localloc */
+       MONO_INST_IS_DEAD    = 2,
+       MONO_INST_TAILCALL   = 4,
+       MONO_INST_VOLATILE   = 4,
+       MONO_INST_BRLABEL    = 4,
+       MONO_INST_UNALIGNED  = 8,
+       /* the address of the variable has been taken */
+       MONO_INST_INDIRECT   = 16
+};
+
+#define inst_c0 data.op[0].const_val
+#define inst_c1 data.op[1].const_val
+#define inst_i0 data.op[0].src
+#define inst_i1 data.op[1].src
+#define inst_p0 data.op[0].p
+#define inst_p1 data.op[1].p
+#define inst_l  data.i8const
+#define inst_r  data.r8const
+#define inst_left  data.op[0].src
+#define inst_right data.op[1].src
+
+#define inst_newa_len   data.op[0].src
+#define inst_newa_class data.op[1].klass
+
+#define inst_switch data.op[0].switch_blocks
+#define inst_var    data.op[0].var
+#define inst_vtype  data.op[1].vtype
+/* in branch instructions */
+#define inst_many_bb   data.op[1].many_blocks
+#define inst_target_bb data.op[0].target_block
+#define inst_true_bb   data.op[1].many_blocks[0]
+#define inst_false_bb  data.op[1].many_blocks[1]
+
+#define inst_basereg sreg1
+#define inst_indexreg sreg2
+#define inst_destbasereg dreg
+#define inst_offset data.op[0].const_val
+#define inst_imm    data.op[1].const_val
+
+#define inst_phi_args   data.op[1].phi_args
+
+/* instruction description for use in regalloc/scheduling */
+enum {
+       MONO_INST_DEST,
+       MONO_INST_SRC1,
+       MONO_INST_SRC2,
+       MONO_INST_FLAGS,
+       MONO_INST_CLOB,
+       MONO_INST_COST,
+       MONO_INST_DELAY,
+       MONO_INST_RES,
+       MONO_INST_LEN,
+       MONO_INST_MAX
+};
+
+typedef union {
+       struct {
+               guint16 tid; /* tree number */
+               guint16 bid; /* block number */
+       } pos ;
+       guint32 abs_pos; 
+} MonoPosition;
+
+typedef struct {
+       MonoPosition first_use, last_use;
+} MonoLiveRange;
+
+/*
+ * Additional information about a variable
+ */
+struct MonoMethodVar {
+       guint           idx; /* inside cfg->varinfo, cfg->vars */
+       guint           last_name;
+       MonoBitSet     *dfrontier;
+       MonoLiveRange   range; /* generated by liveness analysis */
+       int             reg; /* != -1 if allocated into a register */
+       int             spill_costs;
+       MonoBitSet     *def_in; /* used by SSA */
+       MonoInst       *def;    /* used by SSA */
+       MonoBasicBlock *def_bb; /* used by SSA */
+       GList          *uses;   /* used by SSA */
+       char            cpstate;  /* used by SSA conditional  constant propagation */
+};
+
+typedef struct {
+       gpointer          end_of_stack;
+       MonoLMF          *lmf;
+       void            (*abort_func) (MonoObject *object);
+} MonoJitTlsData;
+
+typedef enum {
+       MONO_PATCH_INFO_BB,
+       MONO_PATCH_INFO_ABS,
+       MONO_PATCH_INFO_LABEL,
+       MONO_PATCH_INFO_METHOD,
+       MONO_PATCH_INFO_METHODCONST,
+       MONO_PATCH_INFO_INTERNAL_METHOD,
+       MONO_PATCH_INFO_SWITCH,
+       MONO_PATCH_INFO_EXC,
+       MONO_PATCH_INFO_CLASS,
+        MONO_PATCH_INFO_IMAGE,
+        MONO_PATCH_INFO_FIELD,
+        MONO_PATCH_INFO_R4,
+       MONO_PATCH_INFO_R8,
+       MONO_PATCH_INFO_IP
+} MonoJumpInfoType;
+
+typedef struct MonoJumpInfo MonoJumpInfo;
+struct MonoJumpInfo {
+       MonoJumpInfo *next;
+       union {
+               int i;
+               guint8 *p;
+               MonoInst *label;
+       } ip;
+
+       MonoJumpInfoType type;
+       union {
+               gconstpointer   target;
+               int             offset;
+               MonoBasicBlock *bb;
+               MonoBasicBlock **table;
+               MonoInst       *inst;
+               MonoMethod     *method;
+               MonoClass      *klass;
+               MonoClassField *field;
+               MonoImage      *image;
+               const char     *name;
+       } data;
+
+       int table_size; /* use by switch */
+};
+
+/* optimization flags: keep up to date with the name array in mini.c */
+enum {
+       MONO_OPT_PEEPHOLE = 1 << 0,
+       MONO_OPT_BRANCH   = 1 << 1,
+       MONO_OPT_INLINE   = 1 << 2,
+       MONO_OPT_CFOLD    = 1 << 3,
+       MONO_OPT_CONSPROP = 1 << 4,
+       MONO_OPT_COPYPROP = 1 << 5,
+       MONO_OPT_DEADCE   = 1 << 6,
+       MONO_OPT_LINEARS  = 1 << 7,
+       MONO_OPT_CMOV     = 1 << 8,
+       MONO_OPT_SAHRED   = 1 << 9,
+       MONO_OPT_SCHED    = 1 << 10,
+       MONO_OPT_INTRINS  = 1 << 11,
+       MONO_OPT_TAILC    = 1 << 12,
+       MONO_OPT_LOOP     = 1 << 13,
+       MONO_OPT_FCMOV    = 1 << 14
+};
+
+/*
+ * Control Flow Graph and compilation unit information
+ */
+typedef struct {
+       MonoMethod      *method;
+       MonoMemPool     *mempool;
+       MonoInst       **varinfo;
+       MonoMethodVar  **vars;
+       MonoInst        *ret;
+       MonoBasicBlock  *bb_entry;
+       MonoBasicBlock  *bb_exit;
+       MonoBasicBlock  *bb_init;
+       MonoBasicBlock **bblocks;
+       GHashTable      *bb_hash;
+       MonoMemPool     *state_pool; /* used by instruction selection */
+       MonoBasicBlock  *cbb;        /* used by instruction selection */
+       MonoInst        *prev_ins;   /* in decompose */
+       MonoJumpInfo    *patch_info;
+       guint            num_bblocks;
+       guint            locals_start;
+       guint            num_varinfo; /* used items in varinfo */
+       guint            varinfo_count; /* total storage in varinfo */
+       gint             stack_offset;
+       MonoRegState    *rs;
+       MonoSpillInfo   *spill_info;
+       gint             spill_count;
+       // unsigned char   *cil_code;
+
+       MonoInst        *exvar; /* the exception object passed to catch/filter blocks */
+       MonoInst        *domainvar; /* a cache for the current domain */
+       
+       MonoDomain      *domain;
+
+       unsigned char   *native_code;
+       guint            code_size;
+       guint            code_len;
+       guint            prolog_end;
+       guint            epilog_begin;
+       guint32          used_int_regs;
+       guint32          opt;
+       guint32          flags;
+       guint32          comp_done;
+       guint32          verbose_level;
+       guint32          stack_usage;
+       guint32          param_area;
+       gboolean         disable_aot;
+       gboolean         disable_ssa;
+       gpointer         debug_info;
+} MonoCompile;
+
+typedef enum {
+       MONO_CFG_HAS_ALLOCA = 1 << 0,
+       MONO_CFG_HAS_CALLS  = 1 << 1
+} MonoCompileFlags;
+
+typedef struct {
+       int entries;
+       struct {
+               int iloffset;
+               int count;
+       } data [0];
+} MonoCoverageInfo;
+
+typedef struct {
+       gulong methods_compiled;
+       gulong methods_aot;
+       gulong methods_lookups;
+       gulong method_trampolines;
+       gulong allocate_var;
+       gulong analyze_stack_repeat;
+       gulong cil_code_size;
+       gulong native_code_size;
+       gulong code_reallocs;
+       gulong max_code_size_ratio;
+       gulong biggest_method_size;
+       gulong allocated_code_size;
+       gulong inlineable_methods;
+       gulong inlined_methods;
+       gulong basic_blocks;
+       gulong max_basic_blocks;
+       MonoMethod *max_ratio_method;
+       MonoMethod *biggest_method;
+       gboolean enabled;
+} MonoJitStats;
+
+extern MonoJitStats mono_jit_stats;
+
+/* values for MonoInst.ssa_op */
+enum {
+       MONO_SSA_NOP,
+       MONO_SSA_LOAD,
+       MONO_SSA_STORE,
+       MONO_SSA_MAYBE_LOAD,
+       MONO_SSA_MAYBE_STORE
+};
+
+#define OP_CEQ    (256+CEE_CEQ)
+#define OP_CLT    (256+CEE_CLT)
+#define OP_CLT_UN (256+CEE_CLT_UN)
+#define OP_CGT    (256+CEE_CGT)
+#define OP_CGT_UN (256+CEE_CGT_UN)
+#define OP_LOCALLOC (256+CEE_LOCALLOC)
+
+/* opcodes: value assigned after all the CIL opcodes */
+#ifdef MINI_OP
+#undef MINI_OP
+#endif
+#define MINI_OP(a,b) a,
+enum {
+       OP_START = MONO_CEE_LAST,
+#include "mini-ops.h"
+       OP_LAST
+};
+#undef MINI_OP
+
+/* make this depend on 32bit platform (use OP_LADD otherwise) */
+#define OP_PADD CEE_ADD
+#define OP_PNEG CEE_NEG
+#define OP_PCONV_TO_U2 CEE_CONV_U2
+#define OP_PCONV_TO_OVF_I1_UN CEE_CONV_OVF_I1_UN
+#define OP_PCONV_TO_OVF_I1 CEE_CONV_OVF_I1
+#define OP_PCEQ CEE_CEQ
+
+enum {
+       STACK_INV,
+       STACK_I4,
+       STACK_I8,
+       STACK_PTR,
+       STACK_R8,
+       STACK_MP,
+       STACK_OBJ,
+       STACK_VTYPE,
+       STACK_MAX
+};
+
+typedef struct {
+       union {
+               double   r8;
+               gint32   i4;
+               gint64   i8;
+               gpointer p;
+               MonoClass *klass;
+       } data;
+       int type;
+} StackSlot;
+
+enum {
+       MONO_COMP_DOM = 1,
+       MONO_COMP_IDOM = 2,
+       MONO_COMP_DFRONTIER = 4,
+       MONO_COMP_DOM_REV = 8,
+       MONO_COMP_LIVENESS = 16,
+       MONO_COMP_SSA = 32,
+       MONO_COMP_SSA_DEF_USE = 64,
+       MONO_COMP_REACHABILITY = 128,
+       MONO_COMP_LOOPS = 256
+};
+
+typedef enum {
+       MONO_GRAPH_CFG = 1,
+       MONO_GRAPH_DTREE = 2,
+       MONO_GRAPH_CFG_CODE = 4,
+       MONO_GRAPH_CFG_SSA = 8,
+       MONO_GRAPH_CFG_OPTCODE = 16
+} MonoGraphOptions;
+
+typedef struct {
+       char *name;
+       gconstpointer func;
+       gconstpointer wrapper;
+       MonoMethodSignature *sig;
+} MonoJitICallInfo;
+
+typedef void (*MonoInstFunc) (MonoInst *tree, gpointer data);
+
+/* main function */
+int         mini_main                      (int argc, char* argv[]);
+void        mini_set_defaults              (int verbose_level, guint32 opts);
+MonoDomain* mini_init                      (const char *filename);
+void        mini_cleanup                   (MonoDomain *domain);
+
+/* helper methods */
+int       mini_parse_default_optimizations  (const char* p);
+void      mono_bblock_add_inst              (MonoBasicBlock *bb, MonoInst *inst);
+void      mono_constant_fold                (MonoCompile *cfg);
+void      mono_constant_fold_inst           (MonoInst *inst, gpointer data);
+void      mono_cprop_local                  (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size);
+MonoInst* mono_compile_create_var           (MonoCompile *cfg, MonoType *type, int opcode);
+void      mono_blockset_print               (MonoCompile *cfg, MonoBitSet *set, const char *name, guint idom);
+void      mono_print_tree                   (MonoInst *tree);
+int       mono_spillvar_offset              (MonoCompile *cfg, int spillvar);
+void      mono_select_instructions          (MonoCompile *cfg);
+const char* mono_inst_name                  (int op);
+void      mono_inst_foreach                 (MonoInst *tree, MonoInstFunc func, gpointer data);
+void      mono_disassemble_code             (guint8 *code, int size, char *id);
+guint     mono_type_to_ldind                (MonoType *t);
+guint     mono_type_to_stind                (MonoType *t);
+void      mono_add_patch_info               (MonoCompile *cfg, int ip, MonoJumpInfoType type, gconstpointer target);
+void      mono_remove_patch_info            (MonoCompile *cfg, int ip);
+gpointer  mono_get_lmf_addr                 (void);
+GList    *mono_varlist_insert_sorted        (MonoCompile *cfg, GList *list, MonoMethodVar *mv, gboolean sort_end);
+void      mono_analyze_liveness             (MonoCompile *cfg);
+void      mono_linear_scan                  (MonoCompile *cfg, GList *vars, GList *regs, guint32 *used_mask);
+void      mono_create_jump_table            (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks);
+int       mono_compile_assembly             (MonoAssembly *ass, guint32 opts);
+MonoCompile *mini_method_compile            (MonoMethod *method, guint32 opts, MonoDomain *domain, int parts);
+void      mono_destroy_compile              (MonoCompile *cfg);
+gpointer  mono_aot_get_method               (MonoMethod *method);
+gboolean  mono_method_blittable             (MonoMethod *method);
+void      mono_register_opcode_emulation    (int opcode, MonoMethodSignature *sig, gpointer func);
+void      mono_arch_register_lowlevel_calls (void);
+void      mono_draw_graph                   (MonoCompile *cfg, MonoGraphOptions draw_options);
+void      mono_add_varcopy_to_end           (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest);
+
+int               mono_find_method_opcode      (MonoMethod *method);
+MonoJitICallInfo *mono_find_jit_icall_by_name  (const char *name);
+MonoJitICallInfo *mono_find_jit_icall_by_addr  (gconstpointer addr);
+MonoJitICallInfo *mono_register_jit_icall      (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save);
+
+MonoCoverageInfo *mono_allocate_coverage_info (MonoMethod *method, int size);
+MonoCoverageInfo *mono_get_coverage_info      (MonoMethod *method);
+
+/* methods that must be provided by the arch-specific port */
+guint32   mono_arch_cpu_optimizazions           (void);
+void      mono_arch_instrument_mem_needs        (MonoMethod *method, int *stack, int *code);
+void     *mono_arch_instrument_prolog           (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments);
+void     *mono_arch_instrument_epilog           (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments);
+MonoCallInst *mono_arch_call_opcode             (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual);
+void      mono_codegen                          (MonoCompile *cfg);
+const char *mono_arch_regname                   (int reg);
+gpointer  mono_arch_get_throw_exception         (void);
+gpointer  mono_arch_get_throw_exception_by_name (void);
+gpointer  mono_arch_create_jit_trampoline       (MonoMethod *method);
+GList    *mono_arch_get_allocatable_int_vars    (MonoCompile *cfg);
+GList    *mono_arch_get_global_int_regs         (MonoCompile *cfg);
+gboolean  mono_arch_handle_exception            (struct sigcontext *ctx, gpointer obj, gboolean test_only);
+void      mono_arch_patch_code                  (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji);
+void      mono_arch_flush_icache                (guint8 *code, gint size);
+int       mono_arch_max_epilog_size             (MonoCompile *cfg);
+guint8   *mono_arch_emit_prolog                 (MonoCompile *cfg);
+void      mono_arch_emit_epilog                 (MonoCompile *cfg);
+void      mono_arch_local_regalloc              (MonoCompile *cfg, MonoBasicBlock *bb);
+void      mono_arch_output_basic_block          (MonoCompile *cfg, MonoBasicBlock *bb);
+gboolean  mono_arch_has_unwind_info             (gconstpointer addr);
+void      mono_arch_allocate_vars               (MonoCompile *m);
+void      mono_jit_walk_stack                   (MonoStackWalk func, gpointer user_data);
+MonoArray *ves_icall_get_trace                  (MonoException *exc, gint32 skip, MonoBoolean need_file_info);
+MonoBoolean ves_icall_get_frame_info            (gint32 skip, MonoBoolean need_file_info, 
+                                                MonoReflectionMethod **method, 
+                                                gint32 *iloffset, gint32 *native_offset,
+                                                MonoString **file, gint32 *line, gint32 *column);
+
+/* Dominator/SSA methods */
+void        mono_compile_dominator_info         (MonoCompile *cfg, int dom_flags);
+void        mono_compute_natural_loops          (MonoCompile *cfg);
+MonoBitSet* mono_compile_iterated_dfrontier     (MonoCompile *cfg, MonoBitSet *set);
+void        mono_ssa_compute                    (MonoCompile *cfg);
+void        mono_ssa_remove                     (MonoCompile *cfg);
+void        mono_ssa_cprop                      (MonoCompile *cfg);
+void        mono_ssa_deadce                     (MonoCompile *cfg);
+void        mono_ssa_strength_reduction         (MonoCompile *cfg);
+
+/* debugging support */
+void      mono_debug_init_method                (MonoCompile *cfg, MonoBasicBlock *start_block);
+void      mono_debug_open_method                (MonoCompile *cfg);
+void      mono_debug_close_method               (MonoCompile *cfg);
+void      mono_debug_open_block                 (MonoCompile *cfg, MonoBasicBlock *bb, guint32 address);
+void      mono_debug_record_line_number         (MonoCompile *cfg, MonoInst *ins, guint32 address);
+
+#endif /* __MONO_MINI_H__ */  
diff --git a/mono/mini/mini.prj b/mono/mini/mini.prj
new file mode 100644 (file)
index 0000000..b9bd7c2
--- /dev/null
@@ -0,0 +1,28 @@
+;; -*- Prcs -*-
+(Created-By-Prcs-Version 1 3 2)
+(Project-Description "The mono SSA-based JIT.")
+(Project-Version mini 0 3)
+(Parent-Version mini 0 2)
+(Version-Log "")
+(New-Version-Log "")
+(Checkin-Time "Sat, 21 Sep 2002 12:11:29 +0200")
+(Checkin-Login lupus)
+(Populate-Ignore ())
+(Project-Keywords)
+(Files
+;; This is a comment.  Fill in files here.
+;; For example:  (prcs/checkout.cc ())
+
+;; Files added by populate at Sat, 08 Jun 2002 17:27:56 +0200,
+;; to version 0.0(w), by lupus:
+
+  (regalloc.c (mini/0_regalloc.c 1.1 664))
+  (cfold.c (mini/1_cfold.c 1.1 664))
+  (mini-x86.c (mini/2_mini-x86.c 1.2 664))
+  (mini.h (mini/3_mini.h 1.3 664))
+  (makefile (mini/4_makefile 1.3 664))
+  (test.cs (mini/5_test.cs 1.1 664))
+  (mini.c (mini/6_mini.c 1.3 664))
+)
+(Merge-Parents)
+(New-Merge-Parents)
diff --git a/mono/mini/objects.cs b/mono/mini/objects.cs
new file mode 100644 (file)
index 0000000..e3b15cf
--- /dev/null
@@ -0,0 +1,415 @@
+using System;
+using System.Reflection;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ *     the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ *     all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+struct Simple {
+       public int a;
+       public byte b;
+       public short c;
+       public long d;
+}
+
+class Sample {
+       public int a;
+       public Sample (int v) {
+               a = v;
+       }
+}
+
+class Tests {
+
+       static int Main () {
+               return TestDriver.RunTests (typeof (Tests));
+       }
+       
+       static int test_0_return () {
+               Simple s;
+               s.a = 1;
+               s.b = 2;
+               s.c = (short)(s.a + s.b);
+               s.d = 4;
+               return s.a - 1;
+       }
+
+       static int test_0_string_access () {
+               string s = "Hello";
+               if (s [1] != 'e')
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_string_virtual_call () {
+               string s = "Hello";
+               string s2 = s.ToString ();
+               if (s2 [1] != 'e')
+                       return 1;
+               return 0;
+       }
+
+       static int test_0_iface_call () {
+               string s = "Hello";
+               object o = ((ICloneable)s).Clone ();
+               return 0;
+       }
+
+       static int test_5_newobj () {
+               Sample s = new Sample (5);
+               return s.a;
+       }
+
+       static int test_4_box () {
+               object obj = 4;
+               return (int)obj;
+       }
+
+       static Simple get_simple (int v) {
+               Simple r = new Simple ();
+               r.a = v;
+               r.b = (byte)(v + 1);
+               r.c = (short)(v + 2);
+               r.d = v + 3;
+
+               return r;
+       }
+
+       static int test_3_return_struct () {
+               Simple v = get_simple (1);
+
+               if (v.a != 1)
+                       return 0;
+               if (v.b != 2)
+                       return 0;
+               if (v.c != 3)
+                       return 0;
+               if (v.d != 4)
+                       return 0;
+               return 3;
+       }
+
+       public virtual Simple v_get_simple (int v)
+       {
+               return get_simple (v);
+       }
+       
+       static int test_2_return_struct_virtual () {
+               Tests t = new Tests ();
+               Simple v = t.v_get_simple (2);
+
+               if (v.a != 2)
+                       return 0;
+               if (v.b != 3)
+                       return 0;
+               if (v.c != 4)
+                       return 0;
+               if (v.d != 5)
+                       return 0;
+               return 2;
+       }
+
+       static int receive_simple (int a, Simple v, int b) {
+               if (v.a != 1)
+                       return 0;
+               if (v.b != 2)
+                       return 0;
+               if (v.c != 3)
+                       return 0;
+               if (v.d != 4)
+                       return 0;
+               if (a != 7)
+                       return 0;
+               if (b != 9)
+                       return 0;
+               return 1;
+       }
+       
+       static int test_5_pass_struct () {
+               Simple v = get_simple (1);
+               if (receive_simple (7, v, 9) != 1)
+                       return 0;
+               if (receive_simple (7, get_simple (1), 9) != 1)
+                       return 1;
+               return 5;
+       }
+
+       class TestRegA {
+
+               long buf_start;
+               int buf_length, buf_offset;
+       
+               public long Seek (long position) {
+                       long pos = position;
+                       /* interaction between the register allocator and
+                        * allocating arguments to registers */
+                       if (pos >= buf_start && pos <= buf_start + buf_length) {
+                               buf_offset = (int) (pos - buf_start);
+                               return pos;
+                       }
+                       return buf_start;
+               }
+
+       }
+
+       static int test_0_seektest () {
+               TestRegA t = new TestRegA ();
+               return (int)t.Seek (0);
+       }
+
+       class Super : ICloneable {
+               public virtual object Clone () {
+                       return null;
+               }
+       }
+       class Duper: Super {
+       }
+       
+       static int test_0_super_cast () {
+               Duper d = new Duper ();
+               Super sup = d;
+               Object o = d;
+
+               if (!(o is Super))
+                       return 1;
+               try {
+                       d = (Duper)sup;
+               } catch {
+                       return 2;
+               }
+               if (!(d is Object))
+                       return 3;
+               try {
+                       d = (Duper)(object)sup;
+               } catch {
+                       return 4;
+               }
+               return 0;
+       }
+
+       static int test_0_super_cast_array () {
+               Duper[] d = new Duper [0];
+               Super[] sup = d;
+               Object[] o = d;
+
+               if (!(o is Super[]))
+                       return 1;
+               try {
+                       d = (Duper[])sup;
+               } catch {
+                       return 2;
+               }
+               if (!(d is Object[]))
+                       return 3;
+               try {
+                       d = (Duper[])(object[])sup;
+               } catch {
+                       return 4;
+               }
+               return 0;
+       }
+
+       static int test_0_enum_array_cast () {
+               TypeCode[] tc = new TypeCode [0];
+               object[] oa;
+               ValueType[] vta;
+               int[] inta;
+               Array a = tc;
+               bool ok;
+
+               if (a is object[])
+                       return 1;
+               if (a is ValueType[])
+                       return 2;
+               if (a is Enum[])
+                       return 3;
+               try {
+                       ok = false;
+                       oa = (object[])a;
+               } catch {
+                       ok = true;
+               }
+               if (!ok)
+                       return 4;
+               try {
+                       ok = false;
+                       vta = (ValueType[])a;
+               } catch {
+                       ok = true;
+               }
+               if (!ok)
+                       return 5;
+               try {
+                       ok = true;
+                       inta = (int[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 6;
+               return 0;
+       }
+
+       static int test_0_more_cast_corner_cases () {
+               ValueType[] vta = new ValueType [0];
+               Enum[] ea = new Enum [0];
+               Array a = vta;
+               object[] oa;
+               bool ok;
+
+               if (!(a is object[]))
+                       return 1;
+               if (!(a is ValueType[]))
+                       return 2;
+               if (a is Enum[])
+                       return 3;
+               a = ea;
+               if (!(a is object[]))
+                       return 4;
+               if (!(a is ValueType[]))
+                       return 5;
+               if (!(a is Enum[]))
+                       return 6;
+
+               try {
+                       ok = true;
+                       oa = (object[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 7;
+       
+               try {
+                       ok = true;
+                       oa = (Enum[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 8;
+       
+               try {
+                       ok = true;
+                       oa = (ValueType[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 9;
+
+               a = vta;
+               try {
+                       ok = true;
+                       oa = (object[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 10;
+       
+               try {
+                       ok = true;
+                       oa = (ValueType[])a;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 11;
+       
+               try {
+                       ok = false;
+                       vta = (Enum[])a;
+               } catch {
+                       ok = true;
+               }
+               if (!ok)
+                       return 12;
+               return 0;
+       }
+
+       static int test_0_cast_iface_array () {
+               object o = new ICloneable [0];
+               object o2 = new Duper [0];
+               object t;
+               bool ok;
+
+               if (!(o is object[]))
+                       return 1;
+               if (!(o2 is ICloneable[]))
+                       return 2;
+
+               try {
+                       ok = true;
+                       t = (object[])o;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 3;
+       
+               try {
+                       ok = true;
+                       t = (ICloneable[])o2;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 4;
+
+               try {
+                       ok = true;
+                       t = (ICloneable[])o;
+               } catch {
+                       ok = false;
+               }
+               if (!ok)
+                       return 5;
+
+               /* add tests for interfaces that 'inherit' interfaces */
+               return 0;
+       }
+
+       private static int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+       private static int AbsoluteDays (int year, int month, int day)
+       {
+               int temp = 0, m = 1;
+               int[] days = daysmonthleap;
+               while (m < month)
+                       temp += days[m++];
+               return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
+       }
+
+       static int test_719162_complex_div () {
+               int adays = AbsoluteDays (1970, 1, 1);
+               return adays;
+       }
+
+       static int test_1_store_decimal () {
+               decimal[,] a = {{1}};
+
+               if (a[0,0] != 1m)
+                       return 0;
+               return 1;
+       }
+}
+
diff --git a/mono/mini/opcode-decomp.txt b/mono/mini/opcode-decomp.txt
new file mode 100644 (file)
index 0000000..48968d1
--- /dev/null
@@ -0,0 +1,113 @@
+
+* How to handle complex IL opcodes in an arch-independent way
+
+       Many IL opcodes are very simple: add, ldind etc.
+       Such opcodes can be implemented with a single cpu instruction
+       in most architectures (on some, a group of IL instructions
+       can be converted to a single cpu op).
+       There are many IL opcodes, though, that are more complex, but
+       can be expressed as a series of trees or a single tree of
+       simple operations. Such simple operations are architecture-independent.
+       It makes sense to decompose such complex IL instructions in their
+       simpler equivalent so that we gain in several ways:
+       *) porting effort is easier, because only the simple instructions 
+               need to be implemented in arch-specific code
+       *) we could apply BURG rules to the trees and do pattern matching
+               on them to optimize the expressions according to the host cpu
+       
+       The issue is: where do we do such conversion from coarse opcodes to 
+       simple expressions?
+
+* Doing the conversion in method_to_ir ()
+
+       Some of these conversions can certainly be done in method_to_ir (),
+       but it's not always easy to decide which are better done there and 
+       which in a different pass.
+       For example, let's take ldlen: in the mono implementation, ldlen
+       can be simply implemented with a load from a fixed position in the 
+       array object:
+
+               len = [reg + maxlen_offset]
+       
+       However, ldlen carries also semantics information: the result is the
+       length of the array, and since in the CLR arrays are of fixed size,
+       this information can be useful to later do bounds check removal.
+       If we convert this opcode in method_to_ir () we lost some useful
+       information for further optimizations.
+
+       In some other ways, decomposing an opcode in method_to_ir() may
+       allow for better optimizations later on (need to come up with an 
+       example here ...).
+
+* Doing the conversion in inssel.brg
+
+       Some conversion may be done inside the burg rules: this has the 
+       disadvantage that the instruction selector is not run again on
+       the resulting expression tree and we could miss some optimization
+       (this is what effectively happens with the coarse opcodes in the old 
+       jit). This may also interfere with an efficient local register allocator.
+       It may be possible to add an extension in monoburg that allows a rule 
+       such as:
+
+               recheck: LDLEN (reg) {
+                       create an expression tree representing LDLEN
+                       and return it
+               }
+       
+       When the monoburg label process gets back a recheck, it will run
+       the labeling again on the resulting expression tree.
+       If this is possible at all (and in an efficient way) is a 
+       question for dietmar:-)
+       It should be noted, though, that this may not always work, since
+       some complex IL opcodes may require a series of expression trees
+       and handling such cases in monoburg could become quite hairy.
+       For example, think of opcode that need to do multiple actions on the 
+       same object: this basically means a DUP...
+       On the other end, if a complex opcode needs a DUP, monoburg doesn't
+       actually need to create trees if it emits the instructions in
+       the correct sequence and maintains the right values in the registers
+       (usually the values that need a DUP are not changed...). How
+       this integrates with the current register allocator is not clear, since
+       that assigns registers based on the rule, but the instructions emitted 
+       by the rules may be different (this already happens with the current JIT
+       where a MULT is replaced with lea etc...).
+
+* Doing it in a separate pass.
+
+       Doing the conversion in a separate pass over the instructions
+       is another alternative. This can be done right after method_to_ir ()
+       or after the SSA pass (since the IR after the SSA pass should look
+       almost like the IR we get back from method_to_ir ()).
+
+       This has the following advantages:
+       *) monoburg will handle only the simple opcodes (makes porting easier)
+       *) the instruction selection will be run on all the additional trees
+       *) it's easier to support coarse opcodes that produce multiple expression 
+               trees (and apply the monoburg selector on all of them)
+       *) the SSA optimizer will see the original opcodes and will be able to use
+               the semantic info associated with them
+       
+       The disadvantage is that this is a separate pass on the code and
+       it takes time (how much has not been measured yet, though).
+
+       With this approach, we may also be able to have C implementations
+       of some of the opcodes: this pass would insert a function call to 
+       the C implementation (for example in the cases when first porting
+       to a new arch and implemenating some stuff may be too hard in asm).
+
+* Extended basic blocks
+
+       IL code needs a lot of checks, bounds checks, overflow checks,
+       type checks and so on. This potentially increases by a lot
+       the number of basic blocks in a control flow graph. However,
+       all such blocks end up with a throw opcode that gives control to the
+       exception handling mechanism.
+       After method_to_ir () a MonoBasicBlock can be considered a sort
+       of extended basic block where the additional exits don't point
+       to basic blocks in the same procedure (at least when the method
+       doesn't have exception tables).
+       We need to make sure the passes following method_to_ir () can cope
+       with such kinds of extended basic blocks (especially the passes
+       that we need to apply to all the methods: as a start, we could
+       skip SSA optimizations for methods with exception clauses...)
+
diff --git a/mono/mini/regalloc.c b/mono/mini/regalloc.c
new file mode 100644 (file)
index 0000000..26be7b0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * regalloc.c: register state class
+ *
+ * Authors:
+ *    Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include "mini.h"
+
+MonoRegState*
+mono_regstate_new (void)
+{
+       MonoRegState* rs = g_new0 (MonoRegState, 1);
+
+       mono_regstate_reset (rs);
+
+       return rs;
+}
+
+void
+mono_regstate_reset (MonoRegState *rs) {
+       rs->next_vireg = MONO_MAX_IREGS;
+       rs->next_vfreg = MONO_MAX_FREGS;
+}
+
+void
+mono_regstate_assign (MonoRegState *rs) {
+       int i;
+       g_free (rs->iassign);
+       rs->iassign = g_malloc (MAX (MONO_MAX_IREGS, rs->next_vireg));
+       for (i = 0; i < MONO_MAX_IREGS; ++i) {
+               rs->iassign [i] = i;
+               rs->isymbolic [i] = 0;
+       }
+       for (; i < rs->next_vireg; ++i)
+               rs->iassign [i] = -1;
+}
+
+int
+mono_regstate_alloc_int (MonoRegState *rs, guint32 allow)
+{
+       int i;
+       guint32 mask = allow & rs->ifree_mask;
+       for (i = 0; i < MONO_MAX_IREGS; ++i) {
+               if (mask & (1 << i)) {
+                       rs->ifree_mask &= ~ (1 << i);
+                       return i;
+               }
+       }
+       return -1;
+}
+
+void
+mono_regstate_free_int (MonoRegState *rs, int reg)
+{
+       if (reg >= 0) {
+               rs->ifree_mask |= 1 << reg;
+               rs->isymbolic [reg] = 0;
+       }
+}
+
+inline int
+mono_regstate_next_long (MonoRegState *rs)
+{
+       int rval = rs->next_vireg;
+
+       rs->next_vireg += 2;
+
+       return rval;
+}
+
diff --git a/mono/mini/regalloc.h b/mono/mini/regalloc.h
new file mode 100644 (file)
index 0000000..a344c7c
--- /dev/null
@@ -0,0 +1,53 @@
+
+enum {
+       MONO_REG_FREE,
+       MONO_REG_FREEABLE,
+       MONO_REG_MOVEABLE,
+       MONO_REG_BUSY,
+       MONO_REG_RESERVED
+};
+
+enum {
+       MONO_REG_INT,
+       MONO_REG_DOUBLE
+};
+
+/* make this arch-dependent */
+#define MONO_MAX_IREGS 8
+#define MONO_MAX_FREGS 7
+
+typedef struct {
+       /* symbolic registers */
+       int next_vireg;
+       int next_vfreg;
+
+       /* hard registers */
+       int num_iregs;
+       int num_fregs;
+
+       guint32 ifree_mask;
+       guint32 ffree_mask;
+
+       /* symbolic -> hard register assignment */
+       char *iassign;
+       char *fassign;
+
+       /* hard -> symbolic */
+       int isymbolic [MONO_MAX_IREGS];
+       int fsymbolic [MONO_MAX_FREGS];
+
+       int ispills;
+} MonoRegState;
+
+#define mono_regstate_next_int(rs)   ((rs)->next_vireg++)
+#define mono_regstate_next_float(rs) ((rs)->next_vfreg++)
+
+
+MonoRegState* mono_regstate_new (void);
+
+void          mono_regstate_reset     (MonoRegState *rs);
+void          mono_regstate_assign    (MonoRegState *rs);
+int           mono_regstate_alloc_int (MonoRegState *rs, guint32 allow);
+void          mono_regstate_free_int  (MonoRegState *rs, int reg);
+inline int    mono_regstate_next_long (MonoRegState *rs);
+
diff --git a/mono/mini/regset.c b/mono/mini/regset.c
new file mode 100644 (file)
index 0000000..619b390
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * regset.c: register set abstraction
+ *
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include "regset.h"
+
+MonoRegSet *
+mono_regset_new (int max_regs)
+{
+       MonoRegSet *rs;
+
+       g_return_val_if_fail (max_regs > 0 && max_regs <= 32, NULL);
+
+       rs = g_new0 (MonoRegSet, 1);
+
+       rs->max_regs = max_regs;
+       rs->used_mask = 0;
+       rs->free_mask = ~rs->used_mask;
+       rs->reserved_mask = 0;
+
+       return rs;
+}
+
+void
+mono_regset_free (MonoRegSet *rs)
+{
+       g_free (rs);
+}
+
+void
+mono_regset_reserve_reg (MonoRegSet *rs, int regnum)
+{
+       guint32 ind;
+
+       g_return_if_fail (rs != NULL);
+       g_return_if_fail (rs->max_regs > regnum);
+
+       ind = 1 << regnum;
+
+       rs->reserved_mask |= ind;
+}
+
+int
+mono_regset_alloc_reg (MonoRegSet *rs, int regnum, guint32 exclude_mask)
+{
+       guint32 i, ind;
+
+       g_return_val_if_fail (rs != NULL, -1);
+       g_return_val_if_fail (rs->max_regs > regnum, -1);
+
+       if (regnum < 0) {
+               for (i = 0, ind = 1; i < rs->max_regs; i++, ind = ind << 1) {
+                       if (exclude_mask & ind)
+                               continue;
+                       if ((rs->free_mask & ind) && !(rs->reserved_mask & ind)) {
+                               rs->free_mask &= ~ind;
+                               rs->used_mask |= ind;
+                               return i;
+                       }
+               }
+               return -1;
+       } else {
+               ind = 1 << regnum;
+
+               if (exclude_mask & ind)
+                       return -1;
+
+               if ((rs->free_mask & ind) && !(rs->reserved_mask & ind)) {
+                       rs->free_mask &= ~ind;
+                       rs->used_mask |= ind;
+                       return regnum;
+               }
+               return -1;
+       }
+}
+
+void
+mono_regset_free_reg (MonoRegSet *rs, int regnum)
+{
+       guint32 ind;
+
+       g_return_if_fail (rs != NULL);
+       g_return_if_fail (rs->max_regs > regnum);
+
+       if (regnum < 0)
+               return;
+
+       ind = 1 << regnum;
+
+       g_return_if_fail (rs->free_mask && ind);
+
+       rs->free_mask |= ind;
+}
+
+gboolean
+mono_regset_reg_used (MonoRegSet *rs, int regnum)
+{
+       guint32 ind;
+
+       g_return_val_if_fail (rs != NULL, FALSE);
+       g_return_val_if_fail (rs->max_regs > regnum, FALSE);
+       g_return_val_if_fail (regnum >= 0, FALSE);
+
+       ind = 1 << regnum;
+
+       return rs->used_mask & ind;
+}
+
+
diff --git a/mono/mini/regset.h b/mono/mini/regset.h
new file mode 100644 (file)
index 0000000..0fb15c6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Author:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#ifndef _MONO_JIT_REGSET_H_
+#define _MONO_JIT_REGSET_H_
+
+#include <glib.h>
+
+typedef struct {
+       int max_regs;
+       guint32 free_mask;
+       guint32 used_mask;
+       guint32 reserved_mask;
+} MonoRegSet;
+
+MonoRegSet *
+mono_regset_new         (int max_regs);
+
+void
+mono_regset_free        (MonoRegSet *rs);
+
+int
+mono_regset_alloc_reg   (MonoRegSet *rs, int regnum, guint32 exclude_mask);
+
+void
+mono_regset_free_reg    (MonoRegSet *rs, int regnum);
+
+void
+mono_regset_reserve_reg (MonoRegSet *rs, int regnum);
+
+gboolean
+mono_regset_reg_used    (MonoRegSet *rs, int regnum);
+
+#endif
diff --git a/mono/mini/ssa.c b/mono/mini/ssa.c
new file mode 100644 (file)
index 0000000..8dff2a4
--- /dev/null
@@ -0,0 +1,1099 @@
+/*
+ * ssa.c: Static single assign form support for the JIT compiler.
+ *
+ * Author:
+ *    Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ */
+#include <string.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include "mini.h"
+
+extern guint8 mono_burg_arity [];
+
+#define USE_ORIGINAL_VARS
+#define CREATE_PRUNED_SSA
+
+//#define DEBUG_SSA 1
+
+#define NEW_PHI(cfg,dest,val) do {     \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = OP_PHI;        \
+               (dest)->inst_c0 = (val);        \
+       } while (0)
+
+#define NEW_ICONST(cfg,dest,val) do {  \
+               (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
+               (dest)->opcode = OP_ICONST;     \
+               (dest)->inst_c0 = (val);        \
+               (dest)->type = STACK_I4;        \
+       } while (0)
+
+
+static void 
+unlink_target (MonoBasicBlock *bb, MonoBasicBlock *target)
+{
+       int i;
+
+       for (i = 0; i < bb->out_count; i++) {
+               if (bb->out_bb [i] == target) {
+                       bb->out_bb [i] = bb->out_bb [--bb->out_count];
+                       break;
+               }
+       }
+       for (i = 0; i < target->in_count; i++) {
+               if (target->in_bb [i] == bb) {
+                       target->in_bb [i] = target->in_bb [--target->in_count];
+                       break;
+                       
+               }
+       }
+}
+
+static void
+unlink_unused_bblocks (MonoCompile *cfg) 
+{
+       int i, j;
+       MonoBasicBlock *bb;
+
+       g_assert (cfg->comp_done & MONO_COMP_REACHABILITY);
+
+       for (bb = cfg->bb_entry; bb && bb->next_bb;) {
+               if (!(bb->next_bb->flags & BB_REACHABLE)) {
+                       bb->next_bb = bb->next_bb->next_bb;
+               } else 
+                       bb = bb->next_bb;
+       }
+
+       for (i = 1; i < cfg->num_bblocks; i++) {
+               bb = cfg->bblocks [i];
+              
+               if (!(bb->flags & BB_REACHABLE)) {
+                       for (j = 0; j < bb->in_count; j++) {
+                               unlink_target (bb->in_bb [j], bb);      
+                       }
+                       for (j = 0; j < bb->out_count; j++) {
+                               unlink_target (bb, bb->out_bb [j]);     
+                       }
+               }
+       }
+}
+
+
+
+static void
+replace_usage (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **stack)
+{
+       int arity;
+
+       if (!inst)
+               return;
+
+       arity = mono_burg_arity [inst->opcode];
+
+       if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+               MonoInst *new_var;
+               int idx = inst->inst_i0->inst_c0;
+                       
+               if (stack [idx]) {
+                       new_var = stack [idx];
+               } else {
+                       new_var = cfg->varinfo [idx];
+
+                       if (new_var->opcode != OP_ARG) {
+                               /* uninitialized variable ? */
+                               g_warning ("using uninitialized variables %d in BB%d (%s)", idx, bb->block_num,
+                                          mono_method_full_name (cfg->method, TRUE));
+                               //g_assert_not_reached ();
+                       }
+               }
+#ifdef DEBUG_SSA
+               printf ("REPLACE BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0);
+#endif
+               inst->inst_i0 = new_var;
+       } else {
+
+               if (arity) {
+                       if (inst->ssa_op != MONO_SSA_STORE)
+                               replace_usage (cfg, bb, inst->inst_left, stack);
+                       if (arity > 1)
+                               replace_usage (cfg, bb, inst->inst_right, stack);
+               }
+       }
+}
+
+static void
+mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, MonoInst **stack) 
+{
+       MonoInst *inst, *new_var;
+       int i, j, idx;
+       GList *tmp;
+       MonoInst *new_stack [max_vars];
+
+#ifdef DEBUG_SSA
+       printf ("RENAME VARS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
+#endif
+
+       for (inst = bb->code; inst; inst = inst->next) {
+               if (inst->opcode != OP_PHI)
+                       replace_usage (cfg, bb, inst, stack);
+
+               if (inst->ssa_op == MONO_SSA_STORE && 
+                   (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+                       idx = inst->inst_i0->inst_c0;
+                       g_assert (idx < max_vars);
+
+                       if (!stack [idx] && bb == cfg->bb_init) {
+                               new_var = cfg->varinfo [idx];
+                       } else {
+                               new_var = mono_compile_create_var (cfg, inst->inst_i0->inst_vtype,  inst->inst_i0->opcode);
+                               new_var->flags = inst->inst_i0->flags;
+                       }
+#ifdef DEBUG_SSA
+                       printf ("DEF %d %d\n", idx, new_var->inst_c0);
+#endif
+                       inst->inst_i0 = new_var;
+
+#ifdef USE_ORIGINAL_VARS
+                       cfg->vars [new_var->inst_c0]->reg = idx;
+#endif
+
+                       stack [idx] = new_var;
+               }
+       }
+
+       for (i = 0; i < bb->out_count; i++) {
+               MonoBasicBlock *n = bb->out_bb [i];
+
+               for (j = 0; j < n->in_count; j++)
+                       if (n->in_bb [j] == bb)
+                               break;
+               
+               for (inst = n->code; inst; inst = inst->next) {
+                       if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) {
+                               idx = inst->inst_i1->inst_c0;
+                               if (stack [idx])
+                                       new_var = stack [idx];
+                               else
+                                       new_var = cfg->varinfo [idx];
+#ifdef DEBUG_SSA
+                               printf ("FOUND PHI %d (%d, %d)\n", idx, j, new_var->inst_c0);
+#endif
+                               inst->inst_i1->inst_phi_args [j + 1] = new_var->inst_c0;
+                               
+                       }
+               }
+       }
+
+       for (tmp = bb->dominated; tmp; tmp = tmp->next) {
+               memcpy (new_stack, stack, sizeof (MonoInst *) * max_vars); 
+               mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, new_stack);
+       }
+}
+
+void
+mono_ssa_compute (MonoCompile *cfg)
+{
+       int i, idx;
+       MonoBitSet *set;
+       MonoMethodVar *vinfo = g_new0 (MonoMethodVar, cfg->num_varinfo);
+       MonoInst *inst, *store, **stack;
+
+       g_assert (!(cfg->comp_done & MONO_COMP_SSA));
+
+       /* we dont support methods containing exception clauses */
+       g_assert (((MonoMethodNormal *)cfg->method)->header->num_clauses == 0);
+       g_assert (!cfg->disable_ssa);
+
+       //printf ("COMPUTS SSA %s %d\n", mono_method_full_name (cfg->method, TRUE), cfg->num_varinfo);
+
+#ifdef CREATE_PRUNED_SSA
+       /* we need liveness for pruned SSA */
+       if (!(cfg->comp_done & MONO_COMP_LIVENESS))
+               mono_analyze_liveness (cfg);
+#endif
+
+       mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM | MONO_COMP_DFRONTIER);
+
+       for (i = 0; i < cfg->num_varinfo; ++i) {
+               vinfo [i].def_in = mono_bitset_new (cfg->num_bblocks, 0);
+               vinfo [i].idx = i;
+               /* implizit reference at start */
+               mono_bitset_set (vinfo [i].def_in, 0);
+       }
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               for (inst = cfg->bblocks [i]->code; inst; inst = inst->next) {
+                       if (inst->ssa_op == MONO_SSA_STORE) {
+                               idx = inst->inst_i0->inst_c0;
+                               g_assert (idx < cfg->num_varinfo);
+                               mono_bitset_set (vinfo [idx].def_in, i);
+                       } 
+               }
+       }
+
+       /* insert phi functions */
+       for (i = 0; i < cfg->num_varinfo; ++i) {
+               set = mono_compile_iterated_dfrontier (cfg, vinfo [i].def_in);
+               vinfo [i].dfrontier = set;
+               mono_bitset_foreach_bit (set, idx, cfg->num_bblocks) {
+                       MonoBasicBlock *bb = cfg->bblocks [idx];
+
+                       /* fixme: create pruned SSA? we would need liveness information for that */
+
+                       if (bb == cfg->bb_exit)
+                               continue;
+
+                       if ((cfg->comp_done & MONO_COMP_LIVENESS) && !mono_bitset_test_fast (bb->live_in_set, i)) {
+                               //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
+                               continue;
+                       }
+
+                       NEW_PHI (cfg, inst, i);
+
+                       inst->inst_phi_args =  mono_mempool_alloc0 (cfg->mempool, sizeof (int) * (cfg->bblocks [idx]->in_count + 1));
+                       inst->inst_phi_args [0] = cfg->bblocks [idx]->in_count;
+
+                       store = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));
+                       if (!cfg->varinfo [i]->inst_vtype->type)
+                               g_assert_not_reached ();
+                       store->opcode = mono_type_to_stind (cfg->varinfo [i]->inst_vtype);
+                       store->ssa_op = MONO_SSA_STORE;
+                       store->inst_i0 = cfg->varinfo [i];
+                       store->inst_i1 = inst;
+                       store->klass = store->inst_i0->klass;
+            
+                       store->next = bb->code;
+                       bb->code = store;
+
+#ifdef DEBUG_SSA
+                       printf ("ADD PHI BB%d %s\n", cfg->bblocks [idx]->block_num, mono_method_full_name (cfg->method, TRUE));
+#endif
+               }
+       }
+
+       /* free the stuff */
+       for (i = 0; i < cfg->num_varinfo; ++i)
+               mono_bitset_free (vinfo [i].def_in);
+       g_free (vinfo);
+
+
+       stack = alloca (sizeof (MonoInst *) * cfg->num_varinfo);
+               
+       for (i = 0; i < cfg->num_varinfo; i++)
+               stack [i] = NULL;
+
+       mono_ssa_rename_vars (cfg, cfg->num_varinfo, cfg->bb_entry, stack);
+
+       cfg->comp_done |= MONO_COMP_SSA;
+}
+
+#ifndef USE_ORIGINAL_VARS
+static GPtrArray *
+mono_ssa_get_allocatable_vars (MonoCompile *cfg)
+{
+       GHashTable *type_hash;
+       GPtrArray *varlist_array = g_ptr_array_new ();
+       int tidx, i;
+
+       g_assert (cfg->comp_done & MONO_COMP_LIVENESS);
+
+       type_hash = g_hash_table_new (NULL, NULL);
+
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoInst *ins = cfg->varinfo [i];
+               MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+               /* unused vars */
+               if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos)
+                       continue;
+
+               if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || 
+                   (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG) || vmv->reg != -1)
+                       continue;
+
+               g_assert (ins->inst_vtype);
+               g_assert (vmv->reg == -1);
+               g_assert (i == vmv->idx);
+
+               if (!(tidx = (int)g_hash_table_lookup (type_hash, ins->inst_vtype))) {
+                       GList *vars = g_list_append (NULL, vmv);
+                       g_ptr_array_add (varlist_array, vars);
+                       g_hash_table_insert (type_hash, ins->inst_vtype, (gpointer)varlist_array->len);
+               } else {
+                       tidx--;
+                       g_ptr_array_index (varlist_array, tidx) =
+                               mono_varlist_insert_sorted (cfg, g_ptr_array_index (varlist_array, tidx), vmv, FALSE);
+               }
+       }
+
+       g_hash_table_destroy (type_hash);
+
+       return varlist_array;
+}
+#endif
+
+static void
+mono_ssa_replace_copies (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, char *is_live)
+{
+       int arity;
+
+       if (!inst)
+               return;
+
+       arity = mono_burg_arity [inst->opcode];
+
+       if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD || inst->ssa_op == MONO_SSA_STORE) && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+               MonoInst *new_var;
+               int idx = inst->inst_i0->inst_c0;
+               MonoMethodVar *mv = cfg->vars [idx];
+
+               if (mv->reg != -1 && mv->reg != mv->idx) {
+                      
+                       is_live [mv->reg] = 1;
+
+                       new_var = cfg->varinfo [mv->reg];
+
+#if 0
+                       printf ("REPLACE COPY BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0);
+                       g_assert (cfg->varinfo [mv->reg]->inst_vtype == cfg->varinfo [idx]->inst_vtype);
+#endif
+                       inst->inst_i0 = new_var;
+               } else {
+                       is_live [mv->idx] = 1;
+               }
+       }
+
+
+       if (arity) {
+               mono_ssa_replace_copies (cfg, bb, inst->inst_left, is_live);
+               if (arity > 1)
+                       mono_ssa_replace_copies (cfg, bb, inst->inst_right, is_live);
+       }
+
+       if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->ssa_op == MONO_SSA_LOAD &&
+           inst->inst_i0->inst_c0 == inst->inst_i1->inst_i0->inst_c0) {
+               inst->ssa_op = MONO_SSA_NOP;
+               inst->opcode = CEE_NOP;
+       }
+
+}
+
+void
+mono_ssa_remove (MonoCompile *cfg)
+{
+       MonoInst *inst, *phi;
+       char *is_live;
+       int i, j;
+#ifndef USE_ORIGINAL_VARS
+       GPtrArray *varlist_array;
+       GList *active;
+#endif
+       g_assert (cfg->comp_done & MONO_COMP_SSA);
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+               for (inst = bb->code; inst; inst = inst->next) {
+                       if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) {
+                               
+                               phi = inst->inst_i1;
+                               g_assert (phi->inst_phi_args [0] == bb->in_count);
+
+                               for (j = 0; j < bb->in_count; j++) {
+                                       MonoBasicBlock *pred = bb->in_bb [j];
+                                       int idx = phi->inst_phi_args [j + 1];
+                                       MonoMethodVar *mv = cfg->vars [idx];
+
+                                       if (mv->reg != -1 && mv->reg != mv->idx) {
+                                               //printf ("PHICOPY %d %d -> %d\n", idx, mv->reg, inst->inst_i0->inst_c0);
+                                               idx = mv->reg;
+                                       }
+
+                                       
+                                       if (idx != inst->inst_i0->inst_c0) {
+#ifdef DEBUG_SSA
+                                               printf ("MOVE %d to %d in BB%d\n", idx, inst->inst_i0->inst_c0, pred->block_num);
+#endif
+                                               mono_add_varcopy_to_end (cfg, pred, idx, inst->inst_i0->inst_c0);
+                                       }
+                               }
+
+                               /* remove the phi functions */
+                               inst->opcode = CEE_NOP;
+                               inst->ssa_op = MONO_SSA_NOP;
+                       } 
+               }
+       }
+       
+#ifndef USE_ORIGINAL_VARS
+       /* we compute liveness again */
+       cfg->comp_done &= ~MONO_COMP_LIVENESS;
+       mono_analyze_liveness (cfg);
+
+       varlist_array = mono_ssa_get_allocatable_vars (cfg);
+
+       for (i = 0; i < varlist_array->len; i++) {
+               GList *l, *t, *regs, *vars = g_ptr_array_index (varlist_array, i);
+               MonoMethodVar *vmv, *amv;
+               
+               if (g_list_length (vars) <= 1) {
+                       continue;
+               }
+
+               active = NULL;
+               regs = NULL;
+
+               for (l = vars; l; l = l->next) {
+                       vmv = l->data;
+
+                       /* expire old intervals in active */
+                       while (active) {
+                               amv = (MonoMethodVar *)active->data;
+
+                               if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos)
+                                       break;
+
+                               active = g_list_remove_link (active, active);
+                               regs = g_list_prepend (regs, (gpointer)amv->reg);
+                       }
+
+                       if (!regs)
+                               regs = g_list_prepend (regs, vmv->idx);
+
+                       vmv->reg = (int)regs->data;
+                       regs = g_list_remove_link (regs, regs);
+                       active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE);           
+               }
+
+               g_list_free (active);
+               g_list_free (regs);
+               g_list_free (vars);
+       }
+
+       g_ptr_array_free (varlist_array, TRUE);
+
+#endif
+
+       is_live = alloca (cfg->num_varinfo);
+       memset (is_live, 0, cfg->num_varinfo);
+
+       for (i = 0; i < cfg->num_bblocks; ++i) {
+               MonoBasicBlock *bb = cfg->bblocks [i];
+
+               for (inst = bb->code; inst; inst = inst->next)
+                       mono_ssa_replace_copies (cfg, bb, inst, is_live);
+       }
+
+       for (i = 0; i < cfg->num_varinfo; ++i) {
+               cfg->vars [i]->reg = -1;
+               if (!is_live [i]) {
+                       cfg->varinfo [i]->flags |= MONO_INST_IS_DEAD;
+               }
+       }
+
+       if (cfg->comp_done & MONO_COMP_REACHABILITY)
+               unlink_unused_bblocks (cfg);
+
+       cfg->comp_done &= ~MONO_COMP_SSA;
+}
+
+
+#define IS_CALL(op) (op == CEE_CALLI || op == CEE_CALL || op == CEE_CALLVIRT || (op >= OP_VOIDCALL && op <= OP_CALL_MEMBASE))
+
+typedef struct {
+       MonoBasicBlock *bb;
+       MonoInst *inst;
+} MonoVarUsageInfo;
+
+static void
+analyze_dev_use (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *root, MonoInst *inst)
+{
+       MonoMethodVar *info;
+       int i, idx, arity;
+
+       if (!inst)
+               return;
+
+       arity = mono_burg_arity [inst->opcode];
+
+       if ((inst->ssa_op == MONO_SSA_STORE) && 
+           (inst->inst_i0->opcode == OP_LOCAL /*|| inst->inst_i0->opcode == OP_ARG */)) {
+               idx = inst->inst_i0->inst_c0;
+               info = cfg->vars [idx];
+               //printf ("%d defined in BB%d %p\n", idx, bb->block_num, root);
+               if (info->def) {
+                       g_warning ("more than one definition of variable %d in %s", idx,
+                                  mono_method_full_name (cfg->method, TRUE));
+                       g_assert_not_reached ();
+               }
+               if (!IS_CALL (inst->inst_i1->opcode) /* && inst->inst_i1->opcode == OP_ICONST */) {
+                       g_assert (inst == root);
+                       info->def = root;
+                       info->def_bb = bb;
+               }
+
+               if (inst->inst_i1->opcode == OP_PHI) {
+                       for (i = inst->inst_i1->inst_phi_args [0]; i > 0; i--) {
+                               MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
+                               idx = inst->inst_i1->inst_phi_args [i]; 
+                               info = cfg->vars [idx];
+                               //printf ("FOUND %d\n", idx);
+                               ui->bb = bb;
+                               ui->inst = root;
+                               info->uses = g_list_prepend (info->uses, ui);
+                       }
+               }
+       }
+
+       if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+               MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
+               idx = inst->inst_i0->inst_c0;   
+               info = cfg->vars [idx];
+               //printf ("FOUND %d\n", idx);
+               ui->bb = bb;
+               ui->inst = root;
+               info->uses = g_list_prepend (info->uses, ui);
+       } else {
+               if (arity) {
+                       //if (inst->ssa_op != MONO_SSA_STORE)
+                       analyze_dev_use (cfg, bb, root, inst->inst_left);
+                       if (arity > 1)
+                               analyze_dev_use (cfg, bb, root, inst->inst_right);
+               }
+       }
+}
+
+/* avoid unnecessary copies of variables:
+ * Y <= X; Z = Y; is translated to Z = X;
+ */
+static void
+mono_ssa_avoid_copies (MonoCompile *cfg)
+{
+       MonoInst *inst, *next;
+       MonoBasicBlock *bb;
+       MonoMethodVar *i1, *i2;
+
+       g_assert ((cfg->comp_done & MONO_COMP_SSA_DEF_USE));
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               for (inst = bb->code; inst; inst = inst->next) {
+                       if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i0->opcode == OP_LOCAL) {
+                               i1 = cfg->vars [inst->inst_i0->inst_c0];
+
+                               if ((next = inst->next) && next->ssa_op == MONO_SSA_STORE && next->inst_i0->opcode == OP_LOCAL &&
+                                   next->inst_i1->ssa_op == MONO_SSA_LOAD &&  next->inst_i1->inst_i0->opcode == OP_LOCAL &&
+                                   next->inst_i1->inst_i0->inst_c0 == inst->inst_i0->inst_c0 && g_list_length (i1->uses) == 1 &&
+                                   inst->opcode == next->opcode && inst->inst_i0->type == next->inst_i0->type) {
+                                       i2 = cfg->vars [next->inst_i0->inst_c0];
+                                       //printf ("ELIM. COPY in BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
+                                       inst->inst_i0 = next->inst_i0;
+                                       i2->def = inst;
+                                       i1->def = NULL;
+                                       g_list_free (i1->uses);
+                                       i1->uses = NULL;
+                                       next->opcode = CEE_NOP;
+                                       next->ssa_op = MONO_SSA_NOP;
+                               }
+                       }
+               }
+       }
+}
+
+static void
+mono_ssa_create_def_use (MonoCompile *cfg) 
+{
+       MonoBasicBlock *bb;
+
+       g_assert (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE));
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *inst;
+               for (inst = bb->code; inst; inst = inst->next) {
+                       analyze_dev_use (cfg, bb, inst, inst);
+               }
+       }
+
+       cfg->comp_done |= MONO_COMP_SSA_DEF_USE;
+}
+
+static int
+simulate_compare (int opcode, int a, int b)
+{
+       switch (opcode) {
+       case CEE_BEQ:
+               return a == b;
+       case CEE_BGE:
+               return a >= b;
+       case CEE_BGT:
+               return a > b;
+       case CEE_BLE:
+               return a <= b;
+       case CEE_BLT:
+               return a < b;
+       case CEE_BNE_UN:
+               return a != b;
+       case CEE_BGE_UN:
+               return (unsigned)a >= (unsigned)b;
+       case CEE_BGT_UN:
+               return (unsigned)a > (unsigned)b;
+       case CEE_BLE_UN:
+               return (unsigned)a <= (unsigned)b;
+       case CEE_BLT_UN:
+               return (unsigned)a < (unsigned)b;
+       default:
+               g_assert_not_reached ();
+       }
+
+       return 0;
+}
+
+#define EVAL_CXX(name,op,cast) \
+       case name:      \
+               if (inst->inst_i0->opcode == OP_COMPARE) { \
+                       r1 = evaluate_const_tree (cfg, inst->inst_i0->inst_i0, &a, carray); \
+                       r2 = evaluate_const_tree (cfg, inst->inst_i0->inst_i1, &b, carray); \
+                       if (r1 == 1 && r2 == 1) { \
+                               *res = ((cast)a op (cast)b); \
+                               return 1; \
+                       } else { \
+                               return MAX (r1, r2); \
+                       } \
+               } \
+               break;
+
+#define EVAL_BINOP(name,op)    \
+       case name:      \
+               r1 = evaluate_const_tree (cfg, inst->inst_i0, &a, carray); \
+               r2 = evaluate_const_tree (cfg, inst->inst_i1, &b, carray); \
+               if (r1 == 1 && r2 == 1) { \
+                       *res = (a op b); \
+                       return 1; \
+               } else { \
+                       return MAX (r1, r2); \
+               } \
+               break;
+
+
+static int
+evaluate_const_tree (MonoCompile *cfg, MonoInst *inst, int *res, MonoInst **carray)
+{
+       MonoInst *c0;
+       int a, b, r1, r2;
+
+       if (!inst)
+               return 0;
+
+       if (inst->ssa_op == MONO_SSA_LOAD && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
+           (c0 = carray [inst->inst_i0->inst_c0])) {
+               *res = c0->inst_c0;
+               return 1;
+       }
+
+       switch (inst->opcode) {
+       case OP_ICONST:
+               *res = inst->inst_c0;
+               return 1;
+
+       EVAL_CXX (OP_CEQ,==,gint32)
+       EVAL_CXX (OP_CGT,>,gint32)
+       EVAL_CXX (OP_CGT_UN,>,guint32)
+       EVAL_CXX (OP_CLT,<,gint32)
+       EVAL_CXX (OP_CLT_UN,<,guint32)
+
+       EVAL_BINOP (CEE_ADD,+)
+       EVAL_BINOP (CEE_SUB,-)
+       EVAL_BINOP (CEE_MUL,*)
+       EVAL_BINOP (CEE_AND,&)
+       EVAL_BINOP (CEE_OR,|)
+       EVAL_BINOP (CEE_XOR,^)
+       EVAL_BINOP (CEE_SHL,<<)
+       EVAL_BINOP (CEE_SHR,>>)
+
+       default:
+               return 2;
+       }
+
+       return 2;
+}
+
+static void
+fold_tree (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **carray)
+{
+       MonoInst *c0;
+       int arity, a, b;
+
+       if (!inst)
+               return;
+
+       arity = mono_burg_arity [inst->opcode];
+
+       if (inst->ssa_op == MONO_SSA_STORE && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
+           inst->inst_i1->opcode == OP_PHI && (c0 = carray [inst->inst_i0->inst_c0])) {
+               //{static int cn = 0; printf ("PHICONST %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));}
+               *inst->inst_i1 = *c0;           
+       } else if (inst->ssa_op == MONO_SSA_LOAD && 
+           (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
+           (c0 = carray [inst->inst_i0->inst_c0])) {
+               //{static int cn = 0; printf ("YCCOPY %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));}
+               *inst = *c0;
+       } else {
+
+               if (arity) {
+                       fold_tree (cfg, bb, inst->inst_left, carray);
+                       if (arity > 1)
+                               fold_tree (cfg, bb, inst->inst_right, carray);
+                       mono_constant_fold_inst (inst, NULL); 
+               }
+       }
+
+       if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
+           inst->inst_i0->opcode == OP_COMPARE) {
+               MonoInst *v0 = inst->inst_i0->inst_i0;
+               MonoInst *v1 = inst->inst_i0->inst_i1;
+
+               if (evaluate_const_tree (cfg, v0, &a, carray) == 1 &&
+                   evaluate_const_tree (cfg, v1, &b, carray) == 1) {
+                       MonoBasicBlock *target;
+                               
+                       if (simulate_compare (inst->opcode, a, b)) {
+                               //unlink_target (bb, inst->inst_false_bb);
+                               target = inst->inst_true_bb;
+                       } else {
+                               //unlink_target (bb, inst->inst_true_bb);
+                               target = inst->inst_false_bb;
+                       }
+                       
+                       bb->out_bb [0] = target;
+                       bb->out_count = 1;
+                       inst->opcode = CEE_BR;
+                       inst->inst_target_bb = target;
+               }
+       } else if (inst->opcode == CEE_SWITCH && evaluate_const_tree (cfg, inst->inst_left, &a, carray) == 1) {
+               bb->out_bb [0] = inst->inst_many_bb [a];
+               bb->out_count = 1;
+               inst->inst_target_bb = bb->out_bb [0];
+               inst->opcode = CEE_BR;
+       }
+
+}
+
+static void
+change_varstate (MonoCompile *cfg, GList **cvars, MonoMethodVar *info, int state, MonoInst *c0, MonoInst **carray)
+{
+       if (info->cpstate >= state)
+               return;
+
+       info->cpstate = state;
+
+       //printf ("SETSTATE %d to %d\n", info->idx, info->cpstate);
+
+       if (state == 1)
+               carray [info->idx] = c0;
+       else
+               carray [info->idx] = NULL;
+
+       if (!g_list_find (*cvars, info)) {
+               *cvars = g_list_prepend (*cvars, info);
+       }
+}
+
+static void
+visit_inst (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, GList **cvars, GList **bblist, MonoInst **carray)
+{
+       g_assert (inst);
+
+       if (inst->opcode == CEE_SWITCH) {
+               int r1, i, a;
+
+               r1 = evaluate_const_tree (cfg, inst->inst_left, &a, carray);
+               if (r1 == 1) {
+                       MonoBasicBlock *tb = inst->inst_many_bb [a];
+                       if (!(tb->flags &  BB_REACHABLE)) {
+                               tb->flags |= BB_REACHABLE;
+                               *bblist = g_list_prepend (*bblist, tb);
+                       }
+               } else if (r1 == 2) {
+                       for (i = (int)inst->klass; i >= 0; i--) {
+                               MonoBasicBlock *tb = inst->inst_many_bb [i];
+                               if (!(tb->flags &  BB_REACHABLE)) {
+                                       tb->flags |= BB_REACHABLE;
+                                       *bblist = g_list_prepend (*bblist, tb);
+                               }
+                       }
+               }
+       } else if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
+           inst->inst_i0->opcode == OP_COMPARE) {
+               int a, b, r1, r2;
+               MonoInst *v0 = inst->inst_i0->inst_i0;
+               MonoInst *v1 = inst->inst_i0->inst_i1;
+
+               r1 = evaluate_const_tree (cfg, v0, &a, carray);
+               r2 = evaluate_const_tree (cfg, v1, &b, carray);
+
+               if (r1 == 1 && r2 == 1) {
+                       MonoBasicBlock *target;
+                               
+                       if (simulate_compare (inst->opcode, a, b)) {
+                               target = inst->inst_true_bb;
+                       } else {
+                               target = inst->inst_false_bb;
+                       }
+                       if (!(target->flags &  BB_REACHABLE)) {
+                               target->flags |= BB_REACHABLE;
+                               *bblist = g_list_prepend (*bblist, target);
+                       }
+               } else if (r1 == 2 || r2 == 2) {
+                       if (!(inst->inst_true_bb->flags &  BB_REACHABLE)) {
+                               inst->inst_true_bb->flags |= BB_REACHABLE;
+                               *bblist = g_list_prepend (*bblist, inst->inst_true_bb);
+                       }
+                       if (!(inst->inst_false_bb->flags &  BB_REACHABLE)) {
+                               inst->inst_false_bb->flags |= BB_REACHABLE;
+                               *bblist = g_list_prepend (*bblist, inst->inst_false_bb);
+                       }
+               }       
+       } else if (inst->ssa_op == MONO_SSA_STORE && 
+                  (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+               MonoMethodVar *info = cfg->vars [inst->inst_i0->inst_c0];
+               MonoInst *i1 = inst->inst_i1;
+               int res;
+               
+               if (info->cpstate < 2) {
+                       if (i1->opcode == OP_ICONST) { 
+                               change_varstate (cfg, cvars, info, 1, i1, carray);
+                       } else if (i1->opcode == OP_PHI) {
+                               MonoInst *c0 = NULL;
+                               int j;
+
+                               for (j = 1; j <= i1->inst_phi_args [0]; j++) {
+                                       MonoMethodVar *mv = cfg->vars [i1->inst_phi_args [j]];
+                                       MonoInst *src = mv->def;
+
+                                       if (mv->def_bb && !(mv->def_bb->flags & BB_REACHABLE)) {
+                                               continue;
+                                       }
+
+                                       if (!mv->def || !src || src->ssa_op != MONO_SSA_STORE ||
+                                           !(src->inst_i0->opcode == OP_LOCAL || src->inst_i0->opcode == OP_ARG) ||
+                                           mv->cpstate == 2) {
+                                               change_varstate (cfg, cvars, info, 2, NULL, carray);
+                                               break;
+                                       }
+                                       
+                                       if (mv->cpstate == 0)
+                                               continue;
+
+                                       //g_assert (src->inst_i1->opcode == OP_ICONST);
+                                       g_assert (carray [mv->idx]);
+
+                                       if (!c0) {
+                                               c0 = carray [mv->idx];
+                                       }
+                                       
+                                       if (carray [mv->idx]->inst_c0 != c0->inst_c0) {
+                                               change_varstate (cfg, cvars, info, 2, NULL, carray);
+                                               break;
+                                       }
+                               }
+                               
+                               if (c0 && info->cpstate < 1) {
+                                       change_varstate (cfg, cvars, info, 1, c0, carray);
+                               }
+                       } else {
+                               int state = evaluate_const_tree (cfg, i1, &res, carray);
+                               if (state == 1) {
+                                       NEW_ICONST (cfg, i1, res);
+                                       change_varstate (cfg, cvars, info, 1, i1, carray);
+                               } else {
+                                       change_varstate (cfg, cvars, info, 2, NULL, carray);
+                               }
+                       }
+               }
+       }
+}
+
+void
+mono_ssa_cprop (MonoCompile *cfg) 
+{
+       MonoInst *carray [cfg->num_varinfo];
+       MonoBasicBlock *bb;
+       GList *bblock_list, *cvars;
+       GList *tmp;
+       int i;
+       //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
+
+       if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
+               mono_ssa_create_def_use (cfg);
+
+       bblock_list = g_list_prepend (NULL, cfg->bb_entry);
+       cfg->bb_entry->flags |= BB_REACHABLE;
+
+       memset (carray, 0, sizeof (MonoInst *) * cfg->num_varinfo);
+
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoMethodVar *info = cfg->vars [i];
+               if (!info->def)
+                       info->cpstate = 2;
+       }
+
+       cvars = NULL;
+
+       while (bblock_list) {
+               MonoInst *inst;
+
+               bb = (MonoBasicBlock *)bblock_list->data;
+
+               bblock_list = g_list_remove_link (bblock_list, bblock_list);
+
+               g_assert (bb->flags &  BB_REACHABLE);
+
+               if (bb->out_count == 1) {
+                       if (!(bb->out_bb [0]->flags &  BB_REACHABLE)) {
+                               bb->out_bb [0]->flags |= BB_REACHABLE;
+                               bblock_list = g_list_prepend (bblock_list, bb->out_bb [0]);
+                       }
+               }
+
+               for (inst = bb->code; inst; inst = inst->next) {
+                       visit_inst (cfg, bb, inst, &cvars, &bblock_list, carray);
+               }
+
+               while (cvars) {
+                       MonoMethodVar *info = (MonoMethodVar *)cvars->data;                     
+                       cvars = g_list_remove_link (cvars, cvars);
+
+                       for (tmp = info->uses; tmp; tmp = tmp->next) {
+                               MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
+                               if (!(ui->bb->flags & BB_REACHABLE))
+                                       continue;
+                               visit_inst (cfg, ui->bb, ui->inst, &cvars, &bblock_list, carray);
+                       }
+               }
+       }
+
+       for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
+               MonoInst *inst;
+               for (inst = bb->code; inst; inst = inst->next) {
+                       fold_tree (cfg, bb, inst, carray);
+               }
+       }
+
+       cfg->comp_done |= MONO_COMP_REACHABILITY;
+}
+
+static void
+add_to_dce_worklist (MonoCompile *cfg, MonoMethodVar *var, MonoMethodVar *use, GList **wl)
+{
+       GList *tmp;
+
+       *wl = g_list_prepend (*wl, use);
+
+       for (tmp = use->uses; tmp; tmp = tmp->next) {
+               MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
+               if (ui->inst == var->def) {
+                       use->uses = g_list_remove_link (use->uses, tmp);
+                       break;
+               }
+       }       
+}
+
+void
+mono_ssa_deadce (MonoCompile *cfg) 
+{
+       int i;
+       GList *work_list;
+
+       g_assert (cfg->comp_done & MONO_COMP_SSA);
+
+       //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
+
+       /* fixme: we should update usage infos during cprop, instead of computing it again */
+       cfg->comp_done &=  ~MONO_COMP_SSA_DEF_USE;
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoMethodVar *info = cfg->vars [i];
+               info->def = NULL;
+               info->uses = NULL;
+       }
+
+       if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
+               mono_ssa_create_def_use (cfg);
+
+       mono_ssa_avoid_copies (cfg);
+
+       work_list = NULL;
+       for (i = 0; i < cfg->num_varinfo; i++) {
+               MonoMethodVar *info = cfg->vars [i];
+               work_list = g_list_prepend (work_list, info);
+       }
+
+       while (work_list) {
+               MonoMethodVar *info = (MonoMethodVar *)work_list->data;
+               work_list = g_list_remove_link (work_list, work_list);
+
+               if (!info->uses && info->def) {
+                       MonoInst *i1;
+                       //printf ("ELIMINATE %s: ", mono_method_full_name (cfg->method, TRUE)); mono_print_tree (info->def); printf ("\n");
+
+                       i1 = info->def->inst_i1;
+                       if (i1->opcode == OP_PHI) {
+                               int j;
+                               for (j = i1->inst_phi_args [0]; j > 0; j--) {
+                                       MonoMethodVar *u = cfg->vars [i1->inst_phi_args [j]];
+                                       add_to_dce_worklist (cfg, info, u, &work_list);
+                               }
+                       } else if (i1->ssa_op == MONO_SSA_LOAD &&
+                                  (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG)) {
+                                       MonoMethodVar *u = cfg->vars [i1->inst_i0->inst_c0];
+                                       add_to_dce_worklist (cfg, info, u, &work_list);
+                       }
+
+                       info->def->opcode = CEE_NOP;
+                       info->def->ssa_op = MONO_SSA_NOP;
+               }
+
+       }
+}
+
+#if 0
+void
+mono_ssa_strength_reduction (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       int i;
+
+       g_assert (cfg->comp_done & MONO_COMP_SSA);
+       g_assert (cfg->comp_done & MONO_COMP_LOOPS);
+       g_assert (cfg->comp_done & MONO_COMP_SSA_DEF_USE);
+
+       for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
+               GList *lp = bb->loop_blocks;
+
+               if (lp) {
+                       MonoBasicBlock *h = (MonoBasicBlock *)lp->data;
+
+                       /* we only consider loops with 2 in bblocks */
+                       if (!h->in_count == 2)
+                               continue;
+
+                       for (i = 0; i < cfg->num_varinfo; i++) {
+                               MonoMethodVar *info = cfg->vars [i];
+                       
+                               if (info->def && info->def->ssa_op == MONO_SSA_STORE &&
+                                   info->def->inst_i0->opcode == OP_LOCAL && g_list_find (lp, info->def_bb)) {
+                                       MonoInst *v = info->def->inst_i1;
+
+
+                                       printf ("FOUND %d in %s\n", info->idx, mono_method_full_name (cfg->method, TRUE));
+                               }
+                       }
+               }
+       }
+}
+#endif
diff --git a/mono/mini/test.cs b/mono/mini/test.cs
new file mode 100644 (file)
index 0000000..49e17ad
--- /dev/null
@@ -0,0 +1,348 @@
+using System;
+
+namespace SSA {
+       class Test {
+
+               static void empty () {
+               }
+               static int ret_int () {
+                       return 1;
+               }
+               static int simple_add (int a) {
+                       int b = 5;
+                       return a + b;
+               }
+
+               static int cmov (int a) {
+                       return a >= 10? 1: 2;
+               }
+
+               static int many_shifts (int a, int b, int c) {
+                       return a << b << c << 1;
+               }
+
+               static void test2 (int a) {
+                       int x, y, z;
+                       
+                       z = 1;
+                       if (z > a) {
+                               x = 1;
+                               if (z > 2) {
+                                       y = x + 1;
+                                       return;
+                               }
+                       } else {
+                               x = 2;
+                       }
+                       z = x -3;
+                       x = 4;
+                       goto next;
+                       next:
+                       z = x + 7;
+               }
+
+               static int rfib (int n) {
+                       if (n < 2)
+                               return 1;
+                       return rfib (n - 2) + rfib (n - 1);
+               }
+
+               static int test1 (int v) {
+                       int x, y;
+
+                       x = 1;
+                       if (v != 0) {
+                               y = 2;
+                       } else {
+                               y = x + 1;
+                       }
+                       return y;
+               }
+
+               static int for_loop () {
+                       int j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       return j;
+               }
+
+               static int many_bb2 () {
+                       int j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       return j;
+               }
+
+               static int many_bb4 () {
+                       int j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       return j;
+               }
+
+               static int many_bb8 () {
+                       int j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       return j;
+               }
+
+               static int many_bb16 () {
+                       int j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       j = 0;
+                       for (int i = 0; i < 5; i++) {
+                               j += i;
+                       }
+                       return j;
+               }
+
+               static int many_bb32 () {
+                       int j;
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       j = 0; for (int i = 0; i < 5; i++) { j += i; }
+                       return j;
+               }
+
+               /*static int fib (int n) {
+                       int f0 = 0, f1 = 1, f2 = 0, i;
+
+                       if (n <= 1) goto L3;
+                       i = 2;
+                       L1:
+                       if (i <= n) goto L2;
+                       return f2;
+                       L2:
+                       f2 = f0 + f1;
+                       f0 = f1;
+                       f1 = f2;
+                       i++;
+                       goto L1;
+                       L3:
+                       return n;
+               }*/
+
+               static int nested_loops (int n) {
+                       int m = 1000;
+                       int a = 0;
+                       for (int i = 0; i < n; ++i) {
+                               for (int j = 0; j < m; ++j) {
+                                       a++;
+                               }
+                       }
+                       return a;
+               }
+
+               static int Main() {
+                       if (test1 (1) != 2)
+                               return 1;
+                       return 0;
+               }
+       }
+}
+
diff --git a/mono/mini/tramp-ppc.c b/mono/mini/tramp-ppc.c
new file mode 100644 (file)
index 0000000..0399437
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * trampoline.c: JIT trampoline code
+ *
+ * Authors:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Carlos Valiente <yo@virutass.net>
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/arch/ppc/ppc-codegen.h>
+
+#include "mini.h"
+#include "mini-ppc.h"
+
+/* 
+ * define for the (broken) debugger breakpoint interface:
+ * The debugger should use the hw registers to set the breakpoints.
+ */
+#define mono_method_has_breakpoint(a,b) (0)
+#define mono_remove_breakpoint(a)
+
+/* adapt to mini later... */
+#define mono_jit_share_code (1)
+
+/*
+ * Address of the x86 trampoline code.  This is used by the debugger to check
+ * whether a method is a trampoline.
+ */
+guint8 *mono_generic_trampoline_code = NULL;
+
+/*
+ * Address of a special breakpoint trampoline code for the debugger.
+ */
+guint8 *mono_breakpoint_trampoline_code = NULL;
+
+/*
+ * get_unbox_trampoline:
+ * @m: method pointer
+ * @addr: pointer to native code for @m
+ *
+ * when value type methods are called through the vtable we need to unbox the
+ * this argument. This method returns a pointer to a trampoline which does
+ * unboxing before calling the method
+ */
+static gpointer
+get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+       guint8 *code, *start;
+       int this_pos = 3;
+
+       if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret))
+               this_pos = 4;
+           
+       start = code = g_malloc (20);
+
+       ppc_load (code, ppc_r11, addr);
+       ppc_mtctr (code, ppc_r11);
+       ppc_addi (code, this_pos, this_pos, sizeof (MonoObject));
+       ppc_bcctr (code, 20, 0);
+       g_assert ((code - start) <= 20);
+
+       return start;
+}
+
+/*
+ * get_breakpoint_trampoline:
+ * @m: method pointer
+ * @addr: pointer to native code for @m
+ *
+ * creates a special trampoline for the debugger which is used to get
+ * a breakpoint after compiling a method.
+ */
+static gpointer
+get_breakpoint_trampoline (MonoMethod *m, guint32 breakpoint_id, gpointer addr)
+{
+       guint8 *code, *start, *buf;
+
+       if (!mono_breakpoint_trampoline_code) {
+               mono_breakpoint_trampoline_code = buf = g_malloc (8);
+
+               ppc_break (buf);
+       /*      x86_breakpoint (buf);
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8);
+               x86_ret (buf);*/
+
+               g_assert ((buf - mono_breakpoint_trampoline_code) <= 8);
+       }
+
+       start = code = g_malloc (22);
+       ppc_break (code);
+/*     x86_push_imm (code, addr);
+       x86_push_imm (code, breakpoint_id);
+       x86_push_imm (code, m);
+       x86_jump_code (code, mono_breakpoint_trampoline_code);*/
+       g_assert ((code - start) <= 22);
+
+       return start;
+}
+
+/* Stack size for trampoline function */
+#define STACK 144
+
+/* Method-specific trampoline code framgment size */
+#define METHOD_TRAMPOLINE_SIZE 64
+
+/**
+ * ppc_magic_trampoline:
+ * @code: pointer into caller code
+ * @method: the method to translate
+ * @sp: stack pointer
+ *
+ * This method is called by the function 'arch_create_jit_trampoline', which in
+ * turn is called by the trampoline functions for virtual methods.
+ * After having called the JIT compiler to compile the method, it inspects the
+ * caller code to find the address of the method-specific part of the
+ * trampoline vtable slot for this method, updates it with a fragment that calls
+ * the newly compiled code and returns this address of the compiled code to
+ * 'arch_create_jit_trampoline' 
+ */
+static gpointer
+ppc_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
+{
+       char *o, *start;
+       gpointer addr;
+       int reg;
+
+       EnterCriticalSection(metadata_section);
+       addr = mono_compile_method(method);
+       LeaveCriticalSection(metadata_section);
+       g_assert(addr);
+
+       /* Locate the address of the method-specific trampoline. The call using
+       the vtable slot that took the processing flow to 'arch_create_jit_trampoline' 
+       looks something like this:
+       
+               mtlr rA                 ; Move rA (a register containing the
+                                       ; target address) to LR
+               blrl                    ; Call function at LR
+       
+       PowerPC instructions are 32-bit long, which means that a 32-bit target
+       address cannot be encoded as an immediate value (because we already
+       have spent some bits to encode the branch instruction!). That's why a
+       'b'ranch to the contents of the 'l'ink 'r'egister (with 'l'ink register
+       update) is needed, instead of a simpler 'branch immediate'. This
+       complicates our purpose here, because 'blrl' overwrites LR, which holds
+       the value we're interested in.
+       
+       Therefore, we need to locate the 'mtlr rA' instruction to know which
+       register LR was loaded from, and then retrieve the value from that
+       register */
+       
+       /* This is the 'blrl' instruction */
+       --code;
+       
+       /*
+        * Note that methods are called also with the bl opcode.
+        */
+       if (((*code) >> 26) == 18) {
+               ppc_patch (code, addr);
+               mono_arch_flush_icache (code, 4);
+               return addr;
+       }
+       
+       /* Sanity check: instruction must be 'blrl' */
+       g_assert(*code == 0x4e800021);
+       
+       /* OK, we're now at the 'blrl' instruction. Now walk backwards
+       till we get to a 'mtlr rA' */
+       for(; --code;) {
+               if((*code & 0x7c0803a6) == 0x7c0803a6) {
+                       /* Here we are: we reached the 'mtlr rA'.
+                       Extract the register from the instruction */
+                       reg = (*code & 0x03e00000) >> 21;
+                       switch(reg) {
+                               case 0 : o = *((int *) (sp + STACK - 8));   break;
+                               case 11: o = *((int *) (sp + STACK - 24));  break;
+                               case 12: o = *((int *) (sp + STACK - 28));  break;
+                               case 13: o = *((int *) (sp + STACK - 32));  break;
+                               case 14: o = *((int *) (sp + STACK - 36));  break;
+                               case 15: o = *((int *) (sp + STACK - 40));  break;
+                               case 16: o = *((int *) (sp + STACK - 44));  break;
+                               case 17: o = *((int *) (sp + STACK - 48));  break;
+                               case 18: o = *((int *) (sp + STACK - 52));  break;
+                               case 19: o = *((int *) (sp + STACK - 56));  break;
+                               case 20: o = *((int *) (sp + STACK - 60));  break;
+                               case 21: o = *((int *) (sp + STACK - 64));  break;
+                               case 22: o = *((int *) (sp + STACK - 68));  break;
+                               case 23: o = *((int *) (sp + STACK - 72));  break;
+                               case 24: o = *((int *) (sp + STACK - 76));  break;
+                               case 25: o = *((int *) (sp + STACK - 80));  break;
+                               case 26: o = *((int *) (sp + STACK - 84));  break;
+                               case 27: o = *((int *) (sp + STACK - 88));  break;
+                               case 28: o = *((int *) (sp + STACK - 92));  break;
+                               case 29: o = *((int *) (sp + STACK - 96));  break;
+                               case 30: o = *((int *) (sp + STACK - 100)); break;
+                               case 31: o = *((int *) (sp + STACK - 4));   break;
+                               default:
+                                       printf("%s: Unexpected register %d\n",
+                                               __FUNCTION__, reg);
+                                       g_assert_not_reached();
+                       }
+                       break;
+               }
+       }
+
+       /* this is not done for non-virtual calls, because in that case
+          we won't have an object, but the actual pointer to the 
+          valuetype as the this argument
+        */
+       if (method->klass->valuetype)
+               addr = get_unbox_trampoline (method, addr);
+       
+       /* Finally, replace the method-specific trampoline code (which called
+       the generic trampoline code) with a fragment that calls directly the
+       compiled method */
+       
+       start = o;
+       ppc_stwu (o, ppc_r1, -16, ppc_r1);
+       ppc_mflr (o, ppc_r0);
+       ppc_stw  (o, ppc_r31, 12, ppc_r1);
+       ppc_stw  (o, ppc_r0,  20, ppc_r1);
+       ppc_mr   (o, ppc_r31, ppc_r1);
+       
+       ppc_lis  (o, ppc_r0, (guint32) addr >> 16);
+       ppc_ori  (o, ppc_r0, ppc_r0, (guint32) addr & 0xffff);
+       ppc_mtlr (o, ppc_r0);
+       ppc_blrl (o);
+       
+       ppc_lwz  (o, ppc_r11, 0,  ppc_r1);
+       ppc_lwz  (o, ppc_r0,  4,  ppc_r11);
+       ppc_mtlr (o, ppc_r0);
+       ppc_lwz  (o, ppc_r31, -4, ppc_r11);
+       ppc_mr   (o, ppc_r1, ppc_r11);
+       ppc_blr  (o);
+       
+       mono_arch_flush_icache (start, o - start);
+       g_assert(o - start < METHOD_TRAMPOLINE_SIZE);
+       
+       return addr;
+}
+
+/**
+ * arch_create_jit_trampoline:
+ * @method: pointer to the method info
+ *
+ * Creates a trampoline function for virtual methods. If the created
+ * code is called it first starts JIT compilation of method,
+ * and then calls the newly created method. It also replaces the
+ * corresponding vtable entry (see ppc_magic_trampoline).
+ *
+ * A trampoline consists of two parts: a main fragment, shared by all method
+ * trampolines, and some code specific to each method, which hard-codes a
+ * reference to that method and then calls the main fragment.
+ *
+ * The main fragment contains a call to 'ppc_magic_trampoline', which performs
+ * call to the JIT compiler and substitutes the method-specific fragment with
+ * some code that directly calls the JIT-compiled method.
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_jit_trampoline (MonoMethod *method)
+{
+       guint8 *code, *buf;
+       static guint8 *vc = NULL;
+
+       /* previously created trampoline code */
+       if (method->info)
+               return method->info;
+
+       /* we immediately compile runtime provided functions */
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
+               method->info = mono_compile_method (method);
+               return method->info;
+       }
+
+       /* icalls use method->addr */
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+               MonoMethod *nm;
+               
+               nm = mono_marshal_get_native_wrapper (method);
+               method->info = mono_compile_method (nm);
+               return method->info;
+       }
+
+       if(!vc) {
+               /* Now we'll create in 'buf' the PowerPC trampoline code. This
+                is the trampoline code common to all methods  */
+               
+               vc = buf = g_malloc(512);
+               
+               /*-----------------------------------------------------------
+               STEP 0: First create a non-standard function prologue with a
+               stack size big enough to save our registers:
+               
+                       lr              (We'll be calling functions here, so we
+                                       must save it)
+                       r0              (See ppc_magic_trampoline)
+                       r1 (sp)         (Stack pointer - must save)
+                       r3-r10          Function arguments.
+                       r11-r31         (See ppc_magic_trampoline)
+                       method in r11   (See ppc_magic_trampoline)
+                       
+               This prologue is non-standard because r0 is not saved here - it
+               was saved in the method-specific trampoline code
+               -----------------------------------------------------------*/
+               
+               ppc_stwu (buf, ppc_r1, -STACK, ppc_r1);
+               
+               /* Save r0 before modifying it - we will need its contents in
+               'ppc_magic_trampoline' */
+               ppc_stw  (buf, ppc_r0,  STACK - 8,   ppc_r1);
+               
+               ppc_stw  (buf, ppc_r31, STACK - 4, ppc_r1);
+               ppc_mr   (buf, ppc_r31, ppc_r1);
+               
+               /* Now save our registers. */
+               ppc_stw  (buf, ppc_r3,  STACK - 12,  ppc_r1);
+               ppc_stw  (buf, ppc_r4,  STACK - 16,  ppc_r1);
+               ppc_stw  (buf, ppc_r5,  STACK - 20,  ppc_r1);
+               ppc_stw  (buf, ppc_r6,  STACK - 24,  ppc_r1);
+               ppc_stw  (buf, ppc_r7,  STACK - 28,  ppc_r1);
+               ppc_stw  (buf, ppc_r8,  STACK - 32,  ppc_r1);
+               ppc_stw  (buf, ppc_r9,  STACK - 36,  ppc_r1);
+               ppc_stw  (buf, ppc_r10, STACK - 40,  ppc_r1);
+               /* STACK - 44 contains r11, which is set in the method-specific
+               part of the trampoline (see bellow this 'if' block) */
+               ppc_stw  (buf, ppc_r12, STACK - 48,  ppc_r1);
+               ppc_stw  (buf, ppc_r13, STACK - 52,  ppc_r1);
+               ppc_stw  (buf, ppc_r14, STACK - 56,  ppc_r1);
+               ppc_stw  (buf, ppc_r15, STACK - 60,  ppc_r1);
+               ppc_stw  (buf, ppc_r16, STACK - 64,  ppc_r1);
+               ppc_stw  (buf, ppc_r17, STACK - 68,  ppc_r1);
+               ppc_stw  (buf, ppc_r18, STACK - 72,  ppc_r1);
+               ppc_stw  (buf, ppc_r19, STACK - 76,  ppc_r1);
+               ppc_stw  (buf, ppc_r20, STACK - 80,  ppc_r1);
+               ppc_stw  (buf, ppc_r21, STACK - 84,  ppc_r1);
+               ppc_stw  (buf, ppc_r22, STACK - 88,  ppc_r1);
+               ppc_stw  (buf, ppc_r23, STACK - 92,  ppc_r1);
+               ppc_stw  (buf, ppc_r24, STACK - 96,  ppc_r1);
+               ppc_stw  (buf, ppc_r25, STACK - 100, ppc_r1);
+               ppc_stw  (buf, ppc_r26, STACK - 104, ppc_r1);
+               ppc_stw  (buf, ppc_r27, STACK - 108, ppc_r1);
+               ppc_stw  (buf, ppc_r28, STACK - 112, ppc_r1);
+               ppc_stw  (buf, ppc_r29, STACK - 116, ppc_r1);
+               ppc_stw  (buf, ppc_r30, STACK - 120, ppc_r1);
+               /* Save 'method' pseudo-parameter - the one passed in r11 */
+               ppc_stw  (buf, ppc_r11, STACK - 124, ppc_r1);
+
+               /*----------------------------------------------------------
+               STEP 1: call 'mono_get_lmf_addr()' to get the address of our
+               LMF. We'll need to restore it after the call to
+               'ppc_magic_trampoline' and before the call to the native
+               method.
+               ----------------------------------------------------------*/
+                               
+               /* Calculate the address and make the call. Keep in mind that
+               we're using r0, so we'll have to restore it before calling
+               'ppc_magic_trampoline' */
+               ppc_lis  (buf, ppc_r0, (guint32) mono_get_lmf_addr >> 16);
+               ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) mono_get_lmf_addr & 0xffff);
+               ppc_mtlr (buf, ppc_r0);
+               ppc_blrl (buf);
+
+               /* XXX Update LMF !!! */
+               
+               /*----------------------------------------------------------
+               STEP 2: call 'ppc_magic_trampoline()', who will compile the
+               code and fix the method vtable entry for us
+               ----------------------------------------------------------*/
+                               
+               /* Set arguments */
+               
+               /* Arg 1: MonoMethod *method. It was put in r11 by the
+               method-specific trampoline code, and then saved before the call
+               to mono_get_lmf_addr()'. Restore r11, by the way :-) */
+               ppc_lwz  (buf, ppc_r3,  STACK - 124, ppc_r1);
+               ppc_lwz  (buf, ppc_r11, STACK - 44,  ppc_r1);
+               
+               /* Arg 2: code (next address to the instruction that called us) */
+               ppc_lwz  (buf, ppc_r4, STACK + 4, ppc_r1);
+               
+               /* Arg 3: stack pointer */
+               ppc_mr   (buf, ppc_r5, ppc_r1);
+               
+               /* Calculate call address, restore r0 and call
+               'ppc_magic_trampoline'. Return value will be in r3 */
+               ppc_lis  (buf, ppc_r0, (guint32) ppc_magic_trampoline >> 16);
+               ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) ppc_magic_trampoline & 0xffff);
+               ppc_mtlr (buf, ppc_r0);
+               ppc_lwz  (buf, ppc_r0, STACK - 8,  ppc_r1);
+               ppc_blrl (buf);
+               
+               /* OK, code address is now on r3. Move it to r0, so that we
+               can restore r3 and use it from r0 later */
+               ppc_mr   (buf, ppc_r0, ppc_r3);
+               
+
+               /*----------------------------------------------------------
+               STEP 3: Restore the LMF
+               ----------------------------------------------------------*/
+               
+               /* XXX Do it !!! */
+               
+               /*----------------------------------------------------------
+               STEP 4: call the compiled method
+               ----------------------------------------------------------*/
+               
+               /* Restore registers */
+
+               ppc_lwz  (buf, ppc_r3,  STACK - 12,  ppc_r1);
+               ppc_lwz  (buf, ppc_r4,  STACK - 16,  ppc_r1);
+               ppc_lwz  (buf, ppc_r5,  STACK - 20,  ppc_r1);
+               ppc_lwz  (buf, ppc_r6,  STACK - 24,  ppc_r1);
+               ppc_lwz  (buf, ppc_r7,  STACK - 28,  ppc_r1);
+               ppc_lwz  (buf, ppc_r8,  STACK - 32,  ppc_r1);
+               ppc_lwz  (buf, ppc_r9,  STACK - 36,  ppc_r1);
+               ppc_lwz  (buf, ppc_r10, STACK - 40,  ppc_r1);
+               
+               /* We haven't touched any of these, so there's no need to
+               restore them */
+               /*
+               ppc_lwz  (buf, ppc_r14, STACK - 56,  ppc_r1);
+               ppc_lwz  (buf, ppc_r15, STACK - 60,  ppc_r1);
+               ppc_lwz  (buf, ppc_r16, STACK - 64,  ppc_r1);
+               ppc_lwz  (buf, ppc_r17, STACK - 68,  ppc_r1);
+               ppc_lwz  (buf, ppc_r18, STACK - 72,  ppc_r1);
+               ppc_lwz  (buf, ppc_r19, STACK - 76,  ppc_r1);
+               ppc_lwz  (buf, ppc_r20, STACK - 80,  ppc_r1);
+               ppc_lwz  (buf, ppc_r21, STACK - 84,  ppc_r1);
+               ppc_lwz  (buf, ppc_r22, STACK - 88,  ppc_r1);
+               ppc_lwz  (buf, ppc_r23, STACK - 92,  ppc_r1);
+               ppc_lwz  (buf, ppc_r24, STACK - 96,  ppc_r1);
+               ppc_lwz  (buf, ppc_r25, STACK - 100, ppc_r1);
+               ppc_lwz  (buf, ppc_r26, STACK - 104, ppc_r1);
+               ppc_lwz  (buf, ppc_r27, STACK - 108, ppc_r1);
+               ppc_lwz  (buf, ppc_r28, STACK - 112, ppc_r1);
+               ppc_lwz  (buf, ppc_r29, STACK - 116, ppc_r1);
+               ppc_lwz  (buf, ppc_r30, STACK - 120, ppc_r1);
+               */
+
+               /* Non-standard function epilogue. Instead of doing a proper
+               return, we just call the compiled code, so
+               that, when it finishes, the method returns here. */
+               
+               ppc_mtlr (buf, ppc_r0);
+               ppc_blrl (buf);
+               
+               /* Restore stack pointer, r31, LR and return to caler */
+               ppc_lwz  (buf, ppc_r11,  0, ppc_r1);
+               ppc_lwz  (buf, ppc_r31, -4, ppc_r11);
+               ppc_mr   (buf, ppc_r1, ppc_r11);
+               ppc_lwz  (buf, ppc_r0, 4, ppc_r1);
+               ppc_mtlr (buf, ppc_r0);
+               ppc_blr  (buf); 
+               
+               /* Flush instruction cache, sice we've generated code */
+               mono_arch_flush_icache (vc, buf - vc);
+       
+               /* Sanity check */
+               g_assert ((buf - vc) <= 512);
+       }
+
+       /* This is the method-specific part of the trampoline. Its purpose is
+       to provide the generic part with  the MonoMethod *method pointer. We'll
+       use r11 to keep that value, for instance. However, the generic part of
+       the trampoline relies on r11 having the same value it had before coming
+       here, so we must save it before. */
+       code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
+       
+       /* Save r11. There's nothing magic in the '44', its just an arbitrary
+       position - see above */
+       ppc_stw  (buf, ppc_r11, -44,  ppc_r1);
+       
+       /* Now save LR - we'll overwrite it now */
+       ppc_mflr (buf, ppc_r11);
+       ppc_stw  (buf, ppc_r11, 4, ppc_r1);
+       
+       /* Prepare the jump to the generic trampoline code.*/
+       ppc_lis  (buf, ppc_r11, (guint32) vc >> 16);
+       ppc_ori  (buf, ppc_r11, ppc_r11, (guint32) vc & 0xffff);
+       ppc_mtlr (buf, ppc_r11);
+       
+       /* And finally put 'method' in r11 and fly! */
+       ppc_lis  (buf, ppc_r11, (guint32) method >> 16);
+       ppc_ori  (buf, ppc_r11, ppc_r11, (guint32) method & 0xffff);
+       ppc_blr  (buf);
+       
+       /* Flush instruction cache, sice we've generated code */
+       mono_arch_flush_icache (code, buf - code);
+               
+       /* Sanity check */
+       g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
+       
+       /* store trampoline address */
+       method->info = code;
+
+       mono_jit_stats.method_trampolines++;
+
+       return code;
+}
+
+#if 0
+
+/**
+ * x86_magic_trampoline:
+ * @eax: saved x86 register 
+ * @ecx: saved x86 register 
+ * @edx: saved x86 register 
+ * @esi: saved x86 register 
+ * @edi: saved x86 register 
+ * @ebx: saved x86 register
+ * @code: pointer into caller code
+ * @method: the method to translate
+ *
+ * This method is called by the trampoline functions for virtual
+ * methods. It inspects the caller code to find the address of the
+ * vtable slot, then calls the JIT compiler and writes the address
+ * of the compiled method back to the vtable. All virtual methods 
+ * are called with: x86_call_membase (inst, basereg, disp). We always
+ * use 32 bit displacement to ensure that the length of the call 
+ * instruction is 6 bytes. We need to get the value of the basereg 
+ * and the constant displacement.
+ */
+static gpointer
+x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, 
+                     int ebx, guint8 *code, MonoMethod *m)
+{
+       guint8 reg;
+       gint32 disp;
+       char *o;
+       guint32 breakpoint_id;
+       gpointer addr, trampoline;
+
+       EnterCriticalSection (metadata_section);
+       addr = mono_compile_method (m);
+       LeaveCriticalSection (metadata_section);
+       g_assert (addr);
+
+       /* go to the start of the call instruction
+        *
+        * address_byte = (m << 6) | (o << 3) | reg
+        * call opcode: 0xff address_byte displacement
+        * 0xff m=1,o=2 imm8
+        * 0xff m=2,o=2 imm32
+        */
+       code -= 6;
+       if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) {
+               reg = code [4] & 0x07;
+               disp = (signed char)code [5];
+       } else {
+               if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) {
+                       reg = code [1] & 0x07;
+                       disp = *((gint32*)(code + 2));
+               } else if ((code [1] == 0xe8)) {
+                       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
+                       if (breakpoint_id) {
+                               mono_remove_breakpoint (breakpoint_id);
+                               trampoline = get_breakpoint_trampoline (m, breakpoint_id, addr);
+                       } else
+                               trampoline = addr;
+                       *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; 
+                       return trampoline;
+               } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) {
+                       /*
+                        * This is a interface call: should check the above code can't catch it earlier 
+                        * 8b 40 30   mov    0x30(%eax),%eax
+                        * ff 10      call   *(%eax)
+                        */
+                       disp = 0;
+                       reg = code [5] & 0x07;
+               } else {
+                       printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
+                               code [4], code [5], code [6]);
+                       g_assert_not_reached ();
+               }
+       }
+
+       switch (reg) {
+       case X86_EAX:
+               o = (gpointer)eax;
+               break;
+       case X86_EDX:
+               o = (gpointer)edx;
+               break;
+       case X86_ECX:
+               o = (gpointer)ecx;
+               break;
+       case X86_ESI:
+               o = (gpointer)esi;
+               break;
+       case X86_EDI:
+               o = (gpointer)edi;
+               break;
+       case X86_EBX:
+               o = (gpointer)ebx;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       o += disp;
+
+       if (m->klass->valuetype) {
+               trampoline = *((gpointer *)o) = get_unbox_trampoline (m, addr);
+       } else {
+               trampoline = *((gpointer *)o) = addr;
+       }
+
+       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
+       if (breakpoint_id) {
+               mono_remove_breakpoint (breakpoint_id);
+               return get_breakpoint_trampoline (m, breakpoint_id, trampoline);
+       } else {
+               return trampoline;
+       }
+}
+
+/**
+ * mono_arch_create_jit_trampoline:
+ * @method: pointer to the method info
+ *
+ * Creates a trampoline function for virtual methods. If the created
+ * code is called it first starts JIT compilation of method,
+ * and then calls the newly created method. I also replaces the
+ * corresponding vtable entry (see x86_magic_trampoline).
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_jit_trampoline (MonoMethod *method)
+{
+       guint8 *code, *buf;
+
+       /* previously created trampoline code */
+       if (method->info)
+               return method->info;
+
+       /* we immediately compile runtime provided functions */
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
+               method->info = mono_compile_method (method);
+               return method->info;
+       }
+
+       /* icalls use method->addr */
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+               MonoMethod *nm;
+               
+               nm = mono_marshal_get_native_wrapper (method);
+               method->info = mono_compile_method (nm);
+               return method->info;
+       }
+
+       if (!mono_generic_trampoline_code) {
+               mono_generic_trampoline_code = buf = g_malloc (256);
+               /* save caller save regs because we need to do a call */ 
+               x86_push_reg (buf, X86_EDX);
+               x86_push_reg (buf, X86_EAX);
+               x86_push_reg (buf, X86_ECX);
+
+               /* save LMF begin */
+
+               /* save the IP (caller ip) */
+               x86_push_membase (buf, X86_ESP, 16);
+
+               x86_push_reg (buf, X86_EBX);
+               x86_push_reg (buf, X86_EDI);
+               x86_push_reg (buf, X86_ESI);
+               x86_push_reg (buf, X86_EBP);
+
+               /* save method info */
+               x86_push_membase (buf, X86_ESP, 32);
+               /* get the address of lmf for the current thread */
+               x86_call_code (buf, mono_get_lmf_addr);
+               /* push lmf */
+               x86_push_reg (buf, X86_EAX); 
+               /* push *lfm (previous_lmf) */
+               x86_push_membase (buf, X86_EAX, 0);
+               /* *(lmf) = ESP */
+               x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4);
+               /* save LFM end */
+
+               /* push the method info */
+               x86_push_membase (buf, X86_ESP, 44);
+               /* push the return address onto the stack */
+               x86_push_membase (buf, X86_ESP, 52);
+
+               /* save all register values */
+               x86_push_reg (buf, X86_EBX);
+               x86_push_reg (buf, X86_EDI);
+               x86_push_reg (buf, X86_ESI);
+               x86_push_membase (buf, X86_ESP, 64); /* EDX */
+               x86_push_membase (buf, X86_ESP, 64); /* ECX */
+               x86_push_membase (buf, X86_ESP, 64); /* EAX */
+
+               x86_call_code (buf, x86_magic_trampoline);
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
+
+               /* restore LMF start */
+               /* ebx = previous_lmf */
+               x86_pop_reg (buf, X86_EBX);
+               /* edi = lmf */
+               x86_pop_reg (buf, X86_EDI);
+               /* *(lmf) = previous_lmf */
+               x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4);
+               /* discard method info */
+               x86_pop_reg (buf, X86_ESI);
+               /* restore caller saved regs */
+               x86_pop_reg (buf, X86_EBP);
+               x86_pop_reg (buf, X86_ESI);
+               x86_pop_reg (buf, X86_EDI);
+               x86_pop_reg (buf, X86_EBX);
+               /* discard save IP */
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4);             
+               /* restore LMF end */
+
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
+
+               /* call the compiled method */
+               x86_jump_reg (buf, X86_EAX);
+
+               g_assert ((buf - mono_generic_trampoline_code) <= 256);
+       }
+
+       code = buf = g_malloc (16);
+       x86_push_imm (buf, method);
+       x86_jump_code (buf, mono_generic_trampoline_code);
+       g_assert ((buf - code) <= 16);
+
+       /* store trampoline address */
+       method->info = code;
+
+       //mono_jit_stats.method_trampolines++;
+
+       return code;
+}
+
+#endif
+
diff --git a/mono/mini/tramp-x86.c b/mono/mini/tramp-x86.c
new file mode 100644 (file)
index 0000000..869b69c
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * trampoline.c: JIT trampoline code
+ *
+ * Authors:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/marshal.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/arch/x86/x86-codegen.h>
+
+#include "mini.h"
+#include "mini-x86.h"
+
+/* 
+ * define for the (broken) debugger breakpoint interface:
+ * The debugger should use the hw registers to set the breakpoints.
+ */
+#define mono_method_has_breakpoint(a,b) (0)
+#define mono_remove_breakpoint(a)
+
+/* adapt to mini later... */
+#define mono_jit_share_code (1)
+
+/*
+ * Address of the x86 trampoline code.  This is used by the debugger to check
+ * whether a method is a trampoline.
+ */
+guint8 *mono_generic_trampoline_code = NULL;
+
+/*
+ * Address of a special breakpoint trampoline code for the debugger.
+ */
+guint8 *mono_breakpoint_trampoline_code = NULL;
+
+/*
+ * get_unbox_trampoline:
+ * @m: method pointer
+ * @addr: pointer to native code for @m
+ *
+ * when value type methods are called through the vtable we need to unbox the
+ * this argument. This method returns a pointer to a trampoline which does
+ * unboxing before calling the method
+ */
+static gpointer
+get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+       guint8 *code, *start;
+       int this_pos = 4;
+
+       if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret))
+               this_pos = 8;
+           
+       start = code = g_malloc (16);
+
+       x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
+       x86_jump_code (code, addr);
+       g_assert ((code - start) < 16);
+
+       return start;
+}
+
+/*
+ * get_breakpoint_trampoline:
+ * @m: method pointer
+ * @addr: pointer to native code for @m
+ *
+ * creates a special trampoline for the debugger which is used to get
+ * a breakpoint after compiling a method.
+ */
+static gpointer
+get_breakpoint_trampoline (MonoMethod *m, guint32 breakpoint_id, gpointer addr)
+{
+       guint8 *code, *start, *buf;
+
+       if (!mono_breakpoint_trampoline_code) {
+               mono_breakpoint_trampoline_code = buf = g_malloc (8);
+
+               x86_breakpoint (buf);
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8);
+               x86_ret (buf);
+
+               g_assert ((buf - mono_breakpoint_trampoline_code) <= 8);
+       }
+
+       start = code = g_malloc (22);
+       x86_push_imm (code, addr);
+       x86_push_imm (code, breakpoint_id);
+       x86_push_imm (code, m);
+       x86_jump_code (code, mono_breakpoint_trampoline_code);
+       g_assert ((code - start) <= 22);
+
+       return start;
+}
+
+/**
+ * x86_magic_trampoline:
+ * @eax: saved x86 register 
+ * @ecx: saved x86 register 
+ * @edx: saved x86 register 
+ * @esi: saved x86 register 
+ * @edi: saved x86 register 
+ * @ebx: saved x86 register
+ * @code: pointer into caller code
+ * @method: the method to translate
+ *
+ * This method is called by the trampoline functions for virtual
+ * methods. It inspects the caller code to find the address of the
+ * vtable slot, then calls the JIT compiler and writes the address
+ * of the compiled method back to the vtable. All virtual methods 
+ * are called with: x86_call_membase (inst, basereg, disp). We always
+ * use 32 bit displacement to ensure that the length of the call 
+ * instruction is 6 bytes. We need to get the value of the basereg 
+ * and the constant displacement.
+ */
+static gpointer
+x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, 
+                     int ebx, guint8 *code, MonoMethod *m)
+{
+       guint8 reg;
+       gint32 disp;
+       char *o;
+       guint32 breakpoint_id;
+       gpointer addr, trampoline;
+
+       EnterCriticalSection (metadata_section);
+       addr = mono_compile_method (m);
+       LeaveCriticalSection (metadata_section);
+       g_assert (addr);
+
+       /* go to the start of the call instruction
+        *
+        * address_byte = (m << 6) | (o << 3) | reg
+        * call opcode: 0xff address_byte displacement
+        * 0xff m=1,o=2 imm8
+        * 0xff m=2,o=2 imm32
+        */
+       code -= 6;
+       if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) {
+               reg = code [4] & 0x07;
+               disp = (signed char)code [5];
+       } else {
+               if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) {
+                       reg = code [1] & 0x07;
+                       disp = *((gint32*)(code + 2));
+               } else if ((code [1] == 0xe8)) {
+                       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
+                       if (breakpoint_id) {
+                               mono_remove_breakpoint (breakpoint_id);
+                               trampoline = get_breakpoint_trampoline (m, breakpoint_id, addr);
+                       } else
+                               trampoline = addr;
+                       *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; 
+                       return trampoline;
+               } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) {
+                       /*
+                        * This is a interface call: should check the above code can't catch it earlier 
+                        * 8b 40 30   mov    0x30(%eax),%eax
+                        * ff 10      call   *(%eax)
+                        */
+                       disp = 0;
+                       reg = code [5] & 0x07;
+               } else {
+                       printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
+                               code [4], code [5], code [6]);
+                       g_assert_not_reached ();
+               }
+       }
+
+       switch (reg) {
+       case X86_EAX:
+               o = (gpointer)eax;
+               break;
+       case X86_EDX:
+               o = (gpointer)edx;
+               break;
+       case X86_ECX:
+               o = (gpointer)ecx;
+               break;
+       case X86_ESI:
+               o = (gpointer)esi;
+               break;
+       case X86_EDI:
+               o = (gpointer)edi;
+               break;
+       case X86_EBX:
+               o = (gpointer)ebx;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       o += disp;
+
+       if (m->klass->valuetype) {
+               trampoline = *((gpointer *)o) = get_unbox_trampoline (m, addr);
+       } else {
+               trampoline = *((gpointer *)o) = addr;
+       }
+
+       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
+       if (breakpoint_id) {
+               mono_remove_breakpoint (breakpoint_id);
+               return get_breakpoint_trampoline (m, breakpoint_id, trampoline);
+       } else {
+               return trampoline;
+       }
+}
+
+/**
+ * mono_arch_create_jit_trampoline:
+ * @method: pointer to the method info
+ *
+ * Creates a trampoline function for virtual methods. If the created
+ * code is called it first starts JIT compilation of method,
+ * and then calls the newly created method. I also replaces the
+ * corresponding vtable entry (see x86_magic_trampoline).
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_jit_trampoline (MonoMethod *method)
+{
+       guint8 *code, *buf;
+
+       /* previously created trampoline code */
+       if (method->info)
+               return method->info;
+
+       /* we immediately compile runtime provided functions */
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
+               method->info = mono_compile_method (method);
+               return method->info;
+       }
+
+       /* icalls use method->addr */
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+               MonoMethod *nm;
+               
+               if (!method->addr && (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
+                       mono_lookup_pinvoke_call (method);
+
+#ifdef MONO_USE_EXC_TABLES
+               if (mono_method_blittable (method)) {
+                       method->info = method->addr;
+               } else {
+#endif
+                       nm = mono_marshal_get_native_wrapper (method);
+                       method->info = mono_compile_method (nm);
+#ifdef MONO_USE_EXC_TABLES
+               }
+#endif
+               return method->info;
+       }
+
+       if (!mono_generic_trampoline_code) {
+               mono_generic_trampoline_code = buf = g_malloc (256);
+               /* save caller save regs because we need to do a call */ 
+               x86_push_reg (buf, X86_EDX);
+               x86_push_reg (buf, X86_EAX);
+               x86_push_reg (buf, X86_ECX);
+
+               /* save LMF begin */
+
+               /* save the IP (caller ip) */
+               x86_push_membase (buf, X86_ESP, 16);
+
+               x86_push_reg (buf, X86_EBX);
+               x86_push_reg (buf, X86_EDI);
+               x86_push_reg (buf, X86_ESI);
+               x86_push_reg (buf, X86_EBP);
+
+               /* save method info */
+               x86_push_membase (buf, X86_ESP, 32);
+               /* get the address of lmf for the current thread */
+               x86_call_code (buf, mono_get_lmf_addr);
+               /* push lmf */
+               x86_push_reg (buf, X86_EAX); 
+               /* push *lfm (previous_lmf) */
+               x86_push_membase (buf, X86_EAX, 0);
+               /* *(lmf) = ESP */
+               x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4);
+               /* save LFM end */
+
+               /* push the method info */
+               x86_push_membase (buf, X86_ESP, 44);
+               /* push the return address onto the stack */
+               x86_push_membase (buf, X86_ESP, 52);
+
+               /* save all register values */
+               x86_push_reg (buf, X86_EBX);
+               x86_push_reg (buf, X86_EDI);
+               x86_push_reg (buf, X86_ESI);
+               x86_push_membase (buf, X86_ESP, 64); /* EDX */
+               x86_push_membase (buf, X86_ESP, 64); /* ECX */
+               x86_push_membase (buf, X86_ESP, 64); /* EAX */
+
+               x86_call_code (buf, x86_magic_trampoline);
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
+
+               /* restore LMF start */
+               /* ebx = previous_lmf */
+               x86_pop_reg (buf, X86_EBX);
+               /* edi = lmf */
+               x86_pop_reg (buf, X86_EDI);
+               /* *(lmf) = previous_lmf */
+               x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4);
+               /* discard method info */
+               x86_pop_reg (buf, X86_ESI);
+               /* restore caller saved regs */
+               x86_pop_reg (buf, X86_EBP);
+               x86_pop_reg (buf, X86_ESI);
+               x86_pop_reg (buf, X86_EDI);
+               x86_pop_reg (buf, X86_EBX);
+               /* discard save IP */
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4);             
+               /* restore LMF end */
+
+               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
+
+               /* call the compiled method */
+               x86_jump_reg (buf, X86_EAX);
+
+               g_assert ((buf - mono_generic_trampoline_code) <= 256);
+       }
+
+       code = buf = g_malloc (16);
+       x86_push_imm (buf, method);
+       x86_jump_code (buf, mono_generic_trampoline_code);
+       g_assert ((buf - code) <= 16);
+
+       /* store trampoline address */
+       method->info = code;
+
+       mono_jit_stats.method_trampolines++;
+
+       return code;
+}
diff --git a/mono/mini/viewstat.pl b/mono/mini/viewstat.pl
new file mode 100644 (file)
index 0000000..50bd05b
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+
+use GD::Graph::bars;
+use GD::Graph::bars3d;
+use Getopt::Std;
+
+$Usage = "usage: $0 [-e] [-o file] statfile";
+
+# -e       generate a 3D graph
+# -o file  write the graph to file, instead of starting the viewer
+
+getopts('eo:', \%Opts)
+    or die "$Usage";
+die "$Usage\n"
+    unless (@ARGV == 1);
+
+$statfile = shift;
+
+sub save_chart
+{
+        my $chart = shift or die "Need a chart!";
+        my $name = shift or die "Need a name!";
+        local(*OUT);
+
+        open(OUT, ">$name") or 
+                die "Cannot open $name.$ext for write: $!";
+        binmode OUT;
+        print OUT $chart->gd->png();
+        close OUT;
+}
+
+
+print STDERR "Processing file $statfile\n";
+
+if ($Opts{'e'}) {
+    $graph = new GD::Graph::bars3d(800, 600);
+} else {
+    $graph = new GD::Graph::bars(800, 600);
+}
+$graph->set( 
+            y_label         => 'Time',
+            y_long_ticks      => 1,
+
+            x_long_ticks      => 1,
+
+            y_tick_number   => 8,
+
+            x_labels_vertical   => 1,
+            bar_spacing         => 5,
+            
+            show_values         => 1,
+            values_vertical     => 1,
+                    
+            l_margin            => 10,
+            b_margin            => 10,
+            r_margin            => 10,
+            t_margin            => 10,
+
+            shadow_depth => 1,
+
+            transparent     => 0,
+);
+
+if ($Opts{'e'}) {
+    $graph->set (overwrite => 1);
+    $graph->set (show_values => 0);
+}
+
+require $statfile;
+
+if ($stattitle ne "") {
+    $graph->set (title=> $stattitle);
+}
+
+$outfile = $Opts{'o'};
+
+if ($outfile eq "") {
+    $tmp = $outfile = "/tmp/viewstat" . $$ . ".png";
+}
+
+$graph->plot(\@data);
+save_chart($graph, $outfile);
+
+if ($tmp) {
+    `eog $outfile`;
+    `rm $tmp`;
+}