--- /dev/null
+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
--- /dev/null
+
+* 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
--- /dev/null
+
+ 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
--- /dev/null
+
+* 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...)
+
--- /dev/null
+mini
+mono-debugger-mini-wrapper
+.libs
+*.o
+test.exe
--- /dev/null
+Mini is the new JIT compiler for Mono.
--- /dev/null
+* 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
--- /dev/null
+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);
+ }
+}
+
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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;
+ }
+
+}
+
--- /dev/null
+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;
+ }
+
+}
+
--- /dev/null
+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;
+ }
+}
+
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+ */
+}
+
--- /dev/null
+/*
+ * 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);
+ }
+}
+
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#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;
+}
+
--- /dev/null
+/*
+ * 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
+
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+%%
+
+#
+# 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);
+}
+
+
+%%
--- /dev/null
+/*
+ * 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 ();
+}
+
--- /dev/null
+/*
+ * 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 ();
+}
+
+
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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");
+}
+
--- /dev/null
+.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
--- /dev/null
+%%
+
+#
+# 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);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+%%
--- /dev/null
+%%
+
+#
+# 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 ();
+}
+
+%%
--- /dev/null
+%%
+
+#
+# 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);
+}
+
+%%
--- /dev/null
+%%
+
+#
+# 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);
+}
+
+%%
--- /dev/null
+/*
+ * 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");
+}
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
+}
+
--- /dev/null
+
+* 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
--- /dev/null
+#include "mini.h"
+
+int
+main (int argc, char* argv[])
+{
+ return mini_main (argc, argv);
+}
+
--- /dev/null
+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
+
--- /dev/null
+#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__ */
--- /dev/null
+
+ 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
--- /dev/null
+
+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")
+
+
+
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+#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__ */
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+#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__ */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+#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__ */
--- /dev/null
+;; -*- 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)
--- /dev/null
+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;
+ }
+}
+
--- /dev/null
+
+* 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...)
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+
+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);
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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;
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#!/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`;
+}