From 5a710fa941e8d9c64a6228fe173cf50976eed05e Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Sat, 5 Apr 2003 19:21:32 +0000 Subject: [PATCH] New compilation engine for Mono svn path=/trunk/mono/; revision=13205 --- docs/aot-compiler.txt | 44 + docs/local-regalloc.txt | 208 ++ docs/mini-doc.txt | 628 ++++ docs/opcode-decomp.txt | 113 + mono/mini/.cvsignore | 5 + mono/mini/README | 1 + mono/mini/TODO | 13 + mono/mini/TestDriver.cs | 79 + mono/mini/aot-compiler.txt | 44 + mono/mini/aot.c | 555 +++ mono/mini/arrays.cs | 131 + mono/mini/basic-float.cs | 426 +++ mono/mini/basic-long.cs | 595 ++++ mono/mini/basic.cs | 622 ++++ mono/mini/bench.cs | 244 ++ mono/mini/cfold.c | 216 ++ mono/mini/cprop.c | 130 + mono/mini/cpu-g4.md | 611 ++++ mono/mini/cpu-pentium.md | 619 ++++ mono/mini/debug-dwarf2.c | 1400 ++++++++ mono/mini/debug-mini.c | 164 + mono/mini/debug-private.h | 111 + mono/mini/debug-stabs.c | 231 ++ mono/mini/debug.c | 1138 +++++++ mono/mini/debug.h | 163 + mono/mini/debugger-main.c | 28 + mono/mini/dominators.c | 479 +++ mono/mini/driver.c | 733 ++++ mono/mini/emullong.brg | 86 + mono/mini/exceptions-ppc.c | 953 ++++++ mono/mini/exceptions-x86.c | 972 ++++++ mono/mini/exceptions.cs | 1317 ++++++++ mono/mini/genmdesc.c | 236 ++ mono/mini/graph.c | 323 ++ mono/mini/helpers.c | 80 + mono/mini/iltests.il | 94 + mono/mini/inssel-float.brg | 233 ++ mono/mini/inssel-long.brg | 47 + mono/mini/inssel-long32.brg | 606 ++++ mono/mini/inssel-x86.brg | 497 +++ mono/mini/inssel.brg | 1589 +++++++++ mono/mini/jit-icalls.c | 401 +++ mono/mini/linear-scan.c | 189 ++ mono/mini/liveness.c | 202 ++ mono/mini/local-regalloc.txt | 208 ++ mono/mini/main.c | 8 + mono/mini/makefile | 99 + mono/mini/mini-arch.h | 12 + mono/mini/mini-doc.txt | 628 ++++ mono/mini/mini-ops.h | 314 ++ mono/mini/mini-ppc.c | 2854 ++++++++++++++++ mono/mini/mini-ppc.h | 26 + mono/mini/mini-x86.c | 3141 +++++++++++++++++ mono/mini/mini-x86.h | 37 + mono/mini/mini.c | 6182 ++++++++++++++++++++++++++++++++++ mono/mini/mini.h | 662 ++++ mono/mini/mini.prj | 28 + mono/mini/objects.cs | 415 +++ mono/mini/opcode-decomp.txt | 113 + mono/mini/regalloc.c | 72 + mono/mini/regalloc.h | 53 + mono/mini/regset.c | 114 + mono/mini/regset.h | 38 + mono/mini/ssa.c | 1099 ++++++ mono/mini/test.cs | 348 ++ mono/mini/tramp-ppc.c | 748 ++++ mono/mini/tramp-x86.c | 345 ++ mono/mini/viewstat.pl | 87 + 68 files changed, 35187 insertions(+) create mode 100644 docs/aot-compiler.txt create mode 100644 docs/local-regalloc.txt create mode 100644 docs/mini-doc.txt create mode 100644 docs/opcode-decomp.txt create mode 100644 mono/mini/.cvsignore create mode 100644 mono/mini/README create mode 100644 mono/mini/TODO create mode 100644 mono/mini/TestDriver.cs create mode 100644 mono/mini/aot-compiler.txt create mode 100644 mono/mini/aot.c create mode 100644 mono/mini/arrays.cs create mode 100644 mono/mini/basic-float.cs create mode 100644 mono/mini/basic-long.cs create mode 100644 mono/mini/basic.cs create mode 100644 mono/mini/bench.cs create mode 100644 mono/mini/cfold.c create mode 100644 mono/mini/cprop.c create mode 100644 mono/mini/cpu-g4.md create mode 100644 mono/mini/cpu-pentium.md create mode 100644 mono/mini/debug-dwarf2.c create mode 100644 mono/mini/debug-mini.c create mode 100644 mono/mini/debug-private.h create mode 100644 mono/mini/debug-stabs.c create mode 100644 mono/mini/debug.c create mode 100644 mono/mini/debug.h create mode 100644 mono/mini/debugger-main.c create mode 100644 mono/mini/dominators.c create mode 100644 mono/mini/driver.c create mode 100644 mono/mini/emullong.brg create mode 100644 mono/mini/exceptions-ppc.c create mode 100644 mono/mini/exceptions-x86.c create mode 100644 mono/mini/exceptions.cs create mode 100644 mono/mini/genmdesc.c create mode 100644 mono/mini/graph.c create mode 100644 mono/mini/helpers.c create mode 100644 mono/mini/iltests.il create mode 100644 mono/mini/inssel-float.brg create mode 100644 mono/mini/inssel-long.brg create mode 100644 mono/mini/inssel-long32.brg create mode 100644 mono/mini/inssel-x86.brg create mode 100644 mono/mini/inssel.brg create mode 100644 mono/mini/jit-icalls.c create mode 100644 mono/mini/linear-scan.c create mode 100644 mono/mini/liveness.c create mode 100644 mono/mini/local-regalloc.txt create mode 100644 mono/mini/main.c create mode 100644 mono/mini/makefile create mode 100644 mono/mini/mini-arch.h create mode 100644 mono/mini/mini-doc.txt create mode 100644 mono/mini/mini-ops.h create mode 100644 mono/mini/mini-ppc.c create mode 100644 mono/mini/mini-ppc.h create mode 100644 mono/mini/mini-x86.c create mode 100644 mono/mini/mini-x86.h create mode 100644 mono/mini/mini.c create mode 100644 mono/mini/mini.h create mode 100644 mono/mini/mini.prj create mode 100644 mono/mini/objects.cs create mode 100644 mono/mini/opcode-decomp.txt create mode 100644 mono/mini/regalloc.c create mode 100644 mono/mini/regalloc.h create mode 100644 mono/mini/regset.c create mode 100644 mono/mini/regset.h create mode 100644 mono/mini/ssa.c create mode 100644 mono/mini/test.cs create mode 100644 mono/mini/tramp-ppc.c create mode 100644 mono/mini/tramp-x86.c create mode 100644 mono/mini/viewstat.pl diff --git a/docs/aot-compiler.txt b/docs/aot-compiler.txt new file mode 100644 index 00000000000..ab1af90d965 --- /dev/null +++ b/docs/aot-compiler.txt @@ -0,0 +1,44 @@ +Mono Ahead Of Time Compiler +=========================== + +The new mono JIT has sophisticated optimization features. It uses SSA and has a +pluggable architecture for further optimizations. This makes it possible and +efficient to use the JIT also for AOT compilation. + + +* file format: We use the native object format of the platform. That way it is + possible to reuse existing tools like objdump and the dynamic loader. All we + need is a working assembler, i.e. we write out a text file which is then + passed to gas (the gnu assembler) to generate the object file. + +* file names: we simply add ".so" to the generated file. For example: + basic.exe -> basic.exe.so + corlib.dll -> corlib.dll.so + +* staring the AOT compiler: mini --aot assembly_name + +The following things are saved in the object file: + +* version infos: + +* native code: this is labeled with method_XXXXXXXX: where XXXXXXXX is the + hexadecimal token number of the method. + +* additional informations needed by the runtime: For example we need to store + the code length and the exception tables. We also need a way to patch + constants only available at runtime (for example vtable and class + addresses). This is stored i a binary blob labeled method_info_XXXXXXXX: + +PROBLEMS: + +- all precompiled methods must be domain independent, or we add patch infos to + patch the target doamin. + +- the main problem is how to patch runtime related addresses, for example: + + - current application domain + - string objects loaded with LDSTR + - address of MonoClass data + - static field offsets + - method addreses + - virtual function and interface slots diff --git a/docs/local-regalloc.txt b/docs/local-regalloc.txt new file mode 100644 index 00000000000..a6e523557fe --- /dev/null +++ b/docs/local-regalloc.txt @@ -0,0 +1,208 @@ + +* Proposal for the local register allocator + + The local register allocator deals with allocating registers + for temporaries inside a single basic block, while the global + register allocator is concerned with method-wide allocation of + variables. + The global register allocator uses callee-saved register for it's + purpouse so that there is no need to save and restore these registers + at call sites. + + There are a number of issues the local allocator needs to deal with: + *) some instructions expect operands in specific registers (for example + the shl instruction on x86, or the call instruction with thiscall + convention, or the equivalent call instructions on other architectures, + such as the need to put output registers in %oX on sparc) + *) some instructions deliver results only in specific registers (for example + the div instruction on x86, or the call instructionson on almost all + the architectures). + *) it needs to know what registers may be clobbered by an instruction + (such as in a method call) + *) it should avoid excessive reloads or stores to improve performance + + While which specific instructions have limitations is architecture-dependent, + the problem shold be solved in an arch-independent way to reduce code duplication. + The register allocator will be 'driven' by the arch-dependent code, but it's + implementation should be arch-independent. + + To improve the current local register allocator, we need to + keep more state in it than the current setup that only keeps busy/free info. + + Possible state information is: + + free: the resgister is free to use and it doesn't contain useful info + freeable: the register contains data loaded from a local (there is + also info about _which_ local it contains) as a result from previous + instructions (like, there was a store from the register to the local) + moveable: it contains live data that is needed in a following instruction, but + the contents may be moved to a different register + busy: the register contains live data and it is placed there because + the following instructions need it exactly in that register + allocated: the register is used by the global allocator + + The local register allocator will have the following interfaces: + + int get_register (); + Searches for a register in the free state. If it doesn't find it, + searches for a freeable register. Sets the status to moveable. + Looking for a 'free' register before a freeable one should allow for + removing a few redundant loads (though I'm still unsure if such + things should be delegated entirely to the peephole pass). + + int get_register_force (int reg); + Returns 'reg' if it is free or freeable. If it is moveable, it moves it + to another free or freeable register. + Sets the status of 'reg' to busy. + + void set_register_freeable (int reg); + Sets the status of 'reg' to freeable. + + void set_register_free (int reg); + Sets the status of 'reg' to free. + + void will_clobber (int reg); + Spills the register to the stack. Sets the status to freeable. + After the clobbering has occurred, set the status to free. + + void register_unspill (int reg); + Un-spills register reg and sets the status to moveable. + + FIXME: how is the 'local' information represented? Maybe a MonoInst* pointer. + + Note: the register allocator will insert instructions in the basic block + during it's operation. + +* Examples + + Given the tree (on x86 the right argument to shl needs to be in ecx): + + store (local1, shl (local1, call (some_arg))) + + At the start of the basic block, the registers are set to the free state. + The sequence of instructions may be: + instruction register status -> [%eax %ecx %edx] + start free free free + eax = load local1 mov free free + /* call clobbers eax, ecx, edx */ + spill eax free free free + call mov free free + /* now eax contains the right operand of the shl */ + mov %eax -> %ecx free busy free + un-spill mov busy free + shl %cl, %eax mov free free + + The resulting x86 code is: + mov $fffc(%ebp), %eax + mov %eax, $fff0(%ebp) + push some_arg + call func + mov %eax, %ecx + mov $fff0(%ebp), %eax + shl %cl, %eax + + Note that since shl could operate directly on memory, we could have: + + push some_arg + call func + mov %eax, %ecx + shl %cl, $fffc(%ebp) + + The above example with loading the operand in a register is just to complicate + the example and show that the algorithm should be able to handle it. + + Let's take another example with the this-call call convention (the first argument + is passed in %ecx). + In this case, will_clobber() will be called only on %eax and %edx, while %ecx + will be allocated with get_register_force (). + Note: when a register is allocated with get_register_force(), it should be set + to a different state as soon as possible. + + store (local1, shl (local1, this-call (local1))) + + instruction register status -> [%eax %ecx %edx] + start free free free + eax = load local1 mov free free + /* force load in %ecx */ + ecx = load local1 mov busy free + spill eax free busy free + call mov free free + /* now eax contains the right operand of the shl */ + mov %eax -> %ecx free busy free + un-spill mov busy free + shl %cl, %eax mov free free + + What happens when a register that we need to allocate with get_register_force () + contains an operand for the next instruction? + + instruction register status -> [%eax %ecx %edx] + eax = load local0 mov free free + ecx = load local1 mov mov free + get_register_force (ecx) here. + We have two options: + mov %ecx, %edx + or: + spill %ecx + The first option is way better (and allows the peephole pass to + just load the value in %edx directly, instead of loading first to %ecx). + This doesn't work, though, if the instruction clobbers the %edx register + (like in a this-call). So, we first need to clobber the registers + (so the state of %ecx changes to freebale and there is no issue + with get_register_force ()). + What if an instruction both clobbers a register and requires it as + an operand? Lets' take the x86 idiv instruction as an example: it + requires the dividend in edx:eax and returns the result in eax, + with the modulus in edx. + + store (local1, div (local1, local2)) + + instruction register status -> [%eax %ecx %edx] + eax = load local0 mov free free + will_clobber eax, edx free mov free + force mov %ecx, %eax busy free free + set %edx busy free busy + idiv mov free free + + Note: edx is set to free after idiv, because the modulus is not needed + (if it was a rem, eax would have been freed). + If we load the divisor before will_clobber(), we'll have to spill + eax and reload it later. If we load it just after the idiv, there is no issue. + In any case, the algorithm should give the correct results and allow the operation. + + Working recursively on the isntructions there shouldn't be huge issues + with this algorithm (though, of course, it's not optimal and it may + introduce excessive spills or register moves). The advantage over the current + local reg allocator is that: + 1) the number of spills/moves would be smaller anyway + 2) a separate peephole pass could be able to eliminate reg moves + 3) we'll be able to remove the 'forced' spills we currently do with + the return value of method calls + +* Issues + + How to best integrate such a reg allocator with the burg stuff. + + Think about a call os sparc with two arguments: they got into %o0 and %o1 + and each of them sets the register as busy. But what if the values to put there + are themselves the result of a call? %o0 is no problem, but for all the + next argument n the above algorithm would spill all the 0...n-1 registers... + +* Papers + + More complex solutions to the local register allocator problem: + http://dimacs.rutgers.edu/TechnicalReports/abstracts/1997/97-33.html + + Combining register allocation and instruction scheduling: + http://citeseer.nj.nec.com/motwani95combining.html + + More on LRA euristics: + http://citeseer.nj.nec.com/liberatore97hardness.html + + Linear-time optimal code scheduling for delayedload architectures + http://www.cs.wisc.edu/~fischer/cs701.f01/inst.sched.ps.gz + + Precise Register Allocation for Irregular Architectures + http://citeseer.nj.nec.com/kong98precise.html + + Allocate registers first to subtrees that need more of them. + http://www.upb.de/cs/ag-kastens/compii/folien/comment401-409.2.pdf diff --git a/docs/mini-doc.txt b/docs/mini-doc.txt new file mode 100644 index 00000000000..cfe1a91d365 --- /dev/null +++ b/docs/mini-doc.txt @@ -0,0 +1,628 @@ + + A new JIT compiler for the Mono Project + + Miguel de Icaza (miguel@{ximian.com,gnome.org}), + + +* Abstract + + Mini is a new compilation engine for the Mono runtime. The + new engine is designed to bring new code generation + optimizations, portability and precompilation. + + In this document we describe the design decisions and the + architecture of the new compilation engine. + +* Introduction + + First we discuss the overall architecture of the Mono runtime, + and how code generation fits into it; Then we discuss the + development and basic architecture of our first JIT compiler + for the ECMA CIL framework. The next section covers the + objectives for the work on the new JIT compiler, then we + discuss the new features available in the new JIT compiler, + and finally a technical description of the new code generation + engine. + +* Architecture of the Mono Runtime + + The Mono runtime is an implementation of the ECMA Common + Language Infrastructure (CLI), whose aim is to be a common + platform for executing code in multiple languages. + + Languages that target the CLI generate images that contain + code in high-level intermediate representation called the + "Common Intermediate Language". This intermediate language is + rich enough to allow for programs and pre-compiled libraries + to be reflected. The execution environment allows for an + object oriented execution environment with single inheritance + and multiple interface implementations. + + This runtime provides a number of services for programs that + are targeted to it: Just-in-Time compilation of CIL code into + native code, garbage collection, thread management, I/O + routines, single, double and decimal floating point, + asynchronous method invocation, application domains, and a + framework for building arbitrary RPC systems (remoting) and + integration with system libraries through the Platform Invoke + functionality. + + The focus of this document is on the services provided by the + Mono runtime to transform CIL bytecodes into code that is + native to the underlying architecture. + + The code generation interface is a set of macros that allow a + C programmer to generate code on the fly, this is done + through a set of macros found in the mono/jit/arch/ directory. + These macros are used by the JIT compiler to generate native + code. + + The platform invocation code is interesting, as it generates + CIL code on the fly to marshal parameters, and then this + code is in turned processed by the JIT engine. + +* Previous Experiences + + Mono has built a JIT engine, which has been used to bootstrap + Mono since January, 2002. This JIT engine has reasonable + performance, and uses an tree pattern matching instruction + selector based on the BURS technology. This JIT compiler was + designed by Dietmar Maurer, Paolo Molaro and Miguel de Icaza. + + The existing JIT compiler has three phases: + + * Re-creation of the semantic tree from CIL + byte-codes. + + * Instruction selection, with a cost-driven + engine. + + * Code generation and register allocation. + + It is also hooked into the rest of the runtime to provide + services like marshaling, just-in-time compilation and + invocation of "internal calls". + + This engine constructed a collection of trees, which we + referred to as the "forest of trees", this forest is created by + "hydrating" the CIL instruction stream. + + The first step was to identify the basic blocks on the method, + and computing the control flow graph (cfg) for it. Once this + information was computed, a stack analysis on each basic block + was performed to create a forest of trees for each one of + them. + + So for example, the following statement: + + int a, b; + ... + b = a + 1; + + Which would be represented in CIL as: + + ldloc.0 + ldc.i4.1 + add + stloc.1 + + After the stack analysis would create the following tree: + + (STIND_I4 ADDR_L[EBX|2] ( + ADD (LDIND_I4 ADDR_L[ESI|1]) + CONST_I4[1])) + + This tree contains information from the stack analysis: for + instance, notice that the operations explicitly encode the + data types they are operating on, there is no longer an + ambiguity on the types, because this information has been + inferred. + + At this point the JIT would pass the constructed forest of + trees to the architecture-dependant JIT compiler. + + The architecture dependent code then performed register + allocation (optionally using linear scan allocation for + variables, based on life analysis). + + Once variables had been assigned, a tree pattern matching with + dynamic programming is used (the tree pattern matcher is + custom build for each architecture, using a code + generator: monoburg). The instruction selector used cost + functions to select the best instruction patterns. + + The instruction selector is able to produce instructions that + take advantage of the x86 instruction indexing instructions + for example. + + One problem though is that the code emitter and the register + allocator did not have any visibility outside the current + tree, which meant that some redundant instructions were + generated. A peephole optimizer with this architecture was + hard to write, given the tree-based representation that is + used. + + This JIT was functional, but it did not provide a good + architecture to base future optimizations on. Also the + line between architecture neutral and architecture + specific code and optimizations was hard to draw. + + The JIT engine supported two code generation modes to support + the two optimization modes for applications that host multiple + application domains: generate code that will be shared across + application domains, or generate code that will not be shared + across application domains. + +* Objectives of the new JIT engine. + + We wanted to support a number of features that were missing: + + * Ahead-of-time compilation. + + The idea is to allow developers to pre-compile their code + to native code to reduce startup time, and the working + set that is used at runtime in the just-in-time compiler. + + Although in Mono this has not been a visible problem, we + wanted to pro-actively address this problem. + + When an assembly (a Mono/.NET executable) is installed in + the system, it would then be possible to pre-compile the + code, and have the JIT compiler tune the generated code + to the particular CPU on which the software is + installed. + + This is done in the Microsoft.NET world with a tool + called ngen.exe + + * Have a good platform for doing code optimizations. + + The design called for a good architecture that would + enable various levels of optimizations: some + optimizations are better performed on high-level + intermediate representations, some on medium-level and + some at low-level representations. + + Also it should be possible to conditionally turn these on + or off. Some optimizations are too expensive to be used + in just-in-time compilation scenarios, but these + expensive optimizations can be turned on for + ahead-of-time compilations or when using profile-guided + optimizations on a subset of the executed methods. + + * Reduce the effort required to port the Mono code + generator to new architectures. + + For Mono to gain wide adoption in the Unix world, it is + necessary that the JIT engine works in most of today's + commercial hardware platforms. + +* Features of the new JIT engine. + + The new JIT engine was architected by Dietmar Maurer and Paolo + Molaro, based on the new objectives. + + Mono provides a number of services to applications running + with the new JIT compiler: + + * Just-in-Time compilation of CLI code into native code. + + * Ahead-of-Time compilation of CLI code, to reduce + startup time of applications. + + A number of software development features are also available: + + * Execution time profiling (--profile) + + Generates a report of the times consumed by routines, + as well as the invocation times, as well as the + callers. + + * Memory usage profiling (--profile) + + Generates a report of the memory usage by a program + that is ran under the Mono JIT. + + * Code coverage (--coverage) + + * Execution tracing. + + People who are interested in developing and improving the Mini + JIT compiler will also find a few useful routines: + + * Compilation times + + This is used to time the execution time for the JIT + when compiling a routine. + + * Control Flow Graph and Dominator Tree drawing. + + These are visual aids for the JIT developer: they + render representations of the Control Flow graph, and + for the more advanced optimizations, they draw the + dominator tree graph. + + This requires Dot (from the graphwiz package) and Ghostview. + + * Code generator regression tests. + + The engine contains support for running regression + tests on the virtual machine, which is very helpful to + developers interested in improving the engine. + + * Optimization benchmark framework. + + The JIT engine will generate graphs that compare + various benchmarks embedded in an assembly, and run the + various tests with different optimization flags. + + This requires Perl, GD::Graph. + +* Flexibility + + This is probably the most important component of the new code + generation engine. The internals are relatively easy to + replace and update, even large passes can be replaced and + implemented differently. + +* New code generator + + Compiling a method begins with the `mini_method_to_ir' routine + that converts the CIL representation into a medium + intermediate representation. + + The mini_method_to_ir routine performs a number of operations: + + * Flow analysis and control flow graph computation. + + Unlike the previous version, stack analysis and control + flow graphs are computed in a single pass in the + mini_method_to_ir function, this is done for performance + reasons: although the complexity increases, the benefit + for a JIT compiler is that there is more time available + for performing other optimizations. + + * Basic block computation. + + mini_method_to_ir populates the MonoCompile structure + with an array of basic blocks each of which contains + forest of trees made up of MonoInst structures. + + * Inlining + + Inlining is no longer restricted to methods containing + one single basic block, instead it is possible to inline + arbitrary complex methods. + + The heuristics to choose what to inline are likely going + to be tuned in the future. + + * Method to opcode conversion. + + Some method call invocations like `call Math.Sin' are + transformed into an opcode: this transforms the call + into a semantically rich node, which is later inline + into an FPU instruction. + + Various Array methods invocations are turned into + opcodes as well (The Get, Set and Address methods) + + * Tail recursion elimination + + Basic blocks **** + + The MonoInst structure holds the actual decoded instruction, + with the semantic information from the stack analysis. + MonoInst is interesting because initially it is part of a tree + structure, here is a sample of the same tree with the new JIT + engine: + + (stind.i4 regoffset[0xffffffd4(%ebp)] + (add (ldind.i4 regoffset[0xffffffd8(%ebp)]) + iconst[1])) + + This is a medium-level intermediate representation (MIR). + + Some complex opcodes are decomposed at this stage into a + collection of simpler opcodes. Not every complex opcode is + decomposed at this stage, as we need to preserve the semantic + information during various optimization phases. + + For example a NEWARR opcode carries the length and the type of + the array that could be used later to avoid type checking or + array bounds check. + + There are a number of operations supported on this + representation: + + * Branch optimizations. + + * Variable liveness. + + * Loop optimizations: the dominator trees are + computed, loops are detected, and their nesting + level computed. + + * Conversion of the method into static single assignment + form (SSA form). + + * Dead code elimination. + + * Constant propagation. + + * Copy propagation. + + * Constant folding. + + Once the above optimizations are optionally performed, a + decomposition phase is used to turn some complex opcodes into + internal method calls. In the initial version of the JIT + engine, various operations on longs are emulated instead of + being inlined. Also the newarr invocation is turned into a + call to the runtime. + + At this point, after computing variable liveness, it is + possible to use the linear scan algorithm for allocating + variables to registers. The linear scan pass uses the + information that was previously gathered by the loop nesting + and loop structure computation to favor variables in inner + loops. + + Stack space is then reserved for the local variables and any + temporary variables generated during the various + optimizations. + +** Instruction selection + + At this point, the BURS instruction selector is invoked to + transform the tree-based representation into a list of + instructions. This is done using a tree pattern matcher that + is generated for the architecture using the `monoburg' tool. + + Monoburg takes as input a file that describes tree patterns, + which are matched against the trees that were produced by the + engine in the previous stages. + + The pattern matching might have more than one match for a + particular tree. In this case, the match selected is the one + whose cost is the smallest. A cost can be attached to each + rule, and if no cost is provided, the implicit cost is one. + Smaller costs are selected over higher costs. + + The cost function can be used to select particular blocks of + code for a given architecture, or by using a prohibitive high + number to avoid having the rule match. + + The various rules that our JIT engine uses transform a tree of + MonoInsts into a list of monoinsts: + + +-----------------------------------------------------------+ + | Tree List | + | of ===> Instruction selection ===> of | + | MonoInst MonoInst. | + +-----------------------------------------------------------+ + + During this process various "types" of MonoInst kinds + disappear and turned into lower-level representations. The + JIT compiler just happens to reuse the same structure (this is + done to reduce memory usage and improve memory locality). + + The instruction selection rules are split in a number of + files, each one with a particular purpose: + + inssel.brg + Contains the generic instruction selection + patterns. + + inssel-x86.brg + Contains x86 specific rules. + + inssel-ppc.brg + Contains PowerPC specific rules. + + inssel-long32.brg + burg file for 64bit instructions on 32bit architectures. + + inssel-long.brg + burg file for 64bit architectures. + + inssel-float.brg + burg file for floating point instructions + + For a given build, a set of those files would be included. + For example, for the build of Mono on the x86, the following + set is used: + + inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg + +** Native method generation + + The native method generation has a number of steps: + + * Architecture specific register allocation. + + The information about loop nesting that was + previously gathered is used here to hint the + register allocator. + + * Generating the method prolog/epilog. + + * Optionally generate code to introduce tracing facilities. + + * Hooking into the debugger. + + * Performing any pending fixups. + + * Code generation. + +*** Code Generation + + The actual code generation is contained in the architecture + specific portion of the compiler. The input to the code + generator is each one of the basic blocks with its list of + instructions that were produced in the instruction selection + phase. + + During the instruction selection phase, virtual registers are + assigned. Just before the peephole optimization is performed, + physical registers are assigned. + + A simple peephole and algebraic optimizer is ran at this + stage. + + The peephole optimizer removes some redundant operations at + this point. This is possible because the code generation at + this point has visibility into the basic block that spans the + original trees. + + The algebraic optimizer performs some simple algebraic + optimizations that replace expensive operations with cheaper + operations if possible. + + The rest of the code generation is fairly simple: a switch + statement is used to generate code for each of the MonoInsts + + We always try to allocate code in sequence, instead of just using + malloc. This way we increase spatial locality which gives a massive + speedup on most architectures. + +*** Ahead of Time compilation + + Ahead-of-Time compilation is a new feature of our new + compilation engine. The compilation engine is shared by the + Just-in-Time (JIT) compiler and the Ahead-of-Time compiler + (AOT). + + The difference is on the set of optimizations that are turned + on for each mode: Just-in-Time compilation should be as fast + as possible, while Ahead-of-Time compilation can take as long + as required, because this is not done at a time criticial + time. + + With AOT compilation, we can afford to turn all of the + computationally expensive optimizations on. + + After the code generation phase is done, the code and any + required fixup information is saved into a file that is + readable by "as" (the native assembler available on all + systems). This assembly file is then passed to the native + assembler, which generates a loadable module. + + At execution time, when an assembly is loaded from the disk, + the runtime engine will probe for the existance of a + pre-compiled image. If the pre-compiled image exists, then it + is loaded, and the method invocations are resolved to the code + contained in the loaded module. + + The code generated under the AOT scenario is slightly + different than the JIT scenario. It generates code that is + application-domain relative and that can be shared among + multiple thread. + + This is the same code generation that is used when the runtime + is instructed to maximize code sharing on a multi-application + domain scenario. + +* SSA-based optimizations + + SSA form simplifies many optimization because each variable has exactly + one definition site. All uses of a variable are "dominated" by its + definition, which enables us to implement algorithm like: + + * conditional constant propagation + + * array bound check removal + + * dead code elimination + + And we can implement those algorithm in a efficient way using SSA. + + +* Register allocation. + + Global register allocation is performed on the medium + intermediate representation just before instruction selection + is performed on the method. Local register allocation is + later performed at the basic-block level on the + + Global register allocation uses the following input: + + 1) set of register-sized variables that can be allocated to a + register (this is an architecture specific setting, for x86 + these registers are the callee saved register ESI, EDI and + EBX). + + 2) liveness information for the variables + + 3) (optionally) loop info to favour variables that are used in + inner loops. + + During instruction selection phase, symbolic registers are + assigned to temporary values in expressions. + + Local register allocation assigns hard registers to the + symbolic registers, and it is performed just before the code + is actually emitted and is performed at the basic block level. + A CPU description file describes the input registers, output + registers, fixed registers and clobbered registers by each + operation. + + +---------- +* Bootstrap + + The Mini bootstrap parses the arguments passed on the command + line, and initializes the JIT runtime. Each time the + mini_init() routine is invoked, a new Application Domain will + be returned. + +* Signal handlers + + mono_runtime_install_handlers + +* BURG Code Generator Generator + + monoburg was written by Dietmar Maurer. It is based on the + papers from Christopher W. Fraser, Robert R. Henry and Todd + A. Proebsting: "BURG - Fast Optimal Instruction Selection and + Tree Parsing" and "Engineering a Simple, Efficient Code + Generator Generator". + + The original BURG implementation is unable to work on DAGs, instead only + trees are allowed. Our monoburg implementations is able to generate tree + matcher which works on DAGs, and we use this feature in the new + JIT. This simplifies the code because we can directly pass DAGs and + don't need to convert them to trees. + +* Future + + Profile-based optimization is something that we are very + interested in supporting. There are two possible usage + scenarios: + + * Based on the profile information gathered during + the execution of a program, hot methods can be compiled + with the highest level of optimizations, while bootstrap + code and cold methods can be compiled with the least set + of optimizations and placed in a discardable list. + + * Code reordering: this profile-based optimization would + only make sense for pre-compiled code. The profile + information is used to re-order the assembly code on disk + so that the code is placed on the disk in a way that + increments locality. + + This is the same principle under which SGI's cord program + works. + + The nature of the CIL allows the above optimizations to be + easy to implement and deploy. Since we live and define our + universe for these things, there are no interactions with + system tools required, nor upgrades on the underlying + infrastructure required. + + Instruction scheduling is important for certain kinds of + processors, and some of the framework exists today in our + register allocator and the instruction selector to cope with + this, but has not been finished. The instruction selection + would happen at the same time as local register allocation. \ No newline at end of file diff --git a/docs/opcode-decomp.txt b/docs/opcode-decomp.txt new file mode 100644 index 00000000000..48968d17ab9 --- /dev/null +++ b/docs/opcode-decomp.txt @@ -0,0 +1,113 @@ + +* How to handle complex IL opcodes in an arch-independent way + + Many IL opcodes are very simple: add, ldind etc. + Such opcodes can be implemented with a single cpu instruction + in most architectures (on some, a group of IL instructions + can be converted to a single cpu op). + There are many IL opcodes, though, that are more complex, but + can be expressed as a series of trees or a single tree of + simple operations. Such simple operations are architecture-independent. + It makes sense to decompose such complex IL instructions in their + simpler equivalent so that we gain in several ways: + *) porting effort is easier, because only the simple instructions + need to be implemented in arch-specific code + *) we could apply BURG rules to the trees and do pattern matching + on them to optimize the expressions according to the host cpu + + The issue is: where do we do such conversion from coarse opcodes to + simple expressions? + +* Doing the conversion in method_to_ir () + + Some of these conversions can certainly be done in method_to_ir (), + but it's not always easy to decide which are better done there and + which in a different pass. + For example, let's take ldlen: in the mono implementation, ldlen + can be simply implemented with a load from a fixed position in the + array object: + + len = [reg + maxlen_offset] + + However, ldlen carries also semantics information: the result is the + length of the array, and since in the CLR arrays are of fixed size, + this information can be useful to later do bounds check removal. + If we convert this opcode in method_to_ir () we lost some useful + information for further optimizations. + + In some other ways, decomposing an opcode in method_to_ir() may + allow for better optimizations later on (need to come up with an + example here ...). + +* Doing the conversion in inssel.brg + + Some conversion may be done inside the burg rules: this has the + disadvantage that the instruction selector is not run again on + the resulting expression tree and we could miss some optimization + (this is what effectively happens with the coarse opcodes in the old + jit). This may also interfere with an efficient local register allocator. + It may be possible to add an extension in monoburg that allows a rule + such as: + + recheck: LDLEN (reg) { + create an expression tree representing LDLEN + and return it + } + + When the monoburg label process gets back a recheck, it will run + the labeling again on the resulting expression tree. + If this is possible at all (and in an efficient way) is a + question for dietmar:-) + It should be noted, though, that this may not always work, since + some complex IL opcodes may require a series of expression trees + and handling such cases in monoburg could become quite hairy. + For example, think of opcode that need to do multiple actions on the + same object: this basically means a DUP... + On the other end, if a complex opcode needs a DUP, monoburg doesn't + actually need to create trees if it emits the instructions in + the correct sequence and maintains the right values in the registers + (usually the values that need a DUP are not changed...). How + this integrates with the current register allocator is not clear, since + that assigns registers based on the rule, but the instructions emitted + by the rules may be different (this already happens with the current JIT + where a MULT is replaced with lea etc...). + +* Doing it in a separate pass. + + Doing the conversion in a separate pass over the instructions + is another alternative. This can be done right after method_to_ir () + or after the SSA pass (since the IR after the SSA pass should look + almost like the IR we get back from method_to_ir ()). + + This has the following advantages: + *) monoburg will handle only the simple opcodes (makes porting easier) + *) the instruction selection will be run on all the additional trees + *) it's easier to support coarse opcodes that produce multiple expression + trees (and apply the monoburg selector on all of them) + *) the SSA optimizer will see the original opcodes and will be able to use + the semantic info associated with them + + The disadvantage is that this is a separate pass on the code and + it takes time (how much has not been measured yet, though). + + With this approach, we may also be able to have C implementations + of some of the opcodes: this pass would insert a function call to + the C implementation (for example in the cases when first porting + to a new arch and implemenating some stuff may be too hard in asm). + +* Extended basic blocks + + IL code needs a lot of checks, bounds checks, overflow checks, + type checks and so on. This potentially increases by a lot + the number of basic blocks in a control flow graph. However, + all such blocks end up with a throw opcode that gives control to the + exception handling mechanism. + After method_to_ir () a MonoBasicBlock can be considered a sort + of extended basic block where the additional exits don't point + to basic blocks in the same procedure (at least when the method + doesn't have exception tables). + We need to make sure the passes following method_to_ir () can cope + with such kinds of extended basic blocks (especially the passes + that we need to apply to all the methods: as a start, we could + skip SSA optimizations for methods with exception clauses...) + diff --git a/mono/mini/.cvsignore b/mono/mini/.cvsignore new file mode 100644 index 00000000000..6ba3086dfcb --- /dev/null +++ b/mono/mini/.cvsignore @@ -0,0 +1,5 @@ +mini +mono-debugger-mini-wrapper +.libs +*.o +test.exe diff --git a/mono/mini/README b/mono/mini/README new file mode 100644 index 00000000000..7a64e0aae0e --- /dev/null +++ b/mono/mini/README @@ -0,0 +1 @@ +Mini is the new JIT compiler for Mono. diff --git a/mono/mini/TODO b/mono/mini/TODO new file mode 100644 index 00000000000..a04a7ca85a6 --- /dev/null +++ b/mono/mini/TODO @@ -0,0 +1,13 @@ +* use a pool of MBState structures to speedup monoburg instead of using a + mempool. +* the decode tables in the burg-generated could use short instead of int + (this should save about 1 KB) +* track the use of ESP, so that we can avoid the x86_lea in the epilog + + +Other Ideas: + +* the ORP people avoids optimizations inside catch handlers - just to save + memory (for example allocation of strings - instead they allocate strings when + the code is executed (like the --shared option)). But there are only a few + functions using catch handlers, so I consider this a minor issue. \ No newline at end of file diff --git a/mono/mini/TestDriver.cs b/mono/mini/TestDriver.cs new file mode 100644 index 00000000000..7854bcb2816 --- /dev/null +++ b/mono/mini/TestDriver.cs @@ -0,0 +1,79 @@ +using System; +using System.Reflection; + + +public class TestDriver { + + static public int RunTests (Type type, string[] args) { + int failed = 0, ran = 0; + int result, expected, elen; + int i, j; + string name; + MethodInfo[] methods; + bool do_timings = false; + int tms = 0; + DateTime start, end = DateTime.Now; + + if (args != null && args.Length > 0) { + for (j = 0; j < args.Length; j++) { + if (args [j] == "--time") { + do_timings = true; + string[] new_args = new string [args.Length - 1]; + for (i = 0; i < j; ++i) + new_args [i] = args [i]; + j++; + for (; j < args.Length; ++i, ++j) + new_args [i] = args [j]; + args = new_args; + break; + } + } + } + methods = type.GetMethods (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static); + for (i = 0; i < methods.Length; ++i) { + name = methods [i].Name; + if (!name.StartsWith ("test_")) + continue; + if (args != null && args.Length > 0) { + bool found = false; + for (j = 0; j < args.Length; j++) { + if (name.EndsWith (args [j])) { + found = true; + break; + } + } + if (!found) + continue; + } + for (j = 5; j < name.Length; ++j) + if (!Char.IsDigit (name [j])) + break; + expected = Int32.Parse (name.Substring (5, j - 5)); + start = DateTime.Now; + result = (int)methods [i].Invoke (null, null); + if (do_timings) { + end = DateTime.Now; + long tdiff = end.Ticks - start.Ticks; + int mdiff = (int)tdiff/10000; + tms += mdiff; + Console.WriteLine ("{0} took {1} ms", name, mdiff); + } + ran++; + if (result != expected) { + failed++; + Console.WriteLine ("{0} failed: got {1}, expected {2}", name, result, expected); + } + } + + if (do_timings) { + Console.WriteLine ("Total ms: {0}", tms); + } + Console.WriteLine ("Regression tests: {0} ran, {1} failed in {2}", ran, failed, type); + //Console.WriteLine ("Regression tests: {0} ran, {1} failed in [{2}]{3}", ran, failed, type.Assembly.GetName().Name, type); + return failed; + } + static public int RunTests (Type type) { + return RunTests (type, null); + } +} + diff --git a/mono/mini/aot-compiler.txt b/mono/mini/aot-compiler.txt new file mode 100644 index 00000000000..ab1af90d965 --- /dev/null +++ b/mono/mini/aot-compiler.txt @@ -0,0 +1,44 @@ +Mono Ahead Of Time Compiler +=========================== + +The new mono JIT has sophisticated optimization features. It uses SSA and has a +pluggable architecture for further optimizations. This makes it possible and +efficient to use the JIT also for AOT compilation. + + +* file format: We use the native object format of the platform. That way it is + possible to reuse existing tools like objdump and the dynamic loader. All we + need is a working assembler, i.e. we write out a text file which is then + passed to gas (the gnu assembler) to generate the object file. + +* file names: we simply add ".so" to the generated file. For example: + basic.exe -> basic.exe.so + corlib.dll -> corlib.dll.so + +* staring the AOT compiler: mini --aot assembly_name + +The following things are saved in the object file: + +* version infos: + +* native code: this is labeled with method_XXXXXXXX: where XXXXXXXX is the + hexadecimal token number of the method. + +* additional informations needed by the runtime: For example we need to store + the code length and the exception tables. We also need a way to patch + constants only available at runtime (for example vtable and class + addresses). This is stored i a binary blob labeled method_info_XXXXXXXX: + +PROBLEMS: + +- all precompiled methods must be domain independent, or we add patch infos to + patch the target doamin. + +- the main problem is how to patch runtime related addresses, for example: + + - current application domain + - string objects loaded with LDSTR + - address of MonoClass data + - static field offsets + - method addreses + - virtual function and interface slots diff --git a/mono/mini/aot.c b/mono/mini/aot.c new file mode 100644 index 00000000000..e965b945680 --- /dev/null +++ b/mono/mini/aot.c @@ -0,0 +1,555 @@ +/* + * aot.c: mono Ahead of Time compiler + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include +#include +#include +#include + +#include /* for PAGESIZE */ +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif + +#include +#include +#include +#include +#include +#include + +#include "mini.h" + +#define ENCODE_TYPE_POS(t,l) (((t) << 24) | (l)) +#define DECODE_TYPE(v) ((v) >> 24) +#define DECODE_POS(v) ((v) & 0xffffff) + +gpointer +mono_aot_get_method (MonoMethod *method) +{ + MonoClass *klass = method->klass; + MonoAssembly *ass = klass->image->assembly; + MonoJumpInfo *patch_info = NULL; + GModule *module = ass->aot_module; + char *method_label, *info_label; + guint8 *code = NULL; + gpointer *info; + guint code_len, used_int_regs; + + if (!module) + return NULL; + + if (!method->token) + return NULL; + + g_assert (klass->inited); + + method_label = g_strdup_printf ("method_%08X", method->token); + + if (!g_module_symbol (module, method_label, (gpointer *)&code)) { + g_free (method_label); + return NULL; + } + + info_label = g_strdup_printf ("%s_patch_info", method_label); + if (!g_module_symbol (module, info_label, (gpointer *)&info)) { + g_free (method_label); + g_free (info_label); + return NULL; + } + + //printf ("FOUND AOT compiled code for %s %p %p\n", mono_method_full_name (method, TRUE), code, info); + + code_len = GPOINTER_TO_UINT (*((gpointer **)info)); + info++; + used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info)); + info++; + + if (info) { + MonoMemPool *mp = mono_mempool_new (); + MonoImage *image; + guint8 *page_start; + gpointer *table; + int pages; + int i, err; + + while (*info) { + MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo)); + gpointer *data = *((gpointer **)info); + info++; + ji->type = DECODE_TYPE (GPOINTER_TO_UINT (*info)); + ji->ip.i = DECODE_POS (GPOINTER_TO_UINT (*info)); + + switch (ji->type) { + case MONO_PATCH_INFO_CLASS: + image = mono_image_loaded_by_guid ((char *)data [1]); + g_assert (image); + ji->data.klass = mono_class_get (image, (guint32)data [0]); + g_assert (ji->data.klass); + mono_class_init (ji->data.klass); + break; + case MONO_PATCH_INFO_IMAGE: + ji->data.image = mono_image_loaded_by_guid ((char *)data); + g_assert (ji->data.image); + break; + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHODCONST: + image = mono_image_loaded_by_guid ((char *)data [1]); + g_assert (image); + ji->data.method = mono_get_method (image, (guint32)data [0], NULL); + g_assert (ji->data.method); + mono_class_init (ji->data.method->klass); + break; + case MONO_PATCH_INFO_FIELD: + image = mono_image_loaded_by_guid ((char *)data [1]); + g_assert (image); + ji->data.field = mono_field_from_token (image, (guint32)data [0], NULL); + mono_class_init (ji->data.field->parent); + g_assert (ji->data.field); + break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + ji->data.name = (char *)data; + g_assert (ji->data.name); + break; + case MONO_PATCH_INFO_SWITCH: + ji->table_size = (int)data [0]; + table = g_new (gpointer, ji->table_size); + ji->data.target = table; + for (i = 0; i < ji->table_size; i++) { + table [i] = data [i + 1]; + } + break; + case MONO_PATCH_INFO_R4: + case MONO_PATCH_INFO_R8: + ji->data.target = data; + break; + default: + g_warning ("unhandled type %d", ji->type); + g_assert_not_reached (); + } + + info++; + ji->next = patch_info; + patch_info = ji; + } + + /* disable write protection */ + page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1)); + pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE; + err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC); + g_assert (err == 0); + + mono_arch_patch_code (method, mono_root_domain, code, patch_info); + mono_mempool_destroy (mp); + } + + g_free (info_label); + g_free (method_label); + + { + MonoJitInfo *info; + info = mono_mempool_alloc0 (mono_root_domain->mp, sizeof (MonoJitInfo)); + info->code_size = code_len; + info->used_regs = used_int_regs; + info->method = method; + info->code_start = code; + mono_jit_info_table_add (mono_root_domain, info); + } + mono_jit_stats.methods_aot++; + + return code; +} + +#if 0 +static void +write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align) +{ + int i; + + fprintf (fp, ".globl %s\n", name); + fprintf (fp, ".data\n\t.align %d\n", align); + fprintf (fp, "\t.type %s,@object\n", name); + fprintf (fp, "\t.size %s,%d\n", name, size); + fprintf (fp, "%s:\n", name); + for (i = 0; i < size; i++) { + fprintf (fp, ".byte %d\n", buf [i]); + } + +} +#endif + +static void +write_string_symbol (FILE *fp, const char *name, char *value) +{ + fprintf (fp, ".globl %s\n", name); + fprintf (fp, ".data\n"); + fprintf (fp, "%s:\n", name); + fprintf (fp, "\t.string \"%s\"\n", value); +} + +static guint32 +mono_get_field_token (MonoClassField *field) +{ + MonoClass *klass = field->parent; + int i; + + for (i = 0; i < klass->field.count; ++i) { + if (field == &klass->fields [i]) + return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i); + } + + g_assert_not_reached (); + return 0; +} + +static char * +cond_emit_image_label (FILE *tmpfp, GHashTable *image_hash, MonoImage *image) +{ + char *label; + + if ((label = g_hash_table_lookup (image_hash, image))) + return label; + + label = g_strdup_printf ("image_patch_info_%p", image); + fprintf (tmpfp, "%s:\n", label); + fprintf (tmpfp, "\t.string \"%s\"\n", image->guid); + + g_hash_table_insert (image_hash, image, label); + + return label; +} + +static char * +cond_emit_icall_label (FILE *tmpfp, GHashTable *hash, const char *icall_name) +{ + char *label; + + if ((label = g_hash_table_lookup (hash, icall_name))) + return label; + + label = g_strdup_printf ("icall_patch_info_%s", icall_name); + fprintf (tmpfp, "%s:\n", label); + fprintf (tmpfp, "\t.string \"%s\"\n", icall_name); + + g_hash_table_insert (hash, (gpointer)icall_name, label); + + return label; +} + +static char * +cond_emit_method_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info) +{ + MonoMethod *method = patch_info->data.method; + char *l1, *l2; + + if ((l1 = g_hash_table_lookup (hash, method))) + return l1; + + l2 = cond_emit_image_label (tmpfp, hash, method->klass->image); + fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + l1 = g_strdup_printf ("method_patch_info_%08x_%p", method->token, method); + fprintf (tmpfp, "%s:\n", l1); + fprintf (tmpfp, "\t.long 0x%08x\n", method->token); + g_assert (method->token); + fprintf (tmpfp, "\t.long %s\n", l2); + + g_hash_table_insert (hash, method, l1); + + return l1; +} + +static char * +cond_emit_klass_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info) +{ + MonoClass *klass = patch_info->data.klass; + char *l1, *l2; + + if ((l1 = g_hash_table_lookup (hash, klass))) + return l1; + + l2 = cond_emit_image_label (tmpfp, hash, klass->image); + fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + l1 = g_strdup_printf ("klass_patch_info_%08x_%p", klass->type_token, klass); + fprintf (tmpfp, "%s:\n", l1); + fprintf (tmpfp, "\t.long 0x%08x\n", klass->type_token); + g_assert (klass->type_token); + fprintf (tmpfp, "\t.long %s\n", l2); + + g_hash_table_insert (hash, klass, l1); + + return l1; +} + +static char * +cond_emit_field_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info) +{ + MonoClassField *field = patch_info->data.field; + char *l1, *l2; + guint token; + + if ((l1 = g_hash_table_lookup (hash, field))) + return l1; + + l2 = cond_emit_image_label (tmpfp, hash, field->parent->image); + fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + token = mono_get_field_token (field); + l1 = g_strdup_printf ("klass_patch_info_%08x_%p", token, field); + fprintf (tmpfp, "%s:\n", l1); + fprintf (tmpfp, "\t.long 0x%08x\n", token); + g_assert (token); + fprintf (tmpfp, "\t.long %s\n", l2); + + g_hash_table_insert (hash, field, l1); + + return l1; +} + +int +mono_compile_assembly (MonoAssembly *ass, guint32 opts) +{ + MonoCompile *cfg; + MonoImage *image = ass->image; + MonoMethod *method; + char *com, *tmpfname; + FILE *tmpfp; + int i, j; + guint8 *code, *mname; + int ccount = 0, mcount = 0, lmfcount = 0, ecount = 0, abscount = 0, wrappercount = 0, ocount = 0; + GHashTable *ref_hash; + int func_alignment = 16; + + printf ("Mono AOT compiler - compiling assembly %s\n", image->name); + + tmpfname = g_strdup_printf ("%s/mono_aot_%05d", P_tmpdir, getpid ()); + tmpfp = fopen (tmpfname, "w+"); + g_assert (tmpfp); + + ref_hash = g_hash_table_new (NULL, NULL); + + write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid); + + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoJumpInfo *patch_info; + gboolean skip; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method (image, token, NULL); + + /* fixme: maybe we can also precompile wrapper methods */ + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) { + //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE)); + continue; + } + + mcount++; + + /* fixme: we need to patch the IP for the LMF in that case */ + if (method->save_lmf) { + //printf ("Skip (needs lmf): %s\n", mono_method_full_name (method, TRUE)); + lmfcount++; + continue; + } + + /* fixme: add methods with exception tables */ + if (((MonoMethodNormal *)method)->header->num_clauses) { + //printf ("Skip (exceptions): %s\n", mono_method_full_name (method, TRUE)); + ecount++; + continue; + } + + //printf ("START: %s\n", mono_method_full_name (method, TRUE)); + //mono_compile_method (method); + + cfg = mini_method_compile (method, opts, mono_root_domain, 0); + g_assert (cfg); + + if (cfg->disable_aot) { + ocount++; + continue; + } + + skip = FALSE; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if (patch_info->type == MONO_PATCH_INFO_ABS) { + /* unable to handle this */ + //printf ("Skip (abs addr): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type); + skip = TRUE; + break; + } + } + + if (skip) { + abscount++; + continue; + } + + skip = FALSE; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if ((patch_info->type == MONO_PATCH_INFO_METHOD || + patch_info->type == MONO_PATCH_INFO_METHODCONST) && + patch_info->data.method->wrapper_type) { + /* unable to handle this */ + //printf ("Skip (wrapper call): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type); + skip = TRUE; + break; + } + } + + if (skip) { + wrappercount++; + continue; + } + + //printf ("Compile: %s\n", mono_method_full_name (method, TRUE)); + + code = cfg->native_code; + + fprintf (tmpfp, ".text\n"); + mname = g_strdup_printf ("method_%08X", token); + fprintf (tmpfp, "\t.align %d\n", func_alignment); + fprintf (tmpfp, ".globl %s\n", mname); + fprintf (tmpfp, "%s:\n", mname); + + for (j = 0; j < cfg->code_len; j++) + fprintf (tmpfp, ".byte %d\n", (unsigned int) code [j]); + + fprintf (tmpfp, ".data\n"); + + j = 0; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_LABEL: + case MONO_PATCH_INFO_BB: + /* relative jumps are no problem, there is no need to handle then here */ + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.target; + int k; + + fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); + fprintf (tmpfp, "\t.long %d\n", patch_info->table_size); + + for (k = 0; k < patch_info->table_size; k++) { + fprintf (tmpfp, "\t.long %d\n", (guint8 *)table [k] - code); + } + j++; + break; + } + case MONO_PATCH_INFO_INTERNAL_METHOD: + patch_info->data.name = cond_emit_icall_label (tmpfp, ref_hash, patch_info->data.name); + j++; + break; + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + patch_info->data.name = cond_emit_method_label (tmpfp, ref_hash, patch_info); + j++; + break; + case MONO_PATCH_INFO_FIELD: + patch_info->data.name = cond_emit_field_label (tmpfp, ref_hash, patch_info); + j++; + break; + case MONO_PATCH_INFO_CLASS: + patch_info->data.name = cond_emit_klass_label (tmpfp, ref_hash, patch_info); + j++; + break; + case MONO_PATCH_INFO_IMAGE: + patch_info->data.name = cond_emit_image_label (tmpfp, ref_hash, patch_info->data.image); + j++; + break; + case MONO_PATCH_INFO_R4: + fprintf (tmpfp, "\t.align 8\n"); + fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); + + j++; + break; + case MONO_PATCH_INFO_R8: + fprintf (tmpfp, "\t.align 8\n"); + fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1)); + + j++; + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); + } + } + + fprintf (tmpfp, ".globl %s_patch_info\n", mname); + fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + fprintf (tmpfp, "%s_patch_info:\n", mname); + + fprintf (tmpfp, "\t.long %d\n", cfg->code_len); + fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs); + + if (j) { + j = 0; + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_FIELD: + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_IMAGE: + fprintf (tmpfp, "\t.long %s\n", patch_info->data.name); + fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i)); + j++; + break; + case MONO_PATCH_INFO_SWITCH: + case MONO_PATCH_INFO_R4: + case MONO_PATCH_INFO_R8: + fprintf (tmpfp, "\t.long %s_patch_info_%d\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i)); + j++; + break; + case MONO_PATCH_INFO_LABEL: + case MONO_PATCH_INFO_BB: + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); + } + + } + } + /* NULL terminated array */ + fprintf (tmpfp, "\t.long 0\n"); + + /* fixme: save the rest of the required infos */ + + g_free (mname); + mono_destroy_compile (cfg); + + ccount++; + } + + fclose (tmpfp); + + com = g_strdup_printf ("as %s -o %s.o;ld -shared -o %s.so %s.o;rm %s.o;strip --strip-unneeded --discard-all %s.so", + tmpfname, tmpfname, image->name, tmpfname, tmpfname, image->name); + + printf ("Executing the native assembler now:\n%s\n", com); + system (com); + + printf ("Compile %d out of %d methods (%d%%)\n", ccount, mcount, (ccount*100)/mcount); + printf ("%d methods contains exception tables (%d%%)\n", ecount, (ecount*100)/mcount); + printf ("%d methods contains absolute addresses (%d%%)\n", abscount, (abscount*100)/mcount); + printf ("%d methods contains wrapper references (%d%%)\n", wrappercount, (wrappercount*100)/mcount); + printf ("%d methods contains lmf pointers (%d%%)\n", lmfcount, (lmfcount*100)/mcount); + printf ("%d methods has other problems (%d%%)\n", ocount, (ocount*100)/mcount); + //unlink (tmpfname); + + return 0; +} diff --git a/mono/mini/arrays.cs b/mono/mini/arrays.cs new file mode 100644 index 00000000000..201d994a589 --- /dev/null +++ b/mono/mini/arrays.cs @@ -0,0 +1,131 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_10_create () { + int[] a = new int [10]; + return a.Length; + } + + static int test_0_unset_value () { + int[] a = new int [10]; + return a [5]; + } + + static int test_3_set_value () { + int[] a = new int [10]; + a [5] = 3; + return a [5]; + } + + static int test_0_char_array_1 () { + int value = -30; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp[0] = digitLowerTable[-(value % 10)]; + return 0; + } + + static int test_0_char_array_2 () { + int value = 5; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp[0] = digitLowerTable[value % 10]; + return 0; + } + + static int test_0_char_array_3 () { + int value = -1; + char[] tmp = new char [20]; + char[] digitLowerTable = new char[16]; + tmp [0] = digitLowerTable[value & 15]; + return 0; + } + + unsafe static int test_0_byte_array () { + byte [] src = new byte [8]; + double ret; + byte *dst = (byte *)&ret; + int start = 0; + + dst[0] = src[4 + start]; + + return 0; + } + + public static int test_0_set_after_shift () { + int [] n = new int [1]; + int b = 16; + + n [0] = 100 + (1 << (16 - b)); + + if (n [0] != 101) + return 1; + + return 0; + } + + private Int32[] m_array = new int [10]; + + void setBit (int bitIndex, bool value) { + int index = bitIndex/32; + int shift = bitIndex%32; + + Int32 theBit = 1 << shift; + if (value) + m_array[index] |= theBit; + else + m_array[index] &= ~theBit; + } + + bool getBit (int bitIndex) { + int index = bitIndex/32; + int shift = bitIndex%32; + + Int32 theBit = m_array[index] & (1 << shift); + return (theBit == 0) ? false : true; + + } + + public static int test_1_bit_index () { + Tests t = new Tests (); + t.setBit (0, true); + t.setBit (3, true); + if (t.getBit (1)) + return 4; + if (!t.getBit (0)) + return 5; + if (!t.getBit (3)) + return 6; + return 1; + } + +} + diff --git a/mono/mini/basic-float.cs b/mono/mini/basic-float.cs new file mode 100644 index 00000000000..281175fd71a --- /dev/null +++ b/mono/mini/basic-float.cs @@ -0,0 +1,426 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_0_beq () { + double a = 2.0; + if (a != 2.0) + return 1; + return 0; + } + + static int test_0_bne_un () { + double a = 2.0; + if (a == 1.0) + return 1; + return 0; + } + + static int test_0_conv_r8 () { + double a = 2; + if (a != 2.0) + return 1; + return 0; + } + + static int test_0_conv_i4 () { + double a = 2.0; + int b = (int)a; + if (b != 2) + return 1; + return 0; + } + + static int test_5_add () { + double a = 2.0; + double b = 3.0; + return (int)(a + b); + } + + static int test_5_sub () { + double a = 8.0; + double b = 3.0; + return (int)(a - b); + } + + static int test_24_mul () { + double a = 8.0; + double b = 3.0; + return (int)(a * b); + } + + static int test_4_div () { + double a = 8.0; + double b = 2.0; + return (int)(a / b); + } + + static int test_2_rem () { + double a = 8.0; + double b = 3.0; + return (int)(a % b); + } + + static int test_2_neg () { + double a = -2.0; + return (int)(-a); + } + + static int test_46_float_spill () { + // we overflow the FP stack + double a = 1; + double b = 2; + double c = 3; + double d = 4; + double e = 5; + double f = 6; + double g = 7; + double h = 8; + double i = 9; + + return (int)(1.0 + (a + (b + (c + (d + (e + (f + (g + (h + i))))))))); + } + + static int test_4_long_cast () { + long a = 1000; + double d = (double)a; + long b = (long)d; + if (b != 1000) + return 0; + return 4; + } + + + static int test_15_float_cmp () { + double a = 2.0; + double b = 1.0; + int result = 0; + bool val; + + val = a == a; + if (!val) + return result; + result++; + + val = a < a; + if (val) + return result; + result++; + + val = a > a; + if (val) + return result; + result++; + + val = a <= a; + if (!val) + return result; + result++; + + val = a >= a; + if (!val) + return result; + result++; + + val = b == a; + if (val) + return result; + result++; + + val = b < a; + if (!val) + return result; + result++; + + val = b > a; + if (val) + return result; + result++; + + val = b <= a; + if (!val) + return result; + result++; + + val = b >= a; + if (val) + return result; + result++; + + val = a == b; + if (val) + return result; + result++; + + val = a < b; + if (val) + return result; + result++; + + val = a > b; + if (!val) + return result; + result++; + + val = a <= b; + if (val) + return result; + result++; + + val = a >= b; + if (!val) + return result; + result++; + + return result; + } + + static int test_15_float_cmp_un () { + double a = Double.NaN; + double b = 1.0; + int result = 0; + bool val; + + val = a == a; + if (val) + return result; + result++; + + val = a < a; + if (val) + return result; + result++; + + val = a > a; + if (val) + return result; + result++; + + val = a <= a; + if (val) + return result; + result++; + + val = a >= a; + if (val) + return result; + result++; + + val = b == a; + if (val) + return result; + result++; + + val = b < a; + if (val) + return result; + result++; + + val = b > a; + if (val) + return result; + result++; + + val = b <= a; + if (val) + return result; + result++; + + val = b >= a; + if (val) + return result; + result++; + + val = a == b; + if (val) + return result; + result++; + + val = a < b; + if (val) + return result; + result++; + + val = a > b; + if (val) + return result; + result++; + + val = a <= b; + if (val) + return result; + result++; + + val = a >= b; + if (val) + return result; + result++; + + return result; + } + + static int test_15_float_branch () { + double a = 2.0; + double b = 1.0; + int result = 0; + + if (!(a == a)) + return result; + result++; + + if (a < a) + return result; + result++; + + if (a > a) + return result; + result++; + + if (!(a <= a)) + return result; + result++; + + if (!(a >= a)) + return result; + result++; + + if (b == a) + return result; + result++; + + if (!(b < a)) + return result; + result++; + + if (b > a) + return result; + result++; + + if (!(b <= a)) + return result; + result++; + + if (b >= a) + return result; + result++; + + if (a == b) + return result; + result++; + + if (a < b) + return result; + result++; + + if (!(a > b)) + return result; + result++; + + if (a <= b) + return result; + result++; + + if (!(a >= b)) + return result; + result++; + + return result; + } + + static int test_15_float_branch_un () { + double a = Double.NaN; + double b = 1.0; + int result = 0; + + if (a == a) + return result; + result++; + + if (a < a) + return result; + result++; + + if (a > a) + return result; + result++; + + if (a <= a) + return result; + result++; + + if (a >= a) + return result; + result++; + + if (b == a) + return result; + result++; + + if (b < a) + return result; + result++; + + if (b > a) + return result; + result++; + + if (b <= a) + return result; + result++; + + if (b >= a) + return result; + result++; + + if (a == b) + return result; + result++; + + if (a < b) + return result; + result++; + + if (a > b) + return result; + result++; + + if (a <= b) + return result; + result++; + + if (a >= b) + return result; + result++; + + return result; + } + +} + diff --git a/mono/mini/basic-long.cs b/mono/mini/basic-long.cs new file mode 100644 index 00000000000..dcf32112a91 --- /dev/null +++ b/mono/mini/basic-long.cs @@ -0,0 +1,595 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_0_beq () { + long a = 0xffffffffff; + if (a != 0xffffffffff) + return 1; + return 0; + } + + static int test_0_bne_un () { + long a = 0xffffffffff; + if (a == 0xfffffffffe) + return 1; + return 0; + } + + static int test_0_ble () { + long a = 0xffffffffff; + if (a > 0xffffffffff) + return 1; + return 0; + } + + static int test_0_ble_un () { + ulong a = 0xffffffffff; + if (a > 0xffffffffff) + return 1; + return 0; + } + + static int test_0_bge () { + long a = 0xffffffffff; + if (a < 0xffffffffff) + return 1; + return 0; + } + + static int test_0_bge_un () { + ulong a = 0xffffffffff; + if (a < 0xffffffffff) + return 1; + return 0; + } + + static int test_0_blt () { + long a = 0xfffffffffe; + if (a >= 0xffffffffff) + return 1; + return 0; + } + + static int test_0_blt_un () { + ulong a = 0xfffffffffe; + if (a >= 0xffffffffff) + return 1; + return 0; + } + + static int test_0_bgt () { + long a = 0xffffffffff; + if (a <= 0xfffffffffe) + return 1; + return 0; + } + + static int test_0_conv_to_i4 () { + long a = 0; + + return (int)a; + } + static int test_0_conv_from_i4 () { + long a = 2; + if (a != 2) + return 1; + + int b = 2; + + if (a != b) + return 2; + return 0; + } + + static int test_0_conv_from_i4_negative () { + long a = -2; + if (a != -2) + return 1; + + int b = -2; + + if (a != b) + return 2; + return 0; + } + + /* + static int test_0_conv_from_r8 () { + double b = 2.0; + long a = (long)b; + + if (a != 2) + return 1; + return 0; + } + + static int test_0_conv_from_r4 () { + float b = 2.0F; + long a = (long)b; + + if (a != 2) + return 1; + return 0; + } + */ + + static int test_8_and () { + long a = 0xffffffffff; + long b = 8; + return (int)(a & b); + } + + static int test_8_and_imm () { + long a = 0xffffffffff; + return (int)(a & 8); + } + + static int test_10_or () { + long a = 8; + long b = 2; + return (int)(a | b); + } + + static int test_10_or_imm () { + long a = 8; + return (int)(a | 2); + } + + static int test_5_xor () { + long a = 7; + long b = 2; + return (int)(a ^ b); + } + + static int test_5_xor_imm () { + long a = 7; + return (int)(a ^ 2); + } + + static int test_5_add () { + long a = 2; + long b = 3; + return (int)(a + b); + } + + static int test_5_add_imm () { + long a = 2; + return (int)(a + 3); + } + + static int test_0_add_imm_no_inc () { + // we can't blindly convert an add x, 1 to an inc x + long a = 0x1ffffffff; + long c; + c = a + 2; + if (c == ((a + 1) + 1)) + return 0; + return 1; + } + + static int test_5_sub () { + long a = 8; + long b = 3; + return (int)(a - b); + } + + static int test_5_sub_imm () { + long a = 8; + return (int)(a - 3); + } + + static int test_2_neg () { + long a = -2; + return (int)(-a); + } + + static int test_0_shl () { + long a = 9; + int b = 1; + + if ((a >> b) != 4) + return 1; + + + return 0; + } + + static int test_1_rshift () + { + long a = 9; + int b = 1; + a = -9; + if ((a >> b) != -5) + return 0; + return 1; + } + + static int test_5_shift () + { + long a = 9; + int b = 1; + int count = 0; + + if ((a >> b) != 4) + return count; + count++; + + if ((a >> 63) != 0) + return count; + count++; + + if ((a << 1) != 18) + return count; + count++; + + if ((a << b) != 18) + return count; + count++; + + a = -9; + if ((a >> b) != -5) + return count; + count++; + + return count; + } + + static int test_1_simple_neg () { + long a = 9; + + if (-a != -9) + return 0; + return 1; + } + + static int test_2_compare () { + long a = 1; + long b = 1; + + if (a != b) + return 0; + return 2; + } + + static int test_9_alu () + { + long a = 9, b = 6; + int count = 0; + + if ((a + b) != 15) + return count; + count++; + + if ((a - b) != 3) + return count; + count++; + + if ((a & 8) != 8) + return count; + count++; + + if ((a | 2) != 11) + return count; + count++; + + if ((a * b) != 54) + return count; + count++; + + if ((a / 4) != 2) + return count; + count++; + + if ((a % 4) != 1) + return count; + count++; + + if (-a != -9) + return count; + count++; + + b = -1; + if (~b != 0) + return count; + count++; + + return count; + } + + static int test_24_mul () { + long a = 8; + long b = 3; + return (int)(a * b); + } + + static int test_24_mul_ovf () { + long a = 8; + long b = 3; + long res; + + checked { + res = a * b; + } + return (int)res; + } + + static int test_24_mul_un () { + ulong a = 8; + ulong b = 3; + return (int)(a * b); + } + + static int test_24_mul_ovf_un () { + ulong a = 8; + ulong b = 3; + ulong res; + + checked { + res = a * b; + } + return (int)res; + } + + static int test_4_divun () { + uint b = 12; + int a = 3; + return (int)(b / a); + } + + static int test_1431655764_bigdivun_imm () { + uint b = (uint)-2; + return (int)(b / 3); + } + + static int test_1431655764_bigdivun () { + uint b = (uint)-2; + int a = 3; + return (int)(b / a); + } + + static int test_1_remun () { + uint b = 13; + int a = 3; + return (int)(b % a); + } + + static int test_2_bigremun () { + uint b = (uint)-2; + int a = 3; + return (int)(b % a); + } + + static int test_0_ceq () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002; + + bool val = (a == b); // this should produce a ceq + if (!val) + return 1; + + val = (a == c); // this should produce a ceq + if (val) + return 2; + + val = (a == d); // this should produce a ceq + if (val) + return 3; + + return 0; + } + + static int test_0_clt () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002L; + long e = -1; + + bool val = (a < b); // this should produce a clt + if (val) + return 1; + + val = (a < c); // this should produce a clt + if (!val) + return 2; + + val = (c < a); // this should produce a clt + if (val) + return 3; + + val = (e < d); // this should produce a clt + if (!val) + return 4; + + val = (d < e); // this should produce a clt + if (val) + return 5; + + return 0; + } + + static int test_0_clt_un () { + ulong a = 2; + ulong b = 2; + ulong c = 3; + ulong d = 0xff00000002; + ulong e = 0xffffffffffffffff; + + bool val = (a < b); // this should produce a clt_un + if (val) + return 1; + + val = (a < c); // this should produce a clt_un + if (!val) + return 1; + + val = (d < e); // this should produce a clt_un + if (!val) + return 1; + + val = (e < d); // this should produce a clt_un + if (val) + return 1; + + return 0; + } + + static int test_0_cgt () { + long a = 2; + long b = 2; + long c = 3; + long d = 0xff00000002L; + long e = -1; + + bool val = (a > b); // this should produce a cgt + if (val) + return 1; + + val = (a > c); // this should produce a cgt + if (val) + return 2; + + val = (c > a); // this should produce a cgt + if (!val) + return 3; + + val = (e > d); // this should produce a cgt + if (val) + return 4; + + val = (d > e); // this should produce a cgt + if (!val) + return 5; + + return 0; + } + + static int test_0_cgt_un () { + ulong a = 2; + ulong b = 2; + ulong c = 3; + ulong d = 0xff00000002; + ulong e = 0xffffffffffffffff; + + bool val = (a > b); // this should produce a cgt_un + if (val) + return 1; + + val = (a > c); // this should produce a cgt_un + if (val) + return 1; + + val = (d > e); // this should produce a cgt_un + if (val) + return 1; + + val = (e > d); // this should produce a cgt_un + if (!val) + return 1; + + return 0; + } + + static long return_5low () { + return 5; + } + + static long return_5high () { + return 0x500000000; + } + + static int test_3_long_ret () { + long val = return_5low (); + return (int) (val - 2); + } + + static int test_1_long_ret2 () { + long val = return_5high (); + if (val > 0xffffffff) + return 1; + return 0; + } + + static int test_3_byte_cast () { + ulong val = 0xff00ff00f0f0f0f0; + byte b; + b = (byte) (val & 0xFF); + if (b != 0xf0) + return 1; + return 3; + } + + static int test_4_ushort_cast () { + ulong val = 0xff00ff00f0f0f0f0; + ushort b; + b = (ushort) (val & 0xFFFF); + if (b != 0xf0f0) + return 1; + return 4; + } + + static int test_500_mul_div () { + long val = 1000; + long exp = 10; + long maxexp = 20; + long res = val * exp / maxexp; + + return (int)res; + } + + static long position = 0; + + static int test_4_static_inc_long () { + + int count = 4; + + position = 0; + + position += count; + + return (int)position; + } + + static void doit (double value, out long m) { + m = (long) value; + } + + static int test_0_ftol_clobber () { + long m; + doit (1.3, out m); + if (m != 1) + return 2; + return 0; + } +} + diff --git a/mono/mini/basic.cs b/mono/mini/basic.cs new file mode 100644 index 00000000000..81d4b72c8ab --- /dev/null +++ b/mono/mini/basic.cs @@ -0,0 +1,622 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_0_return () { + return 0; + } + + static int test_100000_return_large () { + return 100000; + } + + static int test_1_load_bool () { + bool a = true; + return a? 1: 0; + } + + static int test_0_load_bool_false () { + bool a = false; + return a? 1: 0; + } + + static int test_200_load_byte () { + byte a = 200; + return a; + } + + static int test_100_load_sbyte () { + sbyte a = 100; + return a; + } + + static int test_200_load_short () { + short a = 200; + return a; + } + + static int test_100_load_ushort () { + ushort a = 100; + return a; + } + + static int test_3_add_simple () { + int a = 1; + int b = 2; + return a + b; + } + + static int test_3_add_imm () { + int a = 1; + return a + 2; + } + + static int test_13407573_add_largeimm () { + int a = 1; + return a + 13407572; + } + + static int test_1_sub_simple () { + int a = 1; + int b = 2; + return b - a; + } + + static int test_1_sub_simple_un () { + uint a = 1; + uint b = 2; + return (int)(b - a); + } + + static int test_1_sub_imm () { + int b = 2; + return b - 1; + } + + static int test_0_sub_inv_imm () { + int b = 2; + return 2 - b; + } + + static int test_2_and () { + int b = 2; + int a = 3; + return b & a; + } + + static int test_0_and_imm () { + int b = 2; + return b & 0x10; + } + + static int test_0_and_large_imm () { + int b = 2; + return b & 0x10000000; + } + + static int test_2_div () { + int b = 6; + int a = 3; + return b / a; + } + + static int test_4_div_imm () { + int b = 12; + return b / 3; + } + + static int test_4_divun_imm () { + uint b = 12; + return (int)(b / 3); + } + + static int test_0_div_fold () { + int b = -1; + return b / 2; + } + + static int test_1_remun_imm () { + uint b = 13; + return (int)(b % 3); + } + + static int test_2_bigremun_imm () { + uint b = (uint)-2; + return (int)(b % 3); + } + + static int test_2_rem () { + int b = 5; + int a = 3; + return b % a; + } + + static int test_4_rem_imm () { + int b = 12; + return b % 8; + } + + static int test_9_mul () { + int b = 3; + int a = 3; + return b * a; + } + + static int test_15_mul_imm () { + int b = 3; + return b * 5; + } + + static int test_24_mul () { + int a = 3; + int b = 8; + int res; + + res = a * b; + + return res; + } + + static int test_24_mul_ovf () { + int a = 3; + int b = 8; + int res; + + checked { + res = a * b; + } + + return res; + } + + static int test_24_mul_un () { + uint a = 3; + uint b = 8; + uint res; + + res = a * b; + + return (int)res; + } + + static int test_24_mul_ovf_un () { + uint a = 3; + uint b = 8; + uint res; + + checked { + res = a * b; + } + + return (int)res; + } + + static int test_3_or () { + int b = 2; + int a = 3; + return b | a; + } + + static int test_3_or_un () { + uint b = 2; + uint a = 3; + return (int)(b | a); + } + + static int test_3_or_short_un () { + ushort b = 2; + ushort a = 3; + return (int)(b | a); + } + + static int test_18_or_imm () { + int b = 2; + return b | 0x10; + } + + static int test_268435458_or_large_imm () { + int b = 2; + return b | 0x10000000; + } + + static int test_1_neg () { + int b = -2; + b++; + return -b; + } + + static int test_2_not () { + int b = ~2; + b = ~b; + return b; + } + + static int test_16_shift () { + int b = 2; + int a = 3; + return b << a; + } + + static int test_16_shift_add () { + int b = 2; + int a = 3; + int c = 0; + return b << (a + c); + } + + static int test_16_shift_add2 () { + int b = 2; + int a = 3; + int c = 0; + return (b + c) << a; + } + + static int test_16_shift_imm () { + int b = 2; + return b << 3; + } + + static int test_12_shift_imm_inv () { + int b = 2; + return 3 << 2; + } + + static int test_12_shift_imm_inv_sbyte () { + sbyte b = 2; + return 3 << 2; + } + + static int test_1_rshift_imm () { + int b = 8; + return b >> 3; + } + + static int test_2_unrshift_imm () { + uint b = 16; + return (int)(b >> 3); + } + + static int test_0_bigunrshift_imm () { + uint b = (uint)-1; + b = b >> 1; + if (b != 0x7fffffff) + return 1; + return 0; + } + + static int test_0_bigrshift_imm () { + int b = -1; + b = b >> 1; + if (b != -1) + return 1; + return 0; + } + + static int test_1_rshift () { + int b = 8; + int a = 3; + return b >> a; + } + + static int test_2_unrshift () { + uint b = 16; + int a = 3; + return (int)(b >> a); + } + + static int test_0_bigunrshift () { + uint b = (uint)-1; + int a = 1; + b = b >> a; + if (b != 0x7fffffff) + return 1; + return 0; + } + + static int test_0_bigrshift () { + int b = -1; + int a = 1; + b = b >> a; + if (b != -1) + return 1; + return 0; + } + + static int test_2_cond () { + int b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + static int test_2_cond_short () { + short b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + static int test_2_cond_sbyte () { + sbyte b = 2, a = 3, c; + if (a == b) + return 0; + return 2; + } + + static int test_6_cascade_cond () { + int b = 2, a = 3, c; + if (a == b) + return 0; + else if (b > a) + return 1; + else if (b != b) + return 2; + else { + c = 1; + } + return a + b + c; + } + + static int test_6_cascade_short () { + short b = 2, a = 3, c; + if (a == b) + return 0; + else if (b > a) + return 1; + else if (b != b) + return 2; + else { + c = 1; + } + return a + b + c; + } + + static int test_15_for_loop () { + int i; + for (i = 0; i < 15; ++i) { + } + return i; + } + + static int test_11_nested_for_loop () { + int i, j = 0; /* mcs bug here if j not set */ + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + return i - j; + } + + static int test_11_several_nested_for_loops () { + int i, j = 0; /* mcs bug here if j not set */ + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + i = j = 0; + for (i = 0; i < 15; ++i) { + for (j = 200; j >= 5; --j) ; + } + return i - j; + } + + static int test_0_conv_ovf_i1 () { + int c; + + //for (int j = 0; j < 10000000; j++) + checked { + c = 127; + sbyte b = (sbyte)c; + c = -128; + b = (sbyte)c; + } + + return 0; + } + + static int test_0_conv_ovf_i1_un () { + uint c; + + checked { + c = 127; + sbyte b = (sbyte)c; + } + + return 0; + } + + static int test_0_conv_ovf_i2 () { + int c; + + checked { + c = 32767; + Int16 b = (Int16)c; + c = -32768; + b = (Int16)c; + } + + return 0; + } + + static int test_0_conv_ovf_i2_un () { + uint c; + + checked { + c = 32767; + Int16 b = (Int16)c; + } + + return 0; + } + + static int test_0_conv_ovf_u2 () { + int c; + + checked { + c = 65535; + UInt16 b = (UInt16)c; + } + + return 0; + } + + static int test_0_conv_ovf_u2_un () { + uint c; + + checked { + c = 65535; + UInt16 b = (UInt16)c; + } + + return 0; + } + + static int test_0_conv_ovf_u4 () { + int c; + + checked { + c = 0x7fffffff; + uint b = (uint)c; + } + + return 0; + } + + static int test_0_bool () { + bool val = true; + if (val) + return 0; + return 1; + } + + static int test_1_bool_inverted () { + bool val = true; + if (!val) + return 0; + return 1; + } + + static int test_1_bool_assign () { + bool val = true; + val = !val; // this should produce a ceq + if (val) + return 0; + return 1; + } + + static int test_1_bool_multi () { + bool val = true; + bool val2 = true; + val = !val; + if ((val && !val2) && (!val2 && val)) + return 0; + return 1; + } + + static int test_16_spill () { + int a = 1; + int b = 2; + int c = 3; + int d = 4; + int e = 5; + + return (1 + (a + (b + (c + (d + e))))); + } + + static int test_1_switch () { + int n = 0; + + switch (n) { + case 0: return 1; + case 1: return 2; + case -1: return 3; + default: + return 4; + } + return 1; + } + + + static int test_0_while_loop_1 () { + + int value = 255; + + do { + value = value >> 4; + } while (value != 0); + + return 0; + } + + static int test_0_while_loop_2 () { + int value = 255; + int position = 5; + + do { + value = value >> 4; + } while (value != 0 && position > 1); + + return 0; + } + + static int test_0_char_conv () { + int i = 1; + + char tc = (char) ('0' + i); + + if (tc != '1') + return 1; + + return 0; + } + + static int test_3_shift_regalloc () { + int shift = 8; + int orig = 1; + byte value = 0xfe; + + orig &= ~(0xff << shift); + orig |= value << shift; + + if (orig == 0xfe01) + return 3; + return 0; + } + + enum E {A, B}; + + static int test_2_optimize_branches () { + switch (E.A) { + case E.A: + if (E.A == E.B) { + } + break; + } + return 2; + } +} diff --git a/mono/mini/bench.cs b/mono/mini/bench.cs new file mode 100644 index 00000000000..c6a23620c56 --- /dev/null +++ b/mono/mini/bench.cs @@ -0,0 +1,244 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } + + static public int test_0_many_nested_loops () { + // we do the loop a few times otherwise it's too fast + for (int i = 0; i < 5; ++i) { + int n = 16; + int x = 0; + int a = n; + while (a-- != 0) { + int b = n; + while (b-- != 0) { + int c = n; + while (c-- != 0) { + int d = n; + while (d-- != 0) { + int e = n; + while (e-- != 0) { + int f = n; + while (f-- != 0) { + x++; + } + } + } + } + } + } + if (x != 16777216) + return 1; + } + return 0; + } + + public static int test_0_logic_run () + { + // GPL: Copyright (C) 2001 Southern Storm Software, Pty Ltd. + int iter, i = 0; + + while (i++ < 10) { + // Initialize. + bool flag1 = true; + bool flag2 = true; + bool flag3 = true; + bool flag4 = true; + bool flag5 = true; + bool flag6 = true; + bool flag7 = true; + bool flag8 = true; + bool flag9 = true; + bool flag10 = true; + bool flag11 = true; + bool flag12 = true; + bool flag13 = true; + + // First set of tests. + for(iter = 0; iter < 2000000; ++iter) { + if((flag1 || flag2) && (flag3 || flag4) && + (flag5 || flag6 || flag7)) + { + flag8 = !flag8; + flag9 = !flag9; + flag10 = !flag10; + flag11 = !flag11; + flag12 = !flag12; + flag13 = !flag13; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + flag1 = !flag1; + flag2 = !flag2; + flag3 = !flag3; + flag4 = !flag4; + flag5 = !flag5; + flag6 = !flag6; + } + } + } + return 0; + } + static public int test_1028_sieve () { + //int NUM = ((argc == 2) ? atoi(argv[1]) : 1); + int NUM = 2000; + byte[] flags = new byte[8192 + 1]; + int i, k; + int count = 0; + + while (NUM-- != 0) { + count = 0; + for (i=2; i <= 8192; i++) { + flags[i] = 1; + } + for (i=2; i <= 8192; i++) { + if (flags[i] != 0) { + // remove all multiples of prime: i + for (k=i+i; k <= 8192; k+=i) { + flags[k] = 0; + } + count++; + } + } + } + //printf("Count: %d\n", count); + return(count); + } + + public static int fib (int n) { + if (n < 2) + return 1; + return fib(n-2)+fib(n-1); + } + + public static int test_3524578_fib () { + for (int i = 0; i < 10; i++) + fib (32); + + return fib (32); + } + + private static ulong numMoves; + + static void movetower (int disc, int from, int to, int use) { + if (disc > 0) { + numMoves++; + movetower (disc-1, from, use, to); + movetower (disc-1, use, to, from); + } + } + + public static int test_0_hanoi () { + int iterations = 5000; + int numdiscs = 12; + + numMoves = 0; + while (iterations > 0) { + iterations--; + movetower (numdiscs, 1, 3, 2); + } + if (numMoves != 20475000) + return 1; + return 0; + } + + public static int test_0_castclass () { + object a = "a"; + + for (int i = 0; i < 100000000; i++) { + string b = (string)a; + if ((object)a != (object)b) + return 1; + } + return 0; + } + + public static int test_23005000_float () { + double a, b, c, d; + bool val; + int loops = 0; + a = 0.0; + b = 0.0001; + c = 2300.5; + d = 1000.0; + + while (a < c) { + if (a == d) + b *= 2; + a += b; + val = b >= c; + if (val) break; + loops++; + } + return loops; + } + + /* + /// Gaussian blur of a generated grayscale picture + private int test_0_blur(int size) { + const int num = 5; // Number of time to blur + byte[,] arr1 = new byte[size, size]; + byte[,] arr2 = new byte[size, size]; + + int iterations = 1; + + while(iterations-- > 0) { + + // Draw fake picture + for(int i = 0; i < size; i++) { + for(int j = 0; j < size; j++) { + arr1[i, j] = (byte) (i%255); + } + } + + for(int n = 0; n < num; n++) { // num rounds of blurring + for(int i = 3; i < size-3; i++) // vertical blur arr1 -> arr2 + for(int j = 0; j < size; j++) + arr2[i, j] = (byte)((arr1[i-3, j] + arr1[i+3, j] + + 6*(arr1[i-2, j]+arr1[i+2, j]) + + 15*(arr1[i-1, j]+arr1[i+1, j]) + + 20*arr1[i, j] + 32)>>6); + + for(int j = 3; j < size-3; j++) // horizontal blur arr1 -> arr2 + for(int i = 0; i < size; i++) + arr1[i, j] = (byte)((arr2[i, j-3] + arr2[i, j+3] + + 6*(arr2[i, j-2]+arr2[i, j+2]) + + 15*(arr2[i, j-1]+arr2[i, j+1]) + + 20*arr2[i, j] + 32)>>6); + } + } + + return 0; + } + */ +} + diff --git a/mono/mini/cfold.c b/mono/mini/cfold.c new file mode 100644 index 00000000000..86471e810e9 --- /dev/null +++ b/mono/mini/cfold.c @@ -0,0 +1,216 @@ +/* + * cfold.c: Constant folding support + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. http://www.ximian.com + */ +#include "mini.h" + +static int +is_power_of_two (guint32 val) +{ + int i, j, k; + + for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) { + if (val & j) + break; + } + if (i == 32 || val & k) + return -1; + return i; +} + +#define FOLD_BINOP(name,op) \ + case name: \ + if (inst->inst_i0->opcode != OP_ICONST) \ + return; \ + if (inst->inst_i1->opcode == OP_ICONST) { \ + inst->opcode = OP_ICONST; \ + inst->inst_c0 = inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0; \ + } \ + return; + +/* + * We try to put constants on the left side of a commutative operation + * because it reduces register pressure and it matches the usual cpu + * instructions with immediates. + */ +#define FOLD_BINOPCOMM(name,op) \ + case name: \ + if (inst->inst_i0->opcode == OP_ICONST) {\ + if (inst->inst_i1->opcode == OP_ICONST) { \ + inst->opcode = OP_ICONST; \ + inst->inst_c0 = inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0; \ + return; \ + } else { \ + MonoInst *tmp = inst->inst_i0; \ + inst->inst_i0 = inst->inst_i1; \ + inst->inst_i1 = tmp; \ + } \ + } \ + if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_MUL) { \ + int power2; \ + if (inst->inst_i1->inst_c0 == 1) { \ + *inst = *(inst->inst_i0); \ + return; \ + } else if (inst->inst_i1->inst_c0 == -1) { \ + inst->opcode = CEE_NEG; \ + return; \ + } \ + power2 = is_power_of_two (inst->inst_i1->inst_c0); \ + if (power2 < 0) return; \ + inst->opcode = CEE_SHL; \ + inst->inst_i1->inst_c0 = power2; \ + } \ + return; + +#define FOLD_BINOPZ(name,op,cast) \ + case name: \ + if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_REM_UN && inst->inst_i1->inst_c0 == 2) { \ + inst->opcode = CEE_AND; \ + inst->inst_i1->inst_c0 = 1; \ + return; \ + } \ + if (inst->inst_i1->opcode == OP_ICONST) { \ + /* let the runtime throw the exception. */ \ + if (!inst->inst_i1->inst_c0) return; \ + if (inst->inst_i0->opcode == OP_ICONST) { \ + inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0; \ + inst->opcode = OP_ICONST; \ + } else { \ + int power2 = is_power_of_two (inst->inst_i1->inst_c0); \ + if (power2 < 0) return; \ + if (inst->opcode == CEE_REM_UN) { \ + inst->opcode = CEE_AND; \ + inst->inst_i1->inst_c0 = (1 << power2) - 1; \ + } else if (inst->opcode == CEE_DIV_UN) { \ + inst->opcode = CEE_SHR_UN; \ + inst->inst_i1->inst_c0 = power2; \ + } \ + } \ + } \ + return; + +#define FOLD_BINOPA(name,op,cast) \ + case name: \ + if (inst->inst_i0->opcode != OP_ICONST) \ + return; \ + if (inst->inst_i1->opcode == OP_ICONST) { \ + inst->opcode = OP_ICONST; \ + inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0; \ + } \ + return; + +#define FOLD_CXX(name,op,cast) \ + case name: \ + if (inst->inst_i0->opcode != OP_COMPARE) \ + return; \ + if (inst->inst_i0->inst_i0->opcode != OP_ICONST) \ + return; \ + if (inst->inst_i0->inst_i1->opcode == OP_ICONST) { \ + inst->opcode = OP_ICONST; \ + inst->inst_c0 = (cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0; \ + } \ + return; + +#define FOLD_UNOP(name,op) \ + case name: \ + if (inst->inst_i0->opcode != OP_ICONST) \ + return; \ + inst->opcode = OP_ICONST; \ + inst->inst_c0 = op inst->inst_i0->inst_c0; \ + return; + +#define FOLD_BRBINOP(name,op,cast) \ + case name: \ + if (inst->inst_i0->opcode != OP_COMPARE) \ + return; \ + if (inst->inst_i0->inst_i0->opcode != OP_ICONST) \ + return; \ + if (inst->inst_i0->inst_i1->opcode == OP_ICONST) { \ + if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0) \ + inst->opcode = CEE_BR; \ + else \ + inst->opcode = CEE_NOP; \ + } \ + return; + +/* + * Helper function to do constant expression evaluation. + * We do constant folding of integers only, FP stuff is much more tricky, + * int64 probably not worth it. + */ +void +mono_constant_fold_inst (MonoInst *inst, gpointer data) +{ + switch (inst->opcode) { + + /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */ + /*FOLD_BRBINOP (CEE_BEQ,==,gint32) + FOLD_BRBINOP (CEE_BGE,>=,gint32) + FOLD_BRBINOP (CEE_BGT,>,gint32) + FOLD_BRBINOP (CEE_BLE,<=,gint32) + FOLD_BRBINOP (CEE_BLT,<,gint32) + FOLD_BRBINOP (CEE_BNE_UN,!=,guint32) + FOLD_BRBINOP (CEE_BGE_UN,>=,guint32) + FOLD_BRBINOP (CEE_BGT_UN,>,guint32) + FOLD_BRBINOP (CEE_BLE_UN,<=,guint32) + FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/ + + FOLD_BINOPCOMM (CEE_MUL,*) + + FOLD_BINOPCOMM (CEE_ADD,+) + FOLD_BINOP (CEE_SUB,-) + FOLD_BINOPZ (CEE_DIV,/,gint32) + FOLD_BINOPZ (CEE_DIV_UN,/,guint32) + FOLD_BINOPZ (CEE_REM,%,gint32) + FOLD_BINOPZ (CEE_REM_UN,%,guint32) + FOLD_BINOPCOMM (CEE_AND,&) + FOLD_BINOPCOMM (CEE_OR,|) + FOLD_BINOPCOMM (CEE_XOR,^) + FOLD_BINOP (CEE_SHL,<<) + FOLD_BINOP (CEE_SHR,>>) + case CEE_SHR_UN: + if (inst->inst_i0->opcode != OP_ICONST) + return; + if (inst->inst_i1->opcode == OP_ICONST) { + inst->opcode = OP_ICONST; + inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0; + } + return; + FOLD_UNOP (CEE_NEG,-) + FOLD_UNOP (CEE_NOT,~) + FOLD_CXX (OP_CEQ,==,gint32) + FOLD_CXX (OP_CGT,>,gint32) + FOLD_CXX (OP_CGT_UN,>,guint32) + FOLD_CXX (OP_CLT,<,gint32) + FOLD_CXX (OP_CLT_UN,<,guint32) + /* we should be able to handle isinst and castclass as well */ + case CEE_ISINST: + case CEE_CASTCLASS: + /* + * TODO: + * conv.* opcodes. + * *ovf* opcodes? I'ts slow and hard to do in C. + * switch can be replaced by a simple jump + */ + default: + return; + } +} + +void +mono_constant_fold (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *ins; + for (ins = bb->code; ins; ins = ins->next) + mono_inst_foreach (ins, mono_constant_fold_inst, NULL); + } +} + diff --git a/mono/mini/cprop.c b/mono/mini/cprop.c new file mode 100644 index 00000000000..c24eb0f9099 --- /dev/null +++ b/mono/mini/cprop.c @@ -0,0 +1,130 @@ +/* + * cprop.c: Constant propagation. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ + +/* dumb list-based implementation for now */ + +typedef struct _MiniACP MiniACP; + +struct _MiniACP { + MiniACP *next; + short dreg; + short sreg; + int type; +}; + +static int +copy_value (MiniACP *acp, int reg, int type) +{ + MiniACP *tmp = acp; + + //g_print ("search reg %d '%c'\n", reg, type); + while (tmp) { + // g_print ("considering dreg %d, sreg %d '%c'\n", tmp->dreg, tmp->sreg, tmp->type); + if (tmp->type == type && tmp->dreg == reg) { + // g_print ("copy prop from %d to %d\n", tmp->sreg, tmp->dreg); + return tmp->sreg; + } + tmp = tmp->next; + } + return reg; +} + +static MiniACP* +remove_acp (MiniACP *acp, int reg, int type) +{ + MiniACP *tmp = acp; + MiniACP *prev = NULL; + + while (tmp) { + if (tmp->type == type && (tmp->dreg == reg || tmp->sreg == reg)) { + if (prev) + prev->next = tmp->next; + else + acp = tmp->next; + } else { + prev = tmp; + } + tmp = tmp->next; + } + return acp; +} + +static MiniACP* +add_acp (MonoCompile *cfg, MiniACP *acp, int sreg, int dreg, int type) +{ + MiniACP *newacp = mono_mempool_alloc (cfg->mempool, sizeof (MiniACP));; + newacp->type = type; + newacp->sreg = sreg; + newacp->dreg = dreg; + + newacp->next = acp; + return newacp; +} + +static void +local_copy_prop (MonoCompile *cfg, MonoInst *code) +{ + MiniACP *acp = NULL; + const char *spec; + MonoInst *ins = code; + + //g_print ("starting BB\n"); + + while (ins) { + spec = ins_spec [ins->opcode]; + //print_ins (0, ins); + + if (spec [MONO_INST_CLOB] != 's' && spec [MONO_INST_CLOB] != '1' && spec [MONO_INST_CLOB] != 'd' && spec [MONO_INST_CLOB] != 'a' && spec [MONO_INST_CLOB] != 'c') { + if (spec [MONO_INST_SRC1] == 'f') { + ins->sreg1 = copy_value (acp, ins->sreg1, 'f'); + } else if (spec [MONO_INST_SRC1]) { + ins->sreg1 = copy_value (acp, ins->sreg1, 'i'); + } + } + + if (spec [MONO_INST_CLOB] != 's') { + if (spec [MONO_INST_SRC2] == 'f') { + ins->sreg2 = copy_value (acp, ins->sreg2, 'f'); + } else if (spec [MONO_INST_SRC2]) { + ins->sreg2 = copy_value (acp, ins->sreg2, 'i'); + } + } + + /* invalidate pairs */ + if (spec [MONO_INST_DEST] == 'f') { + acp = remove_acp (acp, ins->dreg, 'f'); + } else if (spec [MONO_INST_DEST]) { + acp = remove_acp (acp, ins->dreg, 'i'); + } + + /* insert pairs */ + /* + * Later copy-propagate also immediate values and memory stores. + */ + if (ins->opcode == OP_MOVE && ins->sreg1 != ins->dreg) { + // g_print ("added acp of %d <- %d '%c'\n", ins->dreg, ins->sreg1, spec [MONO_INST_SRC1]); + acp = add_acp (cfg, acp, ins->sreg1, ins->dreg, spec [MONO_INST_SRC1]); + } + + if (spec [MONO_INST_CLOB] != 'c') { + /* this is a call, invalidate all the pairs */ + acp = NULL; + } else if ((ins->opcode) == CEE_BR || (ins->opcode >= CEE_BEQ && ins->opcode <= CEE_BLT) || + (ins->opcode >= CEE_BNE_UN && ins->opcode <= CEE_BLT_UN)) { + acp = NULL; /* invalidate all pairs */ + /* it's not enough to invalidate the pairs, because we don't always + * generate extended basic blocks (the BRANCH_LABEL stuff in the burg rules) + * This issue is going to reduce a lot the possibilities for optimization! + */ + return; + } + ins = ins->next; + } +} + diff --git a/mono/mini/cpu-g4.md b/mono/mini/cpu-g4.md new file mode 100644 index 00000000000..a40508d9e6e --- /dev/null +++ b/mono/mini/cpu-g4.md @@ -0,0 +1,611 @@ +# g4-class cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# b base register (used in address references) +# f floating point register +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# r 'reserves' the destination register until a later instruction unreserves it +# used mostly to set output registers in function calls +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +nop: len:4 +break: len:4 +ldarg.0: +ldarg.1: +ldarg.2: +ldarg.3: +ldloc.0: +ldloc.1: +ldloc.2: +ldloc.3: +stloc.0: +stloc.1: +stloc.2: +stloc.3: +ldarg.s: +ldarga.s: +starg.s: +ldloc.s: +ldloca.s: +stloc.s: +ldnull: +ldc.i4.m1: +ldc.i4.0: +ldc.i4.1: +ldc.i4.2: +ldc.i4.3: +ldc.i4.4: +ldc.i4.5: +ldc.i4.6: +ldc.i4.7: +ldc.i4.8: +ldc.i4.s: +ldc.i4: +ldc.i8: +ldc.r4: +ldc.r8: +unused99: +dup: +pop: +jmp: +call: dest:a clob:c len:4 +calli: +ret: +br.s: +brfalse.s: +brtrue.s: +beq.s: +bge.s: +bgt.s: +ble.s: +blt.s: +bne.un.s: +bge.un.s: +bgt.un.s: +ble.un.s: +blt.un.s: +br: len:4 +brfalse: +brtrue: +beq: len:8 +bge: len:8 +bgt: len:8 +ble: len:8 +blt: len:8 +bne.un: len:8 +bge.un: len:8 +bgt.un: len:8 +ble.un: len:8 +blt.un: len:8 +switch: +ldind.i1: dest:i len:8 +ldind.u1: dest:i len:8 +ldind.i2: dest:i len:8 +ldind.u2: dest:i len:8 +ldind.i4: dest:i len:8 +ldind.u4: dest:i len:8 +ldind.i8: +ldind.i: dest:i len:8 +ldind.r4: +ldind.r8: +ldind.ref: dest:i len:8 +stind.ref: src1:b src2:i +stind.i1: src1:b src2:i +stind.i2: src1:b src2:i +stind.i4: src1:b src2:i +stind.i8: +stind.r4: src1:b src2:f +stind.r8: src1:b src2:f +add: dest:i src1:i src2:i len:4 +sub: dest:i src1:i src2:i len:4 +mul: dest:i src1:i src2:i len:4 +div: dest:a src1:i src2:i len:4 +div.un: dest:a src1:i src2:i len:4 +rem: dest:d src1:i src2:i len:12 +rem.un: dest:d src1:i src2:i len:12 +and: dest:i src1:i src2:i len:4 +or: dest:i src1:i src2:i len:4 +xor: dest:i src1:i src2:i len:4 +shl: dest:i src1:i src2:i len:4 +shr: dest:i src1:i src2:i len:4 +shr.un: dest:i src1:i src2:i len:4 +neg: dest:i src1:i len:4 +not: dest:i src1:i len:4 +conv.i1: dest:i src1:i len:4 +conv.i2: dest:i src1:i len:4 +conv.i4: dest:i src1:i len:4 +conv.i8: +conv.r4: dest:f src1:i len:7 +conv.r8: dest:f src1:i len:7 +conv.u4: dest:i src1:i +conv.u8: +callvirt: +cpobj: +ldobj: +ldstr: +newobj: +castclass: +isinst: +conv.r.un: +unused58: +unused1: +unbox: +throw: src1:i len:8 +ldfld: +ldflda: +stfld: +ldsfld: +ldsflda: +stsfld: +stobj: +conv.ovf.i1.un: +conv.ovf.i2.un: +conv.ovf.i4.un: +conv.ovf.i8.un: +conv.ovf.u1.un: +conv.ovf.u2.un: +conv.ovf.u4.un: +conv.ovf.u8.un: +conv.ovf.i.un: +conv.ovf.u.un: +box: +newarr: +ldlen: +ldelema: +ldelem.i1: +ldelem.u1: +ldelem.i2: +ldelem.u2: +ldelem.i4: +ldelem.u4: +ldelem.i8: +ldelem.i: +ldelem.r4: +ldelem.r8: +ldelem.ref: +stelem.i: +stelem.i1: +stelem.i2: +stelem.i4: +stelem.i8: +stelem.r4: +stelem.r8: +stelem.ref: +unused2: +unused3: +unused4: +unused5: +unused6: +unused7: +unused8: +unused9: +unused10: +unused11: +unused12: +unused13: +unused14: +unused15: +unused16: +unused17: +conv.ovf.i1: +conv.ovf.u1: +conv.ovf.i2: +conv.ovf.u2: +conv.ovf.i4: +conv.ovf.u4: +conv.ovf.i8: +conv.ovf.u8: +unused50: +unused18: +unused19: +unused20: +unused21: +unused22: +unused23: +refanyval: +ckfinite: dest:f src1:f len:22 +unused24: +unused25: +mkrefany: +unused59: +unused60: +unused61: +unused62: +unused63: +unused64: +unused65: +unused66: +unused67: +ldtoken: +conv.u2: dest:i src1:i len:4 +conv.u1: dest:i src1:i len:4 +conv.i: dest:i src1:i len:4 +conv.ovf.i: +conv.ovf.u: +add.ovf: +add.ovf.un: +mul.ovf: dest:i src1:i src2:i len:8 +# this opcode is handled specially in the code generator +mul.ovf.un: dest:i src1:i src2:i len:12 +sub.ovf: +sub.ovf.un: +endfinally: len:10 +leave: +leave.s: +stind.i: +conv.u: dest:i src1:i len:4 +unused26: +unused27: +unused28: +unused29: +unused30: +unused31: +unused32: +unused33: +unused34: +unused35: +unused36: +unused37: +unused38: +unused39: +unused40: +unused41: +unused42: +unused43: +unused44: +unused45: +unused46: +unused47: +unused48: +prefix7: +prefix6: +prefix5: +prefix4: +prefix3: +prefix2: +prefix1: +prefixref: +arglist: +ceq: dest:i len:12 +cgt: dest:i len:12 +cgt.un: dest:i len:12 +clt: dest:i len:12 +clt.un: dest:i len:12 +ldftn: +ldvirtftn: +unused56: +ldarg: +ldarga: +starg: +ldloc: +ldloca: +stloc: +localloc: dest:i src1:i len:30 +unused57: +endfilter: +unaligned.: +volatile.: +tail.: +initobj: +unused68: +cpblk: +initblk: +unused69: +rethrow: +unused: +sizeof: +refanytype: +unused52: +unused53: +unused54: +unused55: +unused70: +illegal: +endmac: +mono_func1: +mono_proc2: +mono_proc3: +mono_free: +mono_objaddr: +mono_ldptr: +mono_vtaddr: +mono_newobj: +mono_retobj: +load: +ldaddr: +store: +phi: +rename: +compare: src1:i src2:i len:4 +compare_imm: src1:i len:12 +fcompare: src1:f src2:f len:12 +lcompare: +local: +arg: +outarg: src1:i len:1 +outarg_imm: len:5 +retarg: +setret: dest:a src1:i len:4 +setlret: dest:l src1:i src2:i len:8 +setreg: dest:i src1:i len:4 clob:r +setregimm: dest:i len:8 clob:r +setfreg: dest:f src1:f len:4 clob:r +checkthis: src1:b len:4 +voidcall: len:8 clob:c +voidcall_reg: src1:i len:8 clob:c +voidcall_membase: src1:b len:8 clob:c +fcall: dest:f len:8 clob:c +fcall_reg: dest:f src1:i len:8 clob:c +fcall_membase: dest:f src1:b len:8 clob:c +lcall: dest:l len:8 clob:c +lcall_reg: dest:l src1:i len:8 clob:c +lcall_membase: dest:l src1:b len:8 clob:c +vcall: len:8 clob:c +vcall_reg: src1:i len:8 clob:c +vcall_membase: src1:b len:8 clob:c +call_reg: dest:i src1:i len:8 clob:c +call_membase: dest:i src1:b len:8 clob:c +trap: +iconst: dest:i len:8 +i8const: +r4const: dest:f len:8 +r8const: dest:f len:8 +regvar: +reg: +regoffset: +label: +store_membase_imm: dest:b len:12 +store_membase_reg: dest:b src1:i len:8 +storei1_membase_imm: dest:b len:12 +storei1_membase_reg: dest:b src1:i len:8 +storei2_membase_imm: dest:b len:12 +storei2_membase_reg: dest:b src1:i len:8 +storei4_membase_imm: dest:b len:12 +storei4_membase_reg: dest:b src1:i len:8 +storei8_membase_imm: dest:b +storei8_membase_reg: dest:b src1:i +storer4_membase_reg: dest:b src1:f len:8 +storer8_membase_reg: dest:b src1:f len:8 +load_membase: dest:i src1:b len:12 +loadi1_membase: dest:i src1:b len:12 +loadu1_membase: dest:i src1:b len:12 +loadi2_membase: dest:i src1:b len:12 +loadu2_membase: dest:i src1:b len:12 +loadi4_membase: dest:i src1:b len:12 +loadu4_membase: dest:i src1:b len:12 +loadi8_membase: dest:i src1:b +loadr4_membase: dest:f src1:b len:8 +loadr8_membase: dest:f src1:b len:12 +loadu4_mem: dest:i len:8 +move: dest:i src1:i len:4 +add_imm: dest:i src1:i len:12 +sub_imm: dest:i src1:i len:12 +mul_imm: dest:i src1:i len:12 +# there is no actual support for division or reminder by immediate +# we simulate them, though (but we need to change the burg rules +# to allocate a symbolic reg for src2) +div_imm: dest:a src1:i src2:i len:12 +div_un_imm: dest:a src1:i src2:i len:12 +rem_imm: dest:d src1:i src2:i len:16 +rem_un_imm: dest:d src1:i src2:i len:16 +and_imm: dest:i src1:i len:8 +or_imm: dest:i src1:i len:8 +xor_imm: dest:i src1:i len:8 +shl_imm: dest:i src1:i len:8 +shr_imm: dest:i src1:i len:8 +shr_un_imm: dest:i src1:i len:8 +cond_exc_eq: len:8 +cond_exc_ne_un: len:8 +cond_exc_lt: len:8 +cond_exc_lt_un: len:8 +cond_exc_gt: len:8 +cond_exc_gt_un: len:8 +cond_exc_ge: len:8 +cond_exc_ge_un: len:8 +cond_exc_le: len:8 +cond_exc_le_un: len:8 +cond_exc_ov: len:8 +cond_exc_no: len:8 +cond_exc_c: len:8 +cond_exc_nc: len:8 +long_add: +long_sub: +long_mul: +long_div: +long_div_un: +long_rem: +long_rem_un: +long_and: +long_or: +long_xor: +long_shl: +long_shr: +long_shr_un: +long_neg: +long_not: +long_conv_to_i1: +long_conv_to_i2: +long_conv_to_i4: +long_conv_to_i8: +long_conv_to_r4: +long_conv_to_r8: +long_conv_to_u4: +long_conv_to_u8: +long_conv_to_u2: +long_conv_to_u1: +long_conv_to_i: +long_conv_to_ovf_i: dest:i src1:i src2:i len:30 +long_conv_to_ovf_u: +long_add_ovf: +long_add_ovf_un: +long_mul_ovf: +long_mul_ovf_un: +long_sub_ovf: +long_sub_ovf_un: +long_conv_to_ovf_i1_un: +long_conv_to_ovf_i2_un: +long_conv_to_ovf_i4_un: +long_conv_to_ovf_i8_un: +long_conv_to_ovf_u1_un: +long_conv_to_ovf_u2_un: +long_conv_to_ovf_u4_un: +long_conv_to_ovf_u8_un: +long_conv_to_ovf_i_un: +long_conv_to_ovf_u_un: +long_conv_to_ovf_i1: +long_conv_to_ovf_u1: +long_conv_to_ovf_i2: +long_conv_to_ovf_u2: +long_conv_to_ovf_i4: +long_conv_to_ovf_u4: +long_conv_to_ovf_i8: +long_conv_to_ovf_u8: +long_ceq: +long_cgt: +long_cgt_un: +long_clt: +long_clt_un: +long_conv_to_r_un: dest:f src1:i src2:i len:37 +long_conv_to_u: +long_shr_imm: +long_shr_un_imm: +long_shl_imm: +long_add_imm: +long_sub_imm: +long_beq: +long_bne_un: +long_blt: +long_blt_un: +long_bgt: +long_btg_un: +long_bge: +long_bge_un: +long_ble: +long_ble_un: +float_beq: len:8 +float_bne_un: len:8 +float_blt: len:8 +float_blt_un: len:8 +float_bgt: len:8 +float_btg_un: len:8 +float_bge: len:8 +float_bge_un: len:8 +float_ble: len:8 +float_ble_un: len:8 +float_add: len:4 +float_sub: len:4 +float_mul: len:4 +float_div: len:4 +float_div_un: len:4 +float_rem: len:16 +float_rem_un: len:16 +float_neg: dest:f src1:f len:4 +float_not: dest:f src1:f len:4 +float_conv_to_i1: dest:i src1:f len:40 +float_conv_to_i2: dest:i src1:f len:40 +float_conv_to_i4: dest:i src1:f len:40 +float_conv_to_i8: dest:l src1:f len:40 +float_conv_to_r4: +float_conv_to_r8: +float_conv_to_u4: dest:i src1:f len:40 +float_conv_to_u8: dest:l src1:f len:40 +float_conv_to_u2: dest:i src1:f len:40 +float_conv_to_u1: dest:i src1:f len:40 +float_conv_to_i: dest:i src1:f len:40 +float_conv_to_ovf_i: +float_conv_to_ovd_u: +float_add_ovf: +float_add_ovf_un: +float_mul_ovf: +float_mul_ovf_un: +float_sub_ovf: +float_sub_ovf_un: +float_conv_to_ovf_i1_un: +float_conv_to_ovf_i2_un: +float_conv_to_ovf_i4_un: +float_conv_to_ovf_i8_un: +float_conv_to_ovf_u1_un: +float_conv_to_ovf_u2_un: +float_conv_to_ovf_u4_un: +float_conv_to_ovf_u8_un: +float_conv_to_ovf_i_un: +float_conv_to_ovf_u_un: +float_conv_to_ovf_i1: +float_conv_to_ovf_u1: +float_conv_to_ovf_i2: +float_conv_to_ovf_u2: +float_conv_to_ovf_i4: +float_conv_to_ovf_u4: +float_conv_to_ovf_i8: +float_conv_to_ovf_u8: +float_ceq: dest:i src1:f src2:f len:12 +float_cgt: dest:i src1:f src2:f len:12 +float_cgt_un: dest:i src1:f src2:f len:12 +float_clt: dest:i src1:f src2:f len:12 +float_clt_un: dest:i src1:f src2:f len:12 +float_conv_to_u: dest:i src1:f len:36 +handler: len:12 +op_endfilter: src1:i len:12 +aot_const: dest:i len:8 +x86_test_null: src1:i len:4 +x86_compare_membase_reg: src1:b src2:i len:8 +x86_compare_membase_imm: src1:b len:8 +x86_compare_reg_membase: src1:i src2:b len:8 +x86_inc_reg: dest:i src1:i clob:1 len:1 +x86_inc_membase: src1:b len:6 +x86_dec_reg: dest:i src1:i clob:1 len:1 +x86_dec_membase: src1:b len:6 +x86_add_membase_imm: src1:b len:8 +x86_sub_membase_imm: src1:b len:8 +x86_push: src1:i len:1 +x86_push_imm: len:5 +x86_push_membase: src1:b len:6 +x86_push_obj: src1:b len:30 +x86_lea: dest:i src1:i src2:i len:7 +x86_xchg: src1:i src2:i clob:x len:1 +x86_fpop: src1:f len:2 +x86_fp_load_i8: dest:f src1:b len:7 +x86_fp_load_i4: dest:f src1:b len:7 +adc: dest:i src1:i src2:i len:4 +addcc: dest:i src1:i src2:i len:4 +subcc: dest:i src1:i src2:i len:4 +adc_imm: dest:i src1:i len:8 +sbb: dest:i src1:i src2:i len:4 +sbb_imm: dest:i src1:i len:8 +br_reg: src1:i len:8 diff --git a/mono/mini/cpu-pentium.md b/mono/mini/cpu-pentium.md new file mode 100644 index 00000000000..90e85265423 --- /dev/null +++ b/mono/mini/cpu-pentium.md @@ -0,0 +1,619 @@ +# x86-class cpu description file +# this file is read by genmdesc to pruduce a table with all the relevant information +# about the cpu instructions that may be used by the regsiter allocator, the scheduler +# and other parts of the arch-dependent part of mini. +# +# An opcode name is followed by a colon and optional specifiers. +# A specifier has a name, a colon and a value. Specifiers are separated by white space. +# Here is a description of the specifiers valid for this file and their possible values. +# +# dest:register describes the destination register of an instruction +# src1:register describes the first source register of an instruction +# src2:register describes the second source register of an instruction +# +# register may have the following values: +# i integer register +# b base register (used in address references) +# f floating point register +# a EAX register +# d EDX register +# +# len:number describe the maximun length in bytes of the instruction +# number is a positive integer +# +# cost:number describe how many cycles are needed to complete the instruction (unused) +# +# clob:spec describe if the instruction clobbers registers or has special needs +# +# spec can be one of the following characters: +# c clobbers caller-save registers +# 1 clobbers the first source register +# a EAX is clobbered +# d EAX and EDX are clobbered +# s the src1 operand needs to be in ECX (shift opcodes) +# x both the source operands are clobbered (xchg) +# +# flags:spec describe if the instruction uses or sets the flags (unused) +# +# spec can be one of the following chars: +# s sets the flags +# u uses the flags +# m uses and modifies the flags +# +# res:spec describe what units are used in the processor (unused) +# +# delay: describe delay slots (unused) +# +# the required specifiers are: len, clob (if registers are clobbered), the registers +# specifiers if the registers are actually used, flags (when scheduling is implemented). +# +# See the code in mini-x86.c for more details on how the specifiers are used. +# +nop: +break: len:1 +ldarg.0: +ldarg.1: +ldarg.2: +ldarg.3: +ldloc.0: +ldloc.1: +ldloc.2: +ldloc.3: +stloc.0: +stloc.1: +stloc.2: +stloc.3: +ldarg.s: +ldarga.s: +starg.s: +ldloc.s: +ldloca.s: +stloc.s: +ldnull: +ldc.i4.m1: +ldc.i4.0: +ldc.i4.1: +ldc.i4.2: +ldc.i4.3: +ldc.i4.4: +ldc.i4.5: +ldc.i4.6: +ldc.i4.7: +ldc.i4.8: +ldc.i4.s: +ldc.i4: +ldc.i8: +ldc.r4: +ldc.r8: +unused99: +dup: +pop: +jmp: len:32 +call: dest:a clob:c len:8 +calli: +ret: +br.s: +brfalse.s: +brtrue.s: +beq.s: +bge.s: +bgt.s: +ble.s: +blt.s: +bne.un.s: +bge.un.s: +bgt.un.s: +ble.un.s: +blt.un.s: +br: len:5 +brfalse: +brtrue: +beq: len:6 +bge: len:6 +bgt: len:6 +ble: len:6 +blt: len:6 +bne.un: len:6 +bge.un: len:6 +bgt.un: len:6 +ble.un: len:6 +blt.un: len:6 +switch: +ldind.i1: dest:i len:6 +ldind.u1: dest:i len:6 +ldind.i2: dest:i len:6 +ldind.u2: dest:i len:6 +ldind.i4: dest:i len:6 +ldind.u4: dest:i len:6 +ldind.i8: +ldind.i: dest:i len:6 +ldind.r4: +ldind.r8: +ldind.ref: dest:i len:6 +stind.ref: src1:b src2:i +stind.i1: src1:b src2:i +stind.i2: src1:b src2:i +stind.i4: src1:b src2:i +stind.i8: +stind.r4: src1:b src2:f +stind.r8: src1:b src2:f +add: dest:i src1:i src2:i len:2 clob:1 +sub: dest:i src1:i src2:i len:2 clob:1 +mul: dest:i src1:i src2:i len:3 clob:1 +div: dest:a src1:i src2:i len:15 clob:d +div.un: dest:a src1:i src2:i len:15 clob:d +rem: dest:d src1:i src2:i len:15 clob:d +rem.un: dest:d src1:i src2:i len:15 clob:d +and: dest:i src1:i src2:i len:2 clob:1 +or: dest:i src1:i src2:i len:2 clob:1 +xor: dest:i src1:i src2:i len:2 clob:1 +shl: dest:i src1:i src2:i clob:s len:2 +shr: dest:i src1:i src2:i clob:s len:2 +shr.un: dest:i src1:i src2:i clob:s len:2 +neg: dest:i src1:i len:2 clob:1 +not: dest:i src1:i len:2 clob:1 +conv.i1: dest:i src1:i len:3 +conv.i2: dest:i src1:i len:3 +conv.i4: dest:i src1:i len:2 +conv.i8: +conv.r4: dest:f src1:i len:7 +conv.r8: dest:f src1:i len:7 +conv.u4: dest:i src1:i +conv.u8: +callvirt: +cpobj: +ldobj: +ldstr: +newobj: +castclass: +isinst: +conv.r.un: +unused58: +unused1: +unbox: +throw: src1:i len:6 +ldfld: +ldflda: +stfld: +ldsfld: +ldsflda: +stsfld: +stobj: +conv.ovf.i1.un: +conv.ovf.i2.un: +conv.ovf.i4.un: +conv.ovf.i8.un: +conv.ovf.u1.un: +conv.ovf.u2.un: +conv.ovf.u4.un: +conv.ovf.u8.un: +conv.ovf.i.un: +conv.ovf.u.un: +box: +newarr: +ldlen: +ldelema: +ldelem.i1: +ldelem.u1: +ldelem.i2: +ldelem.u2: +ldelem.i4: +ldelem.u4: +ldelem.i8: +ldelem.i: +ldelem.r4: +ldelem.r8: +ldelem.ref: +stelem.i: +stelem.i1: +stelem.i2: +stelem.i4: +stelem.i8: +stelem.r4: +stelem.r8: +stelem.ref: +unused2: +unused3: +unused4: +unused5: +unused6: +unused7: +unused8: +unused9: +unused10: +unused11: +unused12: +unused13: +unused14: +unused15: +unused16: +unused17: +conv.ovf.i1: +conv.ovf.u1: +conv.ovf.i2: +conv.ovf.u2: +conv.ovf.i4: +conv.ovf.u4: +conv.ovf.i8: +conv.ovf.u8: +unused50: +unused18: +unused19: +unused20: +unused21: +unused22: +unused23: +refanyval: +ckfinite: dest:f src1:f len:22 +unused24: +unused25: +mkrefany: +unused59: +unused60: +unused61: +unused62: +unused63: +unused64: +unused65: +unused66: +unused67: +ldtoken: +conv.u2: dest:i src1:i len:3 +conv.u1: dest:i src1:i len:3 +conv.i: dest:i src1:i len:3 +conv.ovf.i: +conv.ovf.u: +add.ovf: +add.ovf.un: +mul.ovf: dest:i src1:i src2:i clob:1 len:9 +# this opcode is handled specially in the code generator +mul.ovf.un: dest:i src1:i src2:i len:12 +sub.ovf: +sub.ovf.un: +endfinally: len:10 +leave: +leave.s: +stind.i: +conv.u: dest:i src1:i len:3 +unused26: +unused27: +unused28: +unused29: +unused30: +unused31: +unused32: +unused33: +unused34: +unused35: +unused36: +unused37: +unused38: +unused39: +unused40: +unused41: +unused42: +unused43: +unused44: +unused45: +unused46: +unused47: +unused48: +prefix7: +prefix6: +prefix5: +prefix4: +prefix3: +prefix2: +prefix1: +prefixref: +arglist: +ceq: dest:i len:6 +cgt: dest:i len:6 +cgt.un: dest:i len:6 +clt: dest:i len:6 +clt.un: dest:i len:6 +ldftn: +ldvirtftn: +unused56: +ldarg: +ldarga: +starg: +ldloc: +ldloca: +stloc: +localloc: dest:i src1:i len:30 +unused57: +endfilter: +unaligned.: +volatile.: +tail.: +initobj: +unused68: +cpblk: +initblk: +unused69: +rethrow: +unused: +sizeof: +refanytype: +unused52: +unused53: +unused54: +unused55: +unused70: +illegal: +endmac: +mono_func1: +mono_proc2: +mono_proc3: +mono_free: +mono_objaddr: +mono_ldptr: +mono_vtaddr: +mono_newobj: +mono_retobj: +load: +ldaddr: +store: +phi: +rename: +compare: src1:i src2:i len:2 +compare_imm: src1:i len:6 +fcompare: src1:f src2:f clob:a len:9 +lcompare: +local: +arg: +outarg: src1:i len:1 +outarg_imm: len:5 +retarg: +setret: dest:a src1:i len:2 +setlret: dest:l src1:i src2:i len:4 +checkthis: src1:b len:3 +voidcall: len:8 clob:c +voidcall_reg: src1:i len:5 clob:c +voidcall_membase: src1:b len:10 clob:c +fcall: dest:f len:8 clob:c +fcall_reg: dest:f src1:i len:5 clob:c +fcall_membase: dest:f src1:b len:10 clob:c +lcall: dest:l len:8 clob:c +lcall_reg: dest:l src1:i len:5 clob:c +lcall_membase: dest:l src1:b len:10 clob:c +vcall: len:8 clob:c +vcall_reg: src1:i len:5 clob:c +vcall_membase: src1:b len:10 clob:c +call_reg: dest:i src1:i len:5 clob:c +call_membase: dest:i src1:b len:10 clob:c +trap: +iconst: dest:i len:5 +i8const: +r4const: dest:f len:6 +r8const: dest:f len:6 +regvar: +reg: +regoffset: +label: +store_membase_imm: dest:b len:10 +store_membase_reg: dest:b src1:i len:7 +storei1_membase_imm: dest:b len:10 +storei1_membase_reg: dest:b src1:i len:7 +storei2_membase_imm: dest:b len:11 +storei2_membase_reg: dest:b src1:i len:7 +storei4_membase_imm: dest:b len:10 +storei4_membase_reg: dest:b src1:i len:7 +storei8_membase_imm: dest:b +storei8_membase_reg: dest:b src1:i +storer4_membase_reg: dest:b src1:f len:7 +storer8_membase_reg: dest:b src1:f len:6 +load_membase: dest:i src1:b len:6 +loadi1_membase: dest:i src1:b len:7 +loadu1_membase: dest:i src1:b len:7 +loadi2_membase: dest:i src1:b len:7 +loadu2_membase: dest:i src1:b len:7 +loadi4_membase: dest:i src1:b len:6 +loadu4_membase: dest:i src1:b len:6 +loadi8_membase: dest:i src1:b +loadr4_membase: dest:f src1:b len:6 +loadr8_membase: dest:f src1:b len:6 +loadu4_mem: dest:i len:9 +move: dest:i src1:i len:2 +add_imm: dest:i src1:i len:6 clob:1 +sub_imm: dest:i src1:i len:6 clob:1 +mul_imm: dest:i src1:i len:6 +# there is no actual support for division or reminder by immediate +# we simulate them, though (but we need to change the burg rules +# to allocate a symbolic reg for src2) +div_imm: dest:a src1:i src2:i len:15 clob:d +div_un_imm: dest:a src1:i src2:i len:15 clob:d +rem_imm: dest:d src1:i src2:i len:15 clob:d +rem_un_imm: dest:d src1:i src2:i len:15 clob:d +and_imm: dest:i src1:i len:6 clob:1 +or_imm: dest:i src1:i len:6 clob:1 +xor_imm: dest:i src1:i len:6 clob:1 +shl_imm: dest:i src1:i len:6 clob:1 +shr_imm: dest:i src1:i len:6 clob:1 +shr_un_imm: dest:i src1:i len:6 clob:1 +cond_exc_eq: len:6 +cond_exc_ne_un: len:6 +cond_exc_lt: len:6 +cond_exc_lt_un: len:6 +cond_exc_gt: len:6 +cond_exc_gt_un: len:6 +cond_exc_ge: len:6 +cond_exc_ge_un: len:6 +cond_exc_le: len:6 +cond_exc_le_un: len:6 +cond_exc_ov: len:6 +cond_exc_no: len:6 +cond_exc_c: len:6 +cond_exc_nc: len:6 +long_add: +long_sub: +long_mul: +long_div: +long_div_un: +long_rem: +long_rem_un: +long_and: +long_or: +long_xor: +long_shl: +long_shr: +long_shr_un: +long_neg: +long_not: +long_conv_to_i1: +long_conv_to_i2: +long_conv_to_i4: +long_conv_to_i8: +long_conv_to_r4: +long_conv_to_r8: +long_conv_to_u4: +long_conv_to_u8: +long_conv_to_u2: +long_conv_to_u1: +long_conv_to_i: +long_conv_to_ovf_i: dest:i src1:i src2:i len:30 +long_conv_to_ovf_u: +long_add_ovf: +long_add_ovf_un: +long_mul_ovf: +long_mul_ovf_un: +long_sub_ovf: +long_sub_ovf_un: +long_conv_to_ovf_i1_un: +long_conv_to_ovf_i2_un: +long_conv_to_ovf_i4_un: +long_conv_to_ovf_i8_un: +long_conv_to_ovf_u1_un: +long_conv_to_ovf_u2_un: +long_conv_to_ovf_u4_un: +long_conv_to_ovf_u8_un: +long_conv_to_ovf_i_un: +long_conv_to_ovf_u_un: +long_conv_to_ovf_i1: +long_conv_to_ovf_u1: +long_conv_to_ovf_i2: +long_conv_to_ovf_u2: +long_conv_to_ovf_i4: +long_conv_to_ovf_u4: +long_conv_to_ovf_i8: +long_conv_to_ovf_u8: +long_ceq: +long_cgt: +long_cgt_un: +long_clt: +long_clt_un: +long_conv_to_r_un: dest:f src1:i src2:i len:37 +long_conv_to_u: +long_shr_imm: +long_shr_un_imm: +long_shl_imm: +long_add_imm: +long_sub_imm: +long_beq: +long_bne_un: +long_blt: +long_blt_un: +long_bgt: +long_btg_un: +long_bge: +long_bge_un: +long_ble: +long_ble_un: +float_beq: len:12 +float_bne_un: len:12 +float_blt: len:12 +float_blt_un: len:20 +float_bgt: len:12 +float_btg_un: len:20 +float_bge: len:12 +float_bge_un: len:12 +float_ble: len:12 +float_ble_un: len:12 +float_add: len:2 +float_sub: len:2 +float_mul: len:2 +float_div: len:2 +float_div_un: len:2 +float_rem: len:17 +float_rem_un: len:17 +float_neg: dest:f src1:f len:2 +float_not: dest:f src1:f len:2 +float_conv_to_i1: dest:i src1:f len:39 +float_conv_to_i2: dest:i src1:f len:39 +float_conv_to_i4: dest:i src1:f len:39 +float_conv_to_i8: dest:l src1:f len:39 +float_conv_to_r4: +float_conv_to_r8: +float_conv_to_u4: dest:i src1:f len:39 +float_conv_to_u8: dest:l src1:f len:39 +float_conv_to_u2: dest:i src1:f len:39 +float_conv_to_u1: dest:i src1:f len:39 +float_conv_to_i: dest:i src1:f len:39 +float_conv_to_ovf_i: dest:a src1:f len:30 +float_conv_to_ovd_u: dest:a src1:f len:30 +float_add_ovf: +float_add_ovf_un: +float_mul_ovf: +float_mul_ovf_un: +float_sub_ovf: +float_sub_ovf_un: +float_conv_to_ovf_i1_un: +float_conv_to_ovf_i2_un: +float_conv_to_ovf_i4_un: +float_conv_to_ovf_i8_un: +float_conv_to_ovf_u1_un: +float_conv_to_ovf_u2_un: +float_conv_to_ovf_u4_un: +float_conv_to_ovf_u8_un: +float_conv_to_ovf_i_un: +float_conv_to_ovf_u_un: +float_conv_to_ovf_i1: +float_conv_to_ovf_u1: +float_conv_to_ovf_i2: +float_conv_to_ovf_u2: +float_conv_to_ovf_i4: +float_conv_to_ovf_u4: +float_conv_to_ovf_i8: +float_conv_to_ovf_u8: +float_ceq: dest:i src1:f src2:f len:25 +float_cgt: dest:i src1:f src2:f len:25 +float_cgt_un: dest:i src1:f src2:f len:37 +float_clt: dest:i src1:f src2:f len:25 +float_clt_un: dest:i src1:f src2:f len:32 +float_conv_to_u: dest:i src1:f len:36 +handler: len:10 +op_endfilter: src1:i len:10 +aot_const: dest:i len:5 +x86_test_null: src1:i len:2 +x86_compare_membase_reg: src1:b src2:i len:6 +x86_compare_membase_imm: src1:b len:10 +x86_compare_reg_membase: src1:i src2:b len:6 +x86_inc_reg: dest:i src1:i clob:1 len:1 +x86_inc_membase: src1:b len:6 +x86_dec_reg: dest:i src1:i clob:1 len:1 +x86_dec_membase: src1:b len:6 +x86_add_membase_imm: src1:b len:8 +x86_sub_membase_imm: src1:b len:8 +x86_push: src1:i len:1 +x86_push_imm: len:5 +x86_push_membase: src1:b len:6 +x86_push_obj: src1:b len:30 +x86_lea: dest:i src1:i src2:i len:7 +x86_xchg: src1:i src2:i clob:x len:1 +x86_fpop: src1:f len:2 +x86_fp_load_i8: dest:f src1:b len:7 +x86_fp_load_i4: dest:f src1:b len:7 +adc: dest:i src1:i src2:i len:2 clob:1 +addcc: dest:i src1:i src2:i len:2 clob:1 +subcc: dest:i src1:i src2:i len:2 clob:1 +adc_imm: dest:i src1:i len:6 clob:1 +sbb: dest:i src1:i src2:i len:2 clob:1 +sbb_imm: dest:i src1:i len:6 clob:1 +br_reg: src1:i len:2 +sin: dest:f src1:f len:2 +cos: dest:f src1:f len:2 +abs: dest:f src1:f len:2 +tan: dest:f src1:f len:2 +atan: dest:f src1:f len:2 +sqrt: dest:f src1:f len:2 diff --git a/mono/mini/debug-dwarf2.c b/mono/mini/debug-dwarf2.c new file mode 100644 index 00000000000..666f2eb4fb9 --- /dev/null +++ b/mono/mini/debug-dwarf2.c @@ -0,0 +1,1400 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug-private.h" + +#define ABBREV_COMPILE_UNIT 1 +#define ABBREV_SUBPROGRAM 2 +#define ABBREV_SUBPROGRAM_RETVAL 3 +#define ABBREV_BASE_TYPE 4 +#define ABBREV_FORMAL_PARAMETER 5 +#define ABBREV_PARAMETER 6 +#define ABBREV_LOCAL_VARIABLE 7 +#define ABBREV_STRUCT_TYPE 8 +#define ABBREV_STRUCT_MEMBER 9 +#define ABBREV_STRUCT_ACCESS 10 +#define ABBREV_ENUM_TYPE 11 +#define ABBREV_ENUM_VALUE 12 +#define ABBREV_ENUM_VALUE_UNSIGNED 13 +#define ABBREV_ENUM_VALUE_SIGNED 14 +#define ABBREV_CLASS_TYPE 15 +#define ABBREV_CLASS_INHERITANCE 16 +#define ABBREV_POINTER_TYPE 17 +#define ABBREV_CLASS_METHOD 18 +#define ABBREV_CLASS_METHOD_RETVAL 19 +#define ABBREV_ARTIFICIAL_PARAMETER 20 +#define ABBREV_SIMPLE_ARRAY 21 +#define ABBREV_ARRAY 22 +#define ABBREV_SUBRANGE 23 + +// The following constants are defined in the DWARF 2 specification +#define DW_TAG_array_type 0x01 +#define DW_TAG_class_type 0x02 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_inheritance 0x1c +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_subprogram 0x2e +#define DW_TAG_variable 0x34 + +#define DW_CHILDREN_no 0 +#define DW_CHILDREN_yes 1 + +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_byte_size 0x0b +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_const_value 0x1c +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_start_scope 0x2c +#define DW_AT_upper_bound 0x2f +#define DW_AT_accessibility 0x32 +#define DW_AT_artificial 0x34 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_type 0x49 +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d + +/* Martin Baulig's extensions. */ +#define DW_AT_end_scope 0x2121 + +#define DW_FORM_addr 0x01 +#define DW_FORM_block4 0x04 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_data8 0x07 +#define DW_FORM_string 0x08 +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_udata 0x0f +#define DW_FORM_ref4 0x13 + +#define DW_ATE_void 0x00 +#define DW_ATE_address 0x01 +#define DW_ATE_boolean 0x02 +#define DW_ATE_complex_float 0x03 +#define DW_ATE_float 0x04 +#define DW_ATE_signed 0x05 +#define DW_ATE_signed_char 0x06 +#define DW_ATE_unsigned 0x07 +#define DW_ATE_unsigned_char 0x08 + +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_plus 0x22 +#define DW_OP_reg0 0x50 +#define DW_OP_breg0 0x70 +#define DW_OP_fbreg 0x91 +#define DW_OP_piece 0x93 + +#define DW_CC_normal 1 +#define DW_CC_program 2 +#define DW_CC_nocall 3 + +#define DW_ACCESS_public 1 +#define DW_ACCESS_protected 2 +#define DW_ACCESS_private 3 + +#define DW_VIRTUALITY_none 0 +#define DW_VIRTUALITY_virtual 1 +#define DW_VIRTUALITY_pure_virtual 2 + +#define DW_LANG_C_plus_plus 0x04 +#define DW_LANG_Java 0x0b +// This is NOT in the standard, we're using Java for the moment. */ +#define DW_LANG_C_sharp DW_LANG_C_plus_plus + +#define DW_LNS_extended_op 0 +#define DW_LNS_copy 1 +#define DW_LNS_advance_pc 2 +#define DW_LNS_advance_line 3 +#define DW_LNS_set_file 4 +#define DW_LNS_set_column 5 +#define DW_LNS_negate_stmt 6 +#define DW_LNS_set_basic_block 7 +#define DW_LNS_const_add_pc 8 +#define DW_LNS_fixed_advance_pc 9 + +#define DW_LNE_end_sequence 1 +#define DW_LNE_set_address 2 +#define DW_LNE_define_file 3 + + +static const int line_base = 1, line_range = 8, opcode_base = 10; +static const int standard_opcode_sizes [10] = { + 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 +}; + +static void +dwarf2_write_byte (FILE *f, int byte) +{ + fprintf (f, "\t.byte %d\n", byte); +} + +static void +dwarf2_write_2byte (FILE *f, int word) +{ + fprintf (f, "\t.word %d\n", word); +} + +static void +dwarf2_write_pair (FILE *f, int a, int b) +{ + fprintf (f, "\t.uleb128 %d, %d\n", a, b); +} + +static void +dwarf2_write_long (FILE *f, unsigned long value) +{ + fprintf (f, "\t.long %lu\n", value); +} + +static void +dwarf2_write_address (FILE *f, const void *address) +{ + fprintf (f, "\t.long 0x%lx\n", (long) address); +} + +static void +dwarf2_write_string (FILE *f, const char *string) +{ + fprintf (f, "\t.string \"%s\"\n", string); +} + +static void +dwarf2_write_sleb128 (FILE *f, long value) +{ + fprintf (f, "\t.sleb128 %ld\n", value); +} + +static void +dwarf2_write_uleb128 (FILE *f, unsigned long value) +{ + fprintf (f, "\t.uleb128 %lu\n", value); +} + +static void +dwarf2_write_section_start (FILE *f, const char *section) +{ + fprintf (f, "\t.section .%s\n", section); +} + +static void +dwarf2_write_label (FILE *f, const char *label) +{ + fprintf (f, ".L_%s:\n", label); +} + +static void +dwarf2_write_section_size (FILE *f, const char *start_label, const char *end_label) +{ + fprintf (f, "\t.long .L_%s - .L_%s\n", end_label, start_label); +} + +static void +dwarf2_write_ref4 (FILE *f, const char *target_label) +{ + fprintf (f, "\t.long .L_%s\n", target_label); +} + +static void +dwarf2_write_type_ref (FILE *f, unsigned long type_index) +{ + fprintf (f, "\t.long .L_TYPE_%lu - .L_debug_info_b\n", type_index); +} + +static void +dwarf2_write_type_ptr_ref (FILE *f, unsigned long idx) +{ + fprintf (f, "\t.long .L_TYPE_PTR_%lu - .L_debug_info_b\n", idx); +} + +static void +dwarf2_write_relative_ref (FILE *f, const gchar *name, unsigned long idx) +{ + fprintf (f, "\t.long .L_%s_%lu - .L_debug_info_b\n", name, idx); +} + +static void +dwarf2_write_dw_lns_copy (FILE *f) +{ + dwarf2_write_byte (f, DW_LNS_copy); +} + +static void +dwarf2_write_dw_lns_advance_pc (FILE *f, unsigned value) +{ + dwarf2_write_byte (f, DW_LNS_advance_pc); + dwarf2_write_uleb128 (f, value); +} + +static void +dwarf2_write_dw_lns_advance_line (FILE *f, int value) +{ + dwarf2_write_byte (f, DW_LNS_advance_line); + dwarf2_write_sleb128 (f, value); +} + +static void +dwarf2_write_dw_lns_set_file (FILE *f, unsigned value) +{ + dwarf2_write_byte (f, DW_LNS_set_file); + dwarf2_write_uleb128 (f, value + 1); +} + +static void +dwarf2_write_dw_lns_negate_stmt (FILE *f) +{ + dwarf2_write_byte (f, DW_LNS_negate_stmt); +} + +#if 0 /* never used */ +static void +dwarf2_write_dw_lns_set_basic_block (FILE *f) +{ + dwarf2_write_byte (f, DW_LNS_set_basic_block); +} +#endif + +static void +dwarf2_write_dw_lne_end_sequence (FILE *f) +{ + dwarf2_write_byte (f, 0); + dwarf2_write_byte (f, 1); + dwarf2_write_byte (f, DW_LNE_end_sequence); +} + +static void +dwarf2_write_dw_lne_set_address (FILE *f, const void *address) +{ + dwarf2_write_byte (f, 0); + dwarf2_write_byte (f, sizeof (address) + 1); + dwarf2_write_byte (f, DW_LNE_set_address); + dwarf2_write_address (f, address); +} + +static void +dwarf2_write_base_type (MonoDebugHandle *debug, int idx, + int type, int size, const gchar *name) +{ + char buffer [BUFSIZ]; + + sprintf (buffer, "TYPE_%d", idx); + dwarf2_write_label (debug->f, buffer); + // DW_TAG_basic_type + dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE); + dwarf2_write_string (debug->f, name); + dwarf2_write_byte (debug->f, type); + dwarf2_write_byte (debug->f, size); +} + +static void +dwarf2_write_enum_value (MonoDebugHandle *debug, MonoClass *klass, int idx) +{ + const void *ptr; + guint32 field_index = idx + klass->field.first; + guint32 crow; + + crow = mono_metadata_get_constant_index (klass->image, MONO_TOKEN_FIELD_DEF | (field_index + 1)); + if (!crow) { + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_long (debug->f, 0); + return; + } + + crow = mono_metadata_decode_row_col (&klass->image->tables [MONO_TABLE_CONSTANT], crow-1, + MONO_CONSTANT_VALUE); + + ptr = 1 + mono_metadata_blob_heap (klass->image, crow); + + switch (klass->enum_basetype->type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_uleb128 (debug->f, *(guint8 *) ptr); + break; + case MONO_TYPE_I1: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_sleb128 (debug->f, *(gint8 *) ptr); + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_uleb128 (debug->f, *(guint16 *) ptr); + break; + case MONO_TYPE_I2: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_sleb128 (debug->f, *(gint16 *) ptr); + break; + case MONO_TYPE_U4: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_uleb128 (debug->f, *(guint32 *) ptr); + break; + case MONO_TYPE_I4: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_sleb128 (debug->f, *(gint32 *) ptr); + break; + case MONO_TYPE_U8: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_uleb128 (debug->f, *(guint64 *) ptr); + break; + case MONO_TYPE_I8: + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED); + dwarf2_write_string (debug->f, klass->fields [idx].name); + dwarf2_write_sleb128 (debug->f, *(gint64 *) ptr); + break; + default: + g_assert_not_reached (); + } +} + +static void +dwarf2_write_enum_type (MonoDebugHandle *debug, MonoClass *klass) +{ + int i; + + // DW_TAG_enumeration_type + dwarf2_write_byte (debug->f, ABBREV_ENUM_TYPE); + dwarf2_write_string (debug->f, klass->name); + dwarf2_write_long (debug->f, klass->instance_size - sizeof (MonoObject)); + + for (i = 0; i < klass->field.count; i++) { + if (klass->fields [i].type->attrs & FIELD_ATTRIBUTE_LITERAL) + dwarf2_write_enum_value (debug, klass, i); + } + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_enumeration_type ends here +} + +static void +dwarf2_write_class_field (MonoDebugHandle *debug, MonoClass *klass, int idx, + int type_index, int start_offset) +{ + MonoClass *subclass = mono_class_from_mono_type (klass->fields [idx].type); + char start [BUFSIZ], end [BUFSIZ]; + static long label_index = 0; + + // Don't include any static fields, they aren't supported yet. + // If a struct contains a static field which has the same type as + // the struct itself, we'd get a recursion loop there. + if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_STATIC) + return; + + sprintf (start, "DSF1_%ld", ++label_index); + sprintf (end, "DSF2_%ld", label_index); + + // DW_TAG_member + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, klass->fields [idx].name); + if (!subclass->valuetype) + dwarf2_write_type_ptr_ref (debug->f, type_index); + else + dwarf2_write_type_ref (debug->f, type_index); + + if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_PRIVATE) + dwarf2_write_byte (debug->f, DW_ACCESS_private); + else if (klass->fields [idx].type->attrs & FIELD_ATTRIBUTE_FAMILY) + dwarf2_write_byte (debug->f, DW_ACCESS_protected); + else + dwarf2_write_byte (debug->f, DW_ACCESS_public); + + dwarf2_write_section_size (debug->f, start, end); + dwarf2_write_label (debug->f, start); + dwarf2_write_byte (debug->f, DW_OP_constu); + dwarf2_write_uleb128 (debug->f, klass->fields [idx].offset - start_offset); + dwarf2_write_label (debug->f, end); + + dwarf2_write_long (debug->f, subclass->instance_size); +} + +static void +dwarf2_write_class_method (MonoDebugHandle *debug, MonoClass *klass, MonoMethod *method) +{ + MonoType *ret_type = NULL; + gchar **names; + int i; + + if (!MONO_TYPE_IS_VOID (method->signature->ret)) + ret_type = method->signature->ret; + + // DW_TAG_subprogram + if (ret_type) + dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD_RETVAL); + else + dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD); + dwarf2_write_string (debug->f, method->name); + + if (method->flags & METHOD_ATTRIBUTE_PUBLIC) + dwarf2_write_byte (debug->f, DW_ACCESS_public); + else if (method->flags & METHOD_ATTRIBUTE_PRIVATE) + dwarf2_write_byte (debug->f, DW_ACCESS_private); + else + dwarf2_write_byte (debug->f, DW_ACCESS_protected); + + if (method->flags & METHOD_ATTRIBUTE_VIRTUAL) + dwarf2_write_byte (debug->f, DW_VIRTUALITY_pure_virtual); + else + dwarf2_write_byte (debug->f, DW_VIRTUALITY_none); + + dwarf2_write_byte (debug->f, DW_CC_nocall); + + if (ret_type) { + MonoClass *k = mono_class_from_mono_type (ret_type); + int type_index = mono_debug_get_type (debug, k); + dwarf2_write_type_ref (debug->f, type_index); + } + + if (method->signature->hasthis) { + int type_index = mono_debug_get_type (debug, klass); + + dwarf2_write_byte (debug->f, ABBREV_ARTIFICIAL_PARAMETER); + dwarf2_write_string (debug->f, "this"); + dwarf2_write_type_ptr_ref (debug->f, type_index); + dwarf2_write_byte (debug->f, 1); + } + + names = g_new (char *, method->signature->param_count); + mono_method_get_param_names (method, (const char **) names); + + for (i = 0; i < method->signature->param_count; i++) { + MonoType *subtype = method->signature->params [i]; + MonoClass *subklass = mono_class_from_mono_type (subtype); + int type_index = mono_debug_get_type (debug, subklass); + + // DW_TAG_formal_parameter + dwarf2_write_byte (debug->f, ABBREV_FORMAL_PARAMETER); + dwarf2_write_string (debug->f, names [i]); + if (subklass->valuetype) + dwarf2_write_type_ref (debug->f, type_index); + else + dwarf2_write_type_ptr_ref (debug->f, type_index); + } + + g_free (names); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_subprogram ends here +} + +static void +dwarf2_write_struct_type (MonoDebugHandle *debug, MonoClass *klass) +{ + guint32 *idxs; + int i; + + idxs = g_new0 (guint32, klass->field.last - klass->field.first + 1); + for (i = 0; i < klass->field.count; i++) { + MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type); + idxs [i] = mono_debug_get_type (debug, subclass); + } + + // DW_TAG_structure_type + dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE); + dwarf2_write_string (debug->f, klass->name); + dwarf2_write_long (debug->f, klass->instance_size - sizeof (MonoObject)); + + for (i = 0; i < klass->field.count; i++) + dwarf2_write_class_field (debug, klass, i, idxs [i], sizeof (MonoObject)); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_structure_type ends here + + g_free (idxs); +} + +static void +dwarf2_write_class_type (MonoDebugHandle *debug, MonoClass *klass) +{ + guint32 *idxs; + int i; + + idxs = g_new0 (guint32, klass->field.last - klass->field.first + 1); + for (i = 0; i < klass->field.count; i++) { + MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type); + idxs [i] = mono_debug_get_type (debug, subclass); + } + + // DW_TAG_structure_type + dwarf2_write_byte (debug->f, ABBREV_CLASS_TYPE); + dwarf2_write_string (debug->f, klass->name); + dwarf2_write_long (debug->f, klass->instance_size); + if (klass->flags & TYPE_ATTRIBUTE_PUBLIC) + dwarf2_write_byte (debug->f, DW_ACCESS_public); + else + dwarf2_write_byte (debug->f, DW_ACCESS_private); + + if (klass->parent && klass->parent->byval_arg.type == MONO_TYPE_CLASS) { + guint32 parent_index = mono_debug_get_type (debug, klass->parent); + + // DW_TAG_inheritance + dwarf2_write_byte (debug->f, ABBREV_CLASS_INHERITANCE); + dwarf2_write_type_ref (debug->f, parent_index); + if (klass->parent->flags & TYPE_ATTRIBUTE_PUBLIC) + dwarf2_write_byte (debug->f, DW_ACCESS_public); + else + dwarf2_write_byte (debug->f, DW_ACCESS_private); + } + + for (i = 0; i < klass->field.count; i++) + dwarf2_write_class_field (debug, klass, i, idxs [i], 0); + + for (i = 0; i < klass->method.count; i++) { + if (!strcmp (klass->methods [i]->name, ".ctor")) + continue; + + dwarf2_write_class_method (debug, klass, klass->methods [i]); + } + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_class_type ends here + + g_free (idxs); +} + +static void +dwarf2_write_array (MonoDebugHandle *debug, const gchar *name, MonoClass *element_class, + int rank, int idx) +{ + unsigned long uint32_index = mono_debug_get_type (debug, mono_defaults.uint32_class); + char buffer [BUFSIZ]; + MonoArray array; + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE); + dwarf2_write_string (debug->f, name); + dwarf2_write_long (debug->f, sizeof (MonoArray)); + + // DW_TAG_structure_type + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, "max_length"); + dwarf2_write_type_ref (debug->f, uint32_index); + dwarf2_write_byte (debug->f, DW_ACCESS_public); + dwarf2_write_long (debug->f, 2); + dwarf2_write_byte (debug->f, DW_OP_const1u); + dwarf2_write_byte (debug->f, (guchar *) &array.max_length - (guchar *) &array); + dwarf2_write_long (debug->f, 4); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, "bounds"); + dwarf2_write_relative_ref (debug->f, "ARRAY_BOUNDS_PTR", idx); + dwarf2_write_byte (debug->f, DW_ACCESS_public); + dwarf2_write_long (debug->f, 2); + dwarf2_write_byte (debug->f, DW_OP_const1u); + dwarf2_write_byte (debug->f, (guchar *) &array.bounds - (guchar *) &array); + dwarf2_write_long (debug->f, 4); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, "vector"); + dwarf2_write_relative_ref (debug->f, "ARRAY_PTR", idx); + dwarf2_write_byte (debug->f, DW_ACCESS_public); + dwarf2_write_long (debug->f, 2); + dwarf2_write_byte (debug->f, DW_OP_const1u); + dwarf2_write_byte (debug->f, (guchar *) &array.vector - (guchar *) &array); + dwarf2_write_long (debug->f, 4); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_structure_type ends here + + sprintf (buffer, "ARRAY_BOUNDS_PTR_%u", idx); + dwarf2_write_label (debug->f, buffer); + + // DW_TAG_pointer_type + dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE); + dwarf2_write_relative_ref (debug->f, "ARRAY_BOUNDS", idx); + + sprintf (buffer, "ARRAY_BOUNDS_%u", idx); + dwarf2_write_label (debug->f, buffer); + + // DW_TAG_array_type + dwarf2_write_byte (debug->f, ABBREV_ARRAY); + dwarf2_write_string (debug->f, name); + dwarf2_write_type_ref (debug->f, uint32_index); + dwarf2_write_long (debug->f, rank * 2); + + // DW_TAG_subrange_type + dwarf2_write_byte (debug->f, ABBREV_SUBRANGE); + dwarf2_write_long (debug->f, 0); + dwarf2_write_long (debug->f, rank-1); + dwarf2_write_long (debug->f, rank); + + // DW_TAG_subrange_type + dwarf2_write_byte (debug->f, ABBREV_SUBRANGE); + dwarf2_write_long (debug->f, 0); + dwarf2_write_long (debug->f, 1); + dwarf2_write_long (debug->f, 2); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_array_type ends here + + sprintf (buffer, "ARRAY_PTR_%u", idx); + dwarf2_write_label (debug->f, buffer); + + // DW_TAG_array_type + dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY); + dwarf2_write_string (debug->f, name); + if (element_class->valuetype) + dwarf2_write_type_ref (debug->f, mono_debug_get_type (debug, element_class)); + else + dwarf2_write_type_ptr_ref (debug->f, mono_debug_get_type (debug, element_class)); +} + +static void +dwarf2_write_array_type (MonoDebugHandle *debug, MonoClass *klass, int idx) +{ + char buffer [BUFSIZ], *name; + int i; + + buffer[0] = '\0'; + for (i = 0; i < klass->rank; i++) + strcat (buffer, "[]"); + + name = g_strdup_printf ("%s%s", klass->element_class->name, buffer); + + dwarf2_write_array (debug, name, klass->element_class, klass->rank, idx); + + g_free (name); +} + +static void +dwarf2_write_string_type (MonoDebugHandle *debug, MonoClass *klass, int idx) +{ + unsigned long uint32_index = mono_debug_get_type (debug, mono_defaults.uint32_class); + char buffer [BUFSIZ]; + MonoString string; + + // DW_TAG_structure_type + dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE); + dwarf2_write_string (debug->f, klass->name); + dwarf2_write_long (debug->f, sizeof (MonoString)); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, "length"); + dwarf2_write_type_ref (debug->f, uint32_index); + dwarf2_write_byte (debug->f, DW_ACCESS_public); + dwarf2_write_long (debug->f, 2); + dwarf2_write_byte (debug->f, DW_OP_const1u); + dwarf2_write_byte (debug->f, (guchar *) &string.length - (guchar *) &string); + dwarf2_write_long (debug->f, 4); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_string (debug->f, "chars"); + dwarf2_write_relative_ref (debug->f, "CHARS", idx); + dwarf2_write_byte (debug->f, DW_ACCESS_public); + dwarf2_write_long (debug->f, 2); + dwarf2_write_byte (debug->f, DW_OP_const1u); + dwarf2_write_byte (debug->f, (guchar *) &string.chars - (guchar *) &string); + dwarf2_write_long (debug->f, 4); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_structure_type ends here + + sprintf (buffer, "CHARS_%u", idx); + dwarf2_write_label (debug->f, buffer); + + dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY); + dwarf2_write_string (debug->f, "Char[]"); + dwarf2_write_type_ref (debug->f, mono_debug_get_type (debug, mono_defaults.char_class)); +} + +static void +dwarf2_write_class (MonoDebugHandle *debug, MonoClass *klass, int idx) +{ + char buffer [BUFSIZ]; + int print = 0; + + if (!strncmp (klass->name, "My", 2)) { + g_message (G_STRLOC ": %s - %s - %x", klass->name_space, klass->name, klass->flags); + print = 1; + // G_BREAKPOINT (); + } + + if (!klass->valuetype) { + sprintf (buffer, "TYPE_PTR_%u", idx); + dwarf2_write_label (debug->f, buffer); + + // DW_TAG_pointer_type + dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE); + dwarf2_write_type_ref (debug->f, idx); + } + + sprintf (buffer, "TYPE_%u", idx); + dwarf2_write_label (debug->f, buffer); + + if (klass->enumtype) { + dwarf2_write_enum_type (debug, klass); + return; + } + + switch (klass->byval_arg.type) { + case MONO_TYPE_VALUETYPE: + dwarf2_write_struct_type (debug, klass); + break; + case MONO_TYPE_CLASS: + dwarf2_write_class_type (debug, klass); + break; + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + dwarf2_write_array_type (debug, klass, idx); + break; + case MONO_TYPE_STRING: + dwarf2_write_string_type (debug, klass, idx); + break; + default: +#if 0 + g_message (G_STRLOC ": %s.%s - 0x%x - 0x%x", klass->name_space, klass->name, + klass->byval_arg.type, klass->flags); +#endif + + // DW_TAG_basic_type + dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE); + dwarf2_write_string (debug->f, klass->name); + dwarf2_write_byte (debug->f, DW_ATE_address); + dwarf2_write_byte (debug->f, 0); + break; + } +} + +static void +dwarf2_write_variable_location (MonoDebugHandle *debug, MonoDebugVarInfo *var) +{ + switch (var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) { + case MONO_DEBUG_VAR_ADDRESS_MODE_STACK: + /* + * Variable is on the stack. + * + * If `index' is zero, use the normal frame register. Otherwise, bits + * 0..4 of `index' contain the frame register. + * + */ + + if (!var->index) + /* Use the normal frame register (%ebp on the i386). */ + dwarf2_write_byte (debug->f, DW_OP_fbreg); + else + /* Use a custom frame register. */ + dwarf2_write_byte (debug->f, DW_OP_breg0 + (var->index & 0x001f)); + dwarf2_write_sleb128 (debug->f, var->offset); + break; + + case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: + /* + * Variable is in the register whose number is contained in bits 0..4 + * of `index'. + * + */ + dwarf2_write_byte (debug->f, DW_OP_reg0 + (var->index & 0x001f)); + if (var->offset) { + dwarf2_write_byte (debug->f, DW_OP_consts); + dwarf2_write_sleb128 (debug->f, var->offset); + dwarf2_write_byte (debug->f, DW_OP_plus); + } + break; + + case MONO_DEBUG_VAR_ADDRESS_MODE_TWO_REGISTERS: + /* + * Variable is in two registers whose numbers are in bits 0..4 and 5..9 of + * the `index' field. + */ + dwarf2_write_byte (debug->f, DW_OP_reg0 + (var->index & 0x001f)); + dwarf2_write_byte (debug->f, DW_OP_piece); + dwarf2_write_byte (debug->f, sizeof (int)); + + dwarf2_write_byte (debug->f, DW_OP_reg0 + ((var->index & 0x1f0) >> 5)); + dwarf2_write_byte (debug->f, DW_OP_piece); + dwarf2_write_byte (debug->f, sizeof (int)); + + break; + + default: + g_assert_not_reached (); + } +} + +static void +dwarf2_write_parameter (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo, const gchar *name, + MonoDebugVarInfo *var, MonoClass *klass) +{ + static long label_index = 0; + int type_index = mono_debug_get_type (debug, klass); + char start [BUFSIZ], end [BUFSIZ]; + + sprintf (start, "DT1_%ld", ++label_index); + sprintf (end, "DT2_%ld", label_index); + + // DW_TAG_format_parameter + dwarf2_write_byte (debug->f, ABBREV_PARAMETER); + dwarf2_write_string (debug->f, name); + if (klass->valuetype) + dwarf2_write_type_ref (debug->f, type_index); + else + dwarf2_write_type_ptr_ref (debug->f, type_index); + dwarf2_write_section_size (debug->f, start, end); + dwarf2_write_label (debug->f, start); + dwarf2_write_variable_location (debug, var); + dwarf2_write_label (debug->f, end); + dwarf2_write_long (debug->f, minfo->jit->prologue_end); +} + +static void +dwarf2_write_variable (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo, const gchar *name, + MonoDebugVarInfo *var, MonoClass *klass) +{ + static long label_index = 0; + int type_index = mono_debug_get_type (debug, klass); + char start [BUFSIZ], end [BUFSIZ]; + + sprintf (start, "DT3_%ld", ++label_index); + sprintf (end, "DT4_%ld", label_index); + + // DW_TAG_formal_parameter + dwarf2_write_byte (debug->f, ABBREV_LOCAL_VARIABLE); + dwarf2_write_string (debug->f, name); + if (klass->valuetype) + dwarf2_write_type_ref (debug->f, type_index); + else + dwarf2_write_type_ptr_ref (debug->f, type_index); + dwarf2_write_section_size (debug->f, start, end); + dwarf2_write_label (debug->f, start); + dwarf2_write_variable_location (debug, var); + dwarf2_write_label (debug->f, end); + dwarf2_write_address (debug->f, minfo->jit->code_start + var->begin_scope); + dwarf2_write_address (debug->f, minfo->jit->code_start + var->end_scope); +} + +static void +write_method_lines_dwarf2 (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo) +{ + guint32 st_line = 0; + gconstpointer st_address = 0; + DebugMethodInfo *priv = minfo->user_data; + int i; + + if (!minfo->jit || !minfo->jit->line_numbers) + return; + + // Start of statement program + dwarf2_write_dw_lns_set_file (debug->f, priv->source_file); + dwarf2_write_dw_lns_advance_line (debug->f, priv->start_line - 1); + dwarf2_write_dw_lne_set_address (debug->f, minfo->jit->code_start); + dwarf2_write_dw_lns_negate_stmt (debug->f); + dwarf2_write_dw_lns_copy (debug->f); + + st_line = priv->start_line; + st_address = 0; + + for (i = 1; i < minfo->jit->line_numbers->len; i++) { + MonoDebugLineNumberEntry lne = g_array_index ( + minfo->jit->line_numbers, MonoDebugLineNumberEntry, i); + gint32 line_inc, addr_inc, opcode; + int used_standard_opcode = 0; + + line_inc = lne.line - st_line; + addr_inc = (char *)lne.address - (char *)st_address; + + if (addr_inc < 0) { + dwarf2_write_dw_lne_set_address (debug->f, lne.address + minfo->jit->code_start); + used_standard_opcode = 1; + } else if (addr_inc && !line_inc) { + dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc); + used_standard_opcode = 1; + } + + if ((line_inc < 0) || (line_inc >= line_range)) { + dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc); + dwarf2_write_dw_lns_advance_line (debug->f, line_inc); + used_standard_opcode = 1; + } else if (line_inc > 0) { + opcode = (line_inc - 1) + (line_range * addr_inc) + opcode_base; + g_assert (opcode >= 0); + + if (opcode >= 256) { + dwarf2_write_dw_lns_advance_pc (debug->f, addr_inc); + dwarf2_write_dw_lns_advance_line (debug->f, line_inc); + used_standard_opcode = 1; + } else + dwarf2_write_byte (debug->f, opcode); + } + + if (used_standard_opcode) + dwarf2_write_dw_lns_copy (debug->f); + + st_line += line_inc; + st_address = (char *)st_address + addr_inc; + } + + dwarf2_write_dw_lne_set_address (debug->f, + (char *)minfo->jit->code_start + + minfo->jit->epilogue_begin); + dwarf2_write_dw_lns_advance_line (debug->f, priv->last_line - st_line); + dwarf2_write_dw_lns_copy (debug->f); + + dwarf2_write_dw_lns_copy (debug->f); + dwarf2_write_dw_lne_end_sequence (debug->f); +} + +static void +write_method_lines_func (gpointer key, gpointer value, gpointer user_data) +{ + write_method_lines_dwarf2 (user_data, value); +} + +static void +write_method_lines_func_1 (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + + g_hash_table_foreach (info->methods, write_method_lines_func, user_data); +} + +static void +write_line_numbers (MonoDebugHandle *debug) +{ + /* State machine registers. */ + int i; + + // Line number information. + dwarf2_write_section_start (debug->f, "debug_line"); + dwarf2_write_label (debug->f, "debug_line_b"); + dwarf2_write_section_size (debug->f, "DL1", "debug_line_e"); + dwarf2_write_label (debug->f, "DL1"); + dwarf2_write_2byte (debug->f, 2); + dwarf2_write_section_size (debug->f, "DL2", "DL3"); + dwarf2_write_label (debug->f, "DL2"); + // minimum instruction length + dwarf2_write_byte (debug->f, 1); + // default is statement + dwarf2_write_byte (debug->f, 1); + // line base + dwarf2_write_byte (debug->f, line_base); + // line range + dwarf2_write_byte (debug->f, line_range); + // opcode base + dwarf2_write_byte (debug->f, opcode_base); + // standard opcode sizes + for (i = 1; i < opcode_base; i++) + dwarf2_write_byte (debug->f, standard_opcode_sizes [i]); + // include directories + dwarf2_write_byte (debug->f, 0); + // file names + for (i = 0; i < debug->source_files->len; i++) { + gchar *source_file = g_ptr_array_index (debug->source_files, i); + dwarf2_write_string (debug->f, source_file); + dwarf2_write_uleb128 (debug->f, 0); + dwarf2_write_uleb128 (debug->f, 0); + dwarf2_write_uleb128 (debug->f, 0); + } + // end of list + dwarf2_write_byte (debug->f, 0); + dwarf2_write_label (debug->f, "DL3"); + + g_hash_table_foreach (debug->images, write_method_lines_func_1, debug); + + dwarf2_write_label (debug->f, "debug_line_e"); +} + +static void +write_class_dwarf2 (MonoDebugHandle *debug, MonoClass *klass, guint idx) +{ + switch (klass->byval_arg.type) { + case MONO_TYPE_VOID: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 0, "Void"); + break; + case MONO_TYPE_BOOLEAN: + dwarf2_write_base_type (debug, idx, DW_ATE_boolean, 1, "Boolean"); + break; + case MONO_TYPE_CHAR: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned_char, 2, "Char"); + break; + case MONO_TYPE_I1: + dwarf2_write_base_type (debug, idx, DW_ATE_signed, 1, "SByte"); + break; + case MONO_TYPE_U1: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 1, "Byte"); + break; + case MONO_TYPE_I2: + dwarf2_write_base_type (debug, idx, DW_ATE_signed, 2, "Int16"); + break; + case MONO_TYPE_U2: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 2, "UInt16"); + break; + case MONO_TYPE_I4: + dwarf2_write_base_type (debug, idx, DW_ATE_signed, 4, "Int32"); + break; + case MONO_TYPE_U4: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 4, "UInt32"); + break; + case MONO_TYPE_I8: + dwarf2_write_base_type (debug, idx, DW_ATE_signed, 8, "Int64"); + break; + case MONO_TYPE_U8: + dwarf2_write_base_type (debug, idx, DW_ATE_unsigned, 8, "UInt64"); + break; + case MONO_TYPE_R4: + dwarf2_write_base_type (debug, idx, DW_ATE_float, 4, "Float"); + break; + case MONO_TYPE_R8: + dwarf2_write_base_type (debug, idx, DW_ATE_float, 8, "Double"); + break; + default: + dwarf2_write_class (debug, klass, idx); + break; + } +} + +static void +write_class (gpointer key, gpointer value, gpointer user_data) +{ + write_class_dwarf2 (user_data, key, GPOINTER_TO_INT (value)); +} + +static void +write_method_dwarf2 (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo) +{ + int is_external = 0, i; + MonoType *ret_type = NULL; + DebugMethodInfo *priv = minfo->user_data; + gchar **names; + + if (!minfo->jit) + return; + + if (!MONO_TYPE_IS_VOID (minfo->method->signature->ret)) + ret_type = minfo->method->signature->ret; + + // DW_TAG_subprogram + if (ret_type) + dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM_RETVAL); + else + dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM); + dwarf2_write_string (debug->f, priv->name); + dwarf2_write_byte (debug->f, is_external); + dwarf2_write_address (debug->f, minfo->jit->code_start); + dwarf2_write_address (debug->f, (char *)minfo->jit->code_start + minfo->jit->code_size); + dwarf2_write_byte (debug->f, DW_CC_nocall); + if (ret_type) { + MonoClass *klass = mono_class_from_mono_type (ret_type); + int type_index = mono_debug_get_type (debug, klass); + dwarf2_write_type_ref (debug->f, type_index); + } + + if (minfo->method->signature->hasthis) + dwarf2_write_parameter (debug, minfo, "this", minfo->jit->this_var, + minfo->method->klass); + + names = g_new (char *, minfo->method->signature->param_count); + mono_method_get_param_names (minfo->method, (const char **) names); + + for (i = 0; i < minfo->jit->num_params; i++) { + MonoType *type = minfo->method->signature->params [i]; + MonoClass *klass = mono_class_from_mono_type (type); + + dwarf2_write_parameter (debug, minfo, names [i], &minfo->jit->params [i], klass); + } + + g_free (names); + + for (i = 0; i < minfo->jit->num_locals; i++) { + MonoMethodHeader *header = ((MonoMethodNormal*) minfo->method)->header; + MonoClass *klass = mono_class_from_mono_type (header->locals [i]); + char name [BUFSIZ]; + + sprintf (name, "V_%d", i); + dwarf2_write_variable (debug, minfo, name, &minfo->jit->locals [i], klass); + } + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_subprogram ends here +} + +static void +write_method_func (gpointer key, gpointer value, gpointer user_data) +{ + write_method_dwarf2 (user_data, value); +} + +static void +write_method_func_1 (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + + g_hash_table_foreach (info->methods, write_method_func, user_data); +} + +void +mono_debug_write_dwarf2 (MonoDebugHandle *debug) +{ + if (!(debug->f = fopen (debug->filename, "w"))) { + g_warning ("Can't create dwarf file `%s': %s", debug->filename, g_strerror (errno)); + return; + } + + // Produce assembler code which is free of comments and extra whitespaces. + fprintf (debug->f, "#NOAPP\n"); + + // DWARF 2 Abbreviation table. + dwarf2_write_section_start (debug->f, "debug_abbrev"); + dwarf2_write_label (debug->f, "debug_abbrev"); + + dwarf2_write_byte (debug->f, ABBREV_COMPILE_UNIT); + dwarf2_write_byte (debug->f, DW_TAG_compile_unit); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_language, DW_FORM_data2); + dwarf2_write_pair (debug->f, DW_AT_producer, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_stmt_list, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM); + dwarf2_write_byte (debug->f, DW_TAG_subprogram); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_external, DW_FORM_flag); + dwarf2_write_pair (debug->f, DW_AT_low_pc, DW_FORM_addr); + dwarf2_write_pair (debug->f, DW_AT_high_pc, DW_FORM_addr); + dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_SUBPROGRAM_RETVAL); + dwarf2_write_byte (debug->f, DW_TAG_subprogram); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_external, DW_FORM_flag); + dwarf2_write_pair (debug->f, DW_AT_low_pc, DW_FORM_addr); + dwarf2_write_pair (debug->f, DW_AT_high_pc, DW_FORM_addr); + dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_BASE_TYPE); + dwarf2_write_byte (debug->f, DW_TAG_base_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_encoding, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_FORMAL_PARAMETER); + dwarf2_write_byte (debug->f, DW_TAG_formal_parameter); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ARTIFICIAL_PARAMETER); + dwarf2_write_byte (debug->f, DW_TAG_formal_parameter); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_artificial, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_PARAMETER); + dwarf2_write_byte (debug->f, DW_TAG_formal_parameter); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_location, DW_FORM_block4); + dwarf2_write_pair (debug->f, DW_AT_start_scope, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_LOCAL_VARIABLE); + dwarf2_write_byte (debug->f, DW_TAG_variable); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_location, DW_FORM_block4); + dwarf2_write_pair (debug->f, DW_AT_start_scope, DW_FORM_addr); + dwarf2_write_pair (debug->f, DW_AT_end_scope, DW_FORM_addr); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_TYPE); + dwarf2_write_byte (debug->f, DW_TAG_structure_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_MEMBER); + dwarf2_write_byte (debug->f, DW_TAG_member); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_data_member_location, DW_FORM_block4); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_STRUCT_ACCESS); + dwarf2_write_byte (debug->f, DW_TAG_access_declaration); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ENUM_TYPE); + dwarf2_write_byte (debug->f, DW_TAG_enumeration_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE); + dwarf2_write_byte (debug->f, DW_TAG_enumerator); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_UNSIGNED); + dwarf2_write_byte (debug->f, DW_TAG_enumerator); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_udata); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ENUM_VALUE_SIGNED); + dwarf2_write_byte (debug->f, DW_TAG_enumerator); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_const_value, DW_FORM_sdata); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_CLASS_TYPE); + dwarf2_write_byte (debug->f, DW_TAG_class_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_CLASS_INHERITANCE); + dwarf2_write_byte (debug->f, DW_TAG_inheritance); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_POINTER_TYPE); + dwarf2_write_byte (debug->f, DW_TAG_pointer_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD); + dwarf2_write_byte (debug->f, DW_TAG_subprogram); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_virtuality, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_CLASS_METHOD_RETVAL); + dwarf2_write_byte (debug->f, DW_TAG_subprogram); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_accessibility, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_virtuality, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_calling_convention, DW_FORM_data1); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_SIMPLE_ARRAY); + dwarf2_write_byte (debug->f, DW_TAG_array_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_ARRAY); + dwarf2_write_byte (debug->f, DW_TAG_array_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_yes); + dwarf2_write_pair (debug->f, DW_AT_name, DW_FORM_string); + dwarf2_write_pair (debug->f, DW_AT_type, DW_FORM_ref4); + dwarf2_write_pair (debug->f, DW_AT_byte_size, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_byte (debug->f, ABBREV_SUBRANGE); + dwarf2_write_byte (debug->f, DW_TAG_subrange_type); + dwarf2_write_byte (debug->f, DW_CHILDREN_no); + dwarf2_write_pair (debug->f, DW_AT_lower_bound, DW_FORM_data4); + dwarf2_write_pair (debug->f, DW_AT_upper_bound, DW_FORM_data4); + dwarf2_write_pair (debug->f, DW_AT_count, DW_FORM_data4); + dwarf2_write_pair (debug->f, 0, 0); + + dwarf2_write_label (debug->f, "debug_abbrev_e"); + + // Line numbers + write_line_numbers (debug); + + // Compile unit header + dwarf2_write_section_start (debug->f, "debug_info"); + dwarf2_write_label (debug->f, "debug_info_b"); + dwarf2_write_section_size (debug->f, "DI1", "debug_info_e"); + dwarf2_write_label (debug->f, "DI1"); + dwarf2_write_2byte (debug->f, 2); + dwarf2_write_ref4 (debug->f, "debug_abbrev_b"); + dwarf2_write_byte (debug->f, sizeof (gpointer)); + + // DW_TAG_compile_unit + dwarf2_write_byte (debug->f, ABBREV_COMPILE_UNIT); + dwarf2_write_string (debug->f, debug->name); + dwarf2_write_2byte (debug->f, DW_LANG_C_sharp); + dwarf2_write_string (debug->f, debug->producer_name); + dwarf2_write_ref4 (debug->f, "debug_lines_b"); + + // Methods + g_hash_table_foreach (debug->images, write_method_func_1, debug); + + // Derived types + g_hash_table_foreach (debug->type_hash, write_class, debug); + + dwarf2_write_byte (debug->f, 0); + // DW_TAG_compile_unit ends here + + dwarf2_write_label (debug->f, "debug_info_e"); + + fclose (debug->f); + debug->f = NULL; + + if (!(debug->flags & MONO_DEBUG_FLAGS_DONT_ASSEMBLE)) { + char *buf; + + /* yes, it's completely unsafe */ + buf = g_strdup_printf ("as %s -o %s", debug->filename, debug->objfile); + system (buf); + g_free (buf); + } +} diff --git a/mono/mini/debug-mini.c b/mono/mini/debug-mini.c new file mode 100644 index 00000000000..2774bc1294f --- /dev/null +++ b/mono/mini/debug-mini.c @@ -0,0 +1,164 @@ +/* + * debug-mini.c: Mini-specific debugging stuff. + * + * Author: + * Martin Baulig (martin@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ + +#include "mini.h" +#include "mini-x86.h" +#include "debug-private.h" + +void +mono_debug_codegen_breakpoint (guint8 **buf) +{ + x86_breakpoint (*buf); +} + +void +mono_debug_codegen_ret (guint8 **buf) +{ + x86_ret (*buf); +} + +typedef struct +{ + MonoDebugMethodInfo *minfo; + guint32 has_line_numbers; +} MiniDebugMethodInfo; + +void +mono_debug_init_method (MonoCompile *cfg, MonoBasicBlock *start_block) +{ + MonoMethod *method = cfg->method; + MiniDebugMethodInfo *info; + + if (!mono_debug_handle) + return; + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT) || + (method->wrapper_type != MONO_WRAPPER_NONE)) + return; + + info = g_new0 (MiniDebugMethodInfo, 1); + + cfg->debug_info = info; +} + +void +mono_debug_open_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + MonoDebugMethodJitInfo *jit; + MonoMethodHeader *header; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info) + return; + + mono_class_init (cfg->method->klass); + + info->minfo = _mono_debug_lookup_method (cfg->method); + if (!info->minfo || info->minfo->jit) + return; + + mono_debug_handle->dirty = TRUE; + + g_assert (((MonoMethodNormal*)info->minfo->method)->header); + header = ((MonoMethodNormal*)info->minfo->method)->header; + + info->minfo->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1); + jit->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry)); + jit->num_locals = header->num_locals; + jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals); +} + +static void +write_variable (MonoInst *inst, MonoDebugVarInfo *var) +{ + if (inst->opcode == OP_REGVAR) + var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER; + else if (inst->inst_basereg != X86_EBP) { + g_message (G_STRLOC ": %d - %d", inst->inst_basereg, inst->inst_offset); + var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER; + var->offset = inst->inst_offset; + } else + var->offset = inst->inst_offset; +} + +void +mono_debug_close_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + MonoDebugMethodInfo *minfo; + MonoDebugMethodJitInfo *jit; + MonoMethodHeader *header; + MonoMethod *method; + int i; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info || !info->minfo) + return; + + minfo = info->minfo; + method = minfo->method; + header = ((MonoMethodNormal*)method)->header; + + jit = minfo->jit; + jit->code_start = cfg->native_code; + jit->epilogue_begin = cfg->epilog_begin; + jit->code_size = cfg->code_len; + + _mono_debug_generate_line_number (minfo, jit->epilogue_begin, header->code_size, 0); + + jit->num_params = method->signature->param_count; + jit->params = g_new0 (MonoDebugVarInfo, jit->num_params); + + for (i = 0; i < jit->num_locals; i++) + write_variable (cfg->varinfo [cfg->locals_start + i], &jit->locals [i]); + + if (method->signature->hasthis) { + jit->this_var = g_new0 (MonoDebugVarInfo, 1); + write_variable (cfg->varinfo [0], jit->this_var); + } + + for (i = 0; i < jit->num_params; i++) + write_variable (cfg->varinfo [i + method->signature->hasthis], &jit->params [i]); + + if (minfo->symfile) { + mono_debug_symfile_add_method (minfo->symfile, minfo->method); + mono_debugger_event (MONO_DEBUGGER_EVENT_METHOD_ADDED, minfo->symfile, minfo->method); + } +} + +void +mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address) +{ + MiniDebugMethodInfo *info; + MonoMethodHeader *header; + guint32 offset; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (!info || !info->minfo || !ins->cil_code) + return; + + g_assert (((MonoMethodNormal*)info->minfo->method)->header); + header = ((MonoMethodNormal*)info->minfo->method)->header; + + if ((ins->cil_code < header->code) || + (ins->cil_code > header->code + header->code_size)) + return; + + offset = ins->cil_code - header->code; + if (!info->has_line_numbers) { + info->minfo->jit->prologue_end = address; + info->has_line_numbers = TRUE; + } + + _mono_debug_generate_line_number (info->minfo, address, offset, 0); +} diff --git a/mono/mini/debug-private.h b/mono/mini/debug-private.h new file mode 100644 index 00000000000..1a96cafc650 --- /dev/null +++ b/mono/mini/debug-private.h @@ -0,0 +1,111 @@ +/* + * This is a copy of mono/mono/jit/debug-private.h. + * + * Please do *not* modify this copy here, if you need to make changes, do them + * in the original and then copy it over again. + * + * All mini-specific stuff is in debug-mini.c. + */ + +#ifndef __MONO_JIT_DEBUG_PRIVATE_H__ +#define __MONO_JIT_DEBUG_PRIVATE_H__ + +#include + +#include "debug.h" + +typedef struct _AssemblyDebugInfo AssemblyDebugInfo; + +typedef enum { + MONO_DEBUG_FLAGS_NONE = 0, + // Don't run the assembler. + MONO_DEBUG_FLAGS_DONT_ASSEMBLE = (1 << 1), + // Install the generated *.il files in the assembly dir. + MONO_DEBUG_FLAGS_INSTALL_IL_FILES = (1 << 2), + // Don't update the *.il files. + MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES = (1 << 3), + // Don't create any new *.il files. + MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES = (1 << 4), + // Don't fallback to normal dwarf2. + MONO_DEBUG_FLAGS_DONT_FALLBACK = (1 << 5), + // Don't precompile image. + MONO_DEBUG_FLAGS_DONT_PRECOMPILE = (1 << 6), + // Update symbol file on exit. + MONO_DEBUG_FLAGS_UPDATE_ON_EXIT = (1 << 7) +} MonoDebugFlags; + +typedef struct { + AssemblyDebugInfo *info; + gchar *name; + int source_file; + guint32 method_number; + guint32 start_line; + guint32 first_line; + guint32 last_line; +} DebugMethodInfo; + +typedef struct { + MonoMethod *method; + const guint8 *code_start; + guint32 code_size; +} DebugWrapperInfo; + +struct _AssemblyDebugInfo { + MonoDebugFormat format; + MonoDebugHandle *handle; + MonoSymbolFile *symfile; + char *name; + char *ilfile; + char *filename; + char *objfile; + int always_create_il; + int source_file; + int total_lines; + int *mlines; + int *moffsets; + int nmethods; + GHashTable *methods; + GHashTable *wrapper_methods; + MonoImage *image; + gpointer _priv; +}; + +struct _MonoDebugHandle { + MonoDebugFormat format; + MonoDebugFlags flags; + char *name; + char *filename; + char *objfile; + char *producer_name; + GHashTable *type_hash; + GPtrArray *source_files; + int next_idx; + int next_klass_idx; + int dirty; + GHashTable *images; + FILE *f; +}; + +guint32 mono_debug_get_type (MonoDebugHandle* debug, MonoClass *klass); + +void mono_debug_write_stabs (MonoDebugHandle *debug); + +void mono_debug_write_dwarf2 (MonoDebugHandle *debug); + +void mono_debug_codegen_breakpoint (guint8 **buf); + +void mono_debug_codegen_ret (guint8 **buf); + +MonoDebugMethodInfo *_mono_debug_lookup_method (MonoMethod *method); + +gint32 _mono_debug_address_from_il_offset (MonoDebugMethodInfo *minfo, guint32 il_offset); + +AssemblyDebugInfo *_mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image); + +void _mono_debug_generate_line_number (MonoDebugMethodInfo *minfo, guint32 address, + guint32 offset, int debug); + +extern MonoDebugHandle *mono_debug_handle; +extern gboolean mono_debug_initialized; + +#endif /* __MONO_JIT_DEBUG_PRIVATE_H__ */ diff --git a/mono/mini/debug-stabs.c b/mono/mini/debug-stabs.c new file mode 100644 index 00000000000..605f0290ba8 --- /dev/null +++ b/mono/mini/debug-stabs.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug-private.h" + +typedef struct { + const char *name; + const char *spec; +} BaseTypes; + +/* + * Not 64 bit clean. + * Note: same order of MonoTypeEnum. + */ +static BaseTypes +base_types[] = { + {"", NULL}, + {"Void", "(0,1)"}, + {"Boolean", ";0;255;"}, + {"Char", ";0;65535;"}, + {"SByte", ";-128;127;"}, + {"Byte", ";0;255;"}, + {"Int16", ";-32768;32767;"}, + {"UInt16", ";0;65535;"}, + {"Int32", ";0020000000000;0017777777777;"}, + {"UInt32", ";0000000000000;0037777777777;"}, + {"Int64", ";01000000000000000000000;0777777777777777777777;"}, + {"UInt64", ";0000000000000;01777777777777777777777;"}, + {"Single", "r(0,8);4;0;"}, + {"Double", "r(0,8);8;0;"}, + {"String", "(0,41)=*(0,42)=xsMonoString:"}, /*string*/ + {"", }, /*ptr*/ + {"", }, /*byref*/ + {"", }, /*valuetype*/ + {"Class", "(0,44)=*(0,45)=xsMonoObject:"}, /*class*/ + {"", }, /*unused*/ + {"Array", }, /*array*/ + {"", }, /*typedbyref*/ + {"", }, /*unused*/ + {"", }, /*unused*/ + {"IntPtr", ";0020000000000;0017777777777;"}, + {"UIntPtr", ";0000000000000;0037777777777;"}, + {"", }, /*unused*/ + {"FnPtr", "*(0,1)"}, /*fnptr*/ + {"Object", "(0,47)=*(0,48)=xsMonoObject:"}, /*object*/ + {"SzArray", "(0,50)=*(0,51))=xsMonoArray:"}, /*szarray*/ + {NULL, NULL} +}; + +static void +write_method_stabs (MonoDebugHandle *debug, MonoDebugMethodInfo *minfo) +{ + int i; + DebugMethodInfo *priv = minfo->user_data; + MonoMethod *method = minfo->method; + MonoClass *klass = method->klass; + MonoMethodSignature *sig = method->signature; + char **names = g_new (char*, sig->param_count); + gchar *source_file; + + if (!minfo->jit) + return; + + source_file = g_ptr_array_index (debug->source_files, priv->source_file); + + fprintf (debug->f, ".stabs \"%s\",100,0,0,0\n", source_file); + + fprintf (debug->f, ".stabs \"%s:F(0,%d)\",36,0,%d,%p\n", priv->name, sig->ret->type, + priv->start_line, minfo->jit->code_start); + + /* params */ + mono_method_get_param_names (method, (const char **)names); + if (sig->hasthis) + fprintf (debug->f, ".stabs \"this:p(0,%d)=(0,%d)\",160,0,%d,%d\n", + debug->next_idx++, klass->byval_arg.type, priv->start_line, + minfo->jit->this_var->offset); + for (i = 0; i < minfo->jit->num_params; i++) { + int stack_offset = minfo->jit->params [i].offset; + + fprintf (debug->f, ".stabs \"%s:p(0,%d)=(0,%d)\",160,0,%d,%d\n", + names [i], debug->next_idx++, sig->params [i]->type, + priv->start_line, stack_offset); + } + + /* local vars */ + for (i = 0; i < minfo->jit->num_locals; ++i) { + MonoMethodHeader *header = ((MonoMethodNormal*)method)->header; + int stack_offset = minfo->jit->locals [i].offset; + + fprintf (debug->f, ".stabs \"local_%d:(0,%d)=(0,%d)\",128,0,%d,%d\n", + i, debug->next_idx++, header->locals [i]->type, priv->start_line, stack_offset); + } + + if (minfo->jit && minfo->jit->line_numbers) { + fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->start_line, 0); + fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->first_line, + minfo->jit->prologue_end); + + for (i = 1; i < minfo->jit->line_numbers->len; i++) { + MonoDebugLineNumberEntry lne = g_array_index ( + minfo->jit->line_numbers, MonoDebugLineNumberEntry, i); + + fprintf (debug->f, ".stabn 68,0,%d,%d\n", lne.line, lne.address); + } + + fprintf (debug->f, ".stabn 68,0,%d,%d\n", priv->last_line, + minfo->jit->epilogue_begin); + } + + /* end of function */ + fprintf (debug->f, ".stabs \"\",36,0,0,%d\n", minfo->jit->code_size); + + g_free (names); + fflush (debug->f); +} + +static void +get_enumvalue (MonoClass *klass, int idx, char *buf) +{ + guint32 const_cols [MONO_CONSTANT_SIZE]; + const char *ptr; + guint32 crow = mono_metadata_get_constant_index (klass->image, MONO_TOKEN_FIELD_DEF | (idx + 1)); + + if (!crow) { + buf [0] = '0'; + buf [1] = 0; + return; + } + mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE); + ptr = mono_metadata_blob_heap (klass->image, const_cols [MONO_CONSTANT_VALUE]); + switch (const_cols [MONO_CONSTANT_TYPE]) { + case MONO_TYPE_U4: + case MONO_TYPE_I4: + /* FIXME: add other types... */ + default: + g_snprintf (buf, 64, "%d", *(gint32*)ptr); + } +} + +static void +write_method_func (gpointer key, gpointer value, gpointer user_data) +{ + write_method_stabs (user_data, value); +} + +static void +write_method_func_1 (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + + g_hash_table_foreach (info->methods, write_method_func, user_data); +} + +static void +write_class_stabs (MonoDebugHandle *debug, MonoClass *klass, int idx) +{ + char *name; + int i; + char buf [64]; + + /* output enums ...*/ + if (klass->enumtype) { + name = g_strdup_printf ("%s%s%s", klass->name_space, klass->name_space [0]? "_": "", klass->name); + fprintf (debug->f, ".stabs \"%s:T%d=e", name, ++debug->next_idx); + g_free (name); + for (i = 0; i < klass->field.count; ++i) { + if (klass->fields [i].type->attrs & FIELD_ATTRIBUTE_LITERAL) { + get_enumvalue (klass, klass->field.first + i, buf); + fprintf (debug->f, "%s_%s=%s,", klass->name, klass->fields [i].name, buf); + } + } + fprintf (debug->f, ";\",128,0,0,0\n"); + } + fflush (debug->f); +} + +static void +write_class (gpointer key, gpointer value, gpointer user_data) +{ + write_class_stabs (user_data, key, GPOINTER_TO_INT (value)); +} + +void +mono_debug_write_stabs (MonoDebugHandle *debug) +{ + gchar *source_file; + int i; + + if (!(debug->f = fopen (debug->filename, "w"))) { + g_warning ("Can't create stabs file `%s': %s", debug->filename, g_strerror (errno)); + return; + } + + source_file = g_ptr_array_index (debug->source_files, 0); + + fprintf (debug->f, ".stabs \"%s\",100,0,0,0\n", source_file); + + for (i = 0; base_types [i].name; ++i) { + if (! base_types [i].spec) + continue; + fprintf (debug->f, ".stabs \"%s:t(0,%d)=", base_types [i].name, i); + if (base_types [i].spec [0] == ';') { + fprintf (debug->f, "r(0,%d)%s\"", i, base_types [i].spec); + } else { + fprintf (debug->f, "%s\"", base_types [i].spec); + } + fprintf (debug->f, ",128,0,0,0\n"); + } + + g_hash_table_foreach (debug->images, write_method_func_1, debug); + + g_hash_table_foreach (debug->type_hash, write_class, debug); + + fclose (debug->f); + debug->f = NULL; + + if (!(debug->flags & MONO_DEBUG_FLAGS_DONT_ASSEMBLE)) { + char *buf; + + /* yes, it's completely unsafe */ + buf = g_strdup_printf ("as %s -o %s", debug->filename, debug->objfile); + system (buf); + g_free (buf); + } +} diff --git a/mono/mini/debug.c b/mono/mini/debug.c new file mode 100644 index 00000000000..69d2d86eac5 --- /dev/null +++ b/mono/mini/debug.c @@ -0,0 +1,1138 @@ +/* + * debug.c: Debugging support + * + * Author: + * Martin Baulig (martin@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug-private.h" + +/* + * NOTE: Functions and variables starting with `mono_debug_' and `debug_' are + * part of the general debugging code. + * + * Functions and variables starting with `mono_debugger_' and `debugger_' + * are only used when the JIT is running inside the Mono Debugger. + * + * FIXME: This file needs some API loving. + */ + +/* This is incremented each time the symbol table is modified. + * The debugger looks at this variable and if it has a higher value than its current + * copy of the symbol table, it must call mono_debug_update_symbol_file_table(). + */ +guint32 mono_debugger_symbol_file_table_generation = 0; +guint32 mono_debugger_symbol_file_table_modified = 0; + +/* Caution: This variable may be accessed at any time from the debugger; + * it is very important not to modify the memory it is pointing to + * without previously setting this pointer back to NULL. + */ +MonoDebuggerSymbolFileTable *mono_debugger_symbol_file_table = NULL; + +/* Caution: This function MUST be called before touching the symbol table! */ +static void release_symbol_file_table (void); + +MonoDebugHandle *mono_debug_handle = NULL; +gboolean mono_debug_initialized = FALSE; + +static CRITICAL_SECTION debugger_lock_mutex; + +extern void (*mono_debugger_class_init_func) (MonoClass *klass); + +static void mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data); +static void mono_debug_close_assembly (AssemblyDebugInfo* info); +static AssemblyDebugInfo *mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image); + +static int running_in_the_mono_debugger = FALSE; +void (*mono_debugger_event_handler) (MonoDebuggerEvent event, gpointer data, gpointer data2) = NULL; + +#ifndef PLATFORM_WIN32 + +MonoDebuggerIOLayer mono_debugger_io_layer = { + InitializeCriticalSection, DeleteCriticalSection, TryEnterCriticalSection, + EnterCriticalSection, LeaveCriticalSection, WaitForSingleObject, SignalObjectAndWait, + WaitForMultipleObjects, CreateSemaphore, ReleaseSemaphore, CreateThread +}; + +#endif + +void +mono_debugger_event (MonoDebuggerEvent event, gpointer data, gpointer data2) +{ + if (mono_debugger_event_handler) + (* mono_debugger_event_handler) (event, data, data2); +} + +void +mono_debug_init (int in_the_debugger) +{ + if (mono_debug_initialized) + return; + + InitializeCriticalSection (&debugger_lock_mutex); + mono_debug_initialized = TRUE; + running_in_the_mono_debugger = in_the_debugger; +} + +gpointer +mono_debug_create_notification_function (gpointer *notification_address) +{ + guint8 *ptr, *buf; + + ptr = buf = g_malloc0 (16); + mono_debug_codegen_breakpoint (&buf); + if (notification_address) + *notification_address = buf; + mono_debug_codegen_ret (&buf); + + return ptr; +} + +void +mono_debug_lock (void) +{ + if (mono_debug_initialized) + EnterCriticalSection (&debugger_lock_mutex); +} + +void +mono_debug_unlock (void) +{ + if (mono_debug_initialized) + LeaveCriticalSection (&debugger_lock_mutex); +} + +static void +free_method_info (MonoDebugMethodInfo *minfo) +{ + DebugMethodInfo *priv = minfo->user_data; + + if (priv) { + g_free (priv->name); + g_free (priv); + } + + if (minfo->jit) { + g_array_free (minfo->jit->line_numbers, TRUE); + g_free (minfo->jit->this_var); + g_free (minfo->jit->params); + g_free (minfo->jit->locals); + g_free (minfo->jit); + } + + g_free (minfo->il_offsets); + g_free (minfo); +} + +static void +free_wrapper_info (DebugWrapperInfo *winfo) +{ + g_free (winfo); +} + +static void +debug_arg_warning (const char *message) +{ + g_warning ("Error while processing --debug-args arguments: %s", message); +} + +MonoDebugHandle* +mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args) +{ + MonoDebugHandle *debug; + const char **ptr; + + g_assert (!mono_debug_handle); + + debug = g_new0 (MonoDebugHandle, 1); + debug->name = g_strdup (assembly->image->name); + debug->format = format; + debug->source_files = g_ptr_array_new (); + debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION); + debug->next_idx = 100; + debug->dirty = TRUE; + + debug->type_hash = g_hash_table_new (NULL, NULL); + + debug->images = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) mono_debug_close_assembly); + + for (ptr = args; ptr && *ptr; ptr++) { + const char *arg = *ptr; + gchar *message; + + switch (debug->format) { + case MONO_DEBUG_FORMAT_STABS: + case MONO_DEBUG_FORMAT_DWARF2: + if (!strncmp (arg, "filename=", 9)) { + if (debug->filename) + debug_arg_warning ("The `filename' argument can be given only once."); + debug->filename = g_strdup (arg + 9); + continue; + } else if (!strncmp (arg, "objfile=", 8)) { + if (debug->objfile) + debug_arg_warning ("The `objfile' argument can be given only once."); + debug->objfile = g_strdup (arg + 8); + continue; + } + break; + case MONO_DEBUG_FORMAT_MONO: + debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES | + MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES; + break; + default: + break; + } + + if (debug->format != MONO_DEBUG_FORMAT_MONO) { + if (!strcmp (arg, "dont_assemble")) { + debug->flags |= MONO_DEBUG_FLAGS_DONT_ASSEMBLE; + continue; + } else if (!strcmp (arg, "update_on_exit")) { + debug->flags |= MONO_DEBUG_FLAGS_UPDATE_ON_EXIT; + continue; + } else if (!strcmp (arg, "install_il_files")) { + debug->flags |= MONO_DEBUG_FLAGS_INSTALL_IL_FILES; + continue; + } else if (!strcmp (arg, "dont_update_il_files")) { + debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES; + continue; + } else if (!strcmp (arg, "dont_create_il_files")) { + debug->flags |= MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES; + continue; + } + } + + message = g_strdup_printf ("Unknown argument `%s'.", arg); + debug_arg_warning (message); + g_free (message); + } + + switch (debug->format) { + case MONO_DEBUG_FORMAT_STABS: + if (!debug->filename) + debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name)); + if (!debug->objfile) + debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name)); + break; + case MONO_DEBUG_FORMAT_DWARF2: + if (!debug->filename) + debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name)); + if (!debug->objfile) + debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name)); + break; + case MONO_DEBUG_FORMAT_MONO: + mono_debugger_class_init_func = mono_debug_add_type; + break; + default: + g_assert_not_reached (); + } + + mono_debug_lock (); + release_symbol_file_table (); + + mono_debug_handle = debug; + mono_install_assembly_load_hook (mono_debug_add_assembly, NULL); + + mono_debug_open_image (mono_debug_handle, assembly->image); + mono_debug_open_image (mono_debug_handle, mono_defaults.corlib); + + mono_debug_add_type (mono_defaults.object_class); + mono_debug_add_type (mono_defaults.object_class); + mono_debug_add_type (mono_defaults.byte_class); + mono_debug_add_type (mono_defaults.void_class); + mono_debug_add_type (mono_defaults.boolean_class); + mono_debug_add_type (mono_defaults.sbyte_class); + mono_debug_add_type (mono_defaults.int16_class); + mono_debug_add_type (mono_defaults.uint16_class); + mono_debug_add_type (mono_defaults.int32_class); + mono_debug_add_type (mono_defaults.uint32_class); + mono_debug_add_type (mono_defaults.int_class); + mono_debug_add_type (mono_defaults.uint_class); + mono_debug_add_type (mono_defaults.int64_class); + mono_debug_add_type (mono_defaults.uint64_class); + mono_debug_add_type (mono_defaults.single_class); + mono_debug_add_type (mono_defaults.double_class); + mono_debug_add_type (mono_defaults.char_class); + mono_debug_add_type (mono_defaults.string_class); + mono_debug_add_type (mono_defaults.enum_class); + mono_debug_add_type (mono_defaults.array_class); + mono_debug_add_type (mono_defaults.multicastdelegate_class); + mono_debug_add_type (mono_defaults.asyncresult_class); + mono_debug_add_type (mono_defaults.waithandle_class); + mono_debug_add_type (mono_defaults.typehandle_class); + mono_debug_add_type (mono_defaults.fieldhandle_class); + mono_debug_add_type (mono_defaults.methodhandle_class); + mono_debug_add_type (mono_defaults.monotype_class); + mono_debug_add_type (mono_defaults.exception_class); + mono_debug_add_type (mono_defaults.threadabortexception_class); + mono_debug_add_type (mono_defaults.thread_class); + mono_debug_add_type (mono_defaults.transparent_proxy_class); + mono_debug_add_type (mono_defaults.real_proxy_class); + mono_debug_add_type (mono_defaults.mono_method_message_class); + mono_debug_add_type (mono_defaults.appdomain_class); + mono_debug_add_type (mono_defaults.field_info_class); + mono_debug_add_type (mono_defaults.stringbuilder_class); + mono_debug_add_type (mono_defaults.math_class); + mono_debug_add_type (mono_defaults.stack_frame_class); + mono_debug_add_type (mono_defaults.stack_trace_class); + mono_debug_add_type (mono_defaults.marshal_class); + mono_debug_add_type (mono_defaults.iserializeable_class); + mono_debug_add_type (mono_defaults.serializationinfo_class); + mono_debug_add_type (mono_defaults.streamingcontext_class); + + mono_debug_update_symbol_file_table (); + + mono_debug_unlock (); + + return debug; +} + +static void +mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data) +{ + if (!mono_debug_handle) + return; + + mono_debug_lock (); + mono_debug_open_image (mono_debug_handle, assembly->image); + mono_debug_unlock (); +} + +static void +generate_il_offsets (AssemblyDebugInfo *info, MonoMethod *method) +{ + GPtrArray *il_offsets = g_ptr_array_new (); + MonoClass *klass = method->klass; + MonoDebugMethodInfo *minfo; + DebugMethodInfo *priv; + int i; + + g_assert (klass->image == info->image); + + /* FIXME: doesn't work yet. */ + if (!strcmp (klass->name_space, "System.Runtime.Remoting.Proxies")) + return; + + mono_class_init (klass); + + minfo = g_new0 (MonoDebugMethodInfo, 1); + minfo->method = method; + minfo->user_data = priv = g_new0 (DebugMethodInfo, 1); + + priv->name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "", + klass->name, method->name); + priv->source_file = info->source_file; + priv->info = info; + + /* + * Find the method index in the image. + */ + for (i = 0; klass->methods && i < klass->method.count; ++i) { + if (klass->methods [i] == minfo->method) { + priv->method_number = klass->method.first + i + 1; + priv->first_line = info->mlines [priv->method_number]; + break; + } + } + + g_assert (priv->method_number); + + /* info->moffsets contains -1 "outside" of functions. */ + for (i = priv->first_line; (i > 0) && (info->moffsets [i] == 0); i--) + ; + priv->start_line = i + 1; + + for (i = priv->start_line; info->moffsets [i] != -1; i++) { + MonoSymbolFileLineNumberEntry *lne = g_new0 (MonoSymbolFileLineNumberEntry, 1); + + if (!info->moffsets [i] && (i > priv->start_line)) + continue; + + lne->offset = info->moffsets [i]; + lne->row = i; + + g_ptr_array_add (il_offsets, lne); + } + + priv->last_line = i; + + minfo->start_line = priv->first_line; + minfo->end_line = priv->last_line; + + minfo->num_il_offsets = il_offsets->len; + minfo->il_offsets = g_new0 (MonoSymbolFileLineNumberEntry, il_offsets->len); + for (i = 0; i < il_offsets->len; i++) { + MonoSymbolFileLineNumberEntry *il = g_ptr_array_index (il_offsets, i); + + minfo->il_offsets [i] = *il; + } + + g_ptr_array_free (il_offsets, TRUE); + + g_hash_table_insert (info->methods, method, minfo); +} + +static void +debug_load_method_lines (AssemblyDebugInfo* info) +{ + MonoTableInfo *table = &info->image->tables [MONO_TABLE_METHOD]; + FILE *f; + char buf [1024]; + int i, mnum, idx; + int offset = -1; + + if (info->always_create_il || !(info->handle->flags & MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES)) { + char *command = g_strdup_printf ("monodis --output=%s %s", + info->ilfile, info->image->name); + struct stat stata, statb; + int need_update = FALSE; + + if (stat (info->image->name, &stata)) { + g_warning ("cannot access assembly file (%s): %s", + info->image->name, g_strerror (errno)); + g_free (command); + return; + } + + /* If the stat() failed or the file is older. */ + if (stat (info->ilfile, &statb)) { + need_update = TRUE; + } else if (statb.st_mtime < stata.st_mtime) + need_update = TRUE; + + if (need_update) { +#ifndef PLATFORM_WIN32 + struct sigaction act, oldact; + sigset_t old_set; +#endif + int ret; + +#ifndef PLATFORM_WIN32 + act.sa_handler = SIG_IGN; + act.sa_flags = SA_NOCLDSTOP | SA_RESTART; + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGCHLD); + sigprocmask (SIG_BLOCK, &act.sa_mask, &old_set); + sigaction (SIGCHLD, &act, &oldact); +#endif + + g_print ("Recreating %s from %s.\n", info->ilfile, info->image->name); + + ret = system (command); + +#ifndef PLATFORM_WIN32 + sigaction (SIGCHLD, &oldact, NULL); + sigprocmask (SIG_SETMASK, &old_set, NULL); +#endif + + if (ret) { + g_warning ("cannot create IL assembly file (%s): %s", + command, g_strerror (errno)); + g_free (command); + return; + } + } + } + + /* use an env var with directories for searching. */ + if (!(f = fopen (info->ilfile, "r"))) { + g_warning ("cannot open IL assembly file %s", info->ilfile); + return; + } + + info->total_lines = 100; + info->moffsets = g_malloc (info->total_lines * sizeof (int)); + + i = 0; + while (fgets (buf, sizeof (buf), f)) { + int pos = i; + + info->moffsets [i++] = offset; + if (i + 2 >= info->total_lines) { + info->total_lines += 100; + info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int)); + g_assert (info->moffsets); + } + + if (!sscanf (buf, " // method line %d", &mnum)) + continue; + + offset = 0; + + if (mnum >= info->nmethods) + break; + + while (fgets (buf, sizeof (buf), f)) { + int newoffset; + + ++i; + if (i + 2 >= info->total_lines) { + info->total_lines += 100; + info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int)); + g_assert (info->moffsets); + } + + if (strstr (buf, "}")) { + offset = -1; + break; + } + + if (sscanf (buf, " IL_%x:", &newoffset)) { + offset = newoffset; + if (!offset) + pos = i; + } + + info->moffsets [i] = offset; + } + /* g_print ("method %d found at %d\n", mnum, pos); */ + info->mlines [mnum] = pos; + } + fclose (f); + + for (idx = 1; idx <= table->rows; idx++) { + guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx); + MonoMethod *method = mono_get_method (info->image, token, NULL); + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + continue; + + if (method->wrapper_type != MONO_WRAPPER_NONE) + continue; + + generate_il_offsets (info, method); + } +} + +void +_mono_debug_generate_line_number (MonoDebugMethodInfo *minfo, guint32 address, guint32 offset, int debug) +{ + int i; + + if (debug) + g_message (G_STRLOC ": searching IL offset %x", offset); + + for (i = minfo->num_il_offsets - 1; i >= 0; i--) { + MonoDebugLineNumberEntry *lne; + + if (minfo->il_offsets [i].offset > offset) + continue; + + if (debug) + g_message (G_STRLOC ": found entry %d: offset = %x, row = %d", + i, minfo->il_offsets [i].offset, minfo->il_offsets [i].row); + + if (minfo->jit->line_numbers->len) { + MonoDebugLineNumberEntry last = g_array_index ( + minfo->jit->line_numbers, MonoDebugLineNumberEntry, + minfo->jit->line_numbers->len - 1); + + /* Avoid writing more than one entry for the same line. */ + if (minfo->il_offsets [i].row == last.line) { + if (debug) + g_message (G_STRLOC ": skipping line: line = %d, last line = %d, " + "last address = %x, address = %x, " + "last offset = %x, offset = %x", + last.line, minfo->il_offsets [i].row, + last.address, address, last.offset, offset); + + return; + } + } + + if (debug) + g_message (G_STRLOC ": writing entry: line = %d, offfset = %x, address = %x", + minfo->il_offsets [i].row, offset, address); + + lne = g_new0 (MonoDebugLineNumberEntry, 1); + lne->address = address; + lne->offset = offset; + lne->line = minfo->il_offsets [i].row; + + g_array_append_val (minfo->jit->line_numbers, *lne); + return; + } +} + +AssemblyDebugInfo * +_mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image) +{ + return g_hash_table_lookup (debug->images, image); +} + +static AssemblyDebugInfo * +mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image) +{ + AssemblyDebugInfo *info; + MonoAssembly **ptr; + + info = _mono_debug_get_image (debug, image); + if (info != NULL) + return info; + + debug->dirty = TRUE; + + info = g_new0 (AssemblyDebugInfo, 1); + info->image = image; + info->image->ref_count++; + info->name = g_strdup (image->assembly_name); + info->format = debug->format; + info->handle = debug; + info->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) free_method_info); + info->wrapper_methods = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) free_wrapper_info); + + g_hash_table_insert (debug->images, image, info); + + info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1; + info->mlines = g_new0 (int, info->nmethods); + + for (ptr = image->references; ptr && *ptr; ptr++) + mono_debug_add_assembly (*ptr, NULL); + + if (image->assembly->dynamic) + return info; + + switch (info->format) { + case MONO_DEBUG_FORMAT_STABS: + case MONO_DEBUG_FORMAT_DWARF2: + if (debug->flags & MONO_DEBUG_FLAGS_INSTALL_IL_FILES) { + gchar *dirname = g_path_get_dirname (image->name); + info->ilfile = g_strdup_printf ("%s/%s.il", dirname, info->name); + g_free (dirname); + } else + info->ilfile = g_strdup_printf ("%s.il", info->name); + info->source_file = debug->source_files->len; + g_ptr_array_add (debug->source_files, info->ilfile); + break; + case MONO_DEBUG_FORMAT_MONO: + info->symfile = mono_debug_open_mono_symbol_file (info->image, running_in_the_mono_debugger); + mono_debugger_symbol_file_table_generation++; + break; + + default: + break; + } + + if (debug->format != MONO_DEBUG_FORMAT_MONO) + debug_load_method_lines (info); + + return info; +} + +void +mono_debug_write_symbols (MonoDebugHandle *debug) +{ + if (!debug || !debug->dirty) + return; + + release_symbol_file_table (); + + switch (debug->format) { + case MONO_DEBUG_FORMAT_STABS: + mono_debug_write_stabs (debug); + break; + case MONO_DEBUG_FORMAT_DWARF2: + mono_debug_write_dwarf2 (debug); + break; + case MONO_DEBUG_FORMAT_MONO: + break; + default: + g_assert_not_reached (); + } + + debug->dirty = FALSE; +} + +void +mono_debug_make_symbols (void) +{ + if (!mono_debug_handle || !mono_debug_handle->dirty) + return; + + switch (mono_debug_handle->format) { + case MONO_DEBUG_FORMAT_STABS: + mono_debug_write_stabs (mono_debug_handle); + break; + case MONO_DEBUG_FORMAT_DWARF2: + mono_debug_write_dwarf2 (mono_debug_handle); + break; + case MONO_DEBUG_FORMAT_MONO: + mono_debug_update_symbol_file_table (); + break; + default: + g_assert_not_reached (); + } + + mono_debug_handle->dirty = FALSE; +} + +static void +mono_debug_close_assembly (AssemblyDebugInfo* info) +{ + switch (info->format) { + case MONO_DEBUG_FORMAT_MONO: + if (info->symfile != NULL) + mono_debug_close_mono_symbol_file (info->symfile); + break; + default: + break; + } + g_hash_table_destroy (info->methods); + g_hash_table_destroy (info->wrapper_methods); + g_free (info->mlines); + g_free (info->moffsets); + g_free (info->name); + g_free (info->ilfile); + g_free (info->filename); + g_free (info->objfile); + g_free (info); +} + +void +mono_debug_cleanup (void) +{ + release_symbol_file_table (); + + if (!mono_debug_handle) + return; + + if (mono_debug_handle->flags & MONO_DEBUG_FLAGS_UPDATE_ON_EXIT) + mono_debug_write_symbols (mono_debug_handle); + + g_hash_table_destroy (mono_debug_handle->images); + g_ptr_array_free (mono_debug_handle->source_files, FALSE); + g_hash_table_destroy (mono_debug_handle->type_hash); + g_free (mono_debug_handle->producer_name); + g_free (mono_debug_handle->name); + g_free (mono_debug_handle); + + mono_debug_handle = NULL; +} + +guint32 +mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass) +{ + guint index, i; + + mono_class_init (klass); + + index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass)); + if (index) + return index; + + debug->dirty = TRUE; + + index = ++debug->next_klass_idx; + g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index)); + + if (klass->enumtype) + return index; + + switch (klass->byval_arg.type) { + case MONO_TYPE_CLASS: + if (klass->parent) + mono_debug_get_type (debug, klass->parent); + + for (i = 0; i < klass->method.count; i++) { + MonoMethod *method = klass->methods [i]; + MonoType *ret_type = NULL; + int j; + + if (!MONO_TYPE_IS_VOID (method->signature->ret)) + ret_type = method->signature->ret; + + if (ret_type) { + MonoClass *ret_klass = mono_class_from_mono_type (ret_type); + mono_debug_get_type (debug, ret_klass); + } + + for (j = 0; j < method->signature->param_count; j++) { + MonoType *sub_type = method->signature->params [j]; + MonoClass *sub_klass = mono_class_from_mono_type (sub_type); + mono_debug_get_type (debug, sub_klass); + } + } + // fall through + case MONO_TYPE_VALUETYPE: + for (i = 0; i < klass->field.count; i++) { + MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type); + mono_debug_get_type (debug, subclass); + } + break; + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + mono_debug_get_type (debug, klass->element_class); + break; + default: + break; + } + + return index; +} + +static gint32 +il_offset_from_address (MonoDebugMethodInfo *minfo, guint32 address) +{ + int i; + + if (!minfo->jit || !minfo->jit->line_numbers) + return -1; + + for (i = minfo->jit->line_numbers->len - 1; i >= 0; i--) { + MonoDebugLineNumberEntry lne = g_array_index ( + minfo->jit->line_numbers, MonoDebugLineNumberEntry, i); + + if (lne.address <= address) + return lne.offset; + } + + return -1; +} + +void +mono_debug_add_type (MonoClass *klass) +{ + AssemblyDebugInfo* info; + + if (!mono_debug_handle) + return; + + info = _mono_debug_get_image (mono_debug_handle, klass->image); + g_assert (info); + + if (mono_debug_handle->format != MONO_DEBUG_FORMAT_MONO) + return; + + if (info->symfile) { + mono_debug_lock (); + mono_debug_symfile_add_type (info->symfile, klass); + mono_debugger_event (MONO_DEBUGGER_EVENT_TYPE_ADDED, info->symfile, klass); + mono_debug_unlock (); + } +} + +struct LookupMethodData +{ + MonoDebugMethodInfo *minfo; + MonoMethod *method; +}; + +static void +lookup_method_func (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + struct LookupMethodData *data = (struct LookupMethodData *) user_data; + + if (data->minfo) + return; + + if (info->symfile) + data->minfo = mono_debug_find_method (info->symfile, data->method); + else + data->minfo = g_hash_table_lookup (info->methods, data->method); +} + +MonoDebugMethodInfo * +_mono_debug_lookup_method (MonoMethod *method) +{ + struct LookupMethodData data = { NULL, method }; + + if (!mono_debug_handle) + return NULL; + + g_hash_table_foreach (mono_debug_handle->images, lookup_method_func, &data); + return data.minfo; +} + +void +mono_debug_add_wrapper (MonoMethod *method, MonoMethod *wrapper_method) +{ + MonoClass *klass = method->klass; + AssemblyDebugInfo* info; + MonoDebugMethodInfo *minfo; + DebugWrapperInfo *winfo; + MonoDebugMethodJitInfo *jit; + + if (!mono_debug_handle) + return; + + if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) + return; + + mono_class_init (klass); + + info = _mono_debug_get_image (mono_debug_handle, klass->image); + g_assert (info); + + minfo = _mono_debug_lookup_method (method); + if (!minfo || minfo->jit) + return; + + winfo = g_hash_table_lookup (info->wrapper_methods, wrapper_method); + g_assert (winfo); + + mono_debug_lock (); + + mono_debug_handle->dirty = TRUE; + + minfo->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1); + jit->code_start = winfo->code_start; + jit->code_size = winfo->code_size; + jit->prologue_end = 0; + jit->epilogue_begin = winfo->code_size; + jit->num_params = 0; + jit->wrapper_addr = method->addr; + + if (info->symfile) { + mono_debug_symfile_add_method (info->symfile, method); + mono_debugger_event (MONO_DEBUGGER_EVENT_METHOD_ADDED, info->symfile, method); + } + + mono_debug_unlock (); +} + +gchar * +mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number) +{ + MonoDebugMethodInfo *minfo = _mono_debug_lookup_method (method); + + if (!minfo) + return NULL; + + if (minfo->symfile) { + gint32 offset = il_offset_from_address (minfo, address); + + if (offset < 0) + return NULL; + + return mono_debug_find_source_location (minfo->symfile, method, offset, line_number); + } + + return NULL; +} + +gint32 +mono_debug_il_offset_from_address (MonoMethod *method, gint32 address) +{ + MonoDebugMethodInfo *minfo; + + if (address < 0) + return -1; + + minfo = _mono_debug_lookup_method (method); + if (!minfo || !minfo->il_offsets) + return -1; + + return il_offset_from_address (minfo, address); +} + +gint32 +mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset) +{ + MonoDebugMethodInfo *minfo; + + if (il_offset < 0) + return -1; + + minfo = _mono_debug_lookup_method (method); + if (!minfo || !minfo->il_offsets) + return -1; + + return _mono_debug_address_from_il_offset (minfo, il_offset); +} + +static void +release_symbol_file_table () +{ + MonoDebuggerSymbolFileTable *temp; + + if (!mono_debugger_symbol_file_table) + return; + + /* + * Caution: The debugger may access the memory pointed to by this variable + * at any time. It is very important to set the pointer to NULL + * before freeing the area. + */ + + temp = mono_debugger_symbol_file_table; + mono_debugger_symbol_file_table = NULL; + g_free (mono_debugger_symbol_file_table); +} + +static void +update_symbol_file_table_count_func (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + + if (!info->symfile) + return; + if (info->format != MONO_DEBUG_FORMAT_MONO) + return; + + ++ (* (int *) user_data); +} + +struct SymfileTableData +{ + MonoDebuggerSymbolFileTable *symfile_table; + int index; +}; + +static void +update_symbol_file_table_func (gpointer key, gpointer value, gpointer user_data) +{ + AssemblyDebugInfo *info = (AssemblyDebugInfo *) value; + struct SymfileTableData *data = (struct SymfileTableData *) user_data; + + if (!info->symfile) + return; + if (info->format != MONO_DEBUG_FORMAT_MONO) + return; + + data->symfile_table->symfiles [data->index++] = info->symfile; +} + +int +mono_debug_update_symbol_file_table (void) +{ + int count = 0; + MonoDebuggerSymbolFileTable *symfile_table; + struct SymfileTableData data; + guint32 size; + + if (!mono_debug_handle) + return FALSE; + + mono_debug_lock (); + + g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_count_func, &count); + + release_symbol_file_table (); + + size = sizeof (MonoDebuggerSymbolFileTable) + count * sizeof (MonoSymbolFile *); + symfile_table = g_malloc0 (size); + symfile_table->magic = MONO_SYMBOL_FILE_DYNAMIC_MAGIC; + symfile_table->version = MONO_SYMBOL_FILE_DYNAMIC_VERSION; + symfile_table->total_size = size; + symfile_table->count = count; + symfile_table->generation = mono_debugger_symbol_file_table_generation; + symfile_table->global_symfile = mono_debugger_global_symbol_file; + + data.symfile_table = symfile_table; + data.index = 0; + + g_hash_table_foreach (mono_debug_handle->images, update_symbol_file_table_func, &data); + + mono_debugger_symbol_file_table = symfile_table; + + mono_debug_unlock (); + + return TRUE; +} + +static GPtrArray *breakpoints = NULL; + +int +mono_insert_breakpoint_full (MonoMethodDesc *desc, gboolean use_trampoline) +{ + static int last_breakpoint_id = 0; + MonoDebuggerBreakpointInfo *info; + + info = g_new0 (MonoDebuggerBreakpointInfo, 1); + info->desc = desc; + info->use_trampoline = use_trampoline; + info->index = ++last_breakpoint_id; + + if (!breakpoints) + breakpoints = g_ptr_array_new (); + + g_ptr_array_add (breakpoints, info); + + return info->index; +} + +int +mono_remove_breakpoint (int breakpoint_id) +{ + int i; + + if (!breakpoints) + return 0; + + for (i = 0; i < breakpoints->len; i++) { + MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i); + + if (info->index != breakpoint_id) + continue; + + mono_method_desc_free (info->desc); + g_ptr_array_remove (breakpoints, info); + g_free (info); + return 1; + } + + return 0; +} + +int +mono_insert_breakpoint (const gchar *method_name, gboolean include_namespace) +{ + MonoMethodDesc *desc; + + desc = mono_method_desc_new (method_name, include_namespace); + if (!desc) + return 0; + + return mono_insert_breakpoint_full (desc, running_in_the_mono_debugger); +} + +int +mono_method_has_breakpoint (MonoMethod* method, gboolean use_trampoline) +{ + int i; + + if (!breakpoints || (method->wrapper_type != MONO_WRAPPER_NONE)) + return 0; + + for (i = 0; i < breakpoints->len; i++) { + MonoDebuggerBreakpointInfo *info = g_ptr_array_index (breakpoints, i); + + if (info->use_trampoline != use_trampoline) + continue; + + if (!mono_method_desc_full_match (info->desc, method)) + continue; + + return info->index; + } + + return 0; +} + +void +mono_debugger_trampoline_breakpoint_callback (void) +{ + mono_debugger_event (MONO_DEBUGGER_EVENT_BREAKPOINT_TRAMPOLINE, NULL, NULL); +} diff --git a/mono/mini/debug.h b/mono/mini/debug.h new file mode 100644 index 00000000000..844abb9c257 --- /dev/null +++ b/mono/mini/debug.h @@ -0,0 +1,163 @@ +/* + * This is a copy of mono/mono/jit/debug.h. + * + * Please do *not* modify this copy here, if you need to make changes, do them + * in the original and then copy it over again. + * + * All mini-specific stuff is in debug-mini.c. + */ + +#ifndef __MONO_JIT_DEBUG_H__ +#define __MONO_JIT_DEBUG_H__ + +#include +#include +#include +#include +#include + +typedef struct _MonoDebugHandle MonoDebugHandle; +typedef struct _MonoDebuggerSymbolFileTable MonoDebuggerSymbolFileTable; +typedef struct _MonoDebuggerBreakpointInfo MonoDebuggerBreakpointInfo; + +typedef struct _MonoDebuggerIOLayer MonoDebuggerIOLayer; + +typedef enum { + MONO_DEBUG_FORMAT_NONE, + MONO_DEBUG_FORMAT_STABS, + MONO_DEBUG_FORMAT_DWARF2, + MONO_DEBUG_FORMAT_MONO +} MonoDebugFormat; + +typedef enum { + MONO_DEBUGGER_EVENT_TYPE_ADDED, + MONO_DEBUGGER_EVENT_METHOD_ADDED, + MONO_DEBUGGER_EVENT_BREAKPOINT_TRAMPOLINE, + MONO_DEBUGGER_EVENT_THREAD_CREATED +} MonoDebuggerEvent; + +#ifndef PLATFORM_WIN32 + +/* + * Functions we export to the debugger. + */ +struct _MonoDebuggerIOLayer +{ + void (*InitializeCriticalSection) (WapiCriticalSection *section); + void (*DeleteCriticalSection) (WapiCriticalSection *section); + gboolean (*TryEnterCriticalSection) (WapiCriticalSection *section); + void (*EnterCriticalSection) (WapiCriticalSection *section); + void (*LeaveCriticalSection) (WapiCriticalSection *section); + + guint32 (*WaitForSingleObject) (gpointer handle, guint32 timeout); + guint32 (*SignalObjectAndWait) (gpointer signal_handle, gpointer wait, + guint32 timeout, gboolean alertable); + guint32 (*WaitForMultipleObjects) (guint32 numobjects, gpointer *handles, + gboolean waitall, guint32 timeout); + + gpointer (*CreateSemaphore) (WapiSecurityAttributes *security, + gint32 initial, gint32 max, + const guchar *name); + gboolean (*ReleaseSemaphore) (gpointer handle, gint32 count, gint32 *prevcount); + + gpointer (*CreateThread) (WapiSecurityAttributes *security, + guint32 stacksize, WapiThreadStart start, + gpointer param, guint32 create, guint32 *tid); +}; + +extern MonoDebuggerIOLayer mono_debugger_io_layer; + +#endif + +extern void (*mono_debugger_event_handler) (MonoDebuggerEvent event, gpointer data, gpointer data2); + +extern MonoDebugFormat mono_debug_format; + +MonoDebugHandle* mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args); + +void mono_debug_cleanup (void); + +void mono_debug_add_wrapper (MonoMethod *method, MonoMethod *wrapper_method); + +void mono_debug_add_type (MonoClass *klass); + +gchar * mono_debug_source_location_from_address (MonoMethod *method, guint32 address, + guint32 *line_number); + +gint32 mono_debug_il_offset_from_address (MonoMethod *method, gint32 address); + +gint32 mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset); + +int mono_method_has_breakpoint (MonoMethod* method, gboolean use_trampoline); + +int mono_insert_breakpoint (const gchar *method_name, gboolean include_namespace); + +int mono_insert_breakpoint_full (MonoMethodDesc *desc, gboolean use_trampoline); + +int mono_remove_breakpoint (int breakpint_id); + +void mono_debugger_trampoline_breakpoint_callback (void); + +void mono_debugger_event (MonoDebuggerEvent event, gpointer data, gpointer data2); + +gpointer mono_debug_create_notification_function (gpointer *notification_address); + +void mono_debug_init (int running_in_the_mono_debugger); +void mono_debug_lock (void); +void mono_debug_unlock (void); +int mono_debug_update_symbol_file_table (void); + + +/* DEBUGGER PUBLIC FUNCTION: + * + * This is a public function which is supposed to be called from within a debugger + * each time the program stops. It's used to recreate the symbol file to tell the + * debugger about method addresses and such things. After calling this function, + * you must tell your debugger to reload its symbol file. + */ +void mono_debug_make_symbols (void); + +void mono_debug_write_symbols (MonoDebugHandle* debug); + +/* + * Address of the x86 trampoline code. This is used by the debugger to check + * whether a method is a trampoline. + */ +extern guint8 *mono_generic_trampoline_code; + +/* + * Address of a special breakpoint code which is used by the debugger to get a breakpoint + * after compiling a method. + */ +extern guint8 *mono_breakpoint_trampoline_code; + +/* This is incremented each time the symbol table is modified. + * The debugger looks at this variable and if it has a higher value than its current + * copy of the symbol table, it must call debugger_update_symbol_file_table(). + */ +extern guint32 mono_debugger_symbol_file_table_generation; +extern guint32 mono_debugger_symbol_file_table_modified; + +/* Caution: This variable may be accessed at any time from the debugger; + * it is very important not to modify the memory it is pointing to + * without previously setting this pointer back to NULL. + */ +extern MonoDebuggerSymbolFileTable *mono_debugger_symbol_file_table; + +struct _MonoDebuggerSymbolFileTable { + guint64 magic; + guint32 version; + guint32 total_size; + guint32 count; + guint32 generation; + MonoGlobalSymbolFile *global_symfile; + MonoSymbolFile *symfiles [MONO_ZERO_LEN_ARRAY]; +}; + +struct _MonoDebuggerBreakpointInfo { + guint32 index; + gboolean use_trampoline; + MonoMethodDesc *desc; +}; + +#endif /* __MONO_JIT_DEBUG_H__ */ diff --git a/mono/mini/debugger-main.c b/mono/mini/debugger-main.c new file mode 100644 index 00000000000..272d9092306 --- /dev/null +++ b/mono/mini/debugger-main.c @@ -0,0 +1,28 @@ +#include "mini.h" + +int +main (int argc, char **argv, char **envp) +{ + MonoDomain *domain; + const char *file; + int retval; + + g_assert (argc >= 3); + file = argv [2]; + + g_set_prgname (file); + + mini_parse_default_optimizations (argv [1]); + + domain = mini_init (argv [0]); + + mono_config_parse (NULL); + //mono_set_rootdir (); + + retval = mono_debugger_main (domain, file, argc, argv, envp); + + mini_cleanup (domain); + + return retval; +} + diff --git a/mono/mini/dominators.c b/mono/mini/dominators.c new file mode 100644 index 00000000000..f213c0a51c4 --- /dev/null +++ b/mono/mini/dominators.c @@ -0,0 +1,479 @@ +/* + * dominators.c: Dominator computation on the control flow graph + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include +#include + +#include "mini.h" + +//#define DEBUG_DOMINATORS + +/* the simpler, dumber algorithm */ +static void +compute_dominators (MonoCompile *m) { + int change = TRUE; + int i, j, bitsize; + MonoBasicBlock *bb; + MonoBitSet *T; + char* mem; + + g_assert (!(m->comp_done & MONO_COMP_DOM)); + + bitsize = mono_bitset_alloc_size (m->num_bblocks, 0); + /* the first is always the entry */ + bb = m->bblocks [0]; + mem = mono_mempool_alloc0 (m->mempool, bitsize * (m->num_bblocks + 1)); + bb->dominators = mono_bitset_mem_new (mem, m->num_bblocks, 0); + + mem += bitsize; + mono_bitset_set (bb->dominators, 0); + + T = mono_bitset_mem_new (mem, m->num_bblocks, 0); + mem += bitsize; + + + for (i = 1; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + bb->dominators = mono_bitset_mem_new (mem, m->num_bblocks, 0); + mem += bitsize; + mono_bitset_invert (bb->dominators); + +#ifdef DEBUG_DOMINATORS + printf ("BB%d IN: ", bb->block_num); + for (j = 0; j < bb->in_count; ++j) + printf ("%d ", bb->in_bb [j]->block_num); + printf ("\n"); +#endif + } + + do { + change = FALSE; + for (i = 1; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + mono_bitset_set_all (T); + for (j = 0; j < bb->in_count; ++j) { + if (bb->in_bb [j]->dominators) + mono_bitset_intersection (T, bb->in_bb [j]->dominators); + } + mono_bitset_set (T, i); + if (!mono_bitset_equal (T, bb->dominators)) { + change = TRUE; + mono_bitset_copyto (T, bb->dominators); + } + } + } while (change); + + m->comp_done |= MONO_COMP_DOM; + +#ifdef DEBUG_DOMINATORS + printf ("DTREE %s %d\n", mono_method_full_name (m->method, TRUE), + ((MonoMethodNormal *)m->method)->header->num_clauses); + for (i = 0; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + printf ("BB%d: ", bb->block_num); + mono_blockset_print (m, bb->dominators, NULL, -1); + } +#endif +} + +static void +compute_idominators (MonoCompile* m) { + char *mem; + int bitsize, i, s, t; + MonoBitSet **T, *temp; + MonoBasicBlock *bb; + + g_assert (!(m->comp_done & MONO_COMP_IDOM)); + + bitsize = mono_bitset_alloc_size (m->num_bblocks, 0); + mem = mono_mempool_alloc (m->mempool, bitsize * (m->num_bblocks + 1)); + T = mono_mempool_alloc (m->mempool, sizeof (MonoBitSet*) * m->num_bblocks); + + for (i = 0; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + T [i] = mono_bitset_mem_new (mem, m->num_bblocks, 0); + mono_bitset_copyto (bb->dominators, T [i]); + mono_bitset_clear (T [i], i); + if (mono_bitset_count (bb->dominators) - 1 != mono_bitset_count (T [i])) { + mono_blockset_print (m, bb->dominators, "dominators", -1); + mono_blockset_print (m, T [i], "T [i]", -1); + g_error ("problem at %d (%d)\n", i, bb->dfn); + } + mem += bitsize; + } + temp = mono_bitset_mem_new (mem, m->num_bblocks, 0); + + for (i = 1; i < m->num_bblocks; ++i) { + + temp = T [i]; + + mono_bitset_foreach_bit_rev (temp, s, m->num_bblocks) { + + mono_bitset_foreach_bit_rev (temp, t, m->num_bblocks) { + + if (t == s) + continue; + + //if (mono_bitset_test_fast (T [s], t)) + if (mono_bitset_test_fast (m->bblocks [s]->dominators, t)) + mono_bitset_clear (temp, t); + } + } + +#ifdef DEBUG_DOMINATORS + printf ("IDOMSET BB%d %d: ", m->bblocks [i]->block_num, m->num_bblocks); + mono_blockset_print (m, T [i], NULL, -1); +#endif + } + + for (i = 1; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + s = mono_bitset_find_start (T [i]); + g_assert (s != -1); + /*fixme:mono_bitset_count does not really work */ + //g_assert (mono_bitset_count (T [i]) == 1); + t = mono_bitset_find_first (T [i], s); + g_assert (t == -1 || t >= m->num_bblocks); + bb->idom = m->bblocks [s]; + bb->idom->dominated = g_list_prepend (bb->idom->dominated, bb); + } + + m->comp_done |= MONO_COMP_IDOM; +} + +static void +postorder_visit (MonoBasicBlock *start, int *idx, MonoBasicBlock **array) +{ + int i; + + /* we assume the flag was already cleared by the caller. */ + start->flags |= BB_VISITED; + /*g_print ("visit %d at %p\n", *dfn, start->cil_code);*/ + for (i = 0; i < start->out_count; ++i) { + if (start->out_bb [i]->flags & BB_VISITED) + continue; + postorder_visit (start->out_bb [i], idx, array); + } + array [*idx] = start; + (*idx)++; +} + +static void +check_dominance_frontier (MonoBasicBlock *x, MonoBasicBlock *t) +{ + int i, j; + + t->flags |= BB_VISITED; + + if (mono_bitset_test_fast (t->dominators, x->dfn)) { + for (i = 0; i < t->out_count; ++i) { + if (!(t->flags & BB_VISITED)) { + int found = FALSE; + check_dominance_frontier (x, t->out_bb [i]); + + for (j = 0; j < t->out_bb [i]->in_count; j++) { + if (t->out_bb [i]->in_bb [j] == t) + found = TRUE; + } + g_assert (found); + } + } + } else { + if (!mono_bitset_test_fast (x->dfrontier, t->dfn)) { + printf ("BB%d not in frontier of BB%d\n", t->block_num, x->block_num); + g_assert_not_reached (); + } + } +} + +#if 0 +/* there is a bug in this code */ +static void +compute_dominance_frontier_old (MonoCompile *m) { + int i, j, bitsize; + MonoBasicBlock **postorder; + MonoBasicBlock *bb, *z; + char *mem; + + g_assert (!(m->comp_done & MONO_COMP_DFRONTIER)); + + postorder = mono_mempool_alloc (m->mempool, sizeof (MonoBasicBlock*) * m->num_bblocks); + i = 0; + postorder_visit (m->bb_entry, &i, postorder); + /*g_print ("postorder traversal:"); + for (i = 0; i < m->num_bblocks; ++i) + g_print (" B%d", postorder [i]->dfn); + g_print ("\n");*/ + + /* we could reuse the bitsets allocated in compute_idominators() */ + bitsize = mono_bitset_alloc_size (m->num_bblocks, 0); + mem = mono_mempool_alloc0 (m->mempool, bitsize * m->num_bblocks); + + for (i = 0; i < m->num_bblocks; ++i) { + bb = postorder [i]; + bb->dfrontier = mono_bitset_mem_new (mem, m->num_bblocks, 0); + mem += bitsize; + } + for (i = 0; i < m->num_bblocks; ++i) { + bb = postorder [i]; + /* the local component */ + for (j = 0; j < bb->out_count; ++j) { + //if (bb->out_bb [j] != bb->idom) + if (bb->out_bb [j]->idom != bb) + mono_bitset_set (bb->dfrontier, bb->out_bb [j]->dfn); + } + } + for (i = 0; i < m->num_bblocks; ++i) { + bb = postorder [i]; + /* the up component */ + if (bb->idom) { + z = bb->idom; + mono_bitset_foreach_bit (z->dfrontier, j, m->num_bblocks) { + //if (m->bblocks [j] != bb->idom) + if (m->bblocks [j]->idom != bb) + mono_bitset_set (bb->dfrontier, m->bblocks [j]->dfn); + } + } + } + + /* this is a check for the dominator frontier */ + for (i = 0; i < m->num_bblocks; ++i) { + MonoBasicBlock *x = m->bblocks [i]; + + mono_bitset_foreach_bit ((x->dfrontier), j, (m->num_bblocks)) { + MonoBasicBlock *w = m->bblocks [j]; + int k; + /* x must not strictly dominates w */ + if (mono_bitset_test_fast (w->dominators, x->dfn) && w != x) + g_assert_not_reached (); + + for (k = 0; k < m->num_bblocks; ++k) + m->bblocks [k]->flags &= ~BB_VISITED; + + check_dominance_frontier (x, x); + } + } + + m->comp_done |= MONO_COMP_DFRONTIER; +} +#endif + +/* this is an implementation of the dominance frontier algorithm described in + * "modern compiler implementation in C" written by Andrew W. Appel + */ +static void +compute_dominance_frontier_appel (MonoCompile *m, int n) +{ + int i, j; + MonoBasicBlock *bb; + + bb = m->bblocks [n]; + g_assert (!(bb->flags & BB_VISITED)); + bb->flags |= BB_VISITED; + + for (i = 0; i < bb->out_count; ++i) { + MonoBasicBlock *y = bb->out_bb [i]; + if (y->idom != bb) { + g_assert (!(mono_bitset_test_fast (y->dominators, bb->dfn) && bb->dfn != y->dfn)); + mono_bitset_set (bb->dfrontier, y->dfn); + } + } + + + for (i = 0; i < m->num_bblocks; ++i) { + MonoBasicBlock *c = m->bblocks [i]; + if (c->idom == bb) { + if (!(c->flags & BB_VISITED)) + compute_dominance_frontier_appel (m, c->dfn); + mono_bitset_foreach_bit (c->dfrontier, j, m->num_bblocks) { + MonoBasicBlock *w = m->bblocks [j]; + if (!(mono_bitset_test_fast (w->dominators, bb->dfn) && bb->dfn != w->dfn)) + mono_bitset_set (bb->dfrontier, w->dfn); + } + } + } +} + +static void +compute_dominance_frontier (MonoCompile *m) +{ + MonoBasicBlock *bb; + char *mem; + int i, j, bitsize; + + g_assert (!(m->comp_done & MONO_COMP_DFRONTIER)); + + for (i = 0; i < m->num_bblocks; ++i) + m->bblocks [i]->flags &= ~BB_VISITED; + + /* we could reuse the bitsets allocated in compute_idominators() */ + bitsize = mono_bitset_alloc_size (m->num_bblocks, 0); + mem = mono_mempool_alloc0 (m->mempool, bitsize * m->num_bblocks); + + for (i = 0; i < m->num_bblocks; ++i) { + bb = m->bblocks [i]; + bb->dfrontier = mono_bitset_mem_new (mem, m->num_bblocks, 0); + mem += bitsize; + } + + compute_dominance_frontier_appel (m, 0); + +#if 0 + for (i = 0; i < m->num_bblocks; ++i) { + MonoBasicBlock *x = m->bblocks [i]; + + printf ("DFRONT %s BB%d: ", mono_method_full_name (m->method, TRUE), x->block_num); + mono_blockset_print (m, x->dfrontier, NULL, -1); + } +#endif + +#if 1 + /* this is a check for the dominator frontier */ + for (i = 0; i < m->num_bblocks; ++i) { + MonoBasicBlock *x = m->bblocks [i]; + + mono_bitset_foreach_bit ((x->dfrontier), j, (m->num_bblocks)) { + MonoBasicBlock *w = m->bblocks [j]; + int k; + /* x must not strictly dominates w */ + if (mono_bitset_test_fast (w->dominators, x->dfn) && w != x) + g_assert_not_reached (); + + for (k = 0; k < m->num_bblocks; ++k) + m->bblocks [k]->flags &= ~BB_VISITED; + + check_dominance_frontier (x, x); + } + } +#endif + + m->comp_done |= MONO_COMP_DFRONTIER; +} + +void +mono_compile_dominator_info (MonoCompile *cfg, int dom_flags) +{ + if ((dom_flags & MONO_COMP_DOM) && !(cfg->comp_done & MONO_COMP_DOM)) + compute_dominators (cfg); + if ((dom_flags & MONO_COMP_IDOM) && !(cfg->comp_done & MONO_COMP_IDOM)) + compute_idominators (cfg); + if ((dom_flags & MONO_COMP_DFRONTIER) && !(cfg->comp_done & MONO_COMP_DFRONTIER)) + compute_dominance_frontier (cfg); +} + +static void +df_set (MonoCompile *m, MonoBitSet* dest, MonoBitSet *set) +{ + int i; + + mono_bitset_clear_all (dest); + mono_bitset_foreach_bit (set, i, m->num_bblocks) { + mono_bitset_union (dest, m->bblocks [i]->dfrontier); + } +} + +/* TODO: alloc tmp and D on the stack */ +MonoBitSet* +mono_compile_iterated_dfrontier (MonoCompile *m, MonoBitSet *set) +{ + MonoBitSet *result, *D; + int bitsize, change = TRUE; + + bitsize = mono_bitset_alloc_size (m->num_bblocks, 0); + result = mono_bitset_mem_new (mono_mempool_alloc (m->mempool, bitsize), m->num_bblocks, 0); + D = mono_bitset_mem_new (mono_mempool_alloc (m->mempool, bitsize), m->num_bblocks, 0); + + df_set (m, result, set); + do { + change = FALSE; + df_set (m, D, result); + mono_bitset_union (D, result); + + if (!mono_bitset_equal (D, result)) { + mono_bitset_copyto (D, result); + change = TRUE; + } + } while (change); + + return result; +} + +//#define DEBUG_NATURAL_LOOPS + +/* + * code to detect loops and loop nesting level + */ +void +mono_compute_natural_loops (MonoCompile *cfg) +{ + int i, j, k; + + g_assert (!(cfg->comp_done & MONO_COMP_LOOPS)); + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *n = cfg->bblocks [i]; + + for (j = 0; j < n->out_count; j++) { + MonoBasicBlock *h = n->out_bb [j]; + /* check for back-edge from n to h */ + if (n != h && mono_bitset_test (n->dominators, h->dfn)) { + GList *todo; + + /* already in loop_blocks? */ + if (h->loop_blocks && g_list_find (h->loop_blocks, n)) + continue; + + todo = g_list_prepend (NULL, n); + + while (todo) { + MonoBasicBlock *cb = (MonoBasicBlock *)todo->data; + todo = g_list_delete_link (todo, todo); + + if (g_list_find (h->loop_blocks, cb)) + continue; + + h->loop_blocks = g_list_prepend (h->loop_blocks, cb); + cb->nesting++; + + for (k = 0; k < cb->in_count; k++) { + MonoBasicBlock *prev = cb->in_bb [k]; + /* add all previous blocks */ + if (prev != h && !g_list_find (h->loop_blocks, prev)) { + todo = g_list_prepend (todo, prev); + } + } + } + + /* add the header if not already there */ + if (!g_list_find (h->loop_blocks, h)) { + h->loop_blocks = g_list_prepend (h->loop_blocks, h); + h->nesting++; + } + } + } + } + + cfg->comp_done |= MONO_COMP_LOOPS; + +#ifdef DEBUG_NATURAL_LOOPS + for (i = 0; i < cfg->num_bblocks; ++i) { + if (cfg->bblocks [i]->loop_blocks) { + MonoBasicBlock *h = (MonoBasicBlock *)cfg->bblocks [i]->loop_blocks->data; + GList *l; + printf ("LOOP START %d\n", h->block_num); + for (l = h->loop_blocks; l; l = l->next) { + MonoBasicBlock *cb = (MonoBasicBlock *)l->data; + printf (" BB%d %d %p\n", cb->block_num, cb->nesting, cb->loop_blocks); + } + } + } +#endif + +} + diff --git a/mono/mini/driver.c b/mono/mini/driver.c new file mode 100644 index 00000000000..c06deaa7307 --- /dev/null +++ b/mono/mini/driver.c @@ -0,0 +1,733 @@ +/* + * driver.c: The new mono JIT compiler. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mono/metadata/profiler.h" +#include +#include +#include + +#include "mini.h" +#include +#include +#include "inssel.h" +#include "debug.h" + +static FILE *mini_stats_fd = NULL; + +static void mini_usage (void); + +typedef void (*OptFunc) (const char *p); + +/* keep in sync with enum in mini.h */ +typedef struct { + const char* name; + const char* desc; + const OptFunc func; +} OptName; + +static const OptName +opt_names [] = { + {"peephole", "Peephole postpass"}, + {"branch", "Branch optimizations"}, + {"inline", "Inline method calls"}, + {"cfold", "Constant folding"}, + {"consprop", "Constant propagation"}, + {"copyprop", "Copy propagation"}, + {"deadce", "Dead code elimination"}, + {"linears", "Linear scan global reg allocation"}, + {"cmov", "Conditional moves"}, + {"shared", "Emit per-domain code"}, + {"sched", "Instruction scheduling"}, + {"instrins", "Intrinsic method implementations"}, + {"tailc", "Tail recursion and tail calls"}, + {"loop", "Loop related optimizations"}, + {"fcmov", "Fast x86 FP compares"} +}; + +static guint32 +parse_optimizations (const char* p) +{ + /* the default value */ + guint32 opt = MONO_OPT_PEEPHOLE | MONO_OPT_CFOLD /* | MONO_OPT_CONSPROP | MONO_OPT_INLINE*/ | MONO_OPT_BRANCH | /* | MONO_OPT_SAHRED |*/ MONO_OPT_LINEARS; + const char *n; + int i, invert, len; + + /* call out to cpu detection code here that sets the defaults ... */ + opt |= mono_arch_cpu_optimizazions (); + if (!p) + return opt; + + while (*p) { + if (*p == '-') { + p++; + invert = TRUE; + } else { + invert = FALSE; + } + for (i = 0; i < G_N_ELEMENTS (opt_names); ++i) { + n = opt_names [i].name; + len = strlen (n); + if (strncmp (p, n, len) == 0) { + if (invert) + opt &= ~ (1 << i); + else + opt |= 1 << i; + p += len; + if (*p == ',') { + p++; + break; + } else if (*p == '=') { + p++; + if (opt_names [i].func) + opt_names [i].func (p); + while (*p && *p++ != ','); + break; + } + /* error out */ + break; + } + } + if (i == G_N_ELEMENTS (opt_names)) { + if (strncmp (p, "all", 3) == 0) { + if (invert) + opt = 0; + else + opt = ~(MONO_OPT_SAHRED); + p += 3; + if (*p == ',') + p++; + } else { + fprintf (stderr, "Invalid optimization name `%s'\n", p); + exit (1); + } + } + } + return opt; +} + +typedef struct { + const char* name; + const char* desc; + MonoGraphOptions value; +} GraphName; + +static const GraphName +graph_names [] = { + {"cfg", "Control Flow Graph (CFG)" , MONO_GRAPH_CFG}, + {"dtree", "Dominator Tree", MONO_GRAPH_DTREE}, + {"code", "CFG showing code", MONO_GRAPH_CFG_CODE}, + {"ssa", "CFG showing code after SSA translation", MONO_GRAPH_CFG_SSA}, + {"optcode", "CFG showing code after IR optimizations", MONO_GRAPH_CFG_OPTCODE} +}; + +static MonoGraphOptions +mono_parse_graph_options (const char* p) +{ + const char *n; + int i, len; + + for (i = 0; i < G_N_ELEMENTS (graph_names); ++i) { + n = graph_names [i].name; + len = strlen (n); + if (strncmp (p, n, len) == 0) + return graph_names [i].value; + } + + fprintf (stderr, "Invalid graph name provided: %s\n", p); + exit (1); +} + +int +mini_parse_default_optimizations (const char* p) +{ + guint32 opt; + + opt = parse_optimizations (p); + return opt; +} + +static char* +opt_descr (guint32 flags) { + GString *str = g_string_new (""); + int i, need_comma; + + need_comma = 0; + for (i = 0; i < G_N_ELEMENTS (opt_names); ++i) { + if (flags & (1 << i)) { + if (need_comma) + g_string_append_c (str, ','); + g_string_append (str, opt_names [i].name); + need_comma = 1; + } + } + return g_string_free (str, FALSE); +} + +static const guint32 +opt_sets [] = { + 0, + MONO_OPT_PEEPHOLE, + MONO_OPT_BRANCH, + MONO_OPT_CFOLD, + MONO_OPT_FCMOV, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_CFOLD, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE, + MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP +}; + +typedef int (*TestMethod) (void); + +#if 0 +static void +domain_dump_native_code (MonoDomain *domain) { + // need to poke into the domain, move to metadata/domain.c + // need to empty jit_info_table and code_mp +} +#endif + +static int +mini_regression (MonoImage *image, int verbose) { + guint32 i, opt, opt_flags; + MonoMethod *method; + MonoCompile *cfg; + char *n; + int result, expected, failed, cfailed, run, code_size, total; + TestMethod func; + GTimer *timer = g_timer_new (); + + if (mini_stats_fd) { + fprintf (mini_stats_fd, "$stattitle = \'Mono Benchmark Results (various optimizations)\';\n"); + + fprintf (mini_stats_fd, "$graph->set_legend(qw("); + for (opt = 0; opt < G_N_ELEMENTS (opt_sets); opt++) { + opt_flags = opt_sets [opt]; + n = opt_descr (opt_flags); + if (!n [0]) + n = (char *)"none"; + if (opt) + fprintf (mini_stats_fd, " "); + fprintf (mini_stats_fd, "%s", n); + + + } + fprintf (mini_stats_fd, "));\n"); + + fprintf (mini_stats_fd, "@data = (\n"); + fprintf (mini_stats_fd, "["); + } + + /* load the metadata */ + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL); + mono_class_init (method->klass); + + if (!strncmp (method->name, "test_", 5) && mini_stats_fd) { + fprintf (mini_stats_fd, "\"%s\",", method->name); + } + } + if (mini_stats_fd) + fprintf (mini_stats_fd, "],\n"); + + + total = 0; + for (opt = 0; opt < G_N_ELEMENTS (opt_sets); ++opt) { + double elapsed, comp_time, start_time; + opt_flags = opt_sets [opt]; + mini_set_defaults (verbose, opt_flags); + n = opt_descr (opt_flags); + g_print ("Test run: image=%s, opts=%s\n", image->name, n); + g_free (n); + cfailed = failed = run = code_size = 0; + comp_time = elapsed = 0.0; + + /* fixme: ugly hack - delete all previously compiled methods */ + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL); + method->info = NULL; + } + + g_timer_start (timer); + if (mini_stats_fd) + fprintf (mini_stats_fd, "["); + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL); + if (strncmp (method->name, "test_", 5) == 0) { + expected = atoi (method->name + 5); + run++; + start_time = g_timer_elapsed (timer, NULL); + comp_time -= start_time; + cfg = mini_method_compile (method, opt_flags, mono_root_domain, 0); + comp_time += g_timer_elapsed (timer, NULL); + if (cfg) { + if (verbose >= 2) + g_print ("Running '%s' ...\n", method->name); +#ifdef MONO_USE_AOT_COMPILER + if (!(func = mono_aot_get_method (method))) +#endif + func = (TestMethod)cfg->native_code; + result = func (); + if (result != expected) { + failed++; + if (verbose) + g_print ("Test '%s' failed result (got %d, expected %d).\n", method->name, result, expected); + } + code_size += cfg->code_len; + mono_destroy_compile (cfg); + + if (mono_trace_coverage) { + MonoCoverageInfo *cov = mono_get_coverage_info (method); + + if (cov) { + int k; + printf ("COVERAGE INFO %s\n", mono_method_full_name (method, TRUE)); + + for (k = 0; k < cov->entries; k++) { + printf (" BBLOCK %3d %d\n", cov->data [k].iloffset, cov->data [k].count); + } + } + } + + } else { + cfailed++; + if (verbose) + g_print ("Test '%s' failed compilation.\n", method->name); + } + if (mini_stats_fd) + fprintf (mini_stats_fd, "%f, ", + g_timer_elapsed (timer, NULL) - start_time); + } + } + if (mini_stats_fd) + fprintf (mini_stats_fd, "],\n"); + g_timer_stop (timer); + elapsed = g_timer_elapsed (timer, NULL); + g_print ("Results: total tests: %d, failed: %d, cfailed: %d (pass: %.2f%%)\n", + run, failed, cfailed, 100.0*(run-failed-cfailed)/run); + g_print ("Elapsed time: %f secs (%f, %f), Code size: %d\n\n", elapsed, + elapsed - comp_time, comp_time, code_size); + total += failed + cfailed; + } + + if (mini_stats_fd) { + fprintf (mini_stats_fd, ");\n"); + fflush (mini_stats_fd); + } + + g_timer_destroy (timer); + return total; +} + +static int +mini_regression_list (int verbose, int count, char *images []) +{ + int i, total; + MonoAssembly *ass; + + total = 0; + for (i = 0; i < count; ++i) { + ass = mono_assembly_open (images [i], NULL); + if (!ass) { + g_warning ("failed to load assembly: %s", images [i]); + continue; + } + total += mini_regression (ass->image, verbose); + mono_assembly_close (ass); + } + return total; +} + +enum { + DO_BENCH, + DO_REGRESSION, + DO_COMPILE, + DO_EXEC, + DO_DRAW, +}; + +static void +compile_all_methods (MonoAssembly *ass, int verbose) +{ + MonoImage *image = ass->image; + MonoMethod *method; + int i, count = 0; + + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { + method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL); + if (method->flags & METHOD_ATTRIBUTE_ABSTRACT) + continue; + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + continue; + + count++; + if (verbose) { + char * desc = mono_method_full_name (method, TRUE); + g_print ("Compiling %d %s\n", count, desc + 3); + g_free (desc); + } + mono_compile_method (method); + } + +} + +/** + * mono_jit_exec: + * @assembly: reference to an assembly + * @argc: argument count + * @argv: argument vector + * + * Start execution of a program. + */ +static int +mono_jit_exec (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]) +{ + MonoImage *image = assembly->image; + MonoMethod *method; + + method = mono_get_method (image, mono_image_get_entry_point (image), NULL); + + return mono_runtime_run_main (method, argc, argv, NULL); +} + +typedef struct +{ + MonoDomain *domain; + const char *file; + int argc; + char **argv; + guint32 opts; +} MainThreadArgs; + +static void main_thread_handler (gpointer user_data) +{ + MainThreadArgs *main_args = user_data; + MonoAssembly *assembly; + + assembly = mono_domain_assembly_open (main_args->domain, main_args->file); + if (!assembly){ + fprintf (stderr, "Can not open image %s\n", main_args->file); + exit (1); + } + + if (mono_compile_aot) { + int res = mono_compile_assembly (assembly, main_args->opts); + printf ("AOT RESULT %d\n", res); + } else { + mono_jit_exec (main_args->domain, assembly, main_args->argc, main_args->argv); + } +} + +static void +mini_usage (void) +{ + int i; + + fprintf (stderr, + "Usage is: mini [options] assembly\n\n" + "Runtime and JIT debugging:\n" + " --compile METHOD Just compile METHOD in assembly\n" + " --ncompile N Number of times to compile METHOD (default: 1)\n" + " --regression Runs the regression test contained in the assembly\n" + " --trace Enable tracing\n" + " --compile-all Compiles all the methods in the assembly\n" + " --breakonex Inserts a breakpoint on exceptions\n" + " --break METHOD Inserts a breakpoint at METHOD entry\n" + "\n" + "Development:\n" + " --statfile FILE Sets the stat file to FILE\n" + " --aot Compiles the assembly to native code\n" + " --coverage Performs coverage analysis\n" + " --profile Runs in profiling mode\n" + " --graph[=TYPE] METHOD Draws a graph of the specified method:\n"); + + for (i = 0; i < G_N_ELEMENTS (graph_names); ++i) { + fprintf (stderr, " %-10s %s\n", graph_names [i].name, graph_names [i].desc); + } + + fprintf (stderr, + "\n" + "Runtime:\n" + " --config FILE Loads FILE as the Mono config\n" + " --verbose, -v Increases the verbosity level\n" + " --help, -h Show usage information\n" + " --optimize=OPT Turns on a specific optimization:\n"); + + for (i = 0; i < G_N_ELEMENTS (opt_names); ++i) + fprintf (stderr, " %-10s %s\n", opt_names [i].name, opt_names [i].desc); +} + +int +mini_main (int argc, char* argv[]) { + MainThreadArgs main_args; + MonoAssembly *assembly; + MonoMethodDesc *desc; + MonoMethod *method; + MonoCompile *cfg; + MonoDomain *domain; + const char* aname, *mname = NULL; + char *config_file = NULL; + int i, count = 1; + guint32 opt, action = DO_EXEC; + MonoGraphOptions mono_graph_options = 0; + int mini_verbose = 0; + + opt = parse_optimizations (NULL); + + for (i = 1; i < argc; ++i) { + if (argv [i] [0] != '-') + break; + if (strcmp (argv [i], "--regression") == 0) { + action = DO_REGRESSION; + } else if (strcmp (argv [i], "--verbose") == 0 || strcmp (argv [i], "-v") == 0) { + mini_verbose++; + } else if (strcmp (argv [i], "--help") == 0 || strcmp (argv [i], "-h") == 0) { + mini_usage (); + return 0; + } else if (strncmp (argv [i], "--statfile", 10) == 0) { + mini_stats_fd = fopen (argv [++i], "w+"); + } else if (strncmp (argv [i], "--optimize=", 11) == 0) { + opt = parse_optimizations (argv [i] + 11); + } else if (strncmp (argv [i], "-O=", 3) == 0) { + opt = parse_optimizations (argv [i] + 3); + } else if (strcmp (argv [i], "--config") == 0) { + config_file = argv [++i]; + } else if (strcmp (argv [i], "--ncompile") == 0) { + count = atoi (argv [++i]); + action = DO_BENCH; + } else if (strcmp (argv [i], "--trace") == 0) { + mono_jit_trace_calls = TRUE; + } else if (strcmp (argv [i], "--breakonex") == 0) { + mono_break_on_exc = TRUE; + } else if (strcmp (argv [i], "--break") == 0) { + if (!mono_insert_breakpoint (argv [++i], FALSE)) + g_error ("Invalid method name '%s'", argv [i]); + } else if (strcmp (argv [i], "--stats") == 0) { + mono_jit_stats.enabled = TRUE; + } else if (strcmp (argv [i], "--aot") == 0) { + mono_compile_aot = TRUE; + } else if (strcmp (argv [i], "--coverage") == 0) { + mono_trace_coverage = TRUE; + } else if (strcmp (argv [i], "--compile-all") == 0) { + action = DO_COMPILE; + } else if (strcmp (argv [i], "--profile") == 0) { + mono_jit_profile = TRUE; + mono_profiler_install_simple (); + } else if (strcmp (argv [i], "--compile") == 0) { + mname = argv [++i]; + action = DO_BENCH; + } else if (strncmp (argv [i], "--graph=", 8) == 0) { + mono_graph_options = mono_parse_graph_options (argv [i] + 8); + mname = argv [++i]; + action = DO_DRAW; + } else if (strcmp (argv [i], "--graph") == 0) { + mname = argv [++i]; + mono_graph_options = MONO_GRAPH_CFG; + action = DO_DRAW; + } else { + fprintf (stderr, "Unknown command line option: %s", argv [i]); + return 1; + } + } + + mini_set_defaults (mini_verbose, opt); + domain = mini_init (argv [0]); + + switch (action) { + case DO_REGRESSION: + if (mini_regression_list (mini_verbose, argc -i, argv + i)) { + g_print ("Regression ERRORS!\n"); + mini_cleanup (domain); + return 1; + } + mini_cleanup (domain); + return 0; + case DO_BENCH: + if (argc - i != 1 || mname == NULL) { + g_print ("Usage: mini --ncompile num --compile method assembly\n"); + mini_cleanup (domain); + return 1; + } + aname = argv [i]; + break; + case DO_COMPILE: + if (argc - i != 1) { + fprintf (stderr, "Missing assembly name in --compile-all"); + mini_cleanup (domain); + return 1; + } + aname = argv [i]; + break; + case DO_DRAW: + if (argc - i != 1 || mname == NULL) { + fprintf (stderr, "Usage: mini --graph[=TYPE] method assembly\n"); + mini_cleanup (domain); + return 1; + } + aname = argv [i]; + break; + default: + if (argc - i < 1) { + mini_usage (); + mini_cleanup (domain); + return 1; + } + aname = argv [i]; + break; + } + + assembly = mono_assembly_open (aname, NULL); + if (!assembly) { + fprintf (stderr, "cannot open assembly %s\n", aname); + mini_cleanup (domain); + return 2; + } + + if (mono_compile_aot || action == DO_EXEC) { + g_set_prgname (aname); + mono_config_parse (config_file); + //mono_set_rootdir (); + + main_args.domain = domain; + main_args.file = aname; + main_args.argc = argc - i; + main_args.argv = argv + i; + main_args.opts = opt; + + mono_runtime_exec_managed_code (domain, main_thread_handler, &main_args); + mini_cleanup (domain); + /* Look up return value from System.Environment.ExitCode */ + i = mono_environment_exitcode_get (); + return i; + } else if (action == DO_COMPILE) { + compile_all_methods (assembly, mini_verbose); + mini_cleanup (domain); + return 0; + } + desc = mono_method_desc_new (mname, 0); + if (!desc) { + g_print ("Invalid method name %s\n", mname); + mini_cleanup (domain); + return 3; + } + method = mono_method_desc_search_in_image (desc, assembly->image); + if (!method) { + g_print ("Cannot find method %s\n", mname); + mini_cleanup (domain); + return 3; + } + + if (action == DO_DRAW) { + int part = 0; + + switch (mono_graph_options) { + case MONO_GRAPH_DTREE: + part = 1; + opt |= MONO_OPT_LOOP; + break; + case MONO_GRAPH_CFG_CODE: + part = 1; + break; + case MONO_GRAPH_CFG_SSA: + part = 2; + break; + case MONO_GRAPH_CFG_OPTCODE: + part = 3; + break; + default: + break; + } + + cfg = mini_method_compile (method, opt, mono_root_domain, part); + if ((mono_graph_options & MONO_GRAPH_CFG_SSA) && !(cfg->comp_done & MONO_COMP_SSA)) { + g_warning ("no SSA info available (use -O=deadce)"); + return 1; + } + mono_draw_graph (cfg, mono_graph_options); + mono_destroy_compile (cfg); + + } else if (action == DO_BENCH) { + if (mini_stats_fd) { + const char *n; + double no_opt_time = 0.0; + GTimer *timer = g_timer_new (); + fprintf (mini_stats_fd, "$stattitle = \'Compilations times for %s\';\n", + mono_method_full_name (method, TRUE)); + fprintf (mini_stats_fd, "@data = (\n"); + fprintf (mini_stats_fd, "["); + for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) { + opt = opt_sets [i]; + n = opt_descr (opt); + if (!n [0]) + n = "none"; + fprintf (mini_stats_fd, "\"%s\",", n); + } + fprintf (mini_stats_fd, "],\n["); + + for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) { + int j; + double elapsed; + opt = opt_sets [i]; + g_timer_start (timer); + for (j = 0; j < count; ++j) { + cfg = mini_method_compile (method, opt, mono_root_domain, 0); + mono_destroy_compile (cfg); + } + g_timer_stop (timer); + elapsed = g_timer_elapsed (timer, NULL); + if (!opt) + no_opt_time = elapsed; + fprintf (mini_stats_fd, "%f, ", elapsed); + } + fprintf (mini_stats_fd, "]"); + if (no_opt_time > 0.0) { + fprintf (mini_stats_fd, ", \n["); + for (i = 0; i < G_N_ELEMENTS (opt_sets); i++) + fprintf (mini_stats_fd, "%f,", no_opt_time); + fprintf (mini_stats_fd, "]"); + } + fprintf (mini_stats_fd, ");\n"); + } else { + for (i = 0; i < count; ++i) { + cfg = mini_method_compile (method, opt, mono_root_domain, 0); + mono_destroy_compile (cfg); + } + } + } else { + cfg = mini_method_compile (method, opt, mono_root_domain, 0); + mono_destroy_compile (cfg); + } + + mini_cleanup (domain); + return 0; +} + diff --git a/mono/mini/emullong.brg b/mono/mini/emullong.brg new file mode 100644 index 00000000000..fe65083e86b --- /dev/null +++ b/mono/mini/emullong.brg @@ -0,0 +1,86 @@ +%% + +# +# emullong.brg: emulate 64 bit instructions with 32bit instructions +# +# we dont use this, its just a prototype! +# +# Author: +# Dietmar Maurer (dietmar@ximian.com) +# +# (C) 2002 Ximian, Inc. +# + +lreg: OP_I8CONST { + MonoInst *inst; + + MONO_INST_NEW (s, inst, OP_ICONST); + inst->dreg = state->reg1; + inst->inst_c0 = tree->inst_c0; + mono_bblock_add_inst (s->cbb, inst); + + tree->opcode = OP_ICONST; + tree->dreg = state->reg2; + tree->inst_c0 = tree->inst_c1; + mono_bblock_add_inst (s->cbb, tree); +} + +lreg: CEE_CONV_I8 (OP_ICONST) { + MonoInst *inst; + + inst = state->left->tree; + inst->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, inst); + + tree->opcode = OP_ICONST; + tree->dreg = state->reg2; + + if (inst->inst_c0 >= 0) + tree->inst_c0 = 0; + else + tree->inst_c0 = -1; + + mono_bblock_add_inst (s->cbb, inst); +} + +lreg: CEE_CONV_U8 (OP_ICONST) { + MonoInst *inst; + + inst = state->left->tree; + inst->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, inst); + + tree->opcode = OP_ICONST; + tree->dreg = state->reg2; + tree->inst_c0 = 0; + mono_bblock_add_inst (s->cbb, tree); +} + +lreg: CEE_CONV_U8 (reg) { + MonoInst *inst; + + if (state->reg1 != state->left->reg1) { + MONO_INST_NEW (s, inst, OP_MOVE); + inst->dreg = state->reg1; + inst->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, inst); + } + + tree->opcode = OP_ICONST; + tree->dreg = state->reg2; + tree->inst_c0 = 0; + mono_bblock_add_inst (s->cbb, tree); +} + +lreg: CONV_OVF_I8_UN (OP_ICONST) { + state->left->tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, state->left->tree); + + tree->opcode = OP_ICONST; + tree->dreg = state->reg2; + tree->inst_c0 = 0; + mono_bblock_add_inst (s->cbb, tree); +} + + +%% diff --git a/mono/mini/exceptions-ppc.c b/mono/mini/exceptions-ppc.c new file mode 100644 index 00000000000..07679c7e6cf --- /dev/null +++ b/mono/mini/exceptions-ppc.c @@ -0,0 +1,953 @@ +/* + * exception.c: exception support + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mini.h" +#include "mini-ppc.h" +#include "debug-private.h" + +typedef struct sigcontext MonoContext; + +/* we have the stack pointer, not the base pointer in sigcontext */ +#define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->sc_ir = (int)ip; } while (0); +#define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->sc_sp = (int)bp; } while (0); + +#define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->sc_ir)) +#define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->sc_sp)) + +/* disbale this for now */ +#undef MONO_USE_EXC_TABLES + +#ifdef MONO_USE_EXC_TABLES + +/*************************************/ +/* STACK UNWINDING STUFF */ +/*************************************/ + +/* These definitions are from unwind-dw2.c in glibc 2.2.5 */ + +/* For x86 */ +#define DWARF_FRAME_REGISTERS 17 + +typedef struct frame_state +{ + void *cfa; + void *eh_ptr; + long cfa_offset; + long args_size; + long reg_or_offset[DWARF_FRAME_REGISTERS+1]; + unsigned short cfa_reg; + unsigned short retaddr_column; + char saved[DWARF_FRAME_REGISTERS+1]; +} frame_state; + +#if 0 + +static long +get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum) +{ + switch (dwarf_regnum) { + case X86_EAX: + return ctx->eax; + case X86_EBX: + return ctx->ebx; + case X86_ECX: + return ctx->ecx; + case X86_EDX: + return ctx->edx; + case X86_ESI: + return ctx->esi; + case X86_EDI: + return ctx->edi; + case X86_EBP: + return ctx->ebp; + case X86_ESP: + return ctx->esp; + default: + g_assert_not_reached (); + } + + return 0; +} + +static void +set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value) +{ + switch (dwarf_regnum) { + case X86_EAX: + ctx->eax = value; + break; + case X86_EBX: + ctx->ebx = value; + break; + case X86_ECX: + ctx->ecx = value; + break; + case X86_EDX: + ctx->edx = value; + break; + case X86_ESI: + ctx->esi = value; + break; + case X86_EDI: + ctx->edi = value; + break; + case X86_EBP: + ctx->ebp = value; + break; + case X86_ESP: + ctx->esp = value; + break; + case 8: + ctx->eip = value; + break; + default: + g_assert_not_reached (); + } +} + +typedef struct frame_state * (*framesf) (void *, struct frame_state *); + +static framesf frame_state_for = NULL; + +static gboolean inited = FALSE; + +typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size); + +static get_backtrace_symbols_type get_backtrace_symbols = NULL; + +static void +init_frame_state_for (void) +{ + GModule *module; + + /* + * There are two versions of __frame_state_for: one in libgcc.a and the + * other in glibc.so. We need the version from glibc. + * For more info, see this: + * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html + */ + if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) { + + if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for)) + frame_state_for = NULL; + + if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) { + get_backtrace_symbols = NULL; + frame_state_for = NULL; + } + + g_module_close (module); + } + + inited = TRUE; +} + +#endif + +/* mono_arch_has_unwind_info: + * + * Tests if a function has an DWARF exception table able to restore + * all caller saved registers. + */ +gboolean +mono_arch_has_unwind_info (MonoMethod *method) +{ +#if 0 + struct frame_state state_in; + struct frame_state *res; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + g_assert (method->addr); + + memset (&state_in, 0, sizeof (state_in)); + + /* offset 10 is just a guess, but it works for all methods tested */ + if ((res = frame_state_for ((char *)method->addr + 10, &state_in))) { + + if (res->saved [X86_EBX] != 1 || + res->saved [X86_EDI] != 1 || + res->saved [X86_EBP] != 1 || + res->saved [X86_ESI] != 1) { + return FALSE; + } + return TRUE; + } else { + return FALSE; + } +#else + return FALSE; +#endif +} + +struct stack_frame +{ + void *next; + void *return_address; +}; + +static MonoJitInfo * +ppc_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, + struct sigcontext *new_ctx, MonoLMF *lmf, char **trace) +{ +#if 0 + struct stack_frame *frame; + gpointer max_stack; + MonoJitInfo *ji; + struct frame_state state_in; + struct frame_state *res; + + if (trace) + *trace = NULL; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + frame = MONO_CONTEXT_GET_BP (ctx); + + max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack; + + *new_ctx = *ctx; + + memset (&state_in, 0, sizeof (state_in)); + + while ((gpointer)frame->next < (gpointer)max_stack) { + gpointer ip, addr = frame->return_address; + void *cfa; + char *tmp, **symbols; + + if (trace) { + ip = MONO_CONTEXT_GET_IP (new_ctx); + symbols = get_backtrace_symbols (&ip, 1); + if (*trace) + tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]); + else + tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]); + + free (symbols); + g_free (*trace); + *trace = tmp; + } + + if ((res = frame_state_for (addr, &state_in))) { + int i; + + cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset); + frame = (struct stack_frame *)((gint8*)cfa - 8); + for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) { + int how = res->saved[i]; + long val; + g_assert ((how == 0) || (how == 1)); + + if (how == 1) { + val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]); + set_sigcontext_reg (new_ctx, i, val); + } + } + new_ctx->esp = (long)cfa; + + if (res->saved [X86_EBX] == 1 && + res->saved [X86_EDI] == 1 && + res->saved [X86_EBP] == 1 && + res->saved [X86_ESI] == 1 && + (ji = mono_jit_info_table_find (domain, frame->return_address))) { + //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE)); + return ji; + } + + } else { + //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx))); + + MONO_CONTEXT_SET_IP (new_ctx, frame->return_address); + frame = frame->next; + MONO_CONTEXT_SET_BP (new_ctx, frame); + + /* stop if !frame or when we detect an unexpected managed frame */ + if (!frame || mono_jit_info_table_find (domain, frame->return_address)) { + if (trace) { + g_free (*trace); + *trace = NULL; + } + return NULL; + } + } + } + + //if (!lmf) + //g_assert_not_reached (); + + if (trace) { + g_free (*trace); + *trace = NULL; + } +#endif + return NULL; +} + +#endif + +/* + * arch_get_restore_context: + * + * Returns a pointer to a method which restores a previously saved sigcontext. + */ +static gpointer +arch_get_restore_context (void) +{ + static guint8 *start = NULL; + guint8 *code; + + if (start) + return start; + +#if 0 + /* restore_contect (struct sigcontext *ctx) */ + /* we do not restore X86_EAX, X86_EDX */ + + start = code = g_malloc (1024); + + /* load ctx */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + + /* get return address, stored in EDX */ + x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4); + /* restore EBX */ + x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4); + /* restore EDI */ + x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4); + /* restore ESI */ + x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4); + /* restore ESP */ + x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4); + /* restore EBP */ + x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4); + + /* jump to the saved IP */ + x86_jump_reg (code, X86_EDX); + +#endif + return start; +} + +/* + * arch_get_call_filter: + * + * Returns a pointer to a method which calls an exception filter. We + * also use this function to call finally handlers (we pass NULL as + * @exc object in this case). + */ +static gpointer +arch_get_call_filter (void) +{ + static guint8 start [64]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */ + code = start; + +#if 0 + x86_push_reg (code, X86_EBP); + x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4); + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + + /* load ctx */ + x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4); + /* load eip */ + x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4); + /* save EBP */ + x86_push_reg (code, X86_EBP); + /* push exc */ + x86_push_membase (code, X86_EBP, 16); + /* set new EBP */ + x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4); + /* restore registers used by global register allocation (EBX & ESI) */ + x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4); + x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4); + x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4); + /* save the ESP - this is used by endfinally */ + x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4); + /* call the handler */ + x86_call_reg (code, X86_ECX); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + /* restore EBP */ + x86_pop_reg (code, X86_EBP); + /* restore saved regs */ + x86_pop_reg (code, X86_ESI); + x86_pop_reg (code, X86_EDI); + x86_pop_reg (code, X86_EBX); + x86_leave (code); + x86_ret (code); + + g_assert ((code - start) < 64); +#endif + return start; +} + +static void +throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx, + unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc, + unsigned long eip, unsigned long esp) +{ + static void (*restore_context) (struct sigcontext *); + struct sigcontext ctx; + + if (!restore_context) + restore_context = arch_get_restore_context (); + + /* adjust eip so that it point into the call instruction */ + eip -= 1; + +#if 0 + ctx.SC_ESP = esp; + ctx.SC_EIP = eip; + ctx.SC_EBP = ebp; + ctx.SC_EDI = edi; + ctx.SC_ESI = esi; + ctx.SC_EBX = ebx; + ctx.SC_EDX = edx; + ctx.SC_ECX = ecx; + ctx.SC_EAX = eax; +#endif + mono_arch_handle_exception (&ctx, exc, FALSE); + restore_context (&ctx); + + g_assert_not_reached (); +} + +/** + * arch_get_throw_exception: + * + * Returns a function pointer which can be used to raise + * exceptions. The returned function has the following + * signature: void (*func) (MonoException *exc); + * For example to raise an arithmetic exception you can use: + * + * x86_push_imm (code, mono_get_exception_arithmetic ()); + * x86_call_code (code, arch_get_throw_exception ()); + * + */ +gpointer +mono_arch_get_throw_exception (void) +{ + static guint8 start [24]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + code = start; + +#if 0 + x86_push_reg (code, X86_ESP); + x86_push_membase (code, X86_ESP, 4); /* IP */ + x86_push_membase (code, X86_ESP, 12); /* exception */ + x86_push_reg (code, X86_EBP); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDX); + x86_push_reg (code, X86_ECX); + x86_push_reg (code, X86_EAX); + x86_call_code (code, throw_exception); + /* we should never reach this breakpoint */ + x86_breakpoint (code); + + g_assert ((code - start) < 24); +#endif + return start; +} + +/** + * arch_get_throw_exception_by_name: + * + * Returns a function pointer which can be used to raise + * corlib exceptions. The returned function has the following + * signature: void (*func) (char *exc_name); + * For example to raise an arithmetic exception you can use: + * + * x86_push_imm (code, "ArithmeticException"); + * x86_call_code (code, arch_get_throw_exception_by_name ()); + * + */ +gpointer +mono_arch_get_throw_exception_by_name (void) +{ + static guint8 start [32]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + code = start; +#if 0 + x86_push_membase (code, X86_ESP, 4); /* exception name */ + x86_push_imm (code, "System"); + x86_push_imm (code, mono_defaults.exception_class->image); + x86_call_code (code, mono_exception_from_name); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); + /* save the newly create object (overwrite exception name)*/ + x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4); + x86_jump_code (code, mono_arch_get_throw_exception ()); + + g_assert ((code - start) < 32); +#endif + return start; +} + +static MonoArray * +glist_to_array (GList *list) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *res; + int len, i; + + if (!list) + return NULL; + + len = g_list_length (list); + res = mono_array_new (domain, mono_defaults.int_class, len); + + for (i = 0; list; list = list->next, i++) + mono_array_set (res, gpointer, i, list->data); + + return res; +} + +/* mono_arch_find_jit_info: + * + * This function is used to gather information from @ctx. It return the + * MonoJitInfo of the corresponding function, unwinds one stack frame and + * stores the resulting context into @new_ctx. It also stores a string + * describing the stack location into @trace (if not NULL), and modifies + * the @lmf if necessary. @native_offset return the IP offset from the + * start of the function or -1 if that info is not available. + */ +static MonoJitInfo * +mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *ctx, + MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset, + gboolean *managed) +{ + MonoJitInfo *ji; + gpointer ip = MONO_CONTEXT_GET_IP (ctx); + + ji = mono_jit_info_table_find (domain, ip); + + if (trace) + *trace = NULL; + + if (native_offset) + *native_offset = -1; + + if (managed) + *managed = FALSE; + + if (ji != NULL) { + char *source_location, *tmpaddr, *fname; + gint32 address, iloffset; + int offset; + + *new_ctx = *ctx; + + if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { + /* remove any unused lmf */ + *lmf = (*lmf)->previous_lmf; + } + + address = (char *)ip - (char *)ji->code_start; + + if (native_offset) + *native_offset = address; + + if (managed) + if (!ji->method->wrapper_type) + *managed = TRUE; + + if (trace) { + if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) + mono_debug_make_symbols (); + + source_location = mono_debug_source_location_from_address (ji->method, address, NULL); + iloffset = mono_debug_il_offset_from_address (ji->method, address); + source_location = NULL; + iloffset = -1; + + if (iloffset < 0) + tmpaddr = g_strdup_printf ("<0x%05x>", address); + else + tmpaddr = g_strdup_printf ("[0x%05x]", iloffset); + + fname = mono_method_full_name (ji->method, TRUE); + + if (source_location) + *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname); + else + *trace = g_strdup_printf ("in %s %s", tmpaddr, fname); + + g_free (fname); + g_free (source_location); + g_free (tmpaddr); + } +#if 0 + offset = -1; + /* restore caller saved registers */ + if (ji->used_regs & X86_EBX_MASK) { + new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset); + offset--; + } + if (ji->used_regs & X86_EDI_MASK) { + new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset); + offset--; + } + if (ji->used_regs & X86_ESI_MASK) { + new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset); + } + + new_ctx->SC_ESP = ctx->SC_EBP; + /* we substract 1, so that the IP points into the call instruction */ + new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1; + new_ctx->SC_EBP = *((int *)ctx->SC_EBP); +#endif + return ji; +#ifdef MONO_USE_EXC_TABLES + } else if ((ji = ppc_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) { + return ji; +#endif + } else if (*lmf) { + + *new_ctx = *ctx; + + if (!(*lmf)->method) + return (gpointer)-1; + + if (trace) + *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE)); + + ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip); + g_assert (ji != NULL); + +#if 0 + new_ctx->SC_ESI = (*lmf)->esi; + new_ctx->SC_EDI = (*lmf)->edi; + new_ctx->SC_EBX = (*lmf)->ebx; + new_ctx->SC_EBP = (*lmf)->ebp; + new_ctx->SC_EIP = (*lmf)->eip; + /* the lmf is always stored on the stack, so the following + * expression points to a stack location which can be used as ESP */ + new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip); +#endif + *lmf = (*lmf)->previous_lmf; + + return ji; + + } + + return NULL; +} + +MonoArray * +ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *res; + MonoArray *ta = exc->trace_ips; + int i, len; + + len = mono_array_length (ta); + + res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0); + + for (i = skip; i < len; i++) { + MonoJitInfo *ji; + MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class); + gpointer ip = mono_array_get (ta, gpointer, i); + + ji = mono_jit_info_table_find (domain, ip); + g_assert (ji != NULL); + + sf->method = mono_method_get_object (domain, ji->method, NULL); + sf->native_offset = (char *)ip - (char *)ji->code_start; + + sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset); + sf->il_offset = -1; + + if (need_file_info) { + gchar *filename; + + filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line); + filename = NULL; + + sf->filename = filename? mono_string_new (domain, filename): NULL; + sf->column = 0; + + g_free (filename); + } + + mono_array_set (res, gpointer, i, sf); + } + + return res; +} + +void +mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) { + MonoDomain *domain = mono_domain_get (); + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + MonoJitInfo *ji; + gint native_offset, il_offset; + gboolean managed; + + MonoContext ctx, new_ctx; + + MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0)); + MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1)); + + while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) { + + ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed); + g_assert (ji); + + if (ji == (gpointer)-1) + return; + + il_offset = mono_debug_il_offset_from_address (ji->method, native_offset); + + if (func (ji->method, native_offset, il_offset, managed, user_data)) + return; + + ctx = new_ctx; + } +} + +MonoBoolean +ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, + MonoReflectionMethod **method, + gint32 *iloffset, gint32 *native_offset, + MonoString **file, gint32 *line, gint32 *column) +{ + MonoDomain *domain = mono_domain_get (); + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + MonoJitInfo *ji; + MonoContext ctx, new_ctx; + + MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info); + MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0)); + + skip++; + + do { + ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL); + + ctx = new_ctx; + + if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack) + return FALSE; + + if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) + continue; + + skip--; + + } while (skip >= 0); + + *method = mono_method_get_object (domain, ji->method, NULL); + *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset); + *iloffset = -1; + + if (need_file_info) { + gchar *filename; + + filename = mono_debug_source_location_from_address (ji->method, *native_offset, line); + filename = NULL; + + *file = filename? mono_string_new (domain, filename): NULL; + *column = 0; + + g_free (filename); + } + + return TRUE; +} + +/** + * arch_handle_exception: + * @ctx: saved processor state + * @obj: the exception object + * @test_only: only test if the exception is caught, but dont call handlers + * + * + */ +gboolean +mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only) +{ + MonoDomain *domain = mono_domain_get (); + MonoJitInfo *ji; + static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL; + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + GList *trace_ips = NULL; + + g_assert (ctx != NULL); + if (!obj) { + MonoException *ex = mono_get_exception_null_reference (); + ex->message = mono_string_new (domain, + "Object reference not set to an instance of an object"); + obj = (MonoObject *)ex; + } + + g_assert (mono_object_isinst (obj, mono_defaults.exception_class)); + + if (!call_filter) + call_filter = arch_get_call_filter (); + + g_assert (jit_tls->end_of_stack); + g_assert (jit_tls->abort_func); + + if (!test_only) { + MonoContext ctx_cp = *ctx; + if (mono_jit_trace_calls) + g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name); + if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) { + if (mono_break_on_exc) { + if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) + mono_debug_make_symbols (); + G_BREAKPOINT (); + } + mono_unhandled_exception (obj); + } + } + + while (1) { + MonoContext new_ctx; + char *trace = NULL; + + ji = mono_arch_find_jit_info (domain, jit_tls, ctx, &new_ctx, + test_only ? &trace : NULL, &lmf, NULL, NULL); + if (!ji) { + g_warning ("Exception inside function without unwind info"); + g_assert_not_reached (); + } + + if (ji != (gpointer)-1) { + + if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) { + char *tmp, *strace; + + trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx)); + + if (!((MonoException*)obj)->stack_trace) + strace = g_strdup (""); + else + strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace); + + tmp = g_strdup_printf ("%s%s\n", strace, trace); + g_free (strace); + + ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp); + + g_free (tmp); + } + + if (ji->num_clauses) { + int i; + + g_assert (ji->clauses); + + for (i = 0; i < ji->num_clauses; i++) { + MonoJitExceptionInfo *ei = &ji->clauses [i]; + + if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && + MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { + /* catch block */ + if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && + mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) || + ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER && + call_filter (ctx, ei->data.filter, obj)))) { + if (test_only) { + ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips); + g_list_free (trace_ips); + return TRUE; + } + if (mono_jit_trace_calls) + g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); + MONO_CONTEXT_SET_IP (ctx, ei->handler_start); + *((gpointer *)(MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj; + jit_tls->lmf = lmf; + return 0; + } + } + } + + /* no handler found - we need to call all finally handlers */ + if (!test_only) { + for (i = 0; i < ji->num_clauses; i++) { + MonoJitExceptionInfo *ei = &ji->clauses [i]; + + if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && + MONO_CONTEXT_GET_IP (ctx) < ei->try_end && + (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) { + if (mono_jit_trace_calls) + g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); + call_filter (ctx, ei->handler_start, NULL); + } + } + } + } + } + + g_free (trace); + + *ctx = new_ctx; + + if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) { + if (!test_only) { + jit_tls->lmf = lmf; + jit_tls->abort_func (obj); + g_assert_not_reached (); + } else { + ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips); + g_list_free (trace_ips); + return FALSE; + } + } + } + + g_assert_not_reached (); +} + diff --git a/mono/mini/exceptions-x86.c b/mono/mini/exceptions-x86.c new file mode 100644 index 00000000000..2fb177e6456 --- /dev/null +++ b/mono/mini/exceptions-x86.c @@ -0,0 +1,972 @@ +/* + * exception.c: exception support + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mini.h" +#include "mini-x86.h" +#include "debug-private.h" + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# define SC_EAX sc_eax +# define SC_EBX sc_ebx +# define SC_ECX sc_ecx +# define SC_EDX sc_edx +# define SC_EBP sc_ebp +# define SC_EIP sc_eip +# define SC_ESP sc_esp +# define SC_EDI sc_edi +# define SC_ESI sc_esi +#else +# define SC_EAX eax +# define SC_EBX ebx +# define SC_ECX ecx +# define SC_EDX edx +# define SC_EBP ebp +# define SC_EIP eip +# define SC_ESP esp +# define SC_EDI edi +# define SC_ESI esi +#endif + +typedef struct sigcontext MonoContext; + +#define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0); +#define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0); + +#define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP)) +#define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP)) + +#ifdef MONO_USE_EXC_TABLES + +/*************************************/ +/* STACK UNWINDING STUFF */ +/*************************************/ + +/* These definitions are from unwind-dw2.c in glibc 2.2.5 */ + +/* For x86 */ +#define DWARF_FRAME_REGISTERS 17 + +typedef struct frame_state +{ + void *cfa; + void *eh_ptr; + long cfa_offset; + long args_size; + long reg_or_offset[DWARF_FRAME_REGISTERS+1]; + unsigned short cfa_reg; + unsigned short retaddr_column; + char saved[DWARF_FRAME_REGISTERS+1]; +} frame_state; + +static long +get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum) +{ + switch (dwarf_regnum) { + case X86_EAX: + return ctx->eax; + case X86_EBX: + return ctx->ebx; + case X86_ECX: + return ctx->ecx; + case X86_EDX: + return ctx->edx; + case X86_ESI: + return ctx->esi; + case X86_EDI: + return ctx->edi; + case X86_EBP: + return ctx->ebp; + case X86_ESP: + return ctx->esp; + default: + g_assert_not_reached (); + } + + return 0; +} + +static void +set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value) +{ + switch (dwarf_regnum) { + case X86_EAX: + ctx->eax = value; + break; + case X86_EBX: + ctx->ebx = value; + break; + case X86_ECX: + ctx->ecx = value; + break; + case X86_EDX: + ctx->edx = value; + break; + case X86_ESI: + ctx->esi = value; + break; + case X86_EDI: + ctx->edi = value; + break; + case X86_EBP: + ctx->ebp = value; + break; + case X86_ESP: + ctx->esp = value; + break; + case 8: + ctx->eip = value; + break; + default: + g_assert_not_reached (); + } +} + +typedef struct frame_state * (*framesf) (void *, struct frame_state *); + +static framesf frame_state_for = NULL; + +static gboolean inited = FALSE; + +typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size); + +static get_backtrace_symbols_type get_backtrace_symbols = NULL; + +static void +init_frame_state_for (void) +{ + GModule *module; + + /* + * There are two versions of __frame_state_for: one in libgcc.a and the + * other in glibc.so. We need the version from glibc. + * For more info, see this: + * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html + */ + if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) { + + if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for)) + frame_state_for = NULL; + + if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) { + get_backtrace_symbols = NULL; + frame_state_for = NULL; + } + + g_module_close (module); + } + + inited = TRUE; +} + +/* mono_arch_has_unwind_info: + * + * Tests if a function has an DWARF exception table able to restore + * all caller saved registers. + */ +gboolean +mono_arch_has_unwind_info (gconstpointer addr) +{ + struct frame_state state_in; + struct frame_state *res; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + g_assert (addr); + + memset (&state_in, 0, sizeof (state_in)); + + /* offset 10 is just a guess, but it works for all methods tested */ + if ((res = frame_state_for ((char *)addr + 10, &state_in))) { + + if (res->saved [X86_EBX] != 1 || + res->saved [X86_EDI] != 1 || + res->saved [X86_EBP] != 1 || + res->saved [X86_ESI] != 1) { + return FALSE; + } + return TRUE; + } else { + return FALSE; + } +} + +struct stack_frame +{ + void *next; + void *return_address; +}; + +static MonoJitInfo * +x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, + struct sigcontext *new_ctx, MonoLMF *lmf, char **trace) +{ + struct stack_frame *frame; + gpointer max_stack; + MonoJitInfo *ji; + struct frame_state state_in; + struct frame_state *res; + + if (trace) + *trace = NULL; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + frame = MONO_CONTEXT_GET_BP (ctx); + + max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack; + + *new_ctx = *ctx; + + memset (&state_in, 0, sizeof (state_in)); + + while ((gpointer)frame->next < (gpointer)max_stack) { + gpointer ip, addr = frame->return_address; + void *cfa; + char *tmp, **symbols; + + if (trace) { + ip = MONO_CONTEXT_GET_IP (new_ctx); + symbols = get_backtrace_symbols (&ip, 1); + if (*trace) + tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]); + else + tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]); + + free (symbols); + g_free (*trace); + *trace = tmp; + } + + if ((res = frame_state_for (addr, &state_in))) { + int i; + + cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset); + frame = (struct stack_frame *)((gint8*)cfa - 8); + for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) { + int how = res->saved[i]; + long val; + g_assert ((how == 0) || (how == 1)); + + if (how == 1) { + val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]); + set_sigcontext_reg (new_ctx, i, val); + } + } + new_ctx->esp = (long)cfa; + + if (res->saved [X86_EBX] == 1 && + res->saved [X86_EDI] == 1 && + res->saved [X86_EBP] == 1 && + res->saved [X86_ESI] == 1 && + (ji = mono_jit_info_table_find (domain, frame->return_address))) { + //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE)); + return ji; + } + + } else { + //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx))); + + MONO_CONTEXT_SET_IP (new_ctx, frame->return_address); + frame = frame->next; + MONO_CONTEXT_SET_BP (new_ctx, frame); + + /* stop if !frame or when we detect an unexpected managed frame */ + if (!frame || mono_jit_info_table_find (domain, frame->return_address)) { + if (trace) { + g_free (*trace); + *trace = NULL; + } + return NULL; + } + } + } + + //if (!lmf) + //g_assert_not_reached (); + + if (trace) { + g_free (*trace); + *trace = NULL; + } + return NULL; +} + +#endif + +/* + * arch_get_restore_context: + * + * Returns a pointer to a method which restores a previously saved sigcontext. + */ +static gpointer +arch_get_restore_context (void) +{ + static guint8 *start = NULL; + guint8 *code; + + if (start) + return start; + + /* restore_contect (struct sigcontext *ctx) */ + /* we do not restore X86_EAX, X86_EDX */ + + start = code = g_malloc (1024); + + /* load ctx */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + + /* get return address, stored in EDX */ + x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4); + /* restore EBX */ + x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4); + /* restore EDI */ + x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4); + /* restore ESI */ + x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4); + /* restore ESP */ + x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4); + /* restore EBP */ + x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4); + + /* jump to the saved IP */ + x86_jump_reg (code, X86_EDX); + + return start; +} + +/* + * arch_get_call_filter: + * + * Returns a pointer to a method which calls an exception filter. We + * also use this function to call finally handlers (we pass NULL as + * @exc object in this case). + */ +static gpointer +arch_get_call_filter (void) +{ + static guint8 start [64]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */ + code = start; + + x86_push_reg (code, X86_EBP); + x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4); + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + + /* load ctx */ + x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4); + /* load eip */ + x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4); + /* save EBP */ + x86_push_reg (code, X86_EBP); + /* push exc */ + x86_push_membase (code, X86_EBP, 16); + /* set new EBP */ + x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4); + /* restore registers used by global register allocation (EBX & ESI) */ + x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4); + x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4); + x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4); + /* save the ESP - this is used by endfinally */ + x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4); + /* call the handler */ + x86_call_reg (code, X86_ECX); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + /* restore EBP */ + x86_pop_reg (code, X86_EBP); + /* restore saved regs */ + x86_pop_reg (code, X86_ESI); + x86_pop_reg (code, X86_EDI); + x86_pop_reg (code, X86_EBX); + x86_leave (code); + x86_ret (code); + + g_assert ((code - start) < 64); + return start; +} + +static void +throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx, + unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc, + unsigned long eip, unsigned long esp) +{ + static void (*restore_context) (struct sigcontext *); + struct sigcontext ctx; + + if (!restore_context) + restore_context = arch_get_restore_context (); + + /* adjust eip so that it point into the call instruction */ + eip -= 1; + + ctx.SC_ESP = esp; + ctx.SC_EIP = eip; + ctx.SC_EBP = ebp; + ctx.SC_EDI = edi; + ctx.SC_ESI = esi; + ctx.SC_EBX = ebx; + ctx.SC_EDX = edx; + ctx.SC_ECX = ecx; + ctx.SC_EAX = eax; + + mono_arch_handle_exception (&ctx, exc, FALSE); + restore_context (&ctx); + + g_assert_not_reached (); +} + +/** + * arch_get_throw_exception: + * + * Returns a function pointer which can be used to raise + * exceptions. The returned function has the following + * signature: void (*func) (MonoException *exc); + * For example to raise an arithmetic exception you can use: + * + * x86_push_imm (code, mono_get_exception_arithmetic ()); + * x86_call_code (code, arch_get_throw_exception ()); + * + */ +gpointer +mono_arch_get_throw_exception (void) +{ + static guint8 start [24]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + code = start; + + x86_push_reg (code, X86_ESP); + x86_push_membase (code, X86_ESP, 4); /* IP */ + x86_push_membase (code, X86_ESP, 12); /* exception */ + x86_push_reg (code, X86_EBP); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDX); + x86_push_reg (code, X86_ECX); + x86_push_reg (code, X86_EAX); + x86_call_code (code, throw_exception); + /* we should never reach this breakpoint */ + x86_breakpoint (code); + + g_assert ((code - start) < 24); + return start; +} + +/** + * arch_get_throw_exception_by_name: + * + * Returns a function pointer which can be used to raise + * corlib exceptions. The returned function has the following + * signature: void (*func) (char *exc_name); + * For example to raise an arithmetic exception you can use: + * + * x86_push_imm (code, "ArithmeticException"); + * x86_call_code (code, arch_get_throw_exception_by_name ()); + * + */ +gpointer +mono_arch_get_throw_exception_by_name (void) +{ + static guint8 start [32]; + static int inited = 0; + guint8 *code; + + if (inited) + return start; + + inited = 1; + code = start; + + x86_push_membase (code, X86_ESP, 4); /* exception name */ + x86_push_imm (code, "System"); + x86_push_imm (code, mono_defaults.exception_class->image); + x86_call_code (code, mono_exception_from_name); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); + /* save the newly create object (overwrite exception name)*/ + x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4); + x86_jump_code (code, mono_arch_get_throw_exception ()); + + g_assert ((code - start) < 32); + + return start; +} + +static MonoArray * +glist_to_array (GList *list) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *res; + int len, i; + + if (!list) + return NULL; + + len = g_list_length (list); + res = mono_array_new (domain, mono_defaults.int_class, len); + + for (i = 0; list; list = list->next, i++) + mono_array_set (res, gpointer, i, list->data); + + return res; +} + +/* mono_arch_find_jit_info: + * + * This function is used to gather information from @ctx. It return the + * MonoJitInfo of the corresponding function, unwinds one stack frame and + * stores the resulting context into @new_ctx. It also stores a string + * describing the stack location into @trace (if not NULL), and modifies + * the @lmf if necessary. @native_offset return the IP offset from the + * start of the function or -1 if that info is not available. + */ +static MonoJitInfo * +mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoContext *ctx, + MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset, + gboolean *managed) +{ + MonoJitInfo *ji; + gpointer ip = MONO_CONTEXT_GET_IP (ctx); + + ji = mono_jit_info_table_find (domain, ip); + + if (trace) + *trace = NULL; + + if (native_offset) + *native_offset = -1; + + if (managed) + *managed = FALSE; + + if (ji != NULL) { + char *source_location, *tmpaddr, *fname; + gint32 address, iloffset; + int offset; + + *new_ctx = *ctx; + + if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { + /* remove any unused lmf */ + *lmf = (*lmf)->previous_lmf; + } + + address = (char *)ip - (char *)ji->code_start; + + if (native_offset) + *native_offset = address; + + if (managed) + if (!ji->method->wrapper_type) + *managed = TRUE; + + if (trace) { + if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) + mono_debug_make_symbols (); + + source_location = mono_debug_source_location_from_address (ji->method, address, NULL); + iloffset = mono_debug_il_offset_from_address (ji->method, address); + source_location = NULL; + iloffset = -1; + + if (iloffset < 0) + tmpaddr = g_strdup_printf ("<0x%05x>", address); + else + tmpaddr = g_strdup_printf ("[0x%05x]", iloffset); + + fname = mono_method_full_name (ji->method, TRUE); + + if (source_location) + *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname); + else + *trace = g_strdup_printf ("in %s %s", tmpaddr, fname); + + g_free (fname); + g_free (source_location); + g_free (tmpaddr); + } + + offset = -1; + /* restore caller saved registers */ + if (ji->used_regs & X86_EBX_MASK) { + new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset); + offset--; + } + if (ji->used_regs & X86_EDI_MASK) { + new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset); + offset--; + } + if (ji->used_regs & X86_ESI_MASK) { + new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset); + } + + new_ctx->SC_ESP = ctx->SC_EBP; + /* we substract 1, so that the IP points into the call instruction */ + new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1; + new_ctx->SC_EBP = *((int *)ctx->SC_EBP); + + *res = *ji; + return res; +#ifdef MONO_USE_EXC_TABLES + } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) { + *res = *ji; + return res; +#endif + } else if (*lmf) { + + *new_ctx = *ctx; + + if (!(*lmf)->method) + return (gpointer)-1; + + if (trace) + *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE)); + + if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) { + *res = *ji; + } else { + memset (res, 0, sizeof (MonoJitInfo)); + res->method = (*lmf)->method; + } + + new_ctx->SC_ESI = (*lmf)->esi; + new_ctx->SC_EDI = (*lmf)->edi; + new_ctx->SC_EBX = (*lmf)->ebx; + new_ctx->SC_EBP = (*lmf)->ebp; + new_ctx->SC_EIP = (*lmf)->eip; + /* the lmf is always stored on the stack, so the following + * expression points to a stack location which can be used as ESP */ + new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip); + + *lmf = (*lmf)->previous_lmf; + + return res; + + } + + return NULL; +} + +MonoArray * +ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *res; + MonoArray *ta = exc->trace_ips; + int i, len; + + if (ta == NULL) { + /* Exception is not thrown yet */ + return mono_array_new (domain, mono_defaults.stack_frame_class, 0); + } + + len = mono_array_length (ta); + + res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0); + + for (i = skip; i < len; i++) { + MonoJitInfo *ji; + MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class); + gpointer ip = mono_array_get (ta, gpointer, i); + + ji = mono_jit_info_table_find (domain, ip); + if (ji == NULL) { + /* Unmanaged frame */ + mono_array_set (res, gpointer, i, sf); + continue; + } + + g_assert (ji != NULL); + + sf->method = mono_method_get_object (domain, ji->method, NULL); + sf->native_offset = (char *)ip - (char *)ji->code_start; + + sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset); + sf->il_offset = -1; + + if (need_file_info) { + gchar *filename; + + filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line); + filename = NULL; + + sf->filename = filename? mono_string_new (domain, filename): NULL; + sf->column = 0; + + g_free (filename); + } + + mono_array_set (res, gpointer, i, sf); + } + + return res; +} + +void +mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) { + MonoDomain *domain = mono_domain_get (); + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + MonoJitInfo *ji, rji; + gint native_offset, il_offset; + gboolean managed; + + MonoContext ctx, new_ctx; + + MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0)); + MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1)); + + while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) { + + ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed); + g_assert (ji); + + if (ji == (gpointer)-1) + return; + + il_offset = mono_debug_il_offset_from_address (ji->method, native_offset); + + if (func (ji->method, native_offset, il_offset, managed, user_data)) + return; + + ctx = new_ctx; + } +} + +MonoBoolean +ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, + MonoReflectionMethod **method, + gint32 *iloffset, gint32 *native_offset, + MonoString **file, gint32 *line, gint32 *column) +{ + MonoDomain *domain = mono_domain_get (); + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + MonoJitInfo *ji, rji; + MonoContext ctx, new_ctx; + + MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info); + MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0)); + + skip++; + + do { + ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL); + + ctx = new_ctx; + + if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack) + return FALSE; + + if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) + continue; + + skip--; + + } while (skip >= 0); + + *method = mono_method_get_object (domain, ji->method, NULL); + *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset); + *iloffset = -1; + + if (need_file_info) { + gchar *filename; + + filename = mono_debug_source_location_from_address (ji->method, *native_offset, line); + filename = NULL; + + *file = filename? mono_string_new (domain, filename): NULL; + *column = 0; + + g_free (filename); + } + + return TRUE; +} + +/** + * arch_handle_exception: + * @ctx: saved processor state + * @obj: the exception object + * @test_only: only test if the exception is caught, but dont call handlers + * + * + */ +gboolean +mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only) +{ + MonoDomain *domain = mono_domain_get (); + MonoJitInfo *ji, rji; + static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL; + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + MonoLMF *lmf = jit_tls->lmf; + GList *trace_ips = NULL; + + g_assert (ctx != NULL); + if (!obj) { + MonoException *ex = mono_get_exception_null_reference (); + ex->message = mono_string_new (domain, + "Object reference not set to an instance of an object"); + obj = (MonoObject *)ex; + } + + g_assert (mono_object_isinst (obj, mono_defaults.exception_class)); + + if (!call_filter) + call_filter = arch_get_call_filter (); + + g_assert (jit_tls->end_of_stack); + g_assert (jit_tls->abort_func); + + if (!test_only) { + MonoContext ctx_cp = *ctx; + if (mono_jit_trace_calls) + g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name); + if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) { + if (mono_break_on_exc) { + if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) + mono_debug_make_symbols (); + G_BREAKPOINT (); + } + mono_unhandled_exception (obj); + } + } + + while (1) { + MonoContext new_ctx; + char *trace = NULL; + + ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx, + test_only ? &trace : NULL, &lmf, NULL, NULL); + if (!ji) { + g_warning ("Exception inside function without unwind info"); + g_assert_not_reached (); + } + + if (ji != (gpointer)-1) { + + if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) { + char *tmp, *strace; + + trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx)); + + if (!((MonoException*)obj)->stack_trace) + strace = g_strdup (""); + else + strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace); + + tmp = g_strdup_printf ("%s%s\n", strace, trace); + g_free (strace); + + ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp); + + g_free (tmp); + } + + if (ji->num_clauses) { + int i; + + g_assert (ji->clauses); + + for (i = 0; i < ji->num_clauses; i++) { + MonoJitExceptionInfo *ei = &ji->clauses [i]; + + if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && + MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { + /* catch block */ + if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && + mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) || + ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER && + call_filter (ctx, ei->data.filter, obj)))) { + if (test_only) { + ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips); + g_list_free (trace_ips); + return TRUE; + } + if (mono_jit_trace_calls) + g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); + MONO_CONTEXT_SET_IP (ctx, ei->handler_start); + *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj; + jit_tls->lmf = lmf; + return 0; + } + } + } + + /* no handler found - we need to call all finally handlers */ + if (!test_only) { + for (i = 0; i < ji->num_clauses; i++) { + MonoJitExceptionInfo *ei = &ji->clauses [i]; + + if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && + MONO_CONTEXT_GET_IP (ctx) < ei->try_end && + (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) { + if (mono_jit_trace_calls) + g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); + call_filter (ctx, ei->handler_start, NULL); + } + } + } + } + } + + g_free (trace); + + *ctx = new_ctx; + + if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) { + if (!test_only) { + jit_tls->lmf = lmf; + jit_tls->abort_func (obj); + g_assert_not_reached (); + } else { + ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips); + g_list_free (trace_ips); + return FALSE; + } + } + } + + g_assert_not_reached (); +} + + diff --git a/mono/mini/exceptions.cs b/mono/mini/exceptions.cs new file mode 100644 index 00000000000..85213e71044 --- /dev/null +++ b/mono/mini/exceptions.cs @@ -0,0 +1,1317 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_0_catch () { + Exception x = new Exception (); + + try { + throw x; + } catch (Exception e) { + if (e == x) + return 0; + } + return 1; + } + + static int test_0_finally_without_exc () { + int x; + + try { + x = 1; + } catch (Exception e) { + x = 2; + } finally { + x = 0; + } + + return x; + } + + static int test_0_finally () { + int x = 1; + + try { + throw new Exception (); + } catch (Exception e) { + x = 2; + } finally { + x = 0; + } + return x; + } + + static int test_0_byte_cast () { + int a; + long l; + byte b; + bool failed; + + try { + a = 255; + failed = false; + checked { + b = (byte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 1; + + try { + a = 0; + failed = false; + checked { + b = (byte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + try { + a = 256; + failed = true; + checked { + b = (byte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 3; + + try { + a = -1; + failed = true; + checked { + b = (byte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + double d = 0; + failed = false; + checked { + b = (byte)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 5; + + try { + double d = -1; + failed = true; + checked { + b = (byte)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 6; + + try { + double d = 255; + failed = false; + checked { + b = (byte)d; + } + } catch (OverflowException) { + failed = true; + } + + if (failed) + return 7; + + try { + double d = 256; + failed = true; + checked { + b = (byte)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + try { + l = 255; + failed = false; + checked { + b = (byte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 9; + + try { + l = 0; + failed = false; + checked { + b = (byte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 10; + + try { + l = 256; + failed = true; + checked { + b = (byte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 11; + + try { + l = -1; + failed = true; + checked { + b = (byte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 12; + + return 0; + } + + static int test_0_sbyte_cast () { + int a; + long l; + sbyte b; + bool failed; + + try { + a = 255; + failed = true; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 1; + + try { + a = 0; + failed = false; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + try { + a = 256; + failed = true; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 3; + + try { + a = -129; + failed = true; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + a = -1; + failed = false; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 5; + + try { + a = -128; + failed = false; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 6; + + try { + a = 127; + failed = false; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 7; + + try { + a = 128; + failed = true; + checked { + b = (sbyte)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + try { + double d = 127; + failed = false; + checked { + b = (sbyte)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 9; + + try { + double d = -128; + failed = false; + checked { + b = (sbyte)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 10; + + try { + double d = 128; + failed = true; + checked { + b = (sbyte)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 11; + + try { + double d = -129; + failed = true; + checked { + b = (sbyte)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 12; + + try { + l = 255; + failed = true; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 13; + + try { + l = 0; + failed = false; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 14; + + try { + l = 256; + failed = true; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 15; + + try { + l = -129; + failed = true; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 16; + + try { + l = -1; + failed = false; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 17; + + try { + l = -128; + failed = false; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 18; + + try { + l = 127; + failed = false; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 19; + + try { + l = 128; + failed = true; + checked { + b = (sbyte)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 20; + + return 0; + } + + static int test_0_ushort_cast () { + int a; + long l; + ushort b; + bool failed; + + try { + a = System.UInt16.MaxValue; + failed = false; + checked { + b = (ushort)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 1; + + try { + a = 0; + failed = false; + checked { + b = (ushort)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + try { + a = System.UInt16.MaxValue + 1; + failed = true; + checked { + b = (ushort)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 3; + + try { + a = -1; + failed = true; + checked { + b = (ushort)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + double d = 0; + failed = false; + checked { + b = (ushort)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 5; + + try { + double d = System.UInt16.MaxValue; + failed = false; + checked { + b = (ushort)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 6; + + try { + double d = -1; + failed = true; + checked { + b = (ushort)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 7; + + try { + double d = System.UInt16.MaxValue + 1.0; + failed = true; + checked { + b = (ushort)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + try { + l = System.UInt16.MaxValue; + failed = false; + checked { + b = (ushort)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 9; + + try { + l = 0; + failed = false; + checked { + b = (ushort)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 10; + + try { + l = System.UInt16.MaxValue + 1; + failed = true; + checked { + b = (ushort)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 11; + + try { + l = -1; + failed = true; + checked { + b = (ushort)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 12; + + return 0; + } + + static int test_0_short_cast () { + int a; + long l; + short b; + bool failed; + + try { + a = System.UInt16.MaxValue; + failed = true; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 1; + + try { + a = 0; + failed = false; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + try { + a = System.Int16.MaxValue + 1; + failed = true; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 3; + + try { + a = System.Int16.MinValue - 1; + failed = true; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + a = -1; + failed = false; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 5; + + try { + a = System.Int16.MinValue; + failed = false; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 6; + + try { + a = System.Int16.MaxValue; + failed = false; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 7; + + try { + a = System.Int16.MaxValue + 1; + failed = true; + checked { + b = (short)a; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + try { + double d = System.Int16.MaxValue; + failed = false; + checked { + b = (short)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 9; + + try { + double d = System.Int16.MinValue; + failed = false; + checked { + b = (short)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 10; + + try { + double d = System.Int16.MaxValue + 1.0; + failed = true; + checked { + b = (short)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 11; + + try { + double d = System.Int16.MinValue - 1.0; + failed = true; + checked { + b = (short)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 12; + + try { + l = System.Int16.MaxValue + 1; + failed = true; + checked { + b = (short)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 13; + + try { + l = System.Int16.MaxValue; + failed = false; + checked { + b = (short)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 14; + + try { + l = System.Int16.MinValue - 1; + failed = true; + checked { + b = (short)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 15; + + + try { + l = System.Int16.MinValue; + failed = false; + checked { + b = (short)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 16; + + return 0; + } + + static int test_0_int_cast () { + int a; + long l; + bool failed; + + try { + double d = System.Int32.MaxValue + 1.0; + failed = true; + checked { + a = (int)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 1; + + try { + double d = System.Int32.MaxValue; + failed = false; + checked { + a = (int)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + + try { + double d = System.Int32.MinValue; + failed = false; + checked { + a = (int)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 3; + + + try { + double d = System.Int32.MinValue - 1.0; + failed = true; + checked { + a = (int)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + l = System.Int32.MaxValue + (long)1; + failed = true; + checked { + a = (int)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 5; + + try { + l = System.Int32.MaxValue; + failed = false; + checked { + a = (int)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 6; + + + try { + l = System.Int32.MinValue; + failed = false; + checked { + a = (int)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 7; + + + try { + l = System.Int32.MinValue - (long)1; + failed = true; + checked { + a = (int)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + return 0; + } + + static int test_0_uint_cast () { + uint a; + long l; + bool failed; + + try { + double d = System.UInt32.MaxValue; + failed = false; + checked { + a = (uint)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 1; + + try { + double d = System.UInt32.MaxValue + 1.0; + failed = true; + checked { + a = (uint)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 2; + + try { + double d = System.UInt32.MinValue; + failed = false; + checked { + a = (uint)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 3; + + try { + double d = System.UInt32.MinValue - 1.0; + failed = true; + checked { + a = (uint)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + try { + l = System.UInt32.MaxValue; + failed = false; + checked { + a = (uint)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 5; + + try { + l = System.UInt32.MaxValue + (long)1; + failed = true; + checked { + a = (uint)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 6; + + try { + l = System.UInt32.MinValue; + failed = false; + checked { + a = (uint)l; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 7; + + try { + l = System.UInt32.MinValue - (long)1; + failed = true; + checked { + a = (uint)l; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 8; + + return 0; + } + + static int test_0_long_cast () { + long a; + bool failed; + + try { + double d = System.Int64.MaxValue - 512.0; + failed = true; + checked { + a = (long)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 1; + + try { + double d = System.Int64.MaxValue - 513.0; + failed = false; + checked { + a = (long)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + + try { + double d = System.Int64.MinValue - 1024.0; + failed = false; + checked { + a = (long)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 3; + + try { + double d = System.Int64.MinValue - 1025.0; + failed = true; + checked { + a = (long)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + return 0; + } + + static int test_0_ulong_cast () { + ulong a; + bool failed; + + try { + double d = System.UInt64.MaxValue - 1024.0; + failed = true; + checked { + a = (ulong)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 1; + + try { + double d = System.UInt64.MaxValue - 1025.0; + failed = false; + checked { + a = (ulong)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 2; + + + try { + double d = 0; + failed = false; + checked { + a = (ulong)d; + } + } catch (OverflowException) { + failed = true; + } + if (failed) + return 3; + + try { + double d = -1; + failed = true; + checked { + a = (ulong)d; + } + } catch (OverflowException) { + failed = false; + } + if (failed) + return 4; + + return 0; + } + + static int test_0_simple_double_casts () { + + double d = 0xffffffff; + + if ((uint)d != 4294967295) + return 1; + + d = 0xffffffffffffffff; + + if ((ulong)d != 0) + return 2; + + if ((ushort)d != 0) + return 3; + + if ((byte)d != 0) + return 4; + + d = 0xffff; + + if ((ushort)d != 0xffff) + return 5; + + if ((byte)d != 0xff) + return 6; + + return 0; + } + + static int test_0_div_zero () { + int d = 1; + int q = 0; + int val; + bool failed; + + try { + failed = true; + val = d / q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 1; + + try { + failed = true; + val = d % q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 2; + + return 0; + } + + static int test_0_udiv_zero () { + uint d = 1; + uint q = 0; + uint val; + bool failed; + + try { + failed = true; + val = d / q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 1; + + try { + failed = true; + val = d % q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 2; + + return 0; + } + + static int test_0_long_div_zero () { + long d = 1; + long q = 0; + long val; + bool failed; + + try { + failed = true; + val = d / q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 1; + + try { + failed = true; + val = d % q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 2; + + return 0; + } + + static int test_0_ulong_div_zero () { + ulong d = 1; + ulong q = 0; + ulong val; + bool failed; + + try { + failed = true; + val = d / q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 1; + + try { + failed = true; + val = d % q; + } catch (DivideByZeroException) { + failed = false; + } + if (failed) + return 2; + + return 0; + } + + static int test_0_float_div_zero () { + double d = 1; + double q = 0; + double val; + bool failed; + + try { + failed = false; + val = d / q; + } catch (DivideByZeroException) { + failed = true; + } + if (failed) + return 1; + + try { + failed = false; + val = d % q; + } catch (DivideByZeroException) { + failed = true; + } + if (failed) + return 2; + + return 0; + } + +} diff --git a/mono/mini/genmdesc.c b/mono/mini/genmdesc.c new file mode 100644 index 00000000000..e7496983ec1 --- /dev/null +++ b/mono/mini/genmdesc.c @@ -0,0 +1,236 @@ +/* + * genmdesc: Generates the machine description + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include "mini.h" +#include +#include +#include + +typedef struct { + int num; + const char *name; + char *desc; + char *comment; + char spec [MONO_INST_MAX]; +} OpDesc; + +static GHashTable *table; + +#define eat_whitespace(s) while (*(s) && isspace (*(s))) s++; + +static int +load_file (const char *name) { + FILE *f; + char buf [256]; + char *str, *p; + int line; + OpDesc *desc; + GString *comment; + + if (!(f = fopen (name, "r"))) + g_error ("Cannot open file '%s'", name); + + comment = g_string_new (""); + /* + * The format of the lines are: + * # comment + * opcode: [dest:format] [src1:format] [src2:format] [flags:format] [clob:format] + * [cost:num] [res:format] [delay:num] [len:num] + * format is a single letter that depends on the field + * NOTE: no space between the field name and the ':' + * + * len: maximum instruction length + */ + line = 0; + while ((str = fgets (buf, sizeof (buf), f))) { + ++line; + eat_whitespace (str); + if (!str [0]) + continue; + if (str [0] == '#') { + g_string_append (comment, str); + continue; + } + p = strchr (str, ':'); + if (!p) + g_error ("Invalid format at line %d in %s\n", line, name); + *p++ = 0; + eat_whitespace (p); + desc = g_hash_table_lookup (table, str); + if (!desc) + g_error ("Invalid opcode '%s' at line %d in %s\n", str, line, name); + if (desc->desc) + g_error ("Duplicated opcode '%s' at line %d in %s\n", str, line, name); + desc->desc = g_strdup (p); + desc->comment = g_strdup (comment->str); + g_string_truncate (comment, 0); + while (*p) { + if (strncmp (p, "dest:", 5) == 0) { + desc->spec [MONO_INST_DEST] = p [5]; + p += 6; + } else if (strncmp (p, "src1:", 5) == 0) { + desc->spec [MONO_INST_SRC1] = p [5]; + p += 6; + } else if (strncmp (p, "src2:", 5) == 0) { + desc->spec [MONO_INST_SRC2] = p [5]; + p += 6; + } else if (strncmp (p, "cost:", 5) == 0) { + desc->spec [MONO_INST_COST] = p [5]; + p += 6; + } else if (strncmp (p, "clob:", 5) == 0) { + desc->spec [MONO_INST_CLOB] = p [5]; + p += 6; + } else if (strncmp (p, "res:", 4) == 0) { + desc->spec [MONO_INST_RES] = p [4]; + p += 5; + } else if (strncmp (p, "flags:", 6) == 0) { + desc->spec [MONO_INST_FLAGS] = p [6]; + p += 7; + } else if (strncmp (p, "delay:", 6) == 0) { + desc->spec [MONO_INST_DELAY] = p [6]; + p += 7; + } else if (strncmp (p, "len:", 4) == 0) { + p += 4; + desc->spec [MONO_INST_LEN] = strtoul (p, &p, 10); + } else { + g_error ("Parse error at '%s' at line %d in %s\n", p, line, name); + } + eat_whitespace (p); + } + } + fclose (f); + return 0; +} + +static OpDesc *opcodes = NULL; + +static void +init_table (void) { + int i; + OpDesc *desc; + + table = g_hash_table_new (g_str_hash, g_str_equal); + + opcodes = g_new0 (OpDesc, OP_LAST); + for (i = 0; i < MONO_CEE_LAST; ++i) { + desc = opcodes + i; + desc->num = i; + desc->name = mono_inst_name (i); + g_hash_table_insert (table, (char *)desc->name, desc); + } + for (i = OP_LOAD; i < OP_LAST; ++i) { + desc = opcodes + i; + desc->num = i; + desc->name = mono_inst_name (i); + g_hash_table_insert (table, (char *)desc->name, desc); + } +} + +static void +output_char (FILE *f, char c) { + if (isalnum (c)) + fprintf (f, "%c", c); + else + fprintf (f, "\\x%x\" \"", c); +} + +static void +build_table (const char *fname, const char *name) { + FILE *f; + int i, j; + OpDesc *desc; + + if (!(f = fopen (fname, "w"))) + g_error ("Cannot open file '%s'", fname); + fprintf (f, "/* File automatically generated by genmdesc, don't change */\n\n"); + fprintf (f, "static const char * const %s [OP_LAST] = {\n", name); + + for (i = 0; i < MONO_CEE_LAST; ++i) { + desc = opcodes + i; + if (!desc->desc) + fprintf (f, "\tNULL,\t/* %s */\n", desc->name); + else { + fprintf (f, "\t\""); + for (j = 0; j < MONO_INST_MAX; ++j) + output_char (f, desc->spec [j]); + fprintf (f, "\",\t/* %s */\n", desc->name); + } + } + for (i = MONO_CEE_LAST; i < OP_LOAD; ++i) { + fprintf (f, "\tNULL, /* unassigned */\n"); + } + for (i = OP_LOAD; i < OP_LAST; ++i) { + desc = opcodes + i; + if (!desc->desc) + fprintf (f, "\tNULL,\t/* %s */\n", desc->name); + else { + fprintf (f, "\t\""); + for (j = 0; j < MONO_INST_MAX; ++j) + output_char (f, desc->spec [j]); + fprintf (f, "\",\t/* %s */\n", desc->name); + } + } + fprintf (f, "};\n\n"); + fclose (f); +} + +static void +dump (void) { + int i; + OpDesc *desc; + + for (i = 0; i < MONO_CEE_LAST; ++i) { + desc = opcodes + i; + if (desc->comment) + g_print ("%s", desc->comment); + if (!desc->desc) + g_print ("%s:\n", desc->name); + else { + g_print ("%s: %s", desc->name, desc->desc); + if (!strchr (desc->desc, '\n')) + g_print ("\n"); + } + } + for (i = OP_LOAD; i < OP_LAST; ++i) { + desc = opcodes + i; + if (!desc->desc) + g_print ("%s:\n", desc->name); + else { + g_print ("%s: %s", desc->name, desc->desc); + if (!strchr (desc->desc, '\n')) + g_print ("\n"); + } + } +} + +/* + * TODO: output the table (possibly merged), in the input format + */ +int +main (int argc, char* argv []) +{ + init_table (); + switch (argc) { + case 2: + /* useful to get a new file when some opcodes are added: looses the comments, though */ + load_file (argv [1]); + dump (); + break; + case 4: + load_file (argv [1]); + build_table (argv [2], argv [3]); + break; + default: + g_print ("Usage: genmdesc arguments\n"); + g_print ("\tgenmdesc desc Output to stdout the description file.\n"); + g_print ("\tgenmdesc desc output name Write to output the description in a table named 'name'.\n"); + return 1; + } + return 0; +} + diff --git a/mono/mini/graph.c b/mono/mini/graph.c new file mode 100644 index 00000000000..f69239eed60 --- /dev/null +++ b/mono/mini/graph.c @@ -0,0 +1,323 @@ +/* + * graph.c: Helper routines to graph various internal states of the code generator + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include +#include + +#include "mini.h" + +extern guint8 mono_burg_arity []; + +static char * +convert_name (const char *str) +{ + int i, j, len = strlen (str); + char *res = g_malloc (len * 2); + + j = 0; + for (i = 0; i < len; i++) { + char c = str [i]; + + switch (c) { + case '.': + res [j++] = '_'; + break; + default: + res [j++] = c; + } + } + + res [j] = 0; + + return res; +} + +static void +dtree_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h) +{ + MonoBasicBlock *bb; + int i, level = 0; + + if (h) { + level = h->nesting; + fprintf (fp, "subgraph cluster_%d {\n", h->block_num); + fprintf (fp, "label=\"loop_%d\"\n", h->block_num); + } + + for (i = 1; i < cfg->num_bblocks; ++i) { + bb = cfg->bblocks [i]; + + if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) { + if (bb->nesting == level) { + fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num); + } + + if (bb->nesting == (level + 1) && bb->loop_blocks) { + fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num); + dtree_emit_one_loop_level (cfg, fp, bb); + } + } + } + + if (h) { + fprintf (fp, "}\n"); + } +} + +static void +cfg_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h) +{ + MonoBasicBlock *bb; + int i, j, level = 0; + + if (h) { + level = h->nesting; + fprintf (fp, "subgraph cluster_%d {\n", h->block_num); + fprintf (fp, "label=\"loop_%d\"\n", h->block_num); + } + + for (i = 1; i < cfg->num_bblocks; ++i) { + bb = cfg->bblocks [i]; + + if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) { + if (bb->nesting == level) { + for (j = 0; j < bb->in_count; j++) + fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num); + } + + if (bb->nesting == (level + 1) && bb->loop_blocks) { + for (j = 0; j < bb->in_count; j++) + fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num); + cfg_emit_one_loop_level (cfg, fp, bb); + } + } + } + + if (h) { + fprintf (fp, "}\n"); + } +} + +static void +mono_draw_dtree (MonoCompile *cfg, FILE *fp) +{ + g_assert ((cfg->comp_done & MONO_COMP_IDOM)); + + fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); + fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); + fprintf (fp, "label=\"Dominator tree for %s\";\n", mono_method_full_name (cfg->method, TRUE)); + + fprintf (fp, "BB0 [shape=doublecircle];\n"); + fprintf (fp, "BB1 [color=red];\n"); + + dtree_emit_one_loop_level (cfg, fp, NULL); + + fprintf (fp, "}\n"); +} + +static void +mono_draw_cfg (MonoCompile *cfg, FILE *fp) +{ + fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); + fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); + fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE)); + + fprintf (fp, "BB0 [shape=doublecircle];\n"); + fprintf (fp, "BB1 [color=red];\n"); + + cfg_emit_one_loop_level (cfg, fp, NULL); + + fprintf (fp, "}\n"); +} + +static void +mono_print_label (FILE *fp, MonoInst *tree) { + int arity; + + if (!tree) + return; + + arity = mono_burg_arity [tree->opcode]; + + fprintf (fp, "\\ %s%s", arity? "(": "", mono_inst_name (tree->opcode)); + + switch (tree->opcode) { + case OP_ICONST: + fprintf (fp, "[%d]", tree->inst_c0); + break; + case OP_I8CONST: + fprintf (fp, "[%lld]", tree->inst_l); + break; + case OP_R8CONST: + fprintf (fp, "[%f]", *(double*)tree->inst_p0); + break; + case OP_R4CONST: + fprintf (fp, "[%f]", *(float*)tree->inst_p0); + break; + case OP_ARG: + case OP_LOCAL: + fprintf (fp, "[%d]", tree->inst_c0); + break; + case OP_REGOFFSET: + fprintf (fp, "[0x%x(%s)]", tree->inst_offset, mono_arch_regname (tree->inst_basereg)); + break; + case OP_REGVAR: + fprintf (fp, "[%s]", mono_arch_regname (tree->dreg)); + break; + case CEE_NEWARR: + fprintf (fp, "[%s]", tree->inst_newa_class->name); + mono_print_label (fp, tree->inst_newa_len); + break; + case CEE_CALL: + case CEE_CALLVIRT: + case OP_FCALL: + case OP_FCALLVIRT: + case OP_LCALL: + case OP_LCALLVIRT: + case OP_VCALL: + case OP_VCALLVIRT: + case OP_VOIDCALL: + case OP_VOIDCALLVIRT: { + MonoCallInst *call = (MonoCallInst*)tree; + if (call->method) { + if (call->method->signature->hasthis && tree->inst_left) { + mono_print_label (fp, tree->inst_left); + } + fprintf (fp, "[%s]", call->method->name); + } + break; + } + case OP_PHI: { + int i; + fprintf (fp, "[%d\\ (", tree->inst_c0); + for (i = 0; i < tree->inst_phi_args [0]; i++) { + if (i) + fprintf (fp, ",\\ "); + fprintf (fp, "%d", tree->inst_phi_args [i + 1]); + } + fprintf (fp, ")]"); + break; + } + case OP_RENAME: + case OP_RETARG: + case CEE_NOP: + case CEE_JMP: + case CEE_BREAK: + break; + case CEE_BR: + fprintf (fp, "[B%d]", tree->inst_target_bb->block_num); + break; + case CEE_SWITCH: + case CEE_ISINST: + case CEE_CASTCLASS: + case OP_OUTARG: + case OP_CALL_REG: + case OP_FCALL_REG: + case OP_LCALL_REG: + case OP_VCALL_REG: + case OP_VOIDCALL_REG: + mono_print_label (fp, tree->inst_left); + break; + case CEE_BNE_UN: + case CEE_BEQ: + case CEE_BLT: + case CEE_BLT_UN: + case CEE_BGT: + case CEE_BGT_UN: + case CEE_BGE: + case CEE_BGE_UN: + case CEE_BLE: + case CEE_BLE_UN: + fprintf (fp, "[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num); + mono_print_label (fp, tree->inst_left); + break; + default: + if (arity) { + mono_print_label (fp, tree->inst_left); + if (arity > 1) + mono_print_label (fp, tree->inst_right); + } + break; + } + + if (arity) + fprintf (fp, ")"); +} + +static void +mono_draw_code_cfg (MonoCompile *cfg, FILE *fp) +{ + MonoBasicBlock *bb; + + fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); + fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); + fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE)); + + fprintf (fp, "BB0 [shape=doublecircle];\n"); + fprintf (fp, "BB1 [color=red];\n"); + + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + MonoInst *inst; + const char *color; + + if (bb == cfg->bb_exit) + continue; + + if ((cfg->comp_done & MONO_COMP_REACHABILITY) && (bb->flags & BB_REACHABLE)) + color = "color=red,"; + else + color = ""; + + fprintf (fp, "BB%d [%sshape=record,labeljust=l,label=\"{BB%d|", bb->block_num, color, bb->block_num); + + for (inst = bb->code; inst; inst = inst->next) { + mono_print_label (fp, inst); + fprintf (fp, "\\n"); + } + + fprintf (fp, "}\"];\n"); + } + + cfg_emit_one_loop_level (cfg, fp, NULL); + + fprintf (fp, "}\n"); +} + +void +mono_draw_graph (MonoCompile *cfg, MonoGraphOptions draw_options) +{ + char *com; + const char *fn; + FILE *fp; + + fn = "/tmp/minidtree.graph"; + fp = fopen (fn, "w+"); + g_assert (fp); + + switch (draw_options) { + case MONO_GRAPH_DTREE: + mono_draw_dtree (cfg, fp); + break; + case MONO_GRAPH_CFG: + mono_draw_cfg (cfg, fp); + break; + case MONO_GRAPH_CFG_CODE: + case MONO_GRAPH_CFG_OPTCODE: + case MONO_GRAPH_CFG_SSA: + mono_draw_code_cfg (cfg, fp); + break; + } + + fclose (fp); + + //com = g_strdup_printf ("dot %s -Tpng -o %s.png; eog %s.png", fn, fn, fn); + com = g_strdup_printf ("dot %s -Tps -o %s.ps;gv %s.ps", fn, fn, fn); + system (com); + g_free (com); +} + diff --git a/mono/mini/helpers.c b/mono/mini/helpers.c new file mode 100644 index 00000000000..04ea50f0c92 --- /dev/null +++ b/mono/mini/helpers.c @@ -0,0 +1,80 @@ +/* + * helpers.c: Assorted routines + * + * (C) 2003 Ximian, Inc. + */ +#include "mini.h" +#include +#include + +#ifdef MINI_OP +#undef MINI_OP +#endif +#define MINI_OP(a,b) b, +/* keep in sync with the enum in mini.h */ +static const char* +opnames[] = { +#include "mini-ops.h" +}; +#undef MINI_OP + +const char* +mono_inst_name (int op) { + if (op >= OP_LOAD && op <= OP_LAST) + return opnames [op - OP_LOAD]; + if (op < OP_LOAD) + return mono_opcode_names [op]; + g_error ("unknown opcode name for %d", op); + return NULL; +} + +void +mono_blockset_print (MonoCompile *cfg, MonoBitSet *set, const char *name, guint idom) +{ + int i; + + if (name) + g_print ("%s:", name); + + mono_bitset_foreach_bit (set, i, cfg->num_bblocks) { + if (idom == i) + g_print (" [BB%d]", cfg->bblocks [i]->block_num); + else + g_print (" BB%d", cfg->bblocks [i]->block_num); + + } + g_print ("\n"); +} + +/** + * mono_disassemble_code: + * @code: a pointer to the code + * @size: the code size in bytes + * + * Disassemble to code to stdout. + */ +void +mono_disassemble_code (guint8 *code, int size, char *id) +{ + int i; + FILE *ofd; + + if (!(ofd = fopen ("/tmp/test.s", "w"))) + g_assert_not_reached (); + + for (i = 0; id [i]; ++i) { + if (!isalnum (id [i])) + fprintf (ofd, "_"); + else + fprintf (ofd, "%c", id [i]); + } + fprintf (ofd, ":\n"); + + for (i = 0; i < size; ++i) + fprintf (ofd, ".byte %d\n", (unsigned int) code [i]); + + fclose (ofd); + + system ("as /tmp/test.s -o /tmp/test.o;objdump -d /tmp/test.o"); +} + diff --git a/mono/mini/iltests.il b/mono/mini/iltests.il new file mode 100644 index 00000000000..4df34c9aaac --- /dev/null +++ b/mono/mini/iltests.il @@ -0,0 +1,94 @@ +.assembly iltests {} +.assembly extern TestDriver {} +.assembly extern mscorlib {} + +.class public auto ansi sealed beforefieldinit Tests { + + .method static public int32 Main() il managed { + .entrypoint + + ldtoken Tests + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call int32 [TestDriver]TestDriver::RunTests(class [mscorlib]System.Type) + ret + } + + .method static public int32 test_3_copy_used_bug () il managed { + + .locals init ( + int32 size, + int32 res + ) + + ldc.i4 0 + stloc res + + ldc.i4 1 + stloc size + + ldloc size + ldloc size + ldloc size + add + stloc size + ldloc size + add + stloc res + + ldloc res + ret + } + + // demonstrate that the copy_used_var is not a fix for the above bug + .method static public int32 test_3_copy_used_indir_bug () il managed { + + .locals init ( + int32 size, + int32 res + ) + + ldc.i4 0 + stloc res + + ldc.i4 1 + stloc size + + ldloc size + ldloca size + ldloc size + ldloc size + add + stind.i4 + ldloc size + add + stloc res + + ldloc res + ret + } + + .method static public void do_nothing (int32 a) il managed { + ret + } + + // demonstrate the block_split failure: needs -O=inline + // mini -O=inline --compile Tests:test_0_split_block_bug iltests.exe + .method static public int32 test_0_split_block_bug () il managed { + + .locals init ( + int32 i1 + ) + + ldc.i4 1 + stloc i1 + test_label: + ldloc i1 + call void class Tests::do_nothing (int32) + ldc.i4 0 + brtrue test_label + + ldc.i4 0 + ret + } + +} diff --git a/mono/mini/inssel-float.brg b/mono/mini/inssel-float.brg new file mode 100644 index 00000000000..e92fa2487cb --- /dev/null +++ b/mono/mini/inssel-float.brg @@ -0,0 +1,233 @@ +%% + +# +# inssel-float.brg: burg file for floating point instructions +# +# Author: +# Dietmar Maurer (dietmar@ximian.com) +# +# (C) 2002 Ximian, Inc. +# + +# +# load/store +# + +freg: CEE_LDIND_R4 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADR4_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +freg: CEE_LDIND_R8 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADR8_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +stmt: CEE_STIND_R4 (base, freg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORER4_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_R8 (base, freg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORER8_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +freg: OP_R4CONST { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +freg: OP_R8CONST { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +# +# fp alu operations + + +freg: OP_FADD (freg, freg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +freg: OP_FSUB (freg, freg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +freg: OP_FMUL (freg, freg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +freg: OP_FDIV (freg, freg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +freg: OP_FREM (freg, freg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +freg: OP_FNEG (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: CEE_CKFINITE (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_SIN (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_COS (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_ABS (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_TAN (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_ATAN (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_SQRT (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +# +# floating point conversions +# + +reg: OP_FCONV_TO_I4 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_U4 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_I4 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_U4 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_I8 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_U8 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_I (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_OVF_U (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_I (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_U (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_I2 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_U2 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_I1 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_FCONV_TO_U1 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: OP_FCONV_TO_R8 (freg) { + /* nothing to do */ +} + +freg: OP_FCONV_TO_R4 (freg) { + /* fixme: nothing to do ??*/ +} + +freg: CEE_CONV_R4 (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: CEE_CONV_R8 (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +freg: CEE_CONV_R_UN (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +# +# control flow +# + +# CPUs using the same condition flags for integers and float +# can use the following chain rule: +# cflags: fpcflags "0" +# that way all branches are handled by inssel.brg + +fpcflags: OP_COMPARE (freg, freg) { + tree->opcode = OP_FCOMPARE; + tree->sreg1 = state->left->reg1; + tree->sreg2 = state->right->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +# +# miscellaneous fp operations +# + +stmt: CEE_POP (freg) { + /* do nothing */ +} + + +freg: CEE_CKFINITE (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + + + + + + + + + + + + + + + + + + + + + + + +%% diff --git a/mono/mini/inssel-long.brg b/mono/mini/inssel-long.brg new file mode 100644 index 00000000000..11c707077d8 --- /dev/null +++ b/mono/mini/inssel-long.brg @@ -0,0 +1,47 @@ +%% + +# +# inssel-long.brg: burg file for 64bit architectures +# +# Author: +# Dietmar Maurer (dietmar@ximian.com) +# +# (C) 2002 Ximian, Inc. +# + +reg: CEE_LDIND_I8 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI8_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +stmt: CEE_STIND_I8 (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI8_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I8 (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI8_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + +reg: OP_FCONV_TO_I8 (freg) { + g_assert_not_reached (); +} + +reg: OP_FCONV_TO_U8 (freg) { + g_assert_not_reached (); +} + +freg: CEE_CONV_R_UN (reg) { + g_assert_not_reached (); +} + +freg: CEE_CONV_R4 (reg) { + g_assert_not_reached (); +} + +freg: CEE_CONV_R8 (reg) { + g_assert_not_reached (); +} + +%% diff --git a/mono/mini/inssel-long32.brg b/mono/mini/inssel-long32.brg new file mode 100644 index 00000000000..e4fbce5be24 --- /dev/null +++ b/mono/mini/inssel-long32.brg @@ -0,0 +1,606 @@ +%% + +# +# inssel-long32.brg: burg file for 64bit instructions on 32bit architectures +# +# Author: +# Dietmar Maurer (dietmar@ximian.com) +# +# (C) 2002 Ximian, Inc. +# + +# +# We use a new non-terminal called "lreg" for 64bit registers, and +# emulate lreg with 2 32bit registers. +# + +stmt: CEE_POP (lreg) { + /* do nothing */ +} + +i8con: CEE_CONV_I8 (OP_ICONST) "0" { + int data = state->left->tree->inst_c0; + tree->opcode = OP_I8CONST; + tree->inst_c0 = data; + if (data < 0) + tree->inst_c1 = -1; + else + tree->inst_c1 = 0; +} + +i8con: CEE_CONV_U8 (OP_ICONST) "0" { + int data = state->left->tree->inst_c0; + tree->opcode = OP_I8CONST; + tree->inst_c0 = data; + tree->inst_c1 = 0; +} + +i8con: OP_I8CONST "0" + +lreg: OP_ICONST { + int data = state->tree->inst_c0; + + MONO_EMIT_NEW_ICONST (s, state->reg1, data); + + if (data >= 0) + MONO_EMIT_NEW_ICONST (s, state->reg2, 0); + else + MONO_EMIT_NEW_ICONST (s, state->reg2, -1); +} + +lreg: OP_I8CONST { + MONO_EMIT_NEW_ICONST (s, state->reg1, tree->inst_c0); + MONO_EMIT_NEW_ICONST (s, state->reg2, tree->inst_c1); +} + +lreg: CEE_LDIND_I8 (base) { + MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); + MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, state->reg2, + state->left->tree->inst_basereg, state->left->tree->inst_offset + 4); +} + +stmt: CEE_STIND_I8 (base, lreg) { + MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset + 4, state->right->reg2); + MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I8 (base, i8con) { + MONO_EMIT_NEW_STORE_MEMBASE_IMM (s, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset + 4, state->right->tree->inst_c1); + MONO_EMIT_NEW_STORE_MEMBASE_IMM (s, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + + +lreg: OP_LADD (lreg, lreg) { + MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_BIALU (s, tree, OP_ADC, state->reg2, state->left->reg2, state->right->reg2); +} + +lreg: OP_LADD_OVF (lreg, lreg) { + /* ADC sets the condition code */ + MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_BIALU (s, OP_ADC, state->reg2, state->left->reg2, state->right->reg2); + MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException"); +} + +lreg: OP_LADD_OVF_UN (lreg, lreg) { + /* ADC sets the condition code */ + MONO_EMIT_NEW_BIALU (s, CEE_ADD, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_BIALU (s, OP_ADC, state->reg2, state->left->reg2, state->right->reg2); + MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException"); +} + +lreg: OP_LADD (lreg, i8con) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); + MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1); +} + +lreg: OP_LSUB (lreg, lreg) { + MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_BIALU (s, tree, OP_SBB, state->reg2, state->left->reg2, state->right->reg2); +} + +lreg: OP_LSUB (lreg, i8con) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); + MONO_EMIT_BIALU_IMM (s, tree, OP_SBB_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1); +} + +lreg: OP_LSUB_OVF (lreg, lreg) { + /* SBB sets the condition code */ + MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_BIALU (s, OP_SBB, state->reg2, state->left->reg2, state->right->reg2); + MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException"); +} + +lreg: OP_LSUB_OVF_UN (lreg, lreg) { + /* SBB sets the condition code */ + MONO_EMIT_NEW_BIALU (s, CEE_SUB, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_BIALU (s, OP_SBB, state->reg2, state->left->reg2, state->right->reg2); + MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException"); +} + +lreg: OP_LAND (lreg, lreg) { + MONO_EMIT_NEW_BIALU (s, CEE_AND, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_BIALU (s, tree, CEE_AND, state->reg2, state->left->reg2, state->right->reg2); +} + +lreg: OP_LAND (lreg, i8con) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_AND_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1); +} + +lreg: OP_LOR (lreg, lreg) { + MONO_EMIT_NEW_BIALU (s, CEE_OR, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_BIALU (s, tree, CEE_OR, state->reg2, state->left->reg2, state->right->reg2); +} + +lreg: OP_LOR (lreg, i8con) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_OR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); + MONO_EMIT_BIALU_IMM (s, tree, OP_OR_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1); +} + +lreg: OP_LXOR (lreg, lreg) { + MONO_EMIT_NEW_BIALU (s, CEE_XOR, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_BIALU (s, tree, CEE_XOR, state->reg2, state->left->reg2, state->right->reg2); +} + +lreg: OP_LXOR (lreg, i8con) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_XOR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); + MONO_EMIT_BIALU_IMM (s, tree, OP_XOR_IMM, state->reg2, state->left->reg2, state->right->tree->inst_c1); +} + +lreg: OP_LNOT (lreg) { + MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg1, state->left->reg1); + MONO_EMIT_UNALU (s, tree, CEE_NOT, state->reg2, state->left->reg2); +} + +lreg: OP_LNEG (lreg) "4" { + MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg1, state->left->reg1); + MONO_EMIT_NEW_UNALU (s, CEE_NOT, state->reg2, state->left->reg2); + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->reg1, 1); + MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, state->reg2, state->reg2, 0); +} + +reg: OP_CEQ (OP_COMPARE (lreg, lreg)) { + MonoInst *word_differs; + + MONO_NEW_LABEL (s, word_differs); + + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, word_differs); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, word_differs); + MONO_EMIT_NEW_ICONST (s, state->reg1, 1); + + mono_bblock_add_inst (s->cbb, word_differs); +} + +reg: OP_CLT (OP_COMPARE (lreg, lreg)) { + MonoInst *set_to_0, *set_to_1; + + MONO_NEW_LABEL (s, set_to_0); + MONO_NEW_LABEL (s, set_to_1); + + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT, set_to_0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0); + mono_bblock_add_inst (s->cbb, set_to_1); + MONO_EMIT_NEW_ICONST (s, state->reg1, 1); + mono_bblock_add_inst (s->cbb, set_to_0); +} + +reg: OP_CLT_UN (OP_COMPARE (lreg, lreg)) { + MonoInst *set_to_0, *set_to_1; + + MONO_NEW_LABEL (s, set_to_0); + MONO_NEW_LABEL (s, set_to_1); + + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT_UN, set_to_0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0); + mono_bblock_add_inst (s->cbb, set_to_1); + MONO_EMIT_NEW_ICONST (s, state->reg1, 1); + mono_bblock_add_inst (s->cbb, set_to_0); +} + +reg: OP_CGT (OP_COMPARE (lreg, lreg)) { + MonoInst *set_to_0, *set_to_1; + + MONO_NEW_LABEL (s, set_to_0); + MONO_NEW_LABEL (s, set_to_1); + + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg2, state->left->left->reg2); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT, set_to_0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg1, state->left->left->reg1); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0); + mono_bblock_add_inst (s->cbb, set_to_1); + MONO_EMIT_NEW_ICONST (s, state->reg1, 1); + mono_bblock_add_inst (s->cbb, set_to_0); +} + +reg: OP_CGT_UN (OP_COMPARE (lreg, lreg)) { + MonoInst *set_to_0, *set_to_1; + + MONO_NEW_LABEL (s, set_to_0); + MONO_NEW_LABEL (s, set_to_1); + + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg2, state->left->left->reg2); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGT_UN, set_to_0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, set_to_1); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->right->reg1, state->left->left->reg1); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE_UN, set_to_0); + mono_bblock_add_inst (s->cbb, set_to_1); + MONO_EMIT_NEW_ICONST (s, state->reg1, 1); + mono_bblock_add_inst (s->cbb, set_to_0); +} + +stmt: CEE_BNE_UN (OP_COMPARE (lreg, lreg)) { + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BNE_UN (OP_COMPARE (lreg, i8con)) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BEQ (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BEQ (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLE (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb); +} + +stmt: CEE_BLE (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb); +} + +stmt: CEE_BLE_UN (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb); +} + +stmt: CEE_BLE_UN (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLE_UN, tree->inst_true_bb); +} + +stmt: CEE_BGE (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb); +} + +stmt: CEE_BGE (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb); +} + +stmt: CEE_BGE_UN (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb); +} + +stmt: CEE_BGE_UN (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_true_bb); +} + +stmt: CEE_BLT (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); +} + +stmt: CEE_BLT (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); +} + +stmt: CEE_BLT_UN (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); +} + +stmt: CEE_BLT_UN (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BLT_UN, tree->inst_true_bb); +} + +stmt: CEE_BGT (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); +} + +stmt: CEE_BGT (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); +} + +stmt: CEE_BGT_UN (OP_COMPARE (lreg, lreg)) { + + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg2, state->left->right->reg2); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, state->left->left->reg1, state->left->right->reg1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); +} + +stmt: CEE_BGT_UN (OP_COMPARE (lreg, i8con)) { + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg2, state->left->right->tree->inst_c1); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BNE_UN, tree->inst_false_bb); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->left->reg1, state->left->right->tree->inst_c0); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGT_UN, tree->inst_true_bb); +} + +lreg: CEE_CONV_I8 (OP_ICONST) { + int data = state->left->tree->inst_c0; + + MONO_EMIT_NEW_ICONST (s, state->reg1, data); + + if (data >= 0) + MONO_EMIT_NEW_ICONST (s, state->reg2, 0); + else + MONO_EMIT_NEW_ICONST (s, state->reg2, -1); +} + +lreg: CEE_CONV_I8 (reg) { + MonoInst *is_negative, *end_label; + int tmpreg = mono_regstate_next_int (s->rs); + + MONO_NEW_LABEL (s, is_negative); + MONO_NEW_LABEL (s, end_label); + + /* branchless code: + * low = reg; + * tmp = low > -1 ? 1: 0; + * high = tmp - 1; if low is zero or pos high becomes 0, else -1 + * not sure why it doesn't work in practice + */ + MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg1, state->left->reg1); + /*MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->reg1, -1); + tree->dreg = tmpreg; + tree->opcode = OP_CGT; + mono_bblock_add_inst (s->cbb, tree); + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, state->reg2, tmpreg, -1);*/ + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->reg1, 0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT, is_negative); + MONO_EMIT_NEW_ICONST (s, tmpreg, 0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, end_label); + mono_bblock_add_inst (s->cbb, is_negative); + MONO_EMIT_NEW_ICONST (s, tmpreg, -1); + mono_bblock_add_inst (s->cbb, end_label); + MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg2, tmpreg); +} + +lreg: CEE_CONV_U8 (reg) { + MONO_EMIT_NEW_UNALU (s, OP_MOVE, state->reg1, state->left->reg1); + MONO_EMIT_NEW_ICONST (s, state->reg2, 0); +} + +lreg: CEE_CONV_OVF_U8 (reg) { + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_NEW_ICONST (s, state->reg2, 0); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +freg: OP_LCONV_TO_R_UN (lreg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->left->reg2); +} + +lreg: OP_FCONV_TO_I8 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +lreg: OP_FCONV_TO_U8 (freg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_I4 (i8con) { + MONO_EMIT_NEW_ICONST (s, state->reg1, state->left->tree->inst_c0); +} + +reg: OP_LCONV_TO_I4 (lreg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_U4 (lreg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_U (lreg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_I (lreg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_I1 (lreg) { + MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_U1 (lreg) { + MONO_EMIT_UNALU (s, tree, CEE_CONV_U1, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_I2 (lreg) { + MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_U2 (lreg) { + MONO_EMIT_UNALU (s, tree, CEE_CONV_U2, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_I1 (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, -1); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -128); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_U1 (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException"); + + /* probe value to be within 0 to 255 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1)); +} + +reg: OP_LCONV_TO_OVF_I2 (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, -1); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + + /* Probe value to be within -32768 and 32767 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -32768); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_U2 (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException"); + + /* Probe value to be within 0 and 65535 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1)); +} + + +reg: OP_LCONV_TO_OVF_U4_UN (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException"); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_I_UN (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException"); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_U4 (lreg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg2, 0); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "OverflowException"); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); +} + +reg: OP_LCONV_TO_OVF_I (lreg) { + tree->dreg = state->reg1; + tree->sreg1 = state->left->reg1; + tree->sreg2 = state->left->reg2; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_LCONV_TO_OVF_I4 (lreg) { + tree->dreg = state->reg1; + tree->sreg1 = state->left->reg1; + tree->sreg2 = state->left->reg2; + tree->opcode = OP_LCONV_TO_OVF_I; + mono_bblock_add_inst (s->cbb, tree); +} + +%% diff --git a/mono/mini/inssel-x86.brg b/mono/mini/inssel-x86.brg new file mode 100644 index 00000000000..19495c6d008 --- /dev/null +++ b/mono/mini/inssel-x86.brg @@ -0,0 +1,497 @@ +%% + +# +# inssel-x86.brg: burg file for special x86 instructions +# +# Author: +# Dietmar Maurer (dietmar@ximian.com) +# Paolo Molaro (lupus@ximian.com) +# +# (C) 2002 Ximian, Inc. +# + +stmt: CEE_STIND_I8 (OP_REGVAR, lreg) { + /* this should only happen for methods returning a long */ + MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EAX, state->right->reg1); + MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EDX, state->right->reg2); +} + +lreg: OP_LNEG (lreg) "3" { + int tmpr = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_UNALU (s, CEE_NEG, state->reg1, state->left->reg1); + MONO_EMIT_BIALU_IMM (s, tree, OP_ADC_IMM, tmpr, state->left->reg2, 0); + MONO_EMIT_NEW_UNALU (s, CEE_NEG, state->reg2, tmpr); +} + +freg: OP_LCONV_TO_R8 (lreg) { + MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg2); + MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1); + tree->opcode = OP_X86_FP_LOAD_I8; + tree->inst_basereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8); +} + +freg: OP_LCONV_TO_R4 (lreg) { + MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1); + tree->opcode = OP_X86_FP_LOAD_I4; + tree->inst_basereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 4); +} + +freg: CEE_CONV_R_UN (reg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_X86_PUSH_IMM, -1, -1, 0); + MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg1); + tree->opcode = OP_X86_FP_LOAD_I8; + tree->inst_basereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8); +} + +cflags: OP_COMPARE (CEE_LDIND_I4 (base), reg) { + tree->opcode = OP_X86_COMPARE_MEMBASE_REG; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + tree->sreg2 = state->right->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (CEE_LDIND_I4 (base), OP_ICONST) { + tree->opcode = OP_X86_COMPARE_MEMBASE_IMM; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + tree->inst_imm = state->right->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (reg, CEE_LDIND_I4 (base)) { + tree->opcode = OP_X86_COMPARE_REG_MEMBASE; + tree->sreg2 = state->right->left->tree->inst_basereg; + tree->inst_offset = state->right->left->tree->inst_offset; + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_LOCALLOC (OP_ICONST) { + if (tree->flags & MONO_INST_INIT) { + /* microcoded in mini-x86.c */ + tree->sreg1 = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_ICONST (s, tree->sreg1, state->left->tree->inst_c0); + mono_bblock_add_inst (s->cbb, tree); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, state->left->tree->inst_c0); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, X86_ESP); + } +} + +reg: OP_LOCALLOC (reg) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_SETRET (reg) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->reg1; + tree->dreg = X86_EAX; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_SETRET (lreg) { + MONO_EMIT_NEW_UNALU (s, OP_MOVE, X86_EDX, state->left->reg2); + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->reg1; + tree->dreg = X86_EAX; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_SETRET (CEE_LDIND_REF (OP_REGVAR)) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->left->tree->dreg; + tree->dreg = X86_EAX; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_SETRET (freg) { + /* nothing to do */ +} + +stmt: OP_SETRET (OP_ICONST) { + tree->opcode = OP_ICONST; + tree->inst_c0 = state->left->tree->inst_c0; + tree->dreg = X86_EAX; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (reg) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +# we need to reduce this code duplication with some burg syntax extension +stmt: OP_OUTARG (CEE_LDIND_REF (OP_REGVAR)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->left->tree->dreg; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_I4 (OP_REGVAR)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->left->tree->dreg; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_U4 (OP_REGVAR)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->left->tree->dreg; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_I (OP_REGVAR)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->left->tree->dreg; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (lreg) { + MONO_EMIT_NEW_UNALU (s, OP_X86_PUSH, -1, state->left->reg2); + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_I8 (base)) { + MonoInst *ins; + ins = mono_mempool_alloc0 (s->mempool, sizeof (MonoInst)); + ins->opcode = OP_X86_PUSH_MEMBASE; + ins->inst_basereg = state->left->left->tree->inst_basereg; + ins->inst_offset = state->left->left->tree->inst_offset + 4; + mono_bblock_add_inst (s->cbb, ins); + + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (OP_ICONST) { + tree->opcode = OP_X86_PUSH_IMM; + tree->inst_imm = state->left->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_I4 (base)) { + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_U4 (base)) { + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_I (base)) { + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_REF (base)) { + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = state->left->left->tree->inst_basereg; + tree->inst_offset = state->left->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDIND_REF (OP_REGVAR)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->left->tree->dreg; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (CEE_LDOBJ (reg)) { + tree->opcode = OP_X86_PUSH; + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG (freg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8); + tree->opcode = OP_STORER8_MEMBASE_REG; + tree->sreg1 = state->left->reg1; + tree->inst_destbasereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG_R4 (freg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 4); + tree->opcode = OP_STORER4_MEMBASE_REG; + tree->sreg1 = state->left->reg1; + tree->inst_destbasereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG_R8 (freg) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_SUB_IMM, X86_ESP, X86_ESP, 8); + tree->opcode = OP_STORER8_MEMBASE_REG; + tree->sreg1 = state->left->reg1; + tree->inst_destbasereg = X86_ESP; + tree->inst_offset = 0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_OUTARG_VT (CEE_LDOBJ (base)) { + MonoInst *vt = state->left->left->tree; + //g_print ("vt size: %d at R%d + %d\n", tree->inst_imm, vt->inst_basereg, vt->inst_offset); + if (tree->inst_imm <= 4) { + tree->opcode = OP_X86_PUSH_MEMBASE; + tree->inst_basereg = vt->inst_basereg; + tree->inst_offset = vt->inst_offset; + mono_bblock_add_inst (s->cbb, tree); + } else { + tree->opcode = OP_X86_PUSH_OBJ; + tree->inst_basereg = vt->inst_basereg; + tree->inst_offset = vt->inst_offset; + mono_bblock_add_inst (s->cbb, tree); + } +} + +stmt: OP_OUTARG_VT (OP_ICONST) { + tree->opcode = OP_X86_PUSH_IMM; + tree->inst_imm = state->left->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_LDELEMA (reg, reg) "15" { + int length_reg = mono_regstate_next_int (s->rs); + guint32 size = mono_class_array_element_size (tree->klass); + + MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, + state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length)); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, length_reg, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException"); + + if (size == 1 || size == 2 || size == 4 || size == 8) { + static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 }; + tree->opcode = OP_X86_LEA; + tree->dreg = state->reg1; + tree->sreg1 = state->left->reg1; + tree->sreg2 = state->right->reg1; + tree->inst_imm = G_STRUCT_OFFSET (MonoArray, vector); + tree->unused = fast_log2 [size]; + mono_bblock_add_inst (s->cbb, tree); + } else { + int mult_reg = mono_regstate_next_int (s->rs); + int add_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_BIALU_IMM (s, OP_MUL_IMM, mult_reg, state->right->reg1, size); + MONO_EMIT_NEW_BIALU (s, CEE_ADD, add_reg, mult_reg, state->left->reg1); + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, add_reg, G_STRUCT_OFFSET (MonoArray, vector)); + } +} + +stmt: CEE_STIND_R8 (OP_REGVAR, freg) { + /* nothing to do: the value is already on the FP stack */ +} + +stmt: CEE_STIND_I4 (base, CEE_ADD (CEE_LDIND_I4 (base), OP_ICONST)) { + int con = state->right->right->tree->inst_c0; + + if (con == 1) { + tree->opcode = OP_X86_INC_MEMBASE; + } else { + tree->opcode = OP_X86_ADD_MEMBASE_IMM; + tree->inst_imm = con; + } + + tree->inst_basereg = state->left->tree->inst_basereg; + tree->inst_offset = state->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} cost { + MBTREE_TYPE *t1 = state->right->left->left->tree; + MBTREE_TYPE *t2 = state->left->tree; + MBCOND (t1->inst_basereg == t2->inst_basereg && + t1->inst_offset == t2->inst_offset); + return 2; +} + +stmt: CEE_STIND_I4 (base, CEE_SUB (CEE_LDIND_I4 (base), OP_ICONST)) { + int con = state->right->right->tree->inst_c0; + + if (con == 1) { + tree->opcode = OP_X86_DEC_MEMBASE; + } else { + tree->opcode = OP_X86_SUB_MEMBASE_IMM; + tree->inst_imm = con; + } + + tree->inst_basereg = state->left->tree->inst_basereg; + tree->inst_offset = state->left->tree->inst_offset; + mono_bblock_add_inst (s->cbb, tree); +} cost { + MBTREE_TYPE *t1 = state->right->left->left->tree; + MBTREE_TYPE *t2 = state->left->tree; + MBCOND (t1->inst_basereg == t2->inst_basereg && + t1->inst_offset == t2->inst_offset); + return 2; +} + +# +# this rules is incorrect, it needs to do an indirect inc (inc_membase) +#stmt: CEE_STIND_I4 (reg, CEE_ADD (reg, OP_ICONST)) { +# tree->opcode = OP_X86_INC_REG; +# tree->dreg = state->left->reg1; +# mono_bblock_add_inst (s->cbb, tree); +#} cost { +# MBState *s1 = state->left; +# MBState *s2 = state->right->left; +# int con = state->right->right->tree->inst_c0; +# MBCOND (con == 1 && s1->reg1 == s2->reg1); +# return 1; +#} + +stmt: CEE_STIND_I4 (OP_REGVAR, CEE_SUB (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST)) { + int con = state->right->right->tree->inst_c0; + int dreg = state->left->tree->dreg; + int sreg = state->right->left->left->tree->dreg; + + if (con == 1) { + if (dreg != sreg) + MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg); + tree->opcode = OP_X86_DEC_REG; + tree->dreg = tree->sreg1 = dreg; + } else if (con == -1) { + if (dreg != sreg) + MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg); + tree->opcode = OP_X86_INC_REG; + tree->dreg = tree->sreg1 = dreg; + } else { + tree->opcode = OP_SUB_IMM; + tree->inst_imm = con; + tree->sreg1 = sreg; + tree->dreg = dreg; + } + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_STIND_I4 (OP_REGVAR, CEE_ADD (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST)) { + int con = state->right->right->tree->inst_c0; + int dreg = state->left->tree->dreg; + int sreg = state->right->left->left->tree->dreg; + + if (con == 1) { + if (dreg != sreg) + MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg); + tree->opcode = OP_X86_INC_REG; + tree->dreg = tree->sreg1 = dreg; + } else if (con == -1) { + if (dreg != sreg) + MONO_EMIT_NEW_UNALU (s, OP_MOVE, dreg, sreg); + tree->opcode = OP_X86_DEC_REG; + tree->dreg = tree->sreg1 = dreg; + } else { + tree->opcode = OP_ADD_IMM; + tree->inst_imm = con; + tree->sreg1 = sreg; + tree->dreg = dreg; + } + mono_bblock_add_inst (s->cbb, tree); +} + +# on x86, fp compare overwrites EAX, so we must +# either improve the local register allocator or +# emit coarse opcodes which saves EAX for us. + +reg: OP_CEQ (OP_COMPARE (freg, freg)) { + MONO_EMIT_BIALU (s, tree, OP_FCEQ, state->reg1, state->left->left->reg1, + state->left->right->reg1); +} + +reg: OP_CLT (OP_COMPARE (freg, freg)) { + MONO_EMIT_BIALU (s, tree, OP_FCLT, state->reg1, state->left->left->reg1, + state->left->right->reg1); +} + +reg: OP_CLT_UN (OP_COMPARE (freg, freg)) { + MONO_EMIT_BIALU (s, tree, OP_FCLT_UN, state->reg1, state->left->left->reg1, + state->left->right->reg1); +} + +reg: OP_CGT (OP_COMPARE (freg, freg)) { + MONO_EMIT_BIALU (s, tree, OP_FCGT, state->reg1, state->left->left->reg1, + state->left->right->reg1); +} + +reg: OP_CGT_UN (OP_COMPARE (freg, freg)) { + MONO_EMIT_BIALU (s, tree, OP_FCGT_UN, state->reg1, state->left->left->reg1, + state->left->right->reg1); +} + +# fpcflags overwrites EAX, but this does not matter for statements +# because we are the last operation in the tree. + +stmt: CEE_BNE_UN (fpcflags) { + tree->opcode = OP_FBNE_UN; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BEQ (fpcflags) { + tree->opcode = OP_FBEQ; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLT (fpcflags) { + tree->opcode = OP_FBLT; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLT_UN (fpcflags) { + tree->opcode = OP_FBLT_UN; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGT (fpcflags) { + tree->opcode = OP_FBGT; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGT_UN (fpcflags) { + tree->opcode = OP_FBGT_UN; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGE (fpcflags) { + tree->opcode = OP_FBGE; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGE_UN (fpcflags) { + tree->opcode = OP_FBGE_UN; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLE (fpcflags) { + tree->opcode = OP_FBLE; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLE_UN (fpcflags) { + tree->opcode = OP_FBLE_UN; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_POP (freg) "0" { + /* we need to pop the value from the x86 FP stack */ + MONO_EMIT_UNALU (s, tree, OP_X86_FPOP, -1, state->left->reg1); +} + +%% diff --git a/mono/mini/inssel.brg b/mono/mini/inssel.brg new file mode 100644 index 00000000000..80cef24ed0d --- /dev/null +++ b/mono/mini/inssel.brg @@ -0,0 +1,1589 @@ +/* + * inssel.brg: instruction selection + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. + * + */ + +#include + +#include "mini.h" +#include "mini-arch.h" +#include + +#define MBTREE_TYPE MonoInst +#define MBCGEN_TYPE MonoCompile +#define MBCOST_DATA MonoCompile +#define MBALLOC_STATE mono_mempool_alloc (data->state_pool, sizeof (MBState)) +#define MBMAX_OPCODES OP_LAST +#define MBGET_OP_NAME(op) mono_inst_name (op) + +#define MBTREE_OP(t) ((t)->opcode) +#define MBTREE_LEFT(t) ((t)->inst_left) +#define MBTREE_RIGHT(t) ((t)->inst_right) + +#define MONO_EMIT_UNALU(cfg,inst,op,dr,sr1) do { \ + (inst)->opcode = op; \ + (inst)->dreg = dr; \ + (inst)->sreg1 = sr1; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_UNALU(cfg,op,dr,sr1) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->dreg = dr; \ + inst->sreg1 = sr1; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_BIALU(cfg,inst,op,dr,sr1,sr2) do { \ + (inst)->opcode = op; \ + (inst)->dreg = dr; \ + (inst)->sreg1 = sr1; \ + (inst)->sreg2 = sr2; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_BIALU(cfg,op,dr,sr1,sr2) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->dreg = dr; \ + inst->sreg1 = sr1; \ + inst->sreg2 = sr2; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_BIALU_IMM(cfg,inst,op,dr,sr,imm) do { \ + (inst)->opcode = op; \ + (inst)->dreg = dr; \ + (inst)->sreg1 = sr; \ + (inst)->inst_p1 = (gpointer)imm; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_BIALU_IMM(cfg,op,dr,sr,imm) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->dreg = dr; \ + inst->sreg1 = sr; \ + inst->inst_p1 = (gpointer)imm; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_LOAD_MEMBASE(cfg,inst,dr,base,offset) do { \ + (inst)->opcode = OP_LOAD_MEMBASE; \ + (inst)->dreg = dr; \ + (inst)->inst_basereg = base; \ + (inst)->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_LOAD_MEMBASE_OP(cfg,inst,op,dr,base,offset) do { \ + (inst)->opcode = op; \ + (inst)->dreg = dr; \ + (inst)->inst_basereg = base; \ + (inst)->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_LOAD_MEM(cfg,dr,addr) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_LOADU4_MEM; \ + inst->dreg = dr; \ + inst->inst_p0 = addr; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_LOAD_MEMBASE(cfg,dr,base,offset) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_LOAD_MEMBASE; \ + inst->dreg = dr; \ + inst->inst_basereg = base; \ + inst->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg,op,dr,base,offset) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->dreg = dr; \ + inst->inst_basereg = base; \ + inst->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_STORE_MEMBASE(cfg,inst,op,base,offset,sr) do { \ + (inst)->opcode = op; \ + (inst)->sreg1 = sr; \ + (inst)->inst_destbasereg = base; \ + (inst)->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_STORE_MEMBASE(cfg,op,base,offset,sr) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->sreg1 = sr; \ + inst->inst_destbasereg = base; \ + inst->inst_offset = offset; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_STORE_MEMBASE_IMM(cfg,inst,op,base,offset,imm) do { \ + (inst)->opcode = op; \ + (inst)->inst_destbasereg = base; \ + (inst)->inst_offset = offset; \ + (inst)->inst_p1 = (gpointer)imm; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_STORE_MEMBASE_IMM(cfg,op,base,offset,imm) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->inst_destbasereg = base; \ + inst->inst_offset = offset; \ + inst->inst_p1 = (gpointer)imm; \ + mono_bblock_add_inst (cfg->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_COMPARE_IMM(cfg,sr1,imm) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_COMPARE_IMM; \ + inst->sreg1 = sr1; \ + inst->inst_p1 = (gpointer)imm; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_COND_EXC(cfg,cond,name) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_COND_EXC_##cond; \ + inst->inst_p1 = (char*)name; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_ICONST(cfg,dr,imm) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_ICONST; \ + inst->dreg = dr; \ + inst->inst_c0 = imm; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_AOTCONST(cfg,dr,imm,type) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = OP_AOTCONST; \ + inst->dreg = dr; \ + inst->inst_p0 = imm; \ + inst->inst_c1 = type; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_CLASSCONST(cfg,dr,imm) MONO_EMIT_NEW_AOTCONST(cfg,dr,imm,MONO_PATCH_INFO_CLASS) + +#define MONO_EMIT_NEW_BRANCH_BLOCK(cfg,op,targetbb) do { \ + MonoInst *inst; \ + MonoInst *target_label; \ + target_label = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + target_label->opcode = OP_LABEL; \ + target_label->next = (targetbb)->code; \ + target_label->inst_c0 = (targetbb)->native_offset; \ + (targetbb)->code = target_label; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->inst_i0 = target_label; \ + inst->flags = MONO_INST_BRLABEL; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_EMIT_NEW_BRANCH_LABEL(cfg,op,label) do { \ + MonoInst *inst; \ + inst = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + inst->opcode = op; \ + inst->inst_i0 = label; \ + inst->flags = MONO_INST_BRLABEL; \ + mono_bblock_add_inst ((cfg)->cbb, inst); \ + } while (0) + +#define MONO_NEW_LABEL(cfg,inst) do { \ + (inst) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (inst)->opcode = OP_LABEL; \ + } while (0) + +/* we need to kludge state because monoburg puts this stuff before the definition of MBState */ +void mini_emit_virtual_call (MonoCompile *s, void *state, MonoInst *tree, int novirt_op, int virtop); +void mini_emit_isninst_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target); +void mini_emit_isninst_iface_cast (MonoCompile *s, int vtable_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target); +void mini_emit_isninst_iface_class_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target); +void mini_emit_castclass (MonoCompile *s, int klass_reg, MonoClass *klass); +void mini_emit_castclass_iface (MonoCompile *s, int vtable_reg, MonoClass *klass); +void mini_emit_castclass_iface_class (MonoCompile *s, int klass_reg, MonoClass *klass); + + +%% + +%termprefix OP_ CEE_ + +%start stmt + +# +# base addressing mode +# + +base: reg "0" { + /* we create a new MonoInst in chain rules and set state->tree to it + * since a MBState (and hence state->tree) is reused in chain rules and + * normal rules. + */ + MonoInst *inst; + inst = mono_mempool_alloc0 (s->mempool, sizeof (MonoInst)); + inst->opcode = OP_ICONST; /* doesn't matter */ + inst->inst_basereg = state->reg1; + inst->inst_offset = 0; + state->tree = inst; + //g_print ("mybase: (assigned R%d from R%d - R%d)\n", state->reg1, tree->dreg, state->reg2); + //tree->inst_offset = 0; + //tree->inst_basereg = state->reg1; +} + +base: OP_REGOFFSET "0" { + /* nothing to do */ +} + +base: OP_LDADDR (OP_REGOFFSET) "0" { + tree->inst_offset = state->left->tree->inst_offset; + tree->inst_basereg = state->left->tree->inst_basereg; +} + +base: CEE_LDOBJ (OP_REGOFFSET) "0" { + tree->inst_offset = state->left->tree->inst_offset; + tree->inst_basereg = state->left->tree->inst_basereg; +} + +base: CEE_ADD (base, OP_ICONST) "0" { + tree->inst_offset = state->left->tree->inst_offset + state->right->tree->inst_c0; + tree->inst_basereg = state->left->tree->inst_basereg; +} + +# +# helpers +# + +reg: OP_ICONST { + MONO_EMIT_NEW_ICONST (s, state->reg1, tree->inst_c0); +} + +reg: OP_AOTCONST { + MONO_EMIT_NEW_AOTCONST (s, state->reg1, tree->inst_p0, tree->inst_c1); +} + +# +# load/store operations +# + +reg: CEE_LDIND_I (base) { + MONO_EMIT_LOAD_MEMBASE (s, tree, state->reg1, state->left->tree->inst_basereg, + state->left->tree->inst_offset); +} + +reg: CEE_LDIND_REF (base) { + MONO_EMIT_LOAD_MEMBASE (s, tree, state->reg1, state->left->tree->inst_basereg, + state->left->tree->inst_offset); +} + +reg: CEE_LDIND_I1 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI1_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: CEE_LDIND_U1 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU1_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: CEE_LDIND_I2 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI2_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: CEE_LDIND_U2 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU2_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: OP_LDADDR (OP_REGOFFSET) { + /* use LEA */ + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->tree->inst_basereg; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); + if (state->left->tree->inst_offset) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, tree->dreg, tree->dreg, state->left->tree->inst_offset); + } +} + +reg: CEE_LDOBJ (OP_REGOFFSET) { + /* use LEA */ + /* FIXME: this is just an hack */ + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->tree->inst_basereg; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); + if (state->left->tree->inst_offset) { + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, tree->dreg, tree->dreg, state->left->tree->inst_offset); + } +} + +reg: OP_OBJADDR (reg) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->reg1; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_VTADDR (reg) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->reg1; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_LDIND_REF (OP_REGVAR) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->tree->dreg; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_LDIND_I4 (OP_REGVAR) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->tree->dreg; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_LDIND_U4 (OP_REGVAR) { + tree->opcode = OP_MOVE; + tree->sreg1 = state->left->tree->dreg; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_STIND_REF (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_REF (base, CEE_LDIND_REF (OP_REGVAR)) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->left->tree->dreg); +} + +stmt: CEE_STIND_REF (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STORE_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + +stmt: CEE_STIND_REF (OP_REGVAR, CEE_LDIND_REF (OP_REGVAR)) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->left->tree->dreg); +} + + +stmt: CEE_STIND_I (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STORE_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STORE_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + +reg: CEE_LDIND_I4 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: CEE_LDIND_U4 (base) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADU4_MEMBASE, state->reg1, + state->left->tree->inst_basereg, state->left->tree->inst_offset); +} + +reg: CEE_LDIND_I4 (OP_REGVAR) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg); +} + +reg: CEE_LDIND_U4 (OP_REGVAR) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg); +} + +reg: CEE_LDIND_I (OP_REGVAR) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->tree->dreg); +} + +stmt: CEE_STIND_I4 (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I4 (base, CEE_LDIND_I4 (OP_REGVAR)) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI4_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->left->tree->dreg); +} + +stmt: CEE_STIND_I4 (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI4_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + + + +stmt: CEE_STIND_I1 (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI1_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I1 (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI1_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + +stmt: CEE_STIND_I2 (base, reg) { + MONO_EMIT_STORE_MEMBASE (s, tree, OP_STOREI2_MEMBASE_REG, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->reg1); +} + +stmt: CEE_STIND_I2 (base, OP_ICONST) { + MONO_EMIT_STORE_MEMBASE_IMM (s, tree, OP_STOREI2_MEMBASE_IMM, state->left->tree->inst_basereg, + state->left->tree->inst_offset, state->right->tree->inst_c0); +} + +stmt: CEE_STIND_I4 (OP_REGVAR, reg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1); +} + +stmt: CEE_STIND_I2 (OP_REGVAR, reg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1); +} + +stmt: CEE_STIND_I1 (OP_REGVAR, reg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1); +} + +stmt: CEE_STIND_I4 (OP_REGVAR, CEE_LDIND_I4 (OP_REGVAR)) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->left->tree->dreg); +} + +stmt: CEE_STIND_I4 (OP_REGVAR, CEE_LDIND_I4 (base)) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->left->tree->dreg, + state->right->left->tree->inst_basereg, + state->right->left->tree->inst_offset); +} + +stmt: CEE_STIND_I4 (OP_REGVAR, OP_ICONST) { + tree->opcode = OP_ICONST; + tree->dreg = state->left->tree->dreg; + tree->inst_c0 = state->right->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_STIND_REF (OP_REGVAR, reg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1); +} + +stmt: CEE_STIND_REF (OP_REGVAR, OP_ICONST) { + tree->opcode = OP_ICONST; + tree->dreg = state->left->tree->dreg; + tree->inst_c0 = state->right->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_STIND_I (OP_REGVAR, reg) { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->left->tree->dreg, state->right->reg1); +} + +# +# conversions: conv_u can be implemented with AND, also all _ovf conversions? +# + +reg: CEE_CONV_I1 (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_I2 (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_I4 (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_U1 (reg) { + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, 0xff); +} + +reg: CEE_CONV_U2 (reg) { + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, 0xffff); +} + +reg: CEE_CONV_U4 (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_U (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_I (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_OVF_I4 (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_OVF_U4 (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + /* Keep in sync with CONV_OVF_I4_UN below, they are the same on 32-bit machines */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_OVF_I4_UN (reg) { + if (sizeof (void *) == 8) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); + } else { + /* Keep in sync with CONV_OVF_U4 above, they are the same on 32-bit machines */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, state->left->reg1); + } +} + +reg: CEE_CONV_OVF_I1 (reg) { + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -128); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_OVF_I1_UN (reg) { + /* probe values between 0 to 127 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 127); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I1, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_OVF_U1 (reg) { + /* probe value to be within 0 to 255 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1)); +} + +reg: CEE_CONV_OVF_U1_UN (reg) { + /* probe value to be within 0 to 255 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 255); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xff + 1)); +} + +reg: CEE_CONV_OVF_I2 (reg) { + /* Probe value to be within -32768 and 32767 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767); + MONO_EMIT_NEW_COND_EXC (s, GT, "OverflowException"); + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, -32768); + MONO_EMIT_NEW_COND_EXC (s, LT, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_OVF_I2_UN (reg) { + /* Convert uint value into short, value within 0 and 32767 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 32767); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_UNALU (s, tree, CEE_CONV_I2, state->reg1, state->left->reg1); +} + +reg: CEE_CONV_OVF_U2 (reg) { + /* Probe value to be within 0 and 65535 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1)); +} + +reg: CEE_CONV_OVF_U2_UN (reg) { + /* Probe value to be within 0 and 65535 */ + MONO_EMIT_NEW_COMPARE_IMM (s, state->left->reg1, 0xffff); + MONO_EMIT_NEW_COND_EXC (s, GT_UN, "OverflowException"); + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, -(0xffff + 1)); +} + +# +# basic alu operations +# + +reg: CEE_AND (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_AND (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_AND_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_OR (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_OR (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_OR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_XOR (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_XOR (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_XOR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_NEG (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: CEE_NOT (reg) { + MONO_EMIT_UNALU (s, tree, tree->opcode, state->reg1, state->left->reg1); +} + +reg: CEE_ADD (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_ADD (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_ADD_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_ADD_OVF (reg, reg) { + MONO_EMIT_NEW_BIALU (s, OP_ADDCC, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException"); +} + +reg: CEE_ADD_OVF_UN (reg, reg) { + MONO_EMIT_NEW_BIALU (s, OP_ADDCC, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException"); +} + +reg: CEE_SUB (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_SUB (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_SUB_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_SUB_OVF (reg, reg) { + MONO_EMIT_NEW_BIALU (s, OP_SUBCC, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, OV, "OverflowException"); +} + +reg: CEE_SUB_OVF_UN (reg, reg) { + MONO_EMIT_NEW_BIALU (s, OP_SUBCC, state->reg1, state->left->reg1, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, C, "OverflowException"); +} + +# +# mult/div operations +# + +reg: CEE_MUL (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_MUL (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_MUL_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_MUL_OVF (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_MUL_OVF_UN (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_DIV (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +#reg: CEE_DIV (reg, OP_ICONST) { +# MONO_EMIT_BIALU_IMM (s, tree, OP_DIV_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +#} + +reg: CEE_DIV_UN (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +#reg: CEE_DIV_UN (reg, OP_ICONST) { +# MONO_EMIT_BIALU_IMM (s, tree, OP_DIV_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +#} + +reg: CEE_REM (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +#reg: CEE_REM (reg, OP_ICONST) { +# MONO_EMIT_BIALU_IMM (s, tree, OP_REM_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +#} + +reg: CEE_REM_UN (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +#reg: CEE_REM_UN (reg, OP_ICONST) { +# MONO_EMIT_BIALU_IMM (s, tree, OP_REM_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +#} + +# +# shift operations +# + +reg: CEE_SHL (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_SHL (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_SHL_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_SHR (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_SHR (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_SHR_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + +reg: CEE_SHR_UN (reg, reg) { + MONO_EMIT_BIALU (s, tree, tree->opcode, state->reg1, state->left->reg1, state->right->reg1); +} + +reg: CEE_SHR_UN (reg, OP_ICONST) { + MONO_EMIT_BIALU_IMM (s, tree, OP_SHR_UN_IMM, state->reg1, state->left->reg1, state->right->tree->inst_c0); +} + + +# +# other alu operations +# + +reg: OP_CEQ (cflags) { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_CLT (cflags) { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_CLT_UN (cflags) { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_CGT (cflags) { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: OP_CGT_UN (cflags) { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +# +# control flow commands +# + +stmt: OP_LABEL { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_NOP "0" { +} + +stmt: CEE_BREAK "0" { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_SWITCH (reg) { + MonoInst *label; + int offset_reg = mono_regstate_next_int (s->rs); + int target_reg = mono_regstate_next_int (s->rs); + int n = GPOINTER_TO_INT (tree->klass); + + MONO_NEW_LABEL (s, label); + mono_create_jump_table (s, label, tree->inst_many_bb, n); + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, state->left->reg1, n); + MONO_EMIT_NEW_BRANCH_BLOCK (s, CEE_BGE_UN, tree->inst_many_bb [n]); + MONO_EMIT_NEW_BIALU_IMM (s, OP_SHL_IMM, offset_reg, state->left->reg1, 2); + mono_bblock_add_inst (s->cbb, label); + /* the backend must patch the address. we use 0xf0f0f0f0 to avoid the usage + * of special (short) opcodes on x86 */ + MONO_EMIT_NEW_LOAD_MEMBASE (s, target_reg, offset_reg, 0xf0f0f0f0); + MONO_EMIT_UNALU (s, tree, OP_BR_REG, -1, target_reg); +} + +stmt: CEE_BR "0" { + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_CALL { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +reg: CEE_CALLVIRT (reg) { + mini_emit_virtual_call (s, state, tree, CEE_CALL, OP_CALL_MEMBASE); +} + +stmt: OP_VOIDCALLVIRT (reg) { + mini_emit_virtual_call (s, state, tree, OP_VOIDCALL, OP_VOIDCALL_MEMBASE); +} + +lreg: OP_LCALLVIRT (reg) { + mini_emit_virtual_call (s, state, tree, OP_LCALL, OP_LCALL_MEMBASE); +} + +freg: OP_FCALLVIRT (reg) { + mini_emit_virtual_call (s, state, tree, OP_FCALL, OP_FCALL_MEMBASE); +} + +stmt: OP_VCALLVIRT (reg, reg) { + mini_emit_virtual_call (s, state, tree, OP_VCALL, OP_VCALL_MEMBASE); +} + +reg: OP_CALL_REG (reg) { + tree->sreg1 = state->left->reg1; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_VOIDCALL { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_VOIDCALL_REG (reg) { + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +freg: OP_FCALL { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +freg: OP_FCALL_REG (reg) { + tree->sreg1 = state->left->reg1; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +lreg: OP_LCALL { + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +lreg: OP_LCALL_REG (reg) { + tree->sreg1 = state->left->reg1; + tree->dreg = state->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_VCALL (reg) { + MonoInst *vtarg; + /* FIXME: this is actually arch-specific... */ + MONO_INST_NEW (s, vtarg, OP_OUTARG); + vtarg->inst_left = state->left->tree; + vtarg->type = STACK_MP; + vtarg->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, vtarg); + + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_VCALL_REG (reg, reg) { + MonoInst *vtarg; + /* FIXME: this is actually arch-specific... */ + MONO_INST_NEW (s, vtarg, OP_OUTARG); + vtarg->inst_left = state->right->tree; + vtarg->type = STACK_MP; + vtarg->sreg1 = state->right->reg1; + mono_bblock_add_inst (s->cbb, vtarg); + + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_RET "0" { + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (reg, reg) { + tree->sreg1 = state->left->reg1; + tree->sreg2 = state->right->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (CEE_LDIND_I4 (OP_REGVAR), reg) { + tree->sreg1 = state->left->left->tree->dreg; + tree->sreg2 = state->right->reg1; + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (CEE_LDIND_I4 (OP_REGVAR), OP_ICONST) { + tree->opcode = OP_COMPARE_IMM; + tree->sreg1 = state->left->left->tree->dreg; + tree->inst_imm = state->right->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +cflags: OP_COMPARE (reg, OP_ICONST) { + tree->opcode = OP_COMPARE_IMM; + tree->sreg1 = state->left->reg1; + tree->inst_imm = state->right->tree->inst_c0; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BNE_UN (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BEQ (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLT (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLT_UN (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGT (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGT_UN (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGE (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BGE_UN (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLE (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_BLE_UN (cflags) { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_POP (reg) + +stmt: CEE_JMP "0" { + mono_bblock_add_inst (s->cbb, tree); +} + +# exception handling + +stmt: CEE_THROW (reg) { + MONO_EMIT_UNALU (s, tree, CEE_THROW, -1, state->left->reg1); +} + +stmt: CEE_THROW (CEE_LDIND_REF (OP_REGVAR)) { + MONO_EMIT_UNALU (s, tree, CEE_THROW, -1, state->left->left->tree->dreg); +} + +stmt: OP_HANDLER { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_ENDFINALLY { + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_ENDFILTER (reg) "0" { + MONO_EMIT_UNALU (s, tree, OP_ENDFILTER, -1, state->left->reg1); +} + +stmt: OP_CHECK_THIS (reg) { + mono_bblock_add_inst (s->cbb, tree); +} + +# object related opcodes + +reg: CEE_ISINST (reg) { + MonoClass *klass = tree->inst_newa_class; + MonoInst *object_is_null, *end_label, *false_label; + int obj_reg = state->left->reg1; + int vtable_reg = mono_regstate_next_int (s->rs); + + MONO_NEW_LABEL (s, object_is_null); + MONO_NEW_LABEL (s, end_label); + MONO_NEW_LABEL (s, false_label); + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, obj_reg, 0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + + if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) { + MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + /* the object_is_null target simply copies the input register to the output */ + mini_emit_isninst_iface_cast (s, vtable_reg, klass, false_label, object_is_null); + } else { + int klass_reg = mono_regstate_next_int (s->rs); + + MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); + + if (klass->rank) { + int rank_reg = mono_regstate_next_int (s->rs); + int eclass_reg = mono_regstate_next_int (s->rs); + + MONO_EMIT_NEW_LOAD_MEMBASE (s, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, rank_reg, klass->rank); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, false_label); + MONO_EMIT_NEW_LOAD_MEMBASE (s, eclass_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, cast_class)); + if (klass->cast_class == mono_defaults.object_class) { + int parent_reg = mono_regstate_next_int (s->rs); + int const_reg; + MONO_EMIT_NEW_LOAD_MEMBASE (s, parent_reg, eclass_reg, G_STRUCT_OFFSET (MonoClass, parent)); + if (mono_compile_aot) { + const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, parent_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, parent_reg, mono_defaults.enum_class->parent); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, object_is_null); + if (mono_compile_aot) { + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label); + } else if (klass->cast_class == mono_defaults.enum_class->parent) { + int const_reg; + + if (mono_compile_aot) { + const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class->parent); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + if (mono_compile_aot) { + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label); + } else if (klass->cast_class == mono_defaults.enum_class) { + if (mono_compile_aot) { + int const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, false_label); + } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) { + mini_emit_isninst_iface_class_cast (s, eclass_reg, klass->cast_class, false_label, object_is_null); + } else { + /* the object_is_null target simply copies the input register to the output */ + mini_emit_isninst_cast (s, eclass_reg, klass->cast_class, false_label, object_is_null); + } + } else { + + if (klass->marshalbyref) { + MonoInst *no_proxy; + + MONO_NEW_LABEL (s, no_proxy); + + if (mono_compile_aot) { + int tproxy_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, tproxy_reg, mono_defaults.transparent_proxy_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, klass_reg, tproxy_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, klass_reg, mono_defaults.transparent_proxy_class); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, no_proxy); + MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, obj_reg, G_STRUCT_OFFSET (MonoTransparentProxy, klass)); + mono_bblock_add_inst (s->cbb, no_proxy); + } + + /* the object_is_null target simply copies the input register to the output */ + mini_emit_isninst_cast (s, klass_reg, klass, false_label, object_is_null); + } + } + + mono_bblock_add_inst (s->cbb, false_label); + MONO_EMIT_NEW_ICONST (s, state->reg1, 0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BR, end_label); + mono_bblock_add_inst (s->cbb, object_is_null); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, obj_reg); + mono_bblock_add_inst (s->cbb, end_label); +} + +reg: CEE_CASTCLASS (reg) { + MonoClass *klass = tree->inst_newa_class; + MonoInst *object_is_null; + int obj_reg = state->left->reg1; + int vtable_reg = mono_regstate_next_int (s->rs); + + MONO_NEW_LABEL (s, object_is_null); + + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, obj_reg, 0); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + + if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) { + MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + mini_emit_castclass_iface (s, vtable_reg, klass); + } else { + int klass_reg = mono_regstate_next_int (s->rs); + + MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); + + if (klass->rank) { + int rank_reg = mono_regstate_next_int (s->rs); + int eclass_reg = mono_regstate_next_int (s->rs); + + MONO_EMIT_NEW_LOAD_MEMBASE (s, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, rank_reg, klass->rank); + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); + MONO_EMIT_NEW_LOAD_MEMBASE (s, eclass_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, cast_class)); + if (klass->cast_class == mono_defaults.object_class) { + int parent_reg = mono_regstate_next_int (s->rs); + int const_reg; + MONO_EMIT_NEW_LOAD_MEMBASE (s, parent_reg, eclass_reg, G_STRUCT_OFFSET (MonoClass, parent)); + if (mono_compile_aot) { + const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, parent_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, parent_reg, mono_defaults.enum_class->parent); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, object_is_null); + if (mono_compile_aot) { + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); + } else if (klass->cast_class == mono_defaults.enum_class->parent) { + int const_reg = mono_regstate_next_int (s->rs); + if (mono_compile_aot) { + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class->parent); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class->parent); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, object_is_null); + if (mono_compile_aot) { + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); + } else if (klass->cast_class == mono_defaults.enum_class) { + if (mono_compile_aot) { + int const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, mono_defaults.enum_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, eclass_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, eclass_reg, mono_defaults.enum_class); + } + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); + } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) { + mini_emit_castclass_iface_class (s, eclass_reg, klass->cast_class); + } else { + mini_emit_castclass (s, eclass_reg, klass->cast_class); + } + } else { + + if (klass->marshalbyref) { + MonoInst *no_proxy; + + MONO_NEW_LABEL (s, no_proxy); + + if (mono_compile_aot) { + int tproxy_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, tproxy_reg, mono_defaults.transparent_proxy_class); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, klass_reg, tproxy_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, klass_reg, mono_defaults.transparent_proxy_class); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, no_proxy); + MONO_EMIT_NEW_LOAD_MEMBASE (s, klass_reg, obj_reg, G_STRUCT_OFFSET (MonoTransparentProxy, klass)); + mono_bblock_add_inst (s->cbb, no_proxy); + } + + mini_emit_castclass (s, klass_reg, klass); + } + } + + mono_bblock_add_inst (s->cbb, object_is_null); + MONO_EMIT_UNALU (s, tree, OP_MOVE, state->reg1, obj_reg); +} + +reg: CEE_NEWARR (reg) { + g_assert_not_reached (); +} + +lreg: OP_LMUL (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LMUL_OVF (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LMUL_OVF_UN (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LDIV (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LDIV_UN (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LREM (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LREM_UN (lreg, lreg) { + g_assert_not_reached (); +} + +lreg: OP_LSHL (lreg, reg) { + g_assert_not_reached (); +} + +lreg: OP_LSHR (lreg, reg) { + g_assert_not_reached (); +} + +lreg: OP_LSHR_UN (lreg, reg) { + g_assert_not_reached (); +} + +reg: CEE_UNBOX (reg) { + int vtable_reg = mono_regstate_next_int (s->rs); + int class_reg = mono_regstate_next_int (s->rs); + int element_class_reg = mono_regstate_next_int (s->rs); + + MONO_EMIT_NEW_LOAD_MEMBASE (s, vtable_reg, state->left->reg1, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, class_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, element_class_reg, class_reg, G_STRUCT_OFFSET (MonoClass, element_class)); + + if (mono_compile_aot) { + /* fixme: make it work with the AOT compiler */ + g_assert_not_reached (); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, element_class_reg, tree->inst_newa_class); + } + + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); + MONO_EMIT_BIALU_IMM (s, tree, CEE_ADD, state->reg1, state->left->reg1, sizeof (MonoObject)); + g_warning ("untested code!!"); +} + +# +# array support +# + +reg: CEE_LDLEN (reg) { + MONO_EMIT_LOAD_MEMBASE_OP (s, tree, OP_LOADI4_MEMBASE, state->reg1, + state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length)); +} + +reg: CEE_LDELEMA (reg, OP_ICONST) "15" { + int length_reg = mono_regstate_next_int (s->rs); + guint32 size = mono_class_array_element_size (tree->klass); + int ind; + + MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, + state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, length_reg, state->right->tree->inst_c0); + MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException"); + + ind = size * state->right->tree->inst_c0 + G_STRUCT_OFFSET (MonoArray, vector); + + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, state->left->reg1, ind); +} + +reg: CEE_LDELEMA (reg, reg) "20" { + int length_reg = mono_regstate_next_int (s->rs); + int mult_reg = mono_regstate_next_int (s->rs); + int add_reg = mono_regstate_next_int (s->rs); + guint32 size = mono_class_array_element_size (tree->klass); + + MONO_EMIT_NEW_LOAD_MEMBASE_OP (s, OP_LOADI4_MEMBASE, length_reg, + state->left->reg1, G_STRUCT_OFFSET (MonoArray, max_length)); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, length_reg, state->right->reg1); + MONO_EMIT_NEW_COND_EXC (s, LE_UN, "IndexOutOfRangeException"); + + MONO_EMIT_NEW_BIALU_IMM (s, OP_MUL_IMM, mult_reg, state->right->reg1, size); + MONO_EMIT_NEW_BIALU (s, CEE_ADD, add_reg, mult_reg, state->left->reg1); + MONO_EMIT_NEW_BIALU_IMM (s, OP_ADD_IMM, state->reg1, add_reg, G_STRUCT_OFFSET (MonoArray, vector)); +} + +%% + +void +mini_emit_virtual_call (MonoCompile *cfg, void *st, MonoInst *tree, int novirtop, int virtop) +{ + MonoInst *this, *vtarg; + MonoMethod *method = ((MonoCallInst*)tree)->method; + int vtable_reg, slot_reg; + MBState *state = st; + + /* add the this argument */ + MONO_INST_NEW (cfg, this, OP_OUTARG); + this->inst_left = state->left->tree; + this->type = this->inst_left->type; + this->sreg1 = state->left->reg1; + mono_bblock_add_inst (cfg->cbb, this); + + if (novirtop == OP_VCALL) { + /* FIXME: this is actually arch-specific... */ + MONO_INST_NEW (cfg, vtarg, OP_OUTARG); + vtarg->inst_left = state->right->tree; + vtarg->type = STACK_MP; + vtarg->sreg1 = state->right->reg1; + mono_bblock_add_inst (cfg->cbb, vtarg); + } + + if (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || + ((method->flags & METHOD_ATTRIBUTE_FINAL) && + method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) { + /* + * the method is not virtual, we just need to ensure this is not null + * and then we can call the method directly. + */ + if (method->klass->marshalbyref || method->klass == mono_defaults.object_class) { + method = ((MonoCallInst*)tree)->method = mono_marshal_get_remoting_invoke_with_check (method); + } + + if (!method->string_ctor) + MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this->sreg1); + + tree->dreg = state->reg1; + tree->opcode = novirtop; + mono_bblock_add_inst (cfg->cbb, tree); + return; + } + + vtable_reg = mono_regstate_next_int (cfg->rs); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this->sreg1, G_STRUCT_OFFSET (MonoObject, vtable)); + if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) { + int ifoffset_reg = mono_regstate_next_int (cfg->rs); + slot_reg = mono_regstate_next_int (cfg->rs); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ifoffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets)); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, ifoffset_reg, method->klass->interface_id << 2); + tree->inst_offset = method->slot << 2; + cfg->disable_aot = TRUE; + } else { + slot_reg = vtable_reg; + tree->inst_offset = G_STRUCT_OFFSET (MonoVTable, vtable) + (method->slot << 2); + } + + tree->dreg = state->reg1; + tree->opcode = virtop; + tree->sreg1 = slot_reg; + mono_bblock_add_inst (cfg->cbb, tree); +} + +void +mini_emit_isninst_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target) +{ + int idepth_reg = mono_regstate_next_int (s->rs); + int stypes_reg = mono_regstate_next_int (s->rs); + + if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) { + MONO_EMIT_NEW_LOAD_MEMBASE (s, idepth_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, idepth)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target); + } + MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, stypes_reg, ((klass->idepth - 1) << 2)); + if (mono_compile_aot) { + int const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, klass); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, stypes_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, stypes_reg, klass); + } + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BEQ, true_target); +} + +void +mini_emit_isninst_iface_cast (MonoCompile *s, int vtable_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target) +{ + int max_iid_reg = mono_regstate_next_int (s->rs); + int ioffset_reg = mono_regstate_next_int (s->rs); + int intf_reg = mono_regstate_next_int (s->rs); + + s->disable_aot = TRUE; + + MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, max_interface_id)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target); + MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0); + /* the object_is_null target simply copies the input register to the output */ + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BNE_UN, true_target); +} + +/* + * Variant of the aboce that takes a register to the class, not the vtable. + * Note that inside interfaces_offsets the empty value is -1, not NULL, in this case. + */ +void +mini_emit_isninst_iface_class_cast (MonoCompile *s, int klass_reg, MonoClass *klass, MonoInst *false_target, MonoInst *true_target) +{ + int max_iid_reg = mono_regstate_next_int (s->rs); + int ioffset_reg = mono_regstate_next_int (s->rs); + int intf_reg = mono_regstate_next_int (s->rs); + + s->disable_aot = TRUE; + + MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, max_interface_id)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id); + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BLT_UN, false_target); + MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_offsets)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0); + /* the object_is_null target simply copies the input register to the output */ + MONO_EMIT_NEW_BRANCH_LABEL (s, CEE_BGE, true_target); +} + +void +mini_emit_castclass (MonoCompile *s, int klass_reg, MonoClass *klass) +{ + int idepth_reg = mono_regstate_next_int (s->rs); + int stypes_reg = mono_regstate_next_int (s->rs); + + if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) { + MONO_EMIT_NEW_LOAD_MEMBASE (s, idepth_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, idepth)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth); + MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException"); + } + MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, stypes_reg, stypes_reg, ((klass->idepth - 1) << 2)); + if (mono_compile_aot) { + int const_reg = mono_regstate_next_int (s->rs); + MONO_EMIT_NEW_CLASSCONST (s, const_reg, klass); + MONO_EMIT_NEW_BIALU (s, OP_COMPARE, -1, stypes_reg, const_reg); + } else { + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, stypes_reg, klass); + } + MONO_EMIT_NEW_COND_EXC (s, NE_UN, "InvalidCastException"); +} + +void +mini_emit_castclass_iface (MonoCompile *s, int vtable_reg, MonoClass *klass) +{ + int max_iid_reg = mono_regstate_next_int (s->rs); + int ioffset_reg = mono_regstate_next_int (s->rs); + int intf_reg = mono_regstate_next_int (s->rs); + + s->disable_aot = TRUE; + + MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, max_interface_id)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id); + MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException"); + MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_offsets)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0); + MONO_EMIT_NEW_COND_EXC (s, EQ, "InvalidCastException"); +} + +/* + * Variant of the aboce that takes a register to the class, not the vtable. + * Note that inside interfaces_offsets the empty value is -1, not NULL, in this case. + */ +void +mini_emit_castclass_iface_class (MonoCompile *s, int klass_reg, MonoClass *klass) +{ + int max_iid_reg = mono_regstate_next_int (s->rs); + int ioffset_reg = mono_regstate_next_int (s->rs); + int intf_reg = mono_regstate_next_int (s->rs); + + s->disable_aot = TRUE; + + MONO_EMIT_NEW_LOAD_MEMBASE (s, max_iid_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, max_interface_id)); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id); + MONO_EMIT_NEW_COND_EXC (s, LT_UN, "InvalidCastException"); + MONO_EMIT_NEW_LOAD_MEMBASE (s, ioffset_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_offsets)); + MONO_EMIT_NEW_LOAD_MEMBASE (s, intf_reg, ioffset_reg, klass->interface_id << 2); + MONO_EMIT_NEW_BIALU_IMM (s, OP_COMPARE_IMM, -1, intf_reg, 0); + MONO_EMIT_NEW_COND_EXC (s, EQ, "InvalidCastException"); +} + diff --git a/mono/mini/jit-icalls.c b/mono/mini/jit-icalls.c new file mode 100644 index 00000000000..50a5125939f --- /dev/null +++ b/mono/mini/jit-icalls.c @@ -0,0 +1,401 @@ +/* + * jit-icalls.c: internal calls used by the JIT + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include + +static void* +mono_ldftn (MonoMethod *method) +{ + gpointer addr; + + MONO_ARCH_SAVE_REGS; + + EnterCriticalSection (metadata_section); + addr = mono_compile_method (method); + LeaveCriticalSection (metadata_section); + + return addr; +} + +static void* +mono_ldvirtfn (MonoObject *obj, MonoMethod *method) +{ + MONO_ARCH_SAVE_REGS; + + method = mono_object_get_virtual_method (obj, method); + return mono_ldftn (method); +} + +static void +helper_initobj (void *addr, int size) +{ + MONO_ARCH_SAVE_REGS; + + memset (addr, 0, size); +} + +static void +helper_memcpy (void *addr, void *src, int size) +{ + MONO_ARCH_SAVE_REGS; + + memcpy (addr, src, size); +} + +static void +helper_memset (void *addr, int val, int size) +{ + MONO_ARCH_SAVE_REGS; + + memset (addr, val, size); +} + +static gint64 +mono_llmult (gint64 a, gint64 b) +{ + MONO_ARCH_SAVE_REGS; + return a * b; +} + +static guint64 +mono_llmult_ovf_un (guint32 al, guint32 ah, guint32 bl, guint32 bh) +{ + guint64 res, t1; + + MONO_ARCH_SAVE_REGS; + + // fixme: this is incredible slow + + if (ah && bh) + goto raise_exception; + + res = (guint64)al * (guint64)bl; + + t1 = (guint64)ah * (guint64)bl + (guint64)al * (guint64)bh; + + if (t1 > 0xffffffff) + goto raise_exception; + + res += ((guint64)t1) << 32; + + return res; + + raise_exception: + mono_raise_exception (mono_get_exception_overflow ()); + return 0; +} + + +static guint64 +mono_llmult_ovf (guint32 al, gint32 ah, guint32 bl, gint32 bh) +{ + /* + Use Karatsuba algorithm where: + a*b is: AhBh(R^2+R)+(Ah-Al)(Bl-Bh)R+AlBl(R+1) + where Ah is the "high half" (most significant 32 bits) of a and + where Al is the "low half" (least significant 32 bits) of a and + where Bh is the "high half" of b and Bl is the "low half" and + where R is the Radix or "size of the half" (in our case 32 bits) + + Note, for the product of two 64 bit numbers to fit into a 64 + result, ah and/or bh must be 0. This will save us from doing + the AhBh term at all. + + Also note that we refactor so that we don't overflow 64 bits with + intermediate results. So we use [(Ah-Al)(Bl-Bh)+AlBl]R+AlBl + */ + + gint64 res, t1; + gint32 sign; + + MONO_ARCH_SAVE_REGS; + + /* need to work with absoulte values, so find out what the + resulting sign will be and convert any negative numbers + from two's complement + */ + sign = ah ^ bh; + if (ah < 0) { + /* flip the bits and add 1 */ + ah ^= ~0; + if (al == 0) + ah += 1; + else { + al ^= ~0; + al +=1; + } + } + + if (bh < 0) { + /* flip the bits and add 1 */ + bh ^= ~0; + if (bl == 0) + bh += 1; + else { + bl ^= ~0; + bl +=1; + } + } + + /* we overflow for sure if both upper halves are greater + than zero because we would need to shift their + product 64 bits to the left and that will not fit + in a 64 bit result */ + if (ah && bh) + goto raise_exception; + + /* do the AlBl term first */ + t1 = (gint64)al * (gint64)bl; + + res = t1; + + /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */ + t1 += (gint64)(ah - al) * (gint64)(bl - bh); + t1 <<= 32; + /* check for overflow */ + if (t1 > (0x7FFFFFFFFFFFFFFF - res)) + goto raise_exception; + + res += t1; + + if (sign < 0) + return -res; + else + return res; + + raise_exception: + mono_raise_exception (mono_get_exception_overflow ()); + return 0; +} + +static gint64 +mono_lldiv (gint64 a, gint64 b) +{ + MONO_ARCH_SAVE_REGS; + + return a / b; +} + +static gint64 +mono_llrem (gint64 a, gint64 b) +{ + MONO_ARCH_SAVE_REGS; + + return a % b; +} + +static guint64 +mono_lldiv_un (guint64 a, guint64 b) +{ + MONO_ARCH_SAVE_REGS; + + return a / b; +} + +static guint64 +mono_llrem_un (guint64 a, guint64 b) +{ + MONO_ARCH_SAVE_REGS; + + return a % b; +} + +static guint64 +mono_lshl (guint64 a, gint32 shamt) +{ + guint64 res; + + MONO_ARCH_SAVE_REGS; + res = a << shamt; + + /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/ + + return res; +} + +static guint64 +mono_lshr_un (guint64 a, gint32 shamt) +{ + guint64 res; + + MONO_ARCH_SAVE_REGS; + res = a >> shamt; + + /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/ + + return res; +} + +static gint64 +mono_lshr (gint64 a, gint32 shamt) +{ + gint64 res; + + MONO_ARCH_SAVE_REGS; + res = a >> shamt; + + /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/ + + return res; +} + +/** + * ves_array_element_address: + * @this: a pointer to the array object + * + * Returns: the address of an array element. + */ +static gpointer +ves_array_element_address (MonoArray *this, ...) +{ + MonoClass *class; + va_list ap; + int i, ind, esize; + gpointer ea; + + MONO_ARCH_SAVE_REGS; + + g_assert (this != NULL); + + va_start(ap, this); + + class = this->obj.vtable->klass; + + ind = va_arg(ap, int); + g_assert (this->bounds != NULL); + + ind -= this->bounds [0].lower_bound; + for (i = 1; i < class->rank; i++) { + ind = ind*this->bounds [i].length + va_arg(ap, int) - + this->bounds [i].lower_bound;; + } + + if (ind >= this->max_length) + mono_raise_exception (mono_get_exception_index_out_of_range ()); + + esize = mono_array_element_size (class); + ea = (gpointer*)((char*)this->vector + (ind * esize)); + + va_end(ap); + + return ea; +} + +static MonoArray * +mono_array_new_va (MonoMethod *cm, ...) +{ + MonoDomain *domain = mono_domain_get (); + va_list ap; + guint32 *lengths; + guint32 *lower_bounds; + int pcount; + int rank; + int i, d; + + MONO_ARCH_SAVE_REGS; + + pcount = cm->signature->param_count; + rank = cm->klass->rank; + + va_start (ap, cm); + + lengths = alloca (sizeof (guint32) * pcount); + for (i = 0; i < pcount; ++i) + lengths [i] = d = va_arg(ap, int); + + if (rank == pcount) { + /* Only lengths provided. */ + lower_bounds = NULL; + } else { + g_assert (pcount == (rank * 2)); + /* lower bounds are first. */ + lower_bounds = lengths; + lengths += rank; + } + va_end(ap); + + return mono_array_new_full (domain, cm->klass, lengths, lower_bounds); +} + +static gpointer +mono_class_static_field_address (MonoDomain *domain, MonoClassField *field) +{ + MonoVTable *vtable; + + MONO_ARCH_SAVE_REGS; + + //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited); + + mono_class_init (field->parent); + + vtable = mono_class_vtable (domain, field->parent); + + //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset); + + + return (char*)vtable->data + field->offset; +} + +static gpointer +mono_ldtoken_wrapper (MonoImage *image, int token) +{ + MonoClass *handle_class; + gpointer res; + + MONO_ARCH_SAVE_REGS; + res = mono_ldtoken (image, token, &handle_class); + mono_class_init (handle_class); + + return res; +} + +static guint64 +mono_fconv_u8 (double v) +{ + MONO_ARCH_SAVE_REGS; + return (guint64)v; +} + +static guint32 +mono_fconv_u4 (double v) +{ + MONO_ARCH_SAVE_REGS; + return (guint32)v; +} + +static gint64 +mono_fconv_ovf_i8 (double v) +{ + gint64 res; + + MONO_ARCH_SAVE_REGS; + + res = (gint64)v; + + if (isnan(v) || v != res) { + mono_raise_exception (mono_get_exception_overflow ()); + } + return res; +} + +static guint64 +mono_fconv_ovf_u8 (double v) +{ + guint64 res; + + MONO_ARCH_SAVE_REGS; + + res = (guint64)v; + + if (isnan(v) || v != res) { + mono_raise_exception (mono_get_exception_overflow ()); + } + return res; +} diff --git a/mono/mini/linear-scan.c b/mono/mini/linear-scan.c new file mode 100644 index 00000000000..3cf0775d296 --- /dev/null +++ b/mono/mini/linear-scan.c @@ -0,0 +1,189 @@ +/* + * liveness.c: liveness analysis + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include "mini.h" + +GList * +mono_varlist_insert_sorted (MonoCompile *cfg, GList *list, MonoMethodVar *mv, int sort_type) +{ + GList *l; + + if (!list) + return g_list_prepend (NULL, mv); + + for (l = list; l; l = l->next) { + MonoMethodVar *v1 = l->data; + + if (sort_type == 2) { + if (mv->spill_costs >= v1->spill_costs) { + list = g_list_insert_before (list, l, mv); + break; + } + } else if (sort_type == 1) { + if (mv->range.last_use.abs_pos <= v1->range.last_use.abs_pos) { + list = g_list_insert_before (list, l, mv); + break; + } + } else { + if (mv->range.first_use.abs_pos <= v1->range.first_use.abs_pos) { + list = g_list_insert_before (list, l, mv); + break; + } + } + } + if (!l) + list = g_list_append (list, mv); + + return list; +} + +//#define DEBUG_LSCAN + +void +mono_linear_scan (MonoCompile *cfg, GList *vars, GList *regs, guint32 *used_mask) +{ + GList *l, *a, *active = NULL; + MonoMethodVar *vmv, *amv; + int max_regs, gains [32]; + guint32 used_regs = 0; + gboolean cost_driven; + + cost_driven = (cfg->comp_done & MONO_COMP_LOOPS); + +#ifdef DEBUG_LSCAN + printf ("Linears scan for %s\n", mono_method_full_name (cfg->method, TRUE)); +#endif + +#ifdef DEBUG_LSCAN + for (l = vars; l; l = l->next) { + vmv = l->data; + printf ("VAR %d %08x %08x C%d\n", vmv->idx, vmv->range.first_use.abs_pos, + vmv->range.last_use.abs_pos, vmv->spill_costs); + } +#endif + max_regs = g_list_length (regs); + + for (l = regs; l; l = l->next) { + int regnum = (int)l->data; + g_assert (regnum < G_N_ELEMENTS (gains)); + gains [regnum] = 0; + } + + /* linear scan */ + for (l = vars; l; l = l->next) { + vmv = l->data; + +#ifdef DEBUG_LSCAN + printf ("START %2d %08x %08x\n", vmv->idx, vmv->range.first_use.abs_pos, + vmv->range.last_use.abs_pos); +#endif + /* expire old intervals in active */ + while (active) { + amv = (MonoMethodVar *)active->data; + + if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos) + break; + +#ifdef DEBUG_LSCAN + printf ("EXPIR %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos, + amv->range.last_use.abs_pos, amv->spill_costs, amv->reg); +#endif + active = g_list_remove_link (active, active); + regs = g_list_prepend (regs, (gpointer)amv->reg); + gains [amv->reg] += amv->spill_costs; + } + + if (active && g_list_length (active) == max_regs) { + /* Spill */ + + a = g_list_nth (active, max_regs - 1); + amv = (MonoMethodVar *)a->data; + + if ((cost_driven && amv->spill_costs < vmv->spill_costs) || + (!cost_driven && amv->range.last_use.abs_pos > vmv->range.last_use.abs_pos)) { + vmv->reg = amv->reg; + amv->reg = -1; + active = g_list_remove_link (active, a); + + if (cost_driven) + active = mono_varlist_insert_sorted (cfg, active, vmv, 2); + else + active = mono_varlist_insert_sorted (cfg, active, vmv, 1); + +#ifdef DEBUG_LSCAN + printf ("SPILL0 %2d %08x %08x C%d\n", amv->idx, + amv->range.first_use.abs_pos, amv->range.last_use.abs_pos, + amv->spill_costs); +#endif + } else { +#ifdef DEBUG_LSCAN + printf ("SPILL1 %2d %08x %08x C%d\n", vmv->idx, + vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos, + vmv->spill_costs); +#endif + vmv->reg = -1; + } + } else { + /* assign register */ + + g_assert (regs); + + vmv->reg = (int)regs->data; + + used_regs |= 1 << vmv->reg; + + regs = g_list_remove_link (regs, regs); + +#ifdef DEBUG_LSCAN + printf ("ADD %2d %08x %08x C%d\n", vnum, + vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos, + vmv->spill_costs); +#endif + active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE); + } + + +#ifdef DEBUG_LSCAN + for (a = active; a; a = a->next) { + amv = (MonoMethodVar *)a->data; + printf ("ACT %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos, + amv->range.last_use.abs_pos, amv->spill_costs, amv->reg); + } + printf ("NEXT\n"); +#endif + } + + for (a = active; a; a = a->next) { + amv = (MonoMethodVar *)a->data; + gains [amv->reg] += amv->spill_costs; + } + + for (l = vars; l; l = l->next) { + vmv = l->data; + + if (vmv->reg >= 0) { + if (gains [vmv->reg] > 5) { + cfg->varinfo [vmv->idx]->opcode = OP_REGVAR; + cfg->varinfo [vmv->idx]->dreg = vmv->reg; +#ifdef DEBUG_LSCAN + printf ("REGVAR %d C%d R%d\n", vmv->idx, vmv->spill_costs, vmv->reg); +#endif + } else { + used_regs &= ~(1 << vmv->reg); + vmv->reg = -1; + } + } + } + + *used_mask |= used_regs; + + g_list_free (regs); + g_list_free (active); + g_list_free (vars); +} diff --git a/mono/mini/liveness.c b/mono/mini/liveness.c new file mode 100644 index 00000000000..9bfaa8c2be1 --- /dev/null +++ b/mono/mini/liveness.c @@ -0,0 +1,202 @@ +/* + * liveness.c: liveness analysis + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include "mini.h" +#include "inssel.h" + +//#define DEBUG_LIVENESS + + +extern guint8 mono_burg_arity []; + +/* mono_bitset_mp_new: + * + * allocates a MonoBitSet inside a memory pool + */ +static MonoBitSet* +mono_bitset_mp_new (MonoMemPool *mp, guint32 max_size) +{ + int size = mono_bitset_alloc_size (max_size, 0); + gpointer mem; + + mem = mono_mempool_alloc0 (mp, size); + return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE); +} + +#ifdef DEBUG_LIVENESS +static void +mono_bitset_print (MonoBitSet *set) +{ + int i; + + printf ("{"); + for (i = 0; i < mono_bitset_size (set); i++) { + + if (mono_bitset_test (set, i)) + printf ("%d, ", i); + + } + printf ("}"); +} +#endif + +static void +update_live_range (MonoCompile *cfg, int idx, int block_dfn, int tree_pos) +{ + MonoLiveRange *range = &MONO_VARINFO (cfg, idx)->range; + guint32 abs_pos = (block_dfn << 16) | tree_pos; + + if (range->first_use.abs_pos > abs_pos) + range->first_use.abs_pos = abs_pos; + + if (range->last_use.abs_pos < abs_pos) + range->last_use.abs_pos = abs_pos; +} + +static void +update_gen_kill_set (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int inst_num) +{ + int arity = mono_burg_arity [inst->opcode]; + int max_vars = cfg->num_varinfo; + + if (arity) + update_gen_kill_set (cfg, bb, inst->inst_i0, inst_num); + + if (arity > 1) + update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num); + + if (inst->ssa_op == MONO_SSA_LOAD) { + int idx = inst->inst_i0->inst_c0; + MonoMethodVar *vi = MONO_VARINFO (cfg, idx); + g_assert (idx < max_vars); + update_live_range (cfg, idx, bb->dfn, inst_num); + if (!mono_bitset_test (bb->kill_set, idx)) + mono_bitset_set (bb->gen_set, idx); + vi->spill_costs += 1 + (bb->nesting * 2); + } else if (inst->ssa_op == MONO_SSA_STORE) { + int idx = inst->inst_i0->inst_c0; + MonoMethodVar *vi = MONO_VARINFO (cfg, idx); + g_assert (idx < max_vars); + g_assert (inst->inst_i1->opcode != OP_PHI); + + update_live_range (cfg, idx, bb->dfn, inst_num); + mono_bitset_set (bb->kill_set, idx); + vi->spill_costs += 1 + (bb->nesting * 2); + } +} + +/* generic liveness analysis code. CFG specific parts are + * in update_gen_kill_set() + */ +void +mono_analyze_liveness (MonoCompile *cfg) +{ + MonoBitSet *old_live_in_set, *old_live_out_set; + gboolean changes; + int i, j, max_vars = cfg->num_varinfo; + +#ifdef DEBUG_LIVENESS + printf ("LIVENLESS %s\n", mono_method_full_name (cfg->method, TRUE)); +#endif + + g_assert (!(cfg->comp_done & MONO_COMP_LIVENESS)); + + cfg->comp_done |= MONO_COMP_LIVENESS; + + if (max_vars == 0) + return; + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *bb = cfg->bblocks [i]; + + bb->gen_set = mono_bitset_mp_new (cfg->mempool, max_vars); + bb->kill_set = mono_bitset_mp_new (cfg->mempool, max_vars); + bb->live_in_set = mono_bitset_mp_new (cfg->mempool, max_vars); + bb->live_out_set = mono_bitset_mp_new (cfg->mempool, max_vars); + } + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *bb = cfg->bblocks [i]; + MonoInst *inst; + int tree_num; + + for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) { + //mono_print_tree (inst); printf ("\n"); + update_gen_kill_set (cfg, bb, inst, tree_num); + } + +#ifdef DEBUG_LIVENESS + printf ("BLOCK BB%d (", bb->block_num); + for (j = 0; j < bb->out_count; j++) + printf ("BB%d, ", bb->out_bb [j]->block_num); + + printf (")\n"); + printf ("GEN BB%d: ", bb->block_num); mono_bitset_print (bb->gen_set); printf ("\n"); + printf ("KILL BB%d: ", bb->block_num); mono_bitset_print (bb->kill_set); printf ("\n"); +#endif + } + + old_live_in_set = mono_bitset_new (max_vars, 0); + old_live_out_set = mono_bitset_new (max_vars, 0); + + do { + changes = FALSE; + + for (i = cfg->num_bblocks - 1; i >= 0; i--) { + MonoBasicBlock *bb = cfg->bblocks [i]; + + mono_bitset_copyto (bb->live_in_set, old_live_in_set); + mono_bitset_copyto (bb->live_out_set, old_live_out_set); + + mono_bitset_copyto (bb->live_out_set, bb->live_in_set); + mono_bitset_sub (bb->live_in_set, bb->kill_set); + mono_bitset_union (bb->live_in_set, bb->gen_set); + + mono_bitset_clear_all (bb->live_out_set); + + for (j = 0; j < bb->out_count; j++) + mono_bitset_union (bb->live_out_set, bb->out_bb [j]->live_in_set); + + if (!(mono_bitset_equal (old_live_in_set, bb->live_in_set) && + mono_bitset_equal (old_live_out_set, bb->live_out_set))) + changes = TRUE; + } + + } while (changes); + + mono_bitset_free (old_live_in_set); + mono_bitset_free (old_live_out_set); + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *bb = cfg->bblocks [i]; + + for (j = max_vars - 1; j >= 0; j--) { + + if (mono_bitset_test (bb->live_in_set, j)) + update_live_range (cfg, j, bb->dfn, 0); + + if (mono_bitset_test (bb->live_out_set, j)) + update_live_range (cfg, j, bb->dfn, 0xffff); + } + } + +#ifdef DEBUG_LIVENESS + for (i = cfg->num_bblocks - 1; i >= 0; i--) { + MonoBasicBlock *bb = cfg->bblocks [i]; + + printf ("LIVE IN BB%d: ", bb->block_num); + mono_bitset_print (bb->live_in_set); + printf ("\n"); + printf ("LIVE OUT BB%d: ", bb->block_num); + mono_bitset_print (bb->live_out_set); + printf ("\n"); + } +#endif +} + diff --git a/mono/mini/local-regalloc.txt b/mono/mini/local-regalloc.txt new file mode 100644 index 00000000000..a6e523557fe --- /dev/null +++ b/mono/mini/local-regalloc.txt @@ -0,0 +1,208 @@ + +* Proposal for the local register allocator + + The local register allocator deals with allocating registers + for temporaries inside a single basic block, while the global + register allocator is concerned with method-wide allocation of + variables. + The global register allocator uses callee-saved register for it's + purpouse so that there is no need to save and restore these registers + at call sites. + + There are a number of issues the local allocator needs to deal with: + *) some instructions expect operands in specific registers (for example + the shl instruction on x86, or the call instruction with thiscall + convention, or the equivalent call instructions on other architectures, + such as the need to put output registers in %oX on sparc) + *) some instructions deliver results only in specific registers (for example + the div instruction on x86, or the call instructionson on almost all + the architectures). + *) it needs to know what registers may be clobbered by an instruction + (such as in a method call) + *) it should avoid excessive reloads or stores to improve performance + + While which specific instructions have limitations is architecture-dependent, + the problem shold be solved in an arch-independent way to reduce code duplication. + The register allocator will be 'driven' by the arch-dependent code, but it's + implementation should be arch-independent. + + To improve the current local register allocator, we need to + keep more state in it than the current setup that only keeps busy/free info. + + Possible state information is: + + free: the resgister is free to use and it doesn't contain useful info + freeable: the register contains data loaded from a local (there is + also info about _which_ local it contains) as a result from previous + instructions (like, there was a store from the register to the local) + moveable: it contains live data that is needed in a following instruction, but + the contents may be moved to a different register + busy: the register contains live data and it is placed there because + the following instructions need it exactly in that register + allocated: the register is used by the global allocator + + The local register allocator will have the following interfaces: + + int get_register (); + Searches for a register in the free state. If it doesn't find it, + searches for a freeable register. Sets the status to moveable. + Looking for a 'free' register before a freeable one should allow for + removing a few redundant loads (though I'm still unsure if such + things should be delegated entirely to the peephole pass). + + int get_register_force (int reg); + Returns 'reg' if it is free or freeable. If it is moveable, it moves it + to another free or freeable register. + Sets the status of 'reg' to busy. + + void set_register_freeable (int reg); + Sets the status of 'reg' to freeable. + + void set_register_free (int reg); + Sets the status of 'reg' to free. + + void will_clobber (int reg); + Spills the register to the stack. Sets the status to freeable. + After the clobbering has occurred, set the status to free. + + void register_unspill (int reg); + Un-spills register reg and sets the status to moveable. + + FIXME: how is the 'local' information represented? Maybe a MonoInst* pointer. + + Note: the register allocator will insert instructions in the basic block + during it's operation. + +* Examples + + Given the tree (on x86 the right argument to shl needs to be in ecx): + + store (local1, shl (local1, call (some_arg))) + + At the start of the basic block, the registers are set to the free state. + The sequence of instructions may be: + instruction register status -> [%eax %ecx %edx] + start free free free + eax = load local1 mov free free + /* call clobbers eax, ecx, edx */ + spill eax free free free + call mov free free + /* now eax contains the right operand of the shl */ + mov %eax -> %ecx free busy free + un-spill mov busy free + shl %cl, %eax mov free free + + The resulting x86 code is: + mov $fffc(%ebp), %eax + mov %eax, $fff0(%ebp) + push some_arg + call func + mov %eax, %ecx + mov $fff0(%ebp), %eax + shl %cl, %eax + + Note that since shl could operate directly on memory, we could have: + + push some_arg + call func + mov %eax, %ecx + shl %cl, $fffc(%ebp) + + The above example with loading the operand in a register is just to complicate + the example and show that the algorithm should be able to handle it. + + Let's take another example with the this-call call convention (the first argument + is passed in %ecx). + In this case, will_clobber() will be called only on %eax and %edx, while %ecx + will be allocated with get_register_force (). + Note: when a register is allocated with get_register_force(), it should be set + to a different state as soon as possible. + + store (local1, shl (local1, this-call (local1))) + + instruction register status -> [%eax %ecx %edx] + start free free free + eax = load local1 mov free free + /* force load in %ecx */ + ecx = load local1 mov busy free + spill eax free busy free + call mov free free + /* now eax contains the right operand of the shl */ + mov %eax -> %ecx free busy free + un-spill mov busy free + shl %cl, %eax mov free free + + What happens when a register that we need to allocate with get_register_force () + contains an operand for the next instruction? + + instruction register status -> [%eax %ecx %edx] + eax = load local0 mov free free + ecx = load local1 mov mov free + get_register_force (ecx) here. + We have two options: + mov %ecx, %edx + or: + spill %ecx + The first option is way better (and allows the peephole pass to + just load the value in %edx directly, instead of loading first to %ecx). + This doesn't work, though, if the instruction clobbers the %edx register + (like in a this-call). So, we first need to clobber the registers + (so the state of %ecx changes to freebale and there is no issue + with get_register_force ()). + What if an instruction both clobbers a register and requires it as + an operand? Lets' take the x86 idiv instruction as an example: it + requires the dividend in edx:eax and returns the result in eax, + with the modulus in edx. + + store (local1, div (local1, local2)) + + instruction register status -> [%eax %ecx %edx] + eax = load local0 mov free free + will_clobber eax, edx free mov free + force mov %ecx, %eax busy free free + set %edx busy free busy + idiv mov free free + + Note: edx is set to free after idiv, because the modulus is not needed + (if it was a rem, eax would have been freed). + If we load the divisor before will_clobber(), we'll have to spill + eax and reload it later. If we load it just after the idiv, there is no issue. + In any case, the algorithm should give the correct results and allow the operation. + + Working recursively on the isntructions there shouldn't be huge issues + with this algorithm (though, of course, it's not optimal and it may + introduce excessive spills or register moves). The advantage over the current + local reg allocator is that: + 1) the number of spills/moves would be smaller anyway + 2) a separate peephole pass could be able to eliminate reg moves + 3) we'll be able to remove the 'forced' spills we currently do with + the return value of method calls + +* Issues + + How to best integrate such a reg allocator with the burg stuff. + + Think about a call os sparc with two arguments: they got into %o0 and %o1 + and each of them sets the register as busy. But what if the values to put there + are themselves the result of a call? %o0 is no problem, but for all the + next argument n the above algorithm would spill all the 0...n-1 registers... + +* Papers + + More complex solutions to the local register allocator problem: + http://dimacs.rutgers.edu/TechnicalReports/abstracts/1997/97-33.html + + Combining register allocation and instruction scheduling: + http://citeseer.nj.nec.com/motwani95combining.html + + More on LRA euristics: + http://citeseer.nj.nec.com/liberatore97hardness.html + + Linear-time optimal code scheduling for delayedload architectures + http://www.cs.wisc.edu/~fischer/cs701.f01/inst.sched.ps.gz + + Precise Register Allocation for Irregular Architectures + http://citeseer.nj.nec.com/kong98precise.html + + Allocate registers first to subtrees that need more of them. + http://www.upb.de/cs/ag-kastens/compii/folien/comment401-409.2.pdf diff --git a/mono/mini/main.c b/mono/mini/main.c new file mode 100644 index 00000000000..49425864d83 --- /dev/null +++ b/mono/mini/main.c @@ -0,0 +1,8 @@ +#include "mini.h" + +int +main (int argc, char* argv[]) +{ + return mini_main (argc, argv); +} + diff --git a/mono/mini/makefile b/mono/mini/makefile new file mode 100644 index 00000000000..1c776f8b7cd --- /dev/null +++ b/mono/mini/makefile @@ -0,0 +1,99 @@ +count=100000 +mtest=for_loop +monodir=../mono/ +libs= \ + $(monodir)/mono/metadata/libmonoruntime.la \ + $(monodir)/mono/metadata/libmetadata.la \ + $(monodir)/mono/io-layer/libwapi.la \ + $(monodir)/mono/utils/libmonoutils.la +debugger_libs= \ + ../debugger/wrapper/libmonodebuggerwrapper.a + +MCS=mcs +RUNTIME=mono +CC=gcc +WARN=-Wall -Wunused -Wmissing-prototypes -Wmissing-declarations \ + -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wno-cast-qual -Wcast-align -Wwrite-strings +CFLAGS=-g $(WARN) -fexceptions -DMONO_USE_EXC_TABLES +CPPFLAGS=-g $(WARN) -I$(monodir) `pkg-config --cflags glib-2.0 gmodule-2.0 gthread-2.0` +LDFLAGS=-g $(libs) -lgc -lm `pkg-config --libs glib-2.0 gmodule-2.0 gthread-2.0` +debugger_LDFLAGS=-g $(libs) $(debugger_libs) -lgc -lm `pkg-config --libs glib-2.0 gmodule-2.0 gthread-2.0` + +arch_objects=mini-x86.o exceptions-x86.o tramp-x86.o +lib_objects=mini.o dominators.o cfold.o regalloc.o inssel.o regset.o helpers.o liveness.o ssa.o driver.o \ + debug.o debug-stabs.o debug-dwarf2.o debug-mini.o linear-scan.o aot.o graph.o $(arch_objects) +objects=main.o $(lib_objects) +debugger_objects=debugger-main.o $(lib_objects) +regtests=basic.exe arrays.exe basic-float.exe basic-long.exe objects.exe iltests.exe exceptions.exe bench.exe + +%.o: %.c mini.h makefile regset.h mini-x86.h inssel.h cpu-pentium.h mini-ops.h + $(CC) -c $< $(CFLAGS) $(CPPFLAGS) + +%.exe: %.cs TestDriver.dll + $(MCS) /unsafe $< /r:TestDriver.dll + +%.exe: %.il + ilasm /OUTPUT=$*.exe $< + +all: mini genmdesc + +TestDriver.dll: TestDriver.cs + $(MCS) /out:TestDriver.dll /target:library TestDriver.cs + +mini: mini.h $(objects) $(libs) makefile cpu-pentium.h jit-icalls.c + libtool --mode=link gcc -o mini $(objects) $(LDFLAGS) + +mono-debugger-mini-wrapper: mini.h $(debugger_objects) $(libs) makefile cpu-pentium.h jit-icalls.c + libtool --mode=link gcc -o mono-debugger-mini-wrapper $(debugger_objects) $(debugger_LDFLAGS) + +genmdesc: mini.h genmdesc.o helpers.o + libtool --mode=link gcc -o genmdesc genmdesc.o helpers.o $(LDFLAGS) + +cpu-pentium.h: cpu-pentium.md genmdesc + ./genmdesc cpu-pentium.md cpu-pentium.h pentium + +BURGSRC= inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg + +inssel.c inssel.h: $(BURGSRC) + $(monodir)/mono/monoburg/monoburg -c 1 -p -e $(BURGSRC) -d inssel.h -s inssel.c + +testi: mini test.exe + ./mini -v -v --ncompile 1 --compile Test:$(mtest) test.exe + +# ensure the tests are actually correct +checktests: $(regtests) + for i in $(regtests); do $(RUNTIME) $$i; done + +check: mini $(regtests) + ./mini --verbose --regression $(regtests) + +aotcheck: mini $(regtests) + for i in $(regtests); do ./mini --aot $$i; done + ./mini --verbose --regression $(regtests) + rm -f *.exe.so + +bench: mini test.exe + time ./mini --ncompile $(count) --compile Test:$(mtest) test.exe + +mbench: test.exe + time $(monodir)/mono/jit/mono --ncompile $(count) --compile Test:$(mtest) test.exe + +stat1: mini bench.exe + ./mini --verbose --statfile stats.pl --regression bench.exe + perl viewstat.pl stats.pl + +stat2: mini basic.exe + ./mini --verbose --statfile stats.pl --regression basic.exe + perl viewstat.pl -e stats.pl + +stat3: mini bench.exe + ./mini --statfile stats.pl --ncompile 1000 --compile Tests:test_0_many_nested_loops bench.exe + perl viewstat.pl stats.pl + +docu: mini.sgm + docbook2txt mini.sgm + +clean: + rm -f mini a.out gmon.out *.o test.exe + diff --git a/mono/mini/mini-arch.h b/mono/mini/mini-arch.h new file mode 100644 index 00000000000..dd9383df2ed --- /dev/null +++ b/mono/mini/mini-arch.h @@ -0,0 +1,12 @@ +#ifndef __MONO_MINI_ARCH_H__ +#define __MONO_MINI_ARCH_H__ + +#ifdef __i386__ +#include "mini-x86.h" +#elif defined(__ppc__) +#include "mini-ppc.h" +#else +#error add arch specific include file in mini-arch.h +#endif + +#endif /* __MONO_MINI_ARCH_H__ */ diff --git a/mono/mini/mini-doc.txt b/mono/mini/mini-doc.txt new file mode 100644 index 00000000000..cfe1a91d365 --- /dev/null +++ b/mono/mini/mini-doc.txt @@ -0,0 +1,628 @@ + + A new JIT compiler for the Mono Project + + Miguel de Icaza (miguel@{ximian.com,gnome.org}), + + +* Abstract + + Mini is a new compilation engine for the Mono runtime. The + new engine is designed to bring new code generation + optimizations, portability and precompilation. + + In this document we describe the design decisions and the + architecture of the new compilation engine. + +* Introduction + + First we discuss the overall architecture of the Mono runtime, + and how code generation fits into it; Then we discuss the + development and basic architecture of our first JIT compiler + for the ECMA CIL framework. The next section covers the + objectives for the work on the new JIT compiler, then we + discuss the new features available in the new JIT compiler, + and finally a technical description of the new code generation + engine. + +* Architecture of the Mono Runtime + + The Mono runtime is an implementation of the ECMA Common + Language Infrastructure (CLI), whose aim is to be a common + platform for executing code in multiple languages. + + Languages that target the CLI generate images that contain + code in high-level intermediate representation called the + "Common Intermediate Language". This intermediate language is + rich enough to allow for programs and pre-compiled libraries + to be reflected. The execution environment allows for an + object oriented execution environment with single inheritance + and multiple interface implementations. + + This runtime provides a number of services for programs that + are targeted to it: Just-in-Time compilation of CIL code into + native code, garbage collection, thread management, I/O + routines, single, double and decimal floating point, + asynchronous method invocation, application domains, and a + framework for building arbitrary RPC systems (remoting) and + integration with system libraries through the Platform Invoke + functionality. + + The focus of this document is on the services provided by the + Mono runtime to transform CIL bytecodes into code that is + native to the underlying architecture. + + The code generation interface is a set of macros that allow a + C programmer to generate code on the fly, this is done + through a set of macros found in the mono/jit/arch/ directory. + These macros are used by the JIT compiler to generate native + code. + + The platform invocation code is interesting, as it generates + CIL code on the fly to marshal parameters, and then this + code is in turned processed by the JIT engine. + +* Previous Experiences + + Mono has built a JIT engine, which has been used to bootstrap + Mono since January, 2002. This JIT engine has reasonable + performance, and uses an tree pattern matching instruction + selector based on the BURS technology. This JIT compiler was + designed by Dietmar Maurer, Paolo Molaro and Miguel de Icaza. + + The existing JIT compiler has three phases: + + * Re-creation of the semantic tree from CIL + byte-codes. + + * Instruction selection, with a cost-driven + engine. + + * Code generation and register allocation. + + It is also hooked into the rest of the runtime to provide + services like marshaling, just-in-time compilation and + invocation of "internal calls". + + This engine constructed a collection of trees, which we + referred to as the "forest of trees", this forest is created by + "hydrating" the CIL instruction stream. + + The first step was to identify the basic blocks on the method, + and computing the control flow graph (cfg) for it. Once this + information was computed, a stack analysis on each basic block + was performed to create a forest of trees for each one of + them. + + So for example, the following statement: + + int a, b; + ... + b = a + 1; + + Which would be represented in CIL as: + + ldloc.0 + ldc.i4.1 + add + stloc.1 + + After the stack analysis would create the following tree: + + (STIND_I4 ADDR_L[EBX|2] ( + ADD (LDIND_I4 ADDR_L[ESI|1]) + CONST_I4[1])) + + This tree contains information from the stack analysis: for + instance, notice that the operations explicitly encode the + data types they are operating on, there is no longer an + ambiguity on the types, because this information has been + inferred. + + At this point the JIT would pass the constructed forest of + trees to the architecture-dependant JIT compiler. + + The architecture dependent code then performed register + allocation (optionally using linear scan allocation for + variables, based on life analysis). + + Once variables had been assigned, a tree pattern matching with + dynamic programming is used (the tree pattern matcher is + custom build for each architecture, using a code + generator: monoburg). The instruction selector used cost + functions to select the best instruction patterns. + + The instruction selector is able to produce instructions that + take advantage of the x86 instruction indexing instructions + for example. + + One problem though is that the code emitter and the register + allocator did not have any visibility outside the current + tree, which meant that some redundant instructions were + generated. A peephole optimizer with this architecture was + hard to write, given the tree-based representation that is + used. + + This JIT was functional, but it did not provide a good + architecture to base future optimizations on. Also the + line between architecture neutral and architecture + specific code and optimizations was hard to draw. + + The JIT engine supported two code generation modes to support + the two optimization modes for applications that host multiple + application domains: generate code that will be shared across + application domains, or generate code that will not be shared + across application domains. + +* Objectives of the new JIT engine. + + We wanted to support a number of features that were missing: + + * Ahead-of-time compilation. + + The idea is to allow developers to pre-compile their code + to native code to reduce startup time, and the working + set that is used at runtime in the just-in-time compiler. + + Although in Mono this has not been a visible problem, we + wanted to pro-actively address this problem. + + When an assembly (a Mono/.NET executable) is installed in + the system, it would then be possible to pre-compile the + code, and have the JIT compiler tune the generated code + to the particular CPU on which the software is + installed. + + This is done in the Microsoft.NET world with a tool + called ngen.exe + + * Have a good platform for doing code optimizations. + + The design called for a good architecture that would + enable various levels of optimizations: some + optimizations are better performed on high-level + intermediate representations, some on medium-level and + some at low-level representations. + + Also it should be possible to conditionally turn these on + or off. Some optimizations are too expensive to be used + in just-in-time compilation scenarios, but these + expensive optimizations can be turned on for + ahead-of-time compilations or when using profile-guided + optimizations on a subset of the executed methods. + + * Reduce the effort required to port the Mono code + generator to new architectures. + + For Mono to gain wide adoption in the Unix world, it is + necessary that the JIT engine works in most of today's + commercial hardware platforms. + +* Features of the new JIT engine. + + The new JIT engine was architected by Dietmar Maurer and Paolo + Molaro, based on the new objectives. + + Mono provides a number of services to applications running + with the new JIT compiler: + + * Just-in-Time compilation of CLI code into native code. + + * Ahead-of-Time compilation of CLI code, to reduce + startup time of applications. + + A number of software development features are also available: + + * Execution time profiling (--profile) + + Generates a report of the times consumed by routines, + as well as the invocation times, as well as the + callers. + + * Memory usage profiling (--profile) + + Generates a report of the memory usage by a program + that is ran under the Mono JIT. + + * Code coverage (--coverage) + + * Execution tracing. + + People who are interested in developing and improving the Mini + JIT compiler will also find a few useful routines: + + * Compilation times + + This is used to time the execution time for the JIT + when compiling a routine. + + * Control Flow Graph and Dominator Tree drawing. + + These are visual aids for the JIT developer: they + render representations of the Control Flow graph, and + for the more advanced optimizations, they draw the + dominator tree graph. + + This requires Dot (from the graphwiz package) and Ghostview. + + * Code generator regression tests. + + The engine contains support for running regression + tests on the virtual machine, which is very helpful to + developers interested in improving the engine. + + * Optimization benchmark framework. + + The JIT engine will generate graphs that compare + various benchmarks embedded in an assembly, and run the + various tests with different optimization flags. + + This requires Perl, GD::Graph. + +* Flexibility + + This is probably the most important component of the new code + generation engine. The internals are relatively easy to + replace and update, even large passes can be replaced and + implemented differently. + +* New code generator + + Compiling a method begins with the `mini_method_to_ir' routine + that converts the CIL representation into a medium + intermediate representation. + + The mini_method_to_ir routine performs a number of operations: + + * Flow analysis and control flow graph computation. + + Unlike the previous version, stack analysis and control + flow graphs are computed in a single pass in the + mini_method_to_ir function, this is done for performance + reasons: although the complexity increases, the benefit + for a JIT compiler is that there is more time available + for performing other optimizations. + + * Basic block computation. + + mini_method_to_ir populates the MonoCompile structure + with an array of basic blocks each of which contains + forest of trees made up of MonoInst structures. + + * Inlining + + Inlining is no longer restricted to methods containing + one single basic block, instead it is possible to inline + arbitrary complex methods. + + The heuristics to choose what to inline are likely going + to be tuned in the future. + + * Method to opcode conversion. + + Some method call invocations like `call Math.Sin' are + transformed into an opcode: this transforms the call + into a semantically rich node, which is later inline + into an FPU instruction. + + Various Array methods invocations are turned into + opcodes as well (The Get, Set and Address methods) + + * Tail recursion elimination + + Basic blocks **** + + The MonoInst structure holds the actual decoded instruction, + with the semantic information from the stack analysis. + MonoInst is interesting because initially it is part of a tree + structure, here is a sample of the same tree with the new JIT + engine: + + (stind.i4 regoffset[0xffffffd4(%ebp)] + (add (ldind.i4 regoffset[0xffffffd8(%ebp)]) + iconst[1])) + + This is a medium-level intermediate representation (MIR). + + Some complex opcodes are decomposed at this stage into a + collection of simpler opcodes. Not every complex opcode is + decomposed at this stage, as we need to preserve the semantic + information during various optimization phases. + + For example a NEWARR opcode carries the length and the type of + the array that could be used later to avoid type checking or + array bounds check. + + There are a number of operations supported on this + representation: + + * Branch optimizations. + + * Variable liveness. + + * Loop optimizations: the dominator trees are + computed, loops are detected, and their nesting + level computed. + + * Conversion of the method into static single assignment + form (SSA form). + + * Dead code elimination. + + * Constant propagation. + + * Copy propagation. + + * Constant folding. + + Once the above optimizations are optionally performed, a + decomposition phase is used to turn some complex opcodes into + internal method calls. In the initial version of the JIT + engine, various operations on longs are emulated instead of + being inlined. Also the newarr invocation is turned into a + call to the runtime. + + At this point, after computing variable liveness, it is + possible to use the linear scan algorithm for allocating + variables to registers. The linear scan pass uses the + information that was previously gathered by the loop nesting + and loop structure computation to favor variables in inner + loops. + + Stack space is then reserved for the local variables and any + temporary variables generated during the various + optimizations. + +** Instruction selection + + At this point, the BURS instruction selector is invoked to + transform the tree-based representation into a list of + instructions. This is done using a tree pattern matcher that + is generated for the architecture using the `monoburg' tool. + + Monoburg takes as input a file that describes tree patterns, + which are matched against the trees that were produced by the + engine in the previous stages. + + The pattern matching might have more than one match for a + particular tree. In this case, the match selected is the one + whose cost is the smallest. A cost can be attached to each + rule, and if no cost is provided, the implicit cost is one. + Smaller costs are selected over higher costs. + + The cost function can be used to select particular blocks of + code for a given architecture, or by using a prohibitive high + number to avoid having the rule match. + + The various rules that our JIT engine uses transform a tree of + MonoInsts into a list of monoinsts: + + +-----------------------------------------------------------+ + | Tree List | + | of ===> Instruction selection ===> of | + | MonoInst MonoInst. | + +-----------------------------------------------------------+ + + During this process various "types" of MonoInst kinds + disappear and turned into lower-level representations. The + JIT compiler just happens to reuse the same structure (this is + done to reduce memory usage and improve memory locality). + + The instruction selection rules are split in a number of + files, each one with a particular purpose: + + inssel.brg + Contains the generic instruction selection + patterns. + + inssel-x86.brg + Contains x86 specific rules. + + inssel-ppc.brg + Contains PowerPC specific rules. + + inssel-long32.brg + burg file for 64bit instructions on 32bit architectures. + + inssel-long.brg + burg file for 64bit architectures. + + inssel-float.brg + burg file for floating point instructions + + For a given build, a set of those files would be included. + For example, for the build of Mono on the x86, the following + set is used: + + inssel.brg inssel-x86.brg inssel-long32.brg inssel-float.brg + +** Native method generation + + The native method generation has a number of steps: + + * Architecture specific register allocation. + + The information about loop nesting that was + previously gathered is used here to hint the + register allocator. + + * Generating the method prolog/epilog. + + * Optionally generate code to introduce tracing facilities. + + * Hooking into the debugger. + + * Performing any pending fixups. + + * Code generation. + +*** Code Generation + + The actual code generation is contained in the architecture + specific portion of the compiler. The input to the code + generator is each one of the basic blocks with its list of + instructions that were produced in the instruction selection + phase. + + During the instruction selection phase, virtual registers are + assigned. Just before the peephole optimization is performed, + physical registers are assigned. + + A simple peephole and algebraic optimizer is ran at this + stage. + + The peephole optimizer removes some redundant operations at + this point. This is possible because the code generation at + this point has visibility into the basic block that spans the + original trees. + + The algebraic optimizer performs some simple algebraic + optimizations that replace expensive operations with cheaper + operations if possible. + + The rest of the code generation is fairly simple: a switch + statement is used to generate code for each of the MonoInsts + + We always try to allocate code in sequence, instead of just using + malloc. This way we increase spatial locality which gives a massive + speedup on most architectures. + +*** Ahead of Time compilation + + Ahead-of-Time compilation is a new feature of our new + compilation engine. The compilation engine is shared by the + Just-in-Time (JIT) compiler and the Ahead-of-Time compiler + (AOT). + + The difference is on the set of optimizations that are turned + on for each mode: Just-in-Time compilation should be as fast + as possible, while Ahead-of-Time compilation can take as long + as required, because this is not done at a time criticial + time. + + With AOT compilation, we can afford to turn all of the + computationally expensive optimizations on. + + After the code generation phase is done, the code and any + required fixup information is saved into a file that is + readable by "as" (the native assembler available on all + systems). This assembly file is then passed to the native + assembler, which generates a loadable module. + + At execution time, when an assembly is loaded from the disk, + the runtime engine will probe for the existance of a + pre-compiled image. If the pre-compiled image exists, then it + is loaded, and the method invocations are resolved to the code + contained in the loaded module. + + The code generated under the AOT scenario is slightly + different than the JIT scenario. It generates code that is + application-domain relative and that can be shared among + multiple thread. + + This is the same code generation that is used when the runtime + is instructed to maximize code sharing on a multi-application + domain scenario. + +* SSA-based optimizations + + SSA form simplifies many optimization because each variable has exactly + one definition site. All uses of a variable are "dominated" by its + definition, which enables us to implement algorithm like: + + * conditional constant propagation + + * array bound check removal + + * dead code elimination + + And we can implement those algorithm in a efficient way using SSA. + + +* Register allocation. + + Global register allocation is performed on the medium + intermediate representation just before instruction selection + is performed on the method. Local register allocation is + later performed at the basic-block level on the + + Global register allocation uses the following input: + + 1) set of register-sized variables that can be allocated to a + register (this is an architecture specific setting, for x86 + these registers are the callee saved register ESI, EDI and + EBX). + + 2) liveness information for the variables + + 3) (optionally) loop info to favour variables that are used in + inner loops. + + During instruction selection phase, symbolic registers are + assigned to temporary values in expressions. + + Local register allocation assigns hard registers to the + symbolic registers, and it is performed just before the code + is actually emitted and is performed at the basic block level. + A CPU description file describes the input registers, output + registers, fixed registers and clobbered registers by each + operation. + + +---------- +* Bootstrap + + The Mini bootstrap parses the arguments passed on the command + line, and initializes the JIT runtime. Each time the + mini_init() routine is invoked, a new Application Domain will + be returned. + +* Signal handlers + + mono_runtime_install_handlers + +* BURG Code Generator Generator + + monoburg was written by Dietmar Maurer. It is based on the + papers from Christopher W. Fraser, Robert R. Henry and Todd + A. Proebsting: "BURG - Fast Optimal Instruction Selection and + Tree Parsing" and "Engineering a Simple, Efficient Code + Generator Generator". + + The original BURG implementation is unable to work on DAGs, instead only + trees are allowed. Our monoburg implementations is able to generate tree + matcher which works on DAGs, and we use this feature in the new + JIT. This simplifies the code because we can directly pass DAGs and + don't need to convert them to trees. + +* Future + + Profile-based optimization is something that we are very + interested in supporting. There are two possible usage + scenarios: + + * Based on the profile information gathered during + the execution of a program, hot methods can be compiled + with the highest level of optimizations, while bootstrap + code and cold methods can be compiled with the least set + of optimizations and placed in a discardable list. + + * Code reordering: this profile-based optimization would + only make sense for pre-compiled code. The profile + information is used to re-order the assembly code on disk + so that the code is placed on the disk in a way that + increments locality. + + This is the same principle under which SGI's cord program + works. + + The nature of the CIL allows the above optimizations to be + easy to implement and deploy. Since we live and define our + universe for these things, there are no interactions with + system tools required, nor upgrades on the underlying + infrastructure required. + + Instruction scheduling is important for certain kinds of + processors, and some of the framework exists today in our + register allocator and the instruction selector to cope with + this, but has not been finished. The instruction selection + would happen at the same time as local register allocation. \ No newline at end of file diff --git a/mono/mini/mini-ops.h b/mono/mini/mini-ops.h new file mode 100644 index 00000000000..6222414640f --- /dev/null +++ b/mono/mini/mini-ops.h @@ -0,0 +1,314 @@ + +MINI_OP(OP_LOAD, "load") +MINI_OP(OP_LDADDR, "ldaddr") +MINI_OP(OP_STORE, "store") +MINI_OP(OP_OBJADDR, "objaddr") +MINI_OP(OP_VTADDR, "vtaddr") +MINI_OP(OP_PHI, "phi") +MINI_OP(OP_RENAME, "rename") +MINI_OP(OP_COMPARE, "compare") +MINI_OP(OP_COMPARE_IMM, "compare_imm") +MINI_OP(OP_FCOMPARE, "fcompare") +MINI_OP(OP_LCOMPARE, "lcompare") +MINI_OP(OP_LOCAL, "local") +MINI_OP(OP_ARG, "arg") +MINI_OP(OP_OUTARG, "outarg") +MINI_OP(OP_OUTARG_IMM, "outarg_imm") +MINI_OP(OP_OUTARG_R4, "outarg_r4") +MINI_OP(OP_OUTARG_R8, "outarg_r8") +MINI_OP(OP_OUTARG_VT, "outarg_vt") +MINI_OP(OP_RETARG, "retarg") +MINI_OP(OP_SETRET, "setret") +MINI_OP(OP_SETLRET, "setlret") +MINI_OP(OP_SETREG, "setreg") +MINI_OP(OP_SETREGIMM, "setregimm") +MINI_OP(OP_SETFREG, "setfreg") +MINI_OP(OP_CHECK_THIS, "checkthis") +MINI_OP(OP_VOIDCALL, "voidcall") +MINI_OP(OP_VOIDCALLVIRT, "voidcallvirt") +MINI_OP(OP_VOIDCALL_REG, "voidcall_reg") +MINI_OP(OP_VOIDCALL_MEMBASE, "voidcall_membase") +MINI_OP(OP_FCALL, "fcall") +MINI_OP(OP_FCALLVIRT, "fcallvirt") +MINI_OP(OP_FCALL_REG, "fcall_reg") +MINI_OP(OP_FCALL_MEMBASE, "fcall_membase") +MINI_OP(OP_LCALL, "lcall") +MINI_OP(OP_LCALLVIRT, "lcallvirt") +MINI_OP(OP_LCALL_REG, "lcall_reg") +MINI_OP(OP_LCALL_MEMBASE, "lcall_membase") +MINI_OP(OP_VCALL, "vcall") +MINI_OP(OP_VCALLVIRT, "vcallvirt") +MINI_OP(OP_VCALL_REG, "vcall_reg") +MINI_OP(OP_VCALL_MEMBASE, "vcall_membase") +MINI_OP(OP_CALL_REG, "call_reg") +MINI_OP(OP_CALL_MEMBASE, "call_membase") +MINI_OP(OP_TRAP, "trap") +MINI_OP(OP_ICONST, "iconst") +MINI_OP(OP_I8CONST, "i8const") +MINI_OP(OP_R4CONST, "r4const") +MINI_OP(OP_R8CONST, "r8const") +MINI_OP(OP_REGVAR, "regvar") +MINI_OP(OP_REG, "reg") +MINI_OP(OP_REGOFFSET, "regoffset") +MINI_OP(OP_LABEL, "label") + +MINI_OP(OP_STORE_MEMBASE_IMM,"store_membase_imm") +MINI_OP(OP_STORE_MEMBASE_REG,"store_membase_reg") +MINI_OP(OP_STOREI1_MEMBASE_IMM, "storei1_membase_imm") +MINI_OP(OP_STOREI1_MEMBASE_REG, "storei1_membase_reg") +MINI_OP(OP_STOREI2_MEMBASE_IMM, "storei2_membase_imm") +MINI_OP(OP_STOREI2_MEMBASE_REG, "storei2_membase_reg") +MINI_OP(OP_STOREI4_MEMBASE_IMM, "storei4_membase_imm") +MINI_OP(OP_STOREI4_MEMBASE_REG, "storei4_membase_reg") +MINI_OP(OP_STOREI8_MEMBASE_IMM, "storei8_membase_imm") +MINI_OP(OP_STOREI8_MEMBASE_REG, "storei8_membase_reg") +MINI_OP(OP_STORER4_MEMBASE_REG, "storer4_membase_reg") +MINI_OP(OP_STORER8_MEMBASE_REG, "storer8_membase_reg") +MINI_OP(OP_LOAD_MEMBASE, "load_membase") +MINI_OP(OP_LOADI1_MEMBASE,"loadi1_membase") +MINI_OP(OP_LOADU1_MEMBASE,"loadu1_membase") +MINI_OP(OP_LOADI2_MEMBASE,"loadi2_membase") +MINI_OP(OP_LOADU2_MEMBASE,"loadu2_membase") +MINI_OP(OP_LOADI4_MEMBASE,"loadi4_membase") +MINI_OP(OP_LOADU4_MEMBASE,"loadu4_membase") +MINI_OP(OP_LOADI8_MEMBASE,"loadi8_membase") +MINI_OP(OP_LOADR4_MEMBASE,"loadr4_membase") +MINI_OP(OP_LOADR8_MEMBASE,"loadr8_membase") +MINI_OP(OP_LOADU4_MEM,"loadu4_mem") +MINI_OP(OP_MOVE, "move") + +MINI_OP(OP_ADD_IMM, "add_imm") +MINI_OP(OP_SUB_IMM, "sub_imm") +MINI_OP(OP_MUL_IMM, "mul_imm") +MINI_OP(OP_DIV_IMM, "div_imm") +MINI_OP(OP_DIV_UN_IMM, "div_un_imm") +MINI_OP(OP_REM_IMM, "rem_imm") +MINI_OP(OP_REM_UN_IMM, "rem_un_imm") +MINI_OP(OP_AND_IMM, "and_imm") +MINI_OP(OP_OR_IMM, "or_imm") +MINI_OP(OP_XOR_IMM, "xor_imm") +MINI_OP(OP_SHL_IMM, "shl_imm") +MINI_OP(OP_SHR_IMM, "shr_imm") +MINI_OP(OP_SHR_UN_IMM, "shr_un_imm") + +/* exceptions: must be in the same order as the matching CEE_ branch opcodes */ +MINI_OP(OP_COND_EXC_EQ, "cond_exc_eq") +MINI_OP(OP_COND_EXC_GE, "cond_exc_ge") +MINI_OP(OP_COND_EXC_GT, "cond_exc_gt") +MINI_OP(OP_COND_EXC_LE, "cond_exc_le") +MINI_OP(OP_COND_EXC_LT, "cond_exc_lt") +MINI_OP(OP_COND_EXC_NE_UN, "cond_exc_ne_un") +MINI_OP(OP_COND_EXC_GE_UN, "cond_exc_ge_un") +MINI_OP(OP_COND_EXC_GT_UN, "cond_exc_gt_un") +MINI_OP(OP_COND_EXC_LE_UN, "cond_exc_le_un") +MINI_OP(OP_COND_EXC_LT_UN, "cond_exc_lt_un") + +MINI_OP(OP_COND_EXC_OV, "cond_exc_ov") +MINI_OP(OP_COND_EXC_NO, "cond_exc_no") +MINI_OP(OP_COND_EXC_C, "cond_exc_c") +MINI_OP(OP_COND_EXC_NC, "cond_exc_nc") + +/* 64 bit opcodes: must be in the same order as the matching CEE_ opcodes: binops_op_map */ +MINI_OP(OP_LADD, "long_add") +MINI_OP(OP_LSUB, "long_sub") +MINI_OP(OP_LMUL, "long_mul") +MINI_OP(OP_LDIV, "long_div") +MINI_OP(OP_LDIV_UN, "long_div_un") +MINI_OP(OP_LREM, "long_rem") +MINI_OP(OP_LREM_UN, "long_rem_un") +MINI_OP(OP_LAND, "long_and") +MINI_OP(OP_LOR, "long_or") +MINI_OP(OP_LXOR, "long_xor") +MINI_OP(OP_LSHL, "long_shl") +MINI_OP(OP_LSHR, "long_shr") +MINI_OP(OP_LSHR_UN, "long_shr_un") + +/* 64 bit opcodes: must be in the same order as the matching CEE_ opcodes: unops_op_map */ +MINI_OP(OP_LNEG, "long_neg") +MINI_OP(OP_LNOT, "long_not") +MINI_OP(OP_LCONV_TO_I1,"long_conv_to_i1") +MINI_OP(OP_LCONV_TO_I2,"long_conv_to_i2") +MINI_OP(OP_LCONV_TO_I4,"long_conv_to_i4") +MINI_OP(OP_LCONV_TO_I8,"long_conv_to_i8") +MINI_OP(OP_LCONV_TO_R4,"long_conv_to_r4") +MINI_OP(OP_LCONV_TO_R8,"long_conv_to_r8") +MINI_OP(OP_LCONV_TO_U4,"long_conv_to_u4") +MINI_OP(OP_LCONV_TO_U8,"long_conv_to_u8") + +MINI_OP(OP_LCONV_TO_U2, "long_conv_to_u2") +MINI_OP(OP_LCONV_TO_U1, "long_conv_to_u1") +MINI_OP(OP_LCONV_TO_I, "long_conv_to_i") +MINI_OP(OP_LCONV_TO_OVF_I,"long_conv_to_ovf_i") +MINI_OP(OP_LCONV_TO_OVF_U,"long_conv_to_ovf_u") +MINI_OP(OP_LADD_OVF, "long_add_ovf") +MINI_OP(OP_LADD_OVF_UN, "long_add_ovf_un") +MINI_OP(OP_LMUL_OVF, "long_mul_ovf") +MINI_OP(OP_LMUL_OVF_UN, "long_mul_ovf_un") +MINI_OP(OP_LSUB_OVF, "long_sub_ovf") +MINI_OP(OP_LSUB_OVF_UN, "long_sub_ovf_un") + +MINI_OP(OP_LCONV_TO_OVF_I1_UN,"long_conv_to_ovf_i1_un") +MINI_OP(OP_LCONV_TO_OVF_I2_UN,"long_conv_to_ovf_i2_un") +MINI_OP(OP_LCONV_TO_OVF_I4_UN,"long_conv_to_ovf_i4_un") +MINI_OP(OP_LCONV_TO_OVF_I8_UN,"long_conv_to_ovf_i8_un") +MINI_OP(OP_LCONV_TO_OVF_U1_UN,"long_conv_to_ovf_u1_un") +MINI_OP(OP_LCONV_TO_OVF_U2_UN,"long_conv_to_ovf_u2_un") +MINI_OP(OP_LCONV_TO_OVF_U4_UN,"long_conv_to_ovf_u4_un") +MINI_OP(OP_LCONV_TO_OVF_U8_UN,"long_conv_to_ovf_u8_un") +MINI_OP(OP_LCONV_TO_OVF_I_UN, "long_conv_to_ovf_i_un") +MINI_OP(OP_LCONV_TO_OVF_U_UN, "long_conv_to_ovf_u_un") + +MINI_OP(OP_LCONV_TO_OVF_I1,"long_conv_to_ovf_i1") +MINI_OP(OP_LCONV_TO_OVF_U1,"long_conv_to_ovf_u1") +MINI_OP(OP_LCONV_TO_OVF_I2,"long_conv_to_ovf_i2") +MINI_OP(OP_LCONV_TO_OVF_U2,"long_conv_to_ovf_u2") +MINI_OP(OP_LCONV_TO_OVF_I4,"long_conv_to_ovf_i4") +MINI_OP(OP_LCONV_TO_OVF_U4,"long_conv_to_ovf_u4") +MINI_OP(OP_LCONV_TO_OVF_I8,"long_conv_to_ovf_i8") +MINI_OP(OP_LCONV_TO_OVF_U8,"long_conv_to_ovf_u8") + +MINI_OP(OP_LCEQ, "long_ceq") +MINI_OP(OP_LCGT, "long_cgt") +MINI_OP(OP_LCGT_UN,"long_cgt_un") +MINI_OP(OP_LCLT, "long_clt") +MINI_OP(OP_LCLT_UN,"long_clt_un") + +MINI_OP(OP_LCONV_TO_R_UN,"long_conv_to_r_un") +MINI_OP(OP_LCONV_TO_U, "long_conv_to_u") +MINI_OP(OP_LSHR_IMM, "long_shr_imm") +MINI_OP(OP_LSHR_UN_IMM, "long_shr_un_imm") +MINI_OP(OP_LSHL_IMM, "long_shl_imm") +MINI_OP(OP_LADD_IMM, "long_add_imm") +MINI_OP(OP_LSUB_IMM, "long_sub_imm") + +MINI_OP(OP_LBEQ, "long_beq") +MINI_OP(OP_LBNE_UN, "long_bne_un") +MINI_OP(OP_LBLT, "long_blt") +MINI_OP(OP_LBLT_UN, "long_blt_un") +MINI_OP(OP_LBGT, "long_bgt") +MINI_OP(OP_LBGT_UN, "long_btg_un") +MINI_OP(OP_LBGE, "long_bge") +MINI_OP(OP_LBGE_UN, "long_bge_un") +MINI_OP(OP_LBLE, "long_ble") +MINI_OP(OP_LBLE_UN, "long_ble_un") + +MINI_OP(OP_FBEQ, "float_beq") +MINI_OP(OP_FBNE_UN,"float_bne_un") +MINI_OP(OP_FBLT, "float_blt") +MINI_OP(OP_FBLT_UN,"float_blt_un") +MINI_OP(OP_FBGT, "float_bgt") +MINI_OP(OP_FBGT_UN,"float_btg_un") +MINI_OP(OP_FBGE, "float_bge") +MINI_OP(OP_FBGE_UN,"float_bge_un") +MINI_OP(OP_FBLE, "float_ble") +MINI_OP(OP_FBLE_UN,"float_ble_un") + +/* float opcodes: must be in the same order as the matching CEE_ opcodes: binops_op_map */ +MINI_OP(OP_FADD, "float_add") +MINI_OP(OP_FSUB, "float_sub") +MINI_OP(OP_FMUL, "float_mul") +MINI_OP(OP_FDIV, "float_div") +MINI_OP(OP_FDIV_UN,"float_div_un") +MINI_OP(OP_FREM, "float_rem") +MINI_OP(OP_FREM_UN,"float_rem_un") + +/* float opcodes: must be in the same order as the matching CEE_ opcodes: unops_op_map */ +MINI_OP(OP_FNEG, "float_neg") +MINI_OP(OP_FNOT, "float_not") +MINI_OP(OP_FCONV_TO_I1,"float_conv_to_i1") +MINI_OP(OP_FCONV_TO_I2,"float_conv_to_i2") +MINI_OP(OP_FCONV_TO_I4,"float_conv_to_i4") +MINI_OP(OP_FCONV_TO_I8,"float_conv_to_i8") +MINI_OP(OP_FCONV_TO_R4,"float_conv_to_r4") +MINI_OP(OP_FCONV_TO_R8,"float_conv_to_r8") +MINI_OP(OP_FCONV_TO_U4,"float_conv_to_u4") +MINI_OP(OP_FCONV_TO_U8,"float_conv_to_u8") + +MINI_OP(OP_FCONV_TO_U2, "float_conv_to_u2") +MINI_OP(OP_FCONV_TO_U1, "float_conv_to_u1") +MINI_OP(OP_FCONV_TO_I, "float_conv_to_i") +MINI_OP(OP_FCONV_TO_OVF_I,"float_conv_to_ovf_i") +MINI_OP(OP_FCONV_TO_OVF_U,"float_conv_to_ovd_u") +MINI_OP(OP_FADD_OVF, "float_add_ovf") +MINI_OP(OP_FADD_OVF_UN, "float_add_ovf_un") +MINI_OP(OP_FMUL_OVF, "float_mul_ovf") +MINI_OP(OP_FMUL_OVF_UN, "float_mul_ovf_un") +MINI_OP(OP_FSUB_OVF, "float_sub_ovf") +MINI_OP(OP_FSUB_OVF_UN, "float_sub_ovf_un") + +MINI_OP(OP_FCONV_TO_OVF_I1_UN,"float_conv_to_ovf_i1_un") +MINI_OP(OP_FCONV_TO_OVF_I2_UN,"float_conv_to_ovf_i2_un") +MINI_OP(OP_FCONV_TO_OVF_I4_UN,"float_conv_to_ovf_i4_un") +MINI_OP(OP_FCONV_TO_OVF_I8_UN,"float_conv_to_ovf_i8_un") +MINI_OP(OP_FCONV_TO_OVF_U1_UN,"float_conv_to_ovf_u1_un") +MINI_OP(OP_FCONV_TO_OVF_U2_UN,"float_conv_to_ovf_u2_un") +MINI_OP(OP_FCONV_TO_OVF_U4_UN,"float_conv_to_ovf_u4_un") +MINI_OP(OP_FCONV_TO_OVF_U8_UN,"float_conv_to_ovf_u8_un") +MINI_OP(OP_FCONV_TO_OVF_I_UN, "float_conv_to_ovf_i_un") +MINI_OP(OP_FCONV_TO_OVF_U_UN, "float_conv_to_ovf_u_un") + +MINI_OP(OP_FCONV_TO_OVF_I1,"float_conv_to_ovf_i1") +MINI_OP(OP_FCONV_TO_OVF_U1,"float_conv_to_ovf_u1") +MINI_OP(OP_FCONV_TO_OVF_I2,"float_conv_to_ovf_i2") +MINI_OP(OP_FCONV_TO_OVF_U2,"float_conv_to_ovf_u2") +MINI_OP(OP_FCONV_TO_OVF_I4,"float_conv_to_ovf_i4") +MINI_OP(OP_FCONV_TO_OVF_U4,"float_conv_to_ovf_u4") +MINI_OP(OP_FCONV_TO_OVF_I8,"float_conv_to_ovf_i8") +MINI_OP(OP_FCONV_TO_OVF_U8,"float_conv_to_ovf_u8") + +MINI_OP(OP_FCEQ, "float_ceq") +MINI_OP(OP_FCGT, "float_cgt") +MINI_OP(OP_FCGT_UN,"float_cgt_un") +MINI_OP(OP_FCLT, "float_clt") +MINI_OP(OP_FCLT_UN,"float_clt_un") + +MINI_OP(OP_FCONV_TO_U, "float_conv_to_u") + +/* aot compiler */ +MINI_OP(OP_AOTCONST, "aot_const") + +/* exception related opcodes */ +MINI_OP(OP_HANDLER , "handler") +MINI_OP(OP_ENDFILTER, "op_endfilter") + +/* opcodes most architecture have */ +MINI_OP(OP_ADC, "adc") +MINI_OP(OP_ADC_IMM, "adc_imm") +MINI_OP(OP_SBB, "sbb") +MINI_OP(OP_SBB_IMM, "sbb_imm") +MINI_OP(OP_ADDCC, "addcc") +MINI_OP(OP_SUBCC, "subcc") +MINI_OP(OP_BR_REG, "br_reg") + +/* FP functions usually done by the CPU */ +MINI_OP(OP_SIN, "sin") +MINI_OP(OP_COS, "cos") +MINI_OP(OP_ABS, "abs") +MINI_OP(OP_TAN, "tan") +MINI_OP(OP_ATAN, "atan") +MINI_OP(OP_SQRT, "sqrt") + +/* x86 specific */ +MINI_OP(OP_X86_TEST_NULL, "x86_test_null") +MINI_OP(OP_X86_COMPARE_MEMBASE_REG,"x86_compare_membase_reg") +MINI_OP(OP_X86_COMPARE_MEMBASE_IMM,"x86_compare_membase_imm") +MINI_OP(OP_X86_COMPARE_REG_MEMBASE,"x86_compare_reg_membase") +MINI_OP(OP_X86_INC_REG, "x86_inc_reg") +MINI_OP(OP_X86_INC_MEMBASE, "x86_inc_membase") +MINI_OP(OP_X86_DEC_REG, "x86_dec_reg") +MINI_OP(OP_X86_DEC_MEMBASE, "x86_dec_membase") +MINI_OP(OP_X86_ADD_MEMBASE_IMM, "x86_add_membase_imm") +MINI_OP(OP_X86_SUB_MEMBASE_IMM, "x86_sub_membase_imm") +MINI_OP(OP_X86_PUSH_MEMBASE, "x86_push_membase") +MINI_OP(OP_X86_PUSH_IMM, "x86_push_imm") +MINI_OP(OP_X86_PUSH, "x86_push") +MINI_OP(OP_X86_PUSH_FP, "x86_push_fp") +MINI_OP(OP_X86_PUSH_OBJ, "x86_push_obj") +MINI_OP(OP_X86_LEA, "x86_lea") +MINI_OP(OP_X86_XCHG, "x86_xchg") +MINI_OP(OP_X86_FPOP, "x86_fpop") +MINI_OP(OP_X86_FP_LOAD_I8, "x86_fp_load_i8") +MINI_OP(OP_X86_FP_LOAD_I4, "x86_fp_load_i4") + + + diff --git a/mono/mini/mini-ppc.c b/mono/mini/mini-ppc.c new file mode 100644 index 00000000000..fc8a3e81717 --- /dev/null +++ b/mono/mini/mini-ppc.c @@ -0,0 +1,2854 @@ +/* + * mini-ppc.c: PowerPC backend for the Mono code generator + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include "mini.h" +#include + +#include +#include + +#include "mini-ppc.h" +#include "inssel.h" +#include "regset.h" +#include "cpu-g4.h" + +int mono_exc_esp_offset = 0; + +const char* +mono_arch_regname (int reg) { + static const char * rnames[] = { + "ppc_r0", "ppc_sp", "ppc_r2", "ppc_r3", "ppc_r4", + "ppc_r5", "ppc_r6", "ppc_r7", "ppc_r8", "ppc_r9", + "ppc_r10", "ppc_r11", "ppc_r12", "ppc_r13", "ppc_r14", + "ppc_r15", "ppc_r16", "ppc_r17", "ppc_r18", "ppc_r19", + "ppc_r20", "ppc_r21", "ppc_r22", "ppc_r23", "ppc_r24", + "ppc_r25", "ppc_r26", "ppc_r27", "ppc_r28", "ppc_r29", + "ppc_r30", "ppc_r31" + }; + if (reg >= 0 && reg < 32) + return rnames [reg]; + return "unknown"; +} + +typedef struct { + guint16 size; + guint16 offset; + guint8 pad; +} MonoJitArgumentInfo; + +/* + * arch_get_argument_info: + * @csig: a method signature + * @param_count: the number of parameters to consider + * @arg_info: an array to store the result infos + * + * Gathers information on parameters such as size, alignment and + * padding. arg_info should be large enought to hold param_count + 1 entries. + * + * Returns the size of the activation frame. + */ +static int +arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info) +{ + int k, frame_size = 0; + int size, align, pad; + int offset = 8; + + if (MONO_TYPE_ISSTRUCT (csig->ret)) { + frame_size += sizeof (gpointer); + offset += 4; + } + + arg_info [0].offset = offset; + + if (csig->hasthis) { + frame_size += sizeof (gpointer); + offset += 4; + } + + arg_info [0].size = frame_size; + + for (k = 0; k < param_count; k++) { + + if (csig->pinvoke) + size = mono_type_native_stack_size (csig->params [k], &align); + else + size = mono_type_stack_size (csig->params [k], &align); + + /* ignore alignment for now */ + align = 1; + + frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); + arg_info [k].pad = pad; + frame_size += size; + arg_info [k + 1].pad = 0; + arg_info [k + 1].size = size; + offset += pad; + arg_info [k + 1].offset = offset; + offset += size; + } + + align = MONO_ARCH_FRAME_ALIGNMENT; + frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); + arg_info [k].pad = pad; + + return frame_size; +} + +static int indent_level = 0; + +static void indent (int diff) { + int v = indent_level; + while (v-- > 0) { + printf (". "); + } + indent_level += diff; +} + +static void +enter_method (MonoMethod *method, char *ebp) +{ + int i, j; + MonoClass *class; + MonoObject *o; + MonoJitArgumentInfo *arg_info; + MonoMethodSignature *sig; + char *fname; + + fname = mono_method_full_name (method, TRUE); + indent (1); + printf ("ENTER: %s(", fname); + g_free (fname); + + if (((int)ebp & (MONO_ARCH_FRAME_ALIGNMENT - 1)) != 0) { + g_error ("unaligned stack detected (%p)", ebp); + } + + sig = method->signature; + + arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1)); + + arch_get_argument_info (sig, sig->param_count, arg_info); + + if (MONO_TYPE_ISSTRUCT (method->signature->ret)) { + g_assert (!method->signature->ret->byref); + + printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8))); + } + + if (method->signature->hasthis) { + gpointer *this = (gpointer *)(ebp + arg_info [0].offset); + if (method->klass->valuetype) { + printf ("value:%p, ", *this); + } else { + o = *((MonoObject **)this); + + if (o) { + class = o->vtable->klass; + + if (class == mono_defaults.string_class) { + printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o)); + } else { + printf ("this:%p[%s.%s], ", o, class->name_space, class->name); + } + } else + printf ("this:NULL, "); + } + } + + for (i = 0; i < method->signature->param_count; ++i) { + gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset); + int size = arg_info [i + 1].size; + + MonoType *type = method->signature->params [i]; + + if (type->byref) { + printf ("[BYREF:%p], ", *cpos); + } else switch (type->type) { + + case MONO_TYPE_I: + case MONO_TYPE_U: + printf ("%p, ", (gpointer)*((int *)(cpos))); + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + printf ("%d, ", *((int *)(cpos))); + break; + case MONO_TYPE_STRING: { + MonoString *s = *((MonoString **)cpos); + if (s) { + g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class); + printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s)); + } else + printf ("[STRING:null], "); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: { + o = *((MonoObject **)cpos); + if (o) { + class = o->vtable->klass; + + if (class == mono_defaults.string_class) { + printf ("[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o)); + } else if (class == mono_defaults.int32_class) { + printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject))); + } else + printf ("[%s.%s:%p], ", class->name_space, class->name, o); + } else { + printf ("%p, ", *((gpointer *)(cpos))); + } + break; + } + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + printf ("%p, ", *((gpointer *)(cpos))); + break; + case MONO_TYPE_I8: + printf ("%lld, ", *((gint64 *)(cpos))); + break; + case MONO_TYPE_R4: + printf ("%f, ", *((float *)(cpos))); + break; + case MONO_TYPE_R8: + printf ("%f, ", *((double *)(cpos))); + break; + case MONO_TYPE_VALUETYPE: + printf ("["); + for (j = 0; j < size; j++) + printf ("%02x,", *((guint8*)cpos +j)); + printf ("], "); + break; + default: + printf ("XX, "); + } + } + + printf (")\n"); +} + +static void +leave_method (MonoMethod *method, ...) +{ + MonoType *type; + char *fname; + va_list ap; + + va_start(ap, method); + + fname = mono_method_full_name (method, TRUE); + indent (-1); + printf ("LEAVE: %s", fname); + g_free (fname); + + type = method->signature->ret; + +handle_enum: + switch (type->type) { + case MONO_TYPE_VOID: + break; + case MONO_TYPE_BOOLEAN: { + int eax = va_arg (ap, int); + if (eax) + printf ("TRUE:%d", eax); + else + printf ("FALSE"); + + break; + } + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: { + int eax = va_arg (ap, int); + printf ("EAX=%d", eax); + break; + } + case MONO_TYPE_STRING: { + MonoString *s = va_arg (ap, MonoString *); +; + if (s) { + g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class); + printf ("[STRING:%p:%s]", s, mono_string_to_utf8 (s)); + } else + printf ("[STRING:null], "); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: { + MonoObject *o = va_arg (ap, MonoObject *); + + if (o) { + if (o->vtable->klass == mono_defaults.boolean_class) { + printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject))); + } else if (o->vtable->klass == mono_defaults.int32_class) { + printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject)))); + } else if (o->vtable->klass == mono_defaults.int64_class) { + printf ("[INT64:%p:%lld]", o, *((gint64 *)((char *)o + sizeof (MonoObject)))); + } else + printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o); + } else + printf ("[OBJECT:%p]", o); + + break; + } + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + gpointer p = va_arg (ap, gpointer); + printf ("result=%p", p); + break; + } + case MONO_TYPE_I8: { + gint64 l = va_arg (ap, gint64); + printf ("lresult=%lld", l); + break; + } + case MONO_TYPE_R8: { + double f = va_arg (ap, double); + printf ("FP=%f\n", f); + break; + } + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + type = type->data.klass->enum_basetype; + goto handle_enum; + } else { + guint8 *p = va_arg (ap, gpointer); + int j, size, align; + size = mono_type_size (type, &align); + printf ("["); + for (j = 0; p && j < size; j++) + printf ("%02x,", p [j]); + printf ("]"); + } + break; + default: + printf ("(unknown return type %x)", method->signature->ret->type); + } + + printf ("\n"); +} + +static gboolean +is_regsize_var (MonoType *t) { + if (t->byref) + return TRUE; + switch (t->type) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + return TRUE; + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return FALSE; + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) + return is_regsize_var (t->data.klass->enum_basetype); + return FALSE; + } + return FALSE; +} + +GList * +mono_arch_get_allocatable_int_vars (MonoCompile *cfg) +{ + GList *vars = NULL; + int i; + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoInst *ins = cfg->varinfo [i]; + MonoMethodVar *vmv = MONO_VARINFO (cfg, i); + + /* unused vars */ + if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos) + continue; + + if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG)) + continue; + + /* we can only allocate 32 bit values */ + if (is_regsize_var (ins->inst_vtype)) { + g_assert (MONO_VARINFO (cfg, i)->reg == -1); + g_assert (i == vmv->idx); + vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE); + } + } + + return vars; +} + +GList * +mono_arch_get_global_int_regs (void) +{ + GList *regs = NULL; + int i; + + for (i = 13; i < 32; ++i) + regs = g_list_prepend (regs, GUINT_TO_POINTER (i)); + + return regs; +} + +// code from ppc/tramp.c, try to keep in sync +#define MIN_CACHE_LINE 8 + +void +mono_arch_flush_icache (guint8 *code, gint size) +{ + guint i; + guint8 *p; + + p = code; + for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) { + asm ("dcbst 0,%0;" : : "r"(p) : "memory"); + } + asm ("sync"); + p = code; + for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) { + asm ("icbi 0,%0; sync;" : : "r"(p) : "memory"); + } + asm ("sync"); + asm ("isync"); +} + +#define NOT_IMPLEMENTED(x) \ + g_error ("FIXME: %s is not yet implemented. (trampoline)", x); + +#define PROLOG_INS 8 +#define CALL_INS 2 +#define EPILOG_INS 6 +#define FLOAT_REGS 8 +#define GENERAL_REGS 8 +#ifdef __APPLE__ +#define MINIMAL_STACK_SIZE 10 +#define ALWAYS_ON_STACK(s) s +#define FP_ALSO_IN_REG(s) s +#define RET_ADDR_OFFSET 8 +#define STACK_PARAM_OFFSET 24 +#else +#define MINIMAL_STACK_SIZE 5 +#define ALWAYS_ON_STACK(s) +#define FP_ALSO_IN_REG(s) s +#define ALIGN_DOUBLES +#define RET_ADDR_OFFSET 4 +#define STACK_PARAM_OFFSET 8 +#endif + +typedef struct { + gint16 offset; + gint8 reg; + gint8 regtype; /* 0 general, 1 basereg, 2 floating point register */ +} ArgInfo; + +typedef struct { + int nargs; + guint32 stack_usage; + ArgInfo ret; + ArgInfo args [1]; +} CallInfo; + +#define DEBUG(a) + +static void inline +add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple) +{ + if (simple) { + if (*gr >= 3 + GENERAL_REGS) { + ainfo->offset = *stack_size; + ainfo->reg = ppc_sp; /* in the caller */ + ainfo->regtype = 1; + *stack_size += 4; + } else { + ALWAYS_ON_STACK (*stack_size += 4); + ainfo->reg = *gr; + } + } else { + if (*gr >= 3 + GENERAL_REGS - 1) { + ainfo->offset = *stack_size; + ainfo->reg = ppc_sp; /* in the caller */ + ainfo->regtype = 1; + *stack_size += 8; +#ifdef ALIGN_DOUBLES + *stack_size += (*stack_size % 8); +#endif + } else { + ALWAYS_ON_STACK (*stack_size += 8); + ainfo->reg = *gr; + } +#ifdef ALIGN_DOUBLES + if ((*gr) & 1) + (*gr) ++; +#endif + (*gr) ++; + } + (*gr) ++; +} + +static CallInfo* +calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke) +{ + guint i, fr, gr; + int n = sig->hasthis + sig->param_count; + guint32 simpletype; + guint32 stack_size = 0; + CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n); + + fr = 1; + gr = 3; + + /* FIXME: handle returning a struct */ + + n = 0; + if (sig->hasthis) { + add_general (&gr, &stack_size, cinfo->args + n, TRUE); + n++; + } + DEBUG(printf("params: %d\n", sig->param_count)); + for (i = 0; i < sig->param_count; ++i) { + DEBUG(printf("param %d: ", i)); + if (sig->params [i]->byref) { + DEBUG(printf("byref\n")); + add_general (&gr, &stack_size, cinfo->args + n, TRUE); + n++; + continue; + } + simpletype = sig->params [i]->type; + enum_calc_size: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + add_general (&gr, &stack_size, cinfo->args + n, TRUE); + n++; + break; + case MONO_TYPE_VALUETYPE: { + gint size; + if (sig->params [i]->data.klass->enumtype) { + simpletype = sig->params [i]->data.klass->enum_basetype->type; + goto enum_calc_size; + } +#if 0 + size = mono_class_value_size (sig->params [i]->data.klass, NULL); + if (size != 4) { + DEBUG(printf ("copy %d bytes struct on stack\n", + mono_class_value_size (sig->params [i]->data.klass, NULL))); + *stack_size += (size + 3) & (~3); + if (gr > 3 + GENERAL_REGS) { + *stack_size += 4; + } + } else { + DEBUG(printf ("load %d bytes struct\n", + mono_class_value_size (sig->params [i]->data.klass, NULL))); + add_general (&gr, stack_size, code_size, TRUE); + } +#endif + g_assert_not_reached (); + break; + } + case MONO_TYPE_U8: + case MONO_TYPE_I8: + add_general (&gr, &stack_size, cinfo->args + n, FALSE); + n++; + break; + case MONO_TYPE_R4: + if (fr < 7) { + fr ++; + FP_ALSO_IN_REG (gr ++); + ALWAYS_ON_STACK (stack_size += 4); + } else { + NOT_IMPLEMENTED ("R4 arg"); + } + n++; + break; + case MONO_TYPE_R8: + if (fr < 7) { + fr ++; + FP_ALSO_IN_REG (gr += 2); + ALWAYS_ON_STACK (stack_size += 8); + } else { + NOT_IMPLEMENTED ("R8 arg"); + } + n++; + break; + default: + g_error ("Can't trampoline 0x%x", sig->params [i]->type); + } + } + + { + simpletype = sig->ret->type; +enum_retvalue: + switch (simpletype) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + case MONO_TYPE_STRING: + cinfo->ret.reg = ppc_r3; + break; + case MONO_TYPE_U8: + case MONO_TYPE_I8: + cinfo->ret.reg = ppc_r3; + break; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + cinfo->ret.reg = ppc_f1; + cinfo->ret.regtype = 2; + break; + case MONO_TYPE_VALUETYPE: + if (sig->ret->data.klass->enumtype) { + simpletype = sig->ret->data.klass->enum_basetype->type; + goto enum_retvalue; + } + break; + case MONO_TYPE_VOID: + break; + default: + g_error ("Can't handle as return value 0x%x", sig->ret->type); + } + } + + /* align stack size to 16 */ + DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size)); + stack_size = (stack_size + 15) & ~15; + + cinfo->stack_usage = stack_size; + return cinfo; +} + + +/* + * Set var information according to the calling convention. ppc version. + * The locals var stuff should most likely be split in another method. + */ +void +mono_arch_allocate_vars (MonoCompile *m) +{ + MonoMethodSignature *sig; + MonoMethodHeader *header; + MonoInst *inst; + int i, offset, size, align, curinst; + + header = ((MonoMethodNormal *)m->method)->header; + + sig = m->method->signature; + + offset = 0; + curinst = 0; + if (MONO_TYPE_ISSTRUCT (sig->ret)) { + m->ret->opcode = OP_REGVAR; + m->ret->inst_c0 = ppc_r3; + } else { + /* FIXME: handle long and FP values */ + switch (sig->ret->type) { + case MONO_TYPE_VOID: + break; + default: + m->ret->opcode = OP_REGVAR; + m->ret->inst_c0 = ppc_r3; + break; + } + } + /* local vars are at a positive offset from the stack pointer */ + /* + * also note that if the function uses alloca, we use ppc_r31 + * to point at the local variables. + */ + offset = 12; /* linkage area */ + /* align the offset to 16 bytes: not sure this is needed here */ + offset += 16 - 1; + offset &= ~(16 - 1); + + /* add parameter area size for called functions */ + offset += m->param_area; + offset += 16 - 1; + offset &= ~(16 - 1); + + /* FIXME: check how to handle this stuff... reserve space to save LMF and caller saved registers */ + offset += sizeof (MonoLMF); + +#if 0 + /* this stuff should not be needed on ppc and the new jit, + * because a call on ppc to the handlers doesn't change the + * stack pointer and the jist doesn't manipulate the stack pointer + * for operations involving valuetypes. + */ + /* reserve space to store the esp */ + offset += sizeof (gpointer); + + /* this is a global constant */ + mono_exc_esp_offset = offset; +#endif + + curinst = m->locals_start; + for (i = curinst; i < m->num_varinfo; ++i) { + inst = m->varinfo [i]; + if (inst->opcode == OP_REGVAR) + continue; + + /* inst->unused indicates native sized value types, this is used by the + * pinvoke wrappers when they call functions returning structure */ + if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype)) + size = mono_class_native_size (inst->inst_vtype->data.klass, &align); + else + size = mono_type_size (inst->inst_vtype, &align); + + offset += align - 1; + offset &= ~(align - 1); + inst->inst_offset = offset; + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = ppc_sp; + offset += size; + //g_print ("allocating local %d to %d\n", i, inst->inst_offset); + } + + curinst = 0; + if (sig->hasthis) { + inst = m->varinfo [curinst]; + if (inst->opcode != OP_REGVAR) { + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = ppc_sp; + offset += sizeof (gpointer) - 1; + offset &= ~(sizeof (gpointer) - 1); + inst->inst_offset = offset; + offset += sizeof (gpointer); + } + curinst++; + } + + for (i = 0; i < sig->param_count; ++i) { + inst = m->varinfo [curinst]; + if (inst->opcode != OP_REGVAR) { + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = ppc_sp; + size = mono_type_size (sig->params [i], &align); + offset += align - 1; + offset &= ~(align - 1); + inst->inst_offset = offset; + offset += size; + } + curinst++; + } + + /* align the offset to 16 bytes */ + offset += 16 - 1; + offset &= ~(16 - 1); + + /* change sign? */ + m->stack_offset = offset; + +} + +/* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode, + * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info + */ + +/* + * take the arguments and generate the arch-specific + * instructions to properly call the function in call. + * This includes pushing, moving arguments to the right register + * etc. + * Issue: who does the spilling if needed, and when? + */ +MonoCallInst* +mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) { + MonoInst *arg, *in, **new_args; + MonoMethodSignature *sig; + int i, n, type; + MonoType *ptype; + CallInfo *cinfo; + ArgInfo *ainfo; + + sig = call->signature; + n = sig->param_count + sig->hasthis; + new_args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n); + + cinfo = calculate_sizes (sig, sig->pinvoke); + + for (i = 0; i < n; ++i) { + ainfo = cinfo->args + i; + if (is_virtual && i == 0) { + /* the argument will be attached to the call instrucion */ + new_args [n] = arg = NULL; + in = call->args [i]; + } else { + MONO_INST_NEW (cfg, arg, OP_OUTARG); + in = call->args [i]; + arg->cil_code = in->cil_code; + arg->inst_left = in; + arg->type = in->type; + new_args [i] = arg; + if (ainfo->regtype == 0) { + arg->unused = ainfo->reg; + call->used_iregs |= 1 << ainfo->reg; + } else if (ainfo->regtype == 1) { + g_assert_not_reached (); + } else if (ainfo->regtype == 2) { + arg->opcode = OP_OUTARG_R8; + arg->unused = ainfo->reg; + call->used_fregs |= 1 << ainfo->reg; + } else { + g_assert_not_reached (); + } + } + } + call->args = new_args; + call->stack_usage = cinfo->stack_usage; + cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage); + cfg->flags |= MONO_CFG_HAS_CALLS; + /* + * should set more info in call, such as the stack space + * used by the args that needs to be added back to esp + */ + + g_free (cinfo); + return call; +} + +/* + * Allow tracing to work with this interface (with an optional argument) + */ + +/* + * This may be needed on some archs or for debugging support. + */ +void +mono_arch_instrument_mem_needs (MonoMethod *method, int *stack, int *code) +{ + /* no stack room needed now (may be needed for FASTCALL-trace support) */ + *stack = 0; + /* split prolog-epilog requirements? */ + *code = 50; /* max bytes needed: check this number */ +} + +void* +mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) +{ + guchar *code = p; +#if 0 + /* if some args are passed in registers, we need to save them here */ + x86_push_reg (code, X86_EBP); + x86_push_imm (code, cfg->method); + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); + x86_call_code (code, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); +#endif + return code; +} + +enum { + SAVE_NONE, + SAVE_STRUCT, + SAVE_ONE, + SAVE_TWO, + SAVE_FP +}; + +void* +mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) +{ + guchar *code = p; + int arg_size = 0, save_mode = SAVE_NONE; + MonoMethod *method = cfg->method; + int rtype = method->signature->ret->type; + +handle_enum: + switch (rtype) { + case MONO_TYPE_VOID: + /* special case string .ctor icall */ + if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class) + save_mode = SAVE_ONE; + else + save_mode = SAVE_NONE; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + save_mode = SAVE_TWO; + break; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + save_mode = SAVE_FP; + break; + case MONO_TYPE_VALUETYPE: + if (method->signature->ret->data.klass->enumtype) { + rtype = method->signature->ret->data.klass->enum_basetype->type; + goto handle_enum; + } + save_mode = SAVE_STRUCT; + break; + default: + save_mode = SAVE_ONE; + break; + } + + switch (save_mode) { + case SAVE_TWO: + //x86_push_reg (code, X86_EDX); + //x86_push_reg (code, X86_EAX); + if (enable_arguments) { + //x86_push_reg (code, X86_EDX); + //x86_push_reg (code, X86_EAX); + arg_size = 8; + } + break; + case SAVE_ONE: + //x86_push_reg (code, X86_EAX); + if (enable_arguments) { + //x86_push_reg (code, X86_EAX); + arg_size = 4; + } + break; + case SAVE_FP: + //x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + ///x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE); + if (enable_arguments) { + //x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + //x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE); + arg_size = 8; + } + break; + case SAVE_STRUCT: + if (enable_arguments) { + //x86_push_membase (code, X86_EBP, 8); + arg_size = 4; + } + break; + case SAVE_NONE: + default: + break; + } + + /*x86_push_imm (code, method); + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); + x86_call_code (code, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, arg_size + 4); + */ + + switch (save_mode) { + case SAVE_TWO: + //x86_pop_reg (code, X86_EAX); + //x86_pop_reg (code, X86_EDX); + break; + case SAVE_ONE: + //x86_pop_reg (code, X86_EAX); + break; + case SAVE_FP: + //x86_fld_membase (code, X86_ESP, 0, TRUE); + //x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); + break; + case SAVE_NONE: + default: + break; + } + + return code; +} + +#define EMIT_COND_BRANCH(ins,cond) \ +if (ins->flags & MONO_INST_BRLABEL) { \ + if (ins->inst_i0->inst_c0) { \ + ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffff); \ + } else { \ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \ + ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], 0); \ + } \ +} else { \ + if (0 && ins->inst_true_bb->native_offset) { \ + ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffff); \ + } else { \ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \ + ppc_bc (code, branch_b0_table [cond], branch_b1_table [cond], 0); \ + } \ +} + +/* emit an exception if condition is fail */ +#define EMIT_COND_SYSTEM_EXCEPTION(cond,signed,exc_name) \ + do { \ + mono_add_patch_info (cfg, code - cfg->native_code, \ + MONO_PATCH_INFO_EXC, exc_name); \ + x86_branch32 (code, cond, 0, signed); \ + } while (0); + +#define EMIT_FPCOMPARE(code) do { \ + x86_fcompp (code); \ + x86_fnstsw (code); \ + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); \ +} while (0); + +static void +peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins, *last_ins = NULL; + ins = bb->code; + + while (ins) { + + switch (ins->opcode) { + case OP_MUL_IMM: + /* remove unnecessary multiplication with 1 */ + if (ins->inst_imm == 1) { + if (ins->dreg != ins->sreg1) { + ins->opcode = OP_MOVE; + } else { + last_ins->next = ins->next; + ins = ins->next; + continue; + } + } + break; + case OP_LOAD_MEMBASE: + case OP_LOADI4_MEMBASE: + /* + * OP_STORE_MEMBASE_REG reg, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg + */ + if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG + || last_ins->opcode == OP_STORE_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + + /* + * Note: reg1 must be different from the basereg in the second load + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_MOVE reg1, reg2 + */ + } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE + || last_ins->opcode == OP_LOAD_MEMBASE) && + ins->inst_basereg != last_ins->dreg && + ins->inst_basereg == last_ins->inst_basereg && + ins->inst_offset == last_ins->inst_offset) { + + if (ins->dreg == last_ins->dreg) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->dreg; + } + + //g_assert_not_reached (); + +#if 0 + /* + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg + * --> + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_ICONST reg, imm + */ + } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM + || last_ins->opcode == OP_STORE_MEMBASE_IMM) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_ICONST; + ins->inst_c0 = last_ins->inst_imm; + g_assert_not_reached (); // check this rule +#endif + } + break; + case OP_LOADU1_MEMBASE: + case OP_LOADI1_MEMBASE: + if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + } + break; + case OP_LOADU2_MEMBASE: + case OP_LOADI2_MEMBASE: + if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + } + break; + case CEE_CONV_I4: + case CEE_CONV_U4: + case OP_MOVE: + /* + * OP_MOVE reg, reg + */ + if (ins->dreg == ins->sreg1) { + if (last_ins) + last_ins->next = ins->next; + ins = ins->next; + continue; + } + /* + * OP_MOVE sreg, dreg + * OP_MOVE dreg, sreg + */ + if (last_ins && last_ins->opcode == OP_MOVE && + ins->sreg1 == last_ins->dreg && + ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } + break; + } + last_ins = ins; + ins = ins->next; + } + bb->last_ins = last_ins; +} + +/* + * the branch_b0_table should maintain the order of these + * opcodes. +case CEE_BEQ: +case CEE_BGE: +case CEE_BGT: +case CEE_BLE: +case CEE_BLT: +case CEE_BNE_UN: +case CEE_BGE_UN: +case CEE_BGT_UN: +case CEE_BLE_UN: +case CEE_BLT_UN: + */ +static const guchar +branch_b0_table [] = { + PPC_BR_TRUE, + PPC_BR_FALSE, + PPC_BR_TRUE, + PPC_BR_FALSE, + PPC_BR_TRUE, + + PPC_BR_FALSE, + PPC_BR_FALSE, + PPC_BR_TRUE, + PPC_BR_FALSE, + PPC_BR_TRUE +}; + +static const guchar +branch_b1_table [] = { + PPC_BR_EQ, + PPC_BR_LT, + PPC_BR_GT, + PPC_BR_GT, + PPC_BR_LT, + + PPC_BR_EQ, + PPC_BR_LT, + PPC_BR_GT, + PPC_BR_GT, + PPC_BR_LT +}; + +#undef DEBUG +#define DEBUG(a) if (cfg->verbose_level > 1) a +//#define DEBUG(a) +#define reg_is_freeable(r) ((r) >= 3 && (r) <= 10) + +typedef struct { + int born_in; + int killed_in; + int last_use; + int prev_use; +} RegTrack; + +static const char*const * ins_spec = ppcg4; + +static void +print_ins (int i, MonoInst *ins) +{ + const char *spec = ins_spec [ins->opcode]; + g_print ("\t%-2d %s", i, mono_inst_name (ins->opcode)); + if (spec [MONO_INST_DEST]) { + if (ins->dreg >= MONO_MAX_IREGS) + g_print (" R%d <-", ins->dreg); + else + g_print (" %s <-", mono_arch_regname (ins->dreg)); + } + if (spec [MONO_INST_SRC1]) { + if (ins->sreg1 >= MONO_MAX_IREGS) + g_print (" R%d", ins->sreg1); + else + g_print (" %s", mono_arch_regname (ins->sreg1)); + } + if (spec [MONO_INST_SRC2]) { + if (ins->sreg2 >= MONO_MAX_IREGS) + g_print (" R%d", ins->sreg2); + else + g_print (" %s", mono_arch_regname (ins->sreg2)); + } + if (spec [MONO_INST_CLOB]) + g_print (" clobbers: %c", spec [MONO_INST_CLOB]); + g_print ("\n"); +} + +static void +print_regtrack (RegTrack *t, int num) +{ + int i; + char buf [32]; + const char *r; + + for (i = 0; i < num; ++i) { + if (!t [i].born_in) + continue; + if (i >= MONO_MAX_IREGS) { + g_snprintf (buf, sizeof(buf), "R%d", i); + r = buf; + } else + r = mono_arch_regname (i); + g_print ("liveness: %s [%d - %d]\n", r, t [i].born_in, t[i].last_use); + } +} + +typedef struct InstList InstList; + +struct InstList { + InstList *prev; + InstList *next; + MonoInst *data; +}; + +static inline InstList* +inst_list_prepend (MonoMemPool *pool, InstList *list, MonoInst *data) +{ + InstList *item = mono_mempool_alloc (pool, sizeof (InstList)); + item->data = data; + item->prev = NULL; + item->next = list; + if (list) + list->prev = item; + return item; +} + +/* + * Force the spilling of the variable in the symbolic register 'reg'. + */ +static int +get_register_force_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, int reg) +{ + MonoInst *load; + int i, sel, spill; + + sel = cfg->rs->iassign [reg]; + /*i = cfg->rs->isymbolic [sel]; + g_assert (i == reg);*/ + i = reg; + spill = ++cfg->spill_count; + cfg->rs->iassign [i] = -spill - 1; + mono_regstate_free_int (cfg->rs, sel); + /* we need to create a spill var and insert a load to sel after the current instruction */ + MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE); + load->dreg = sel; + load->inst_basereg = ppc_sp; + load->inst_offset = mono_spillvar_offset (cfg, spill); + if (item->prev) { + while (ins->next != item->prev->data) + ins = ins->next; + } + load->next = ins->next; + ins->next = load; + DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%sp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel))); + i = mono_regstate_alloc_int (cfg->rs, 1 << sel); + g_assert (i == sel); + + return sel; +} + +static int +get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32 regmask, int reg) +{ + MonoInst *load; + int i, sel, spill; + + DEBUG (g_print ("start regmask to assign R%d: 0x%08x (R%d <- R%d R%d)\n", reg, regmask, ins->dreg, ins->sreg1, ins->sreg2)); + /* exclude the registers in the current instruction */ + if (reg != ins->sreg1 && (reg_is_freeable (ins->sreg1) || (ins->sreg1 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg1] >= 0))) { + if (ins->sreg1 >= MONO_MAX_IREGS) + regmask &= ~ (1 << cfg->rs->iassign [ins->sreg1]); + else + regmask &= ~ (1 << ins->sreg1); + DEBUG (g_print ("excluding sreg1 %s\n", mono_arch_regname (ins->sreg1))); + } + if (reg != ins->sreg2 && (reg_is_freeable (ins->sreg2) || (ins->sreg2 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg2] >= 0))) { + if (ins->sreg2 >= MONO_MAX_IREGS) + regmask &= ~ (1 << cfg->rs->iassign [ins->sreg2]); + else + regmask &= ~ (1 << ins->sreg2); + DEBUG (g_print ("excluding sreg2 %s %d\n", mono_arch_regname (ins->sreg2), ins->sreg2)); + } + if (reg != ins->dreg && reg_is_freeable (ins->dreg)) { + regmask &= ~ (1 << ins->dreg); + DEBUG (g_print ("excluding dreg %s\n", mono_arch_regname (ins->dreg))); + } + + DEBUG (g_print ("available regmask: 0x%08x\n", regmask)); + g_assert (regmask); /* need at least a register we can free */ + sel = -1; + /* we should track prev_use and spill the register that's farther */ + for (i = 0; i < MONO_MAX_IREGS; ++i) { + if (regmask & (1 << i)) { + sel = i; + DEBUG (g_print ("selected register %s has assignment %d\n", mono_arch_regname (sel), cfg->rs->iassign [sel])); + break; + } + } + i = cfg->rs->isymbolic [sel]; + spill = ++cfg->spill_count; + cfg->rs->iassign [i] = -spill - 1; + mono_regstate_free_int (cfg->rs, sel); + /* we need to create a spill var and insert a load to sel after the current instruction */ + MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE); + load->dreg = sel; + load->inst_basereg = ppc_sp; + load->inst_offset = mono_spillvar_offset (cfg, spill); + if (item->prev) { + while (ins->next != item->prev->data) + ins = ins->next; + } + load->next = ins->next; + ins->next = load; + DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%sp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel))); + i = mono_regstate_alloc_int (cfg->rs, 1 << sel); + g_assert (i == sel); + + return sel; +} + +static MonoInst* +create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins) +{ + MonoInst *copy; + MONO_INST_NEW (cfg, copy, OP_MOVE); + copy->dreg = dest; + copy->sreg1 = src; + if (ins) { + copy->next = ins->next; + ins->next = copy; + } + DEBUG (g_print ("\tforced copy from %s to %s\n", mono_arch_regname (src), mono_arch_regname (dest))); + return copy; +} + +static MonoInst* +create_spilled_store (MonoCompile *cfg, int spill, int reg, int prev_reg, MonoInst *ins) +{ + MonoInst *store; + MONO_INST_NEW (cfg, store, OP_STORE_MEMBASE_REG); + store->sreg1 = reg; + store->inst_destbasereg = ppc_r1; + store->inst_offset = mono_spillvar_offset (cfg, spill); + if (ins) { + store->next = ins->next; + ins->next = store; + } + DEBUG (g_print ("SPILLED STORE (%d at 0x%08x(%%sp)) R%d (from %s)\n", spill, store->inst_offset, prev_reg, mono_arch_regname (reg))); + return store; +} + +static void +insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert) +{ + MonoInst *prev; + g_assert (item->next); + prev = item->next->data; + + while (prev->next != ins) + prev = prev->next; + to_insert->next = ins; + prev->next = to_insert; + /* + * needed otherwise in the next instruction we can add an ins to the + * end and that would get past this instruction. + */ + item->data = to_insert; +} + +static int +alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, guint32 allow_mask) +{ + int val = cfg->rs->iassign [sym_reg]; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (cfg->rs, allow_mask); + if (val < 0) + val = get_register_spilling (cfg, curinst, ins, allow_mask, sym_reg); + cfg->rs->iassign [sym_reg] = val; + /* add option to store before the instruction for src registers */ + if (spill) + create_spilled_store (cfg, spill, val, sym_reg, ins); + } + cfg->rs->isymbolic [val] = sym_reg; + return val; +} + +/* use ppc_r3-ppc_310 as temp registers */ +#define PPC_CALLER_REGS (0xf<<3) + +/* + * Local register allocation. + * We first scan the list of instructions and we save the liveness info of + * each register (when the register is first used, when it's value is set etc.). + * We also reverse the list of instructions (in the InstList list) because assigning + * registers backwards allows for more tricks to be used. + */ +void +mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins; + MonoRegState *rs = cfg->rs; + int i, val, fpcount; + RegTrack *reginfo, *reginfof; + RegTrack *reginfo1, *reginfo2, *reginfod; + InstList *tmp, *reversed = NULL; + const char *spec; + guint32 src1_mask, src2_mask, dest_mask; + + if (!bb->code) + return; + rs->next_vireg = bb->max_ireg; + rs->next_vfreg = bb->max_freg; + mono_regstate_assign (rs); + reginfo = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vireg); + reginfof = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vfreg); + rs->ifree_mask = PPC_CALLER_REGS; + + ins = bb->code; + i = 1; + fpcount = 0; /* FIXME: track fp stack utilization */ + DEBUG (g_print ("LOCAL regalloc: basic block: %d\n", bb->block_num)); + /* forward pass on the instructions to collect register liveness info */ + while (ins) { + spec = ins_spec [ins->opcode]; + DEBUG (print_ins (i, ins)); + if (spec [MONO_INST_SRC1]) { + if (spec [MONO_INST_SRC1] == 'f') + reginfo1 = reginfof; + else + reginfo1 = reginfo; + reginfo1 [ins->sreg1].prev_use = reginfo1 [ins->sreg1].last_use; + reginfo1 [ins->sreg1].last_use = i; + } else { + ins->sreg1 = -1; + } + if (spec [MONO_INST_SRC2]) { + if (spec [MONO_INST_SRC2] == 'f') + reginfo2 = reginfof; + else + reginfo2 = reginfo; + reginfo2 [ins->sreg2].prev_use = reginfo2 [ins->sreg2].last_use; + reginfo2 [ins->sreg2].last_use = i; + } else { + ins->sreg2 = -1; + } + if (spec [MONO_INST_DEST]) { + if (spec [MONO_INST_DEST] == 'f') + reginfod = reginfof; + else + reginfod = reginfo; + if (spec [MONO_INST_DEST] != 'b') /* it's not just a base register */ + reginfod [ins->dreg].killed_in = i; + reginfod [ins->dreg].prev_use = reginfod [ins->dreg].last_use; + reginfod [ins->dreg].last_use = i; + if (reginfod [ins->dreg].born_in == 0 || reginfod [ins->dreg].born_in > i) + reginfod [ins->dreg].born_in = i; + if (spec [MONO_INST_DEST] == 'l') { + /* result in eax:edx, the virtual register is allocated sequentially */ + reginfod [ins->dreg + 1].prev_use = reginfod [ins->dreg + 1].last_use; + reginfod [ins->dreg + 1].last_use = i; + if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i) + reginfod [ins->dreg + 1].born_in = i; + } + } else { + ins->dreg = -1; + } + reversed = inst_list_prepend (cfg->mempool, reversed, ins); + ++i; + ins = ins->next; + } + + DEBUG (print_regtrack (reginfo, rs->next_vireg)); + DEBUG (print_regtrack (reginfof, rs->next_vfreg)); + tmp = reversed; + while (tmp) { + int prev_dreg, prev_sreg1, prev_sreg2; + dest_mask = src1_mask = src2_mask = PPC_CALLER_REGS; + --i; + ins = tmp->data; + spec = ins_spec [ins->opcode]; + DEBUG (g_print ("processing:")); + DEBUG (print_ins (i, ins)); + /* update for use with FP regs... */ + if (spec [MONO_INST_DEST] != 'f' && ins->dreg >= MONO_MAX_IREGS) { + val = rs->iassign [ins->dreg]; + prev_dreg = ins->dreg; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, dest_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg); + rs->iassign [ins->dreg] = val; + if (spill) + create_spilled_store (cfg, spill, val, prev_dreg, ins); + } + DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg)); + rs->isymbolic [val] = prev_dreg; + ins->dreg = val; + if (spec [MONO_INST_DEST] == 'l') { + int hreg = prev_dreg + 1; + val = rs->iassign [hreg]; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, dest_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, dest_mask, hreg); + rs->iassign [hreg] = val; + if (spill) + create_spilled_store (cfg, spill, val, hreg, ins); + } + DEBUG (g_print ("\tassigned hreg %s to dest R%d\n", mono_arch_regname (val), hreg)); + rs->isymbolic [val] = hreg; + /* FIXME:? ins->dreg = val; */ + if (ins->dreg == ppc_r3) { + if (val != ppc_r4) + create_copy_ins (cfg, val, ppc_r4, ins); + } else if (ins->dreg == ppc_r4) { + if (val == ppc_r3) { + /* swap */ + g_assert_not_reached (); + } else { + /* two forced copies */ + create_copy_ins (cfg, val, ppc_r4, ins); + create_copy_ins (cfg, ins->dreg, ppc_r3, ins); + } + } else { + if (val == ppc_r4) { + create_copy_ins (cfg, ins->dreg, ppc_r3, ins); + } else { + /* two forced copies */ + create_copy_ins (cfg, val, ppc_r4, ins); + create_copy_ins (cfg, ins->dreg, ppc_r3, ins); + } + } + if (reg_is_freeable (val) && hreg >= 0 && reginfo [hreg].born_in >= i) { + DEBUG (g_print ("\tfreeable %s (R%d)\n", mono_arch_regname (val), hreg)); + mono_regstate_free_int (rs, val); + } + } + } else { + prev_dreg = -1; + } + if (spec [MONO_INST_DEST] != 'f' && reg_is_freeable (ins->dreg) && prev_dreg >= 0 && reginfo [prev_dreg].born_in >= i) { + DEBUG (g_print ("\tfreeable %s (R%d) (born in %d)\n", mono_arch_regname (ins->dreg), prev_dreg, reginfo [prev_dreg].born_in)); + mono_regstate_free_int (rs, ins->dreg); + } + if (spec [MONO_INST_SRC1] != 'f' && ins->sreg1 >= MONO_MAX_IREGS) { + val = rs->iassign [ins->sreg1]; + prev_sreg1 = ins->sreg1; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + if (0 && ins->opcode == OP_MOVE) { + /* + * small optimization: the dest register is already allocated + * but the src one is not: we can simply assign the same register + * here and peephole will get rid of the instruction later. + * This optimization may interfere with the clobbering handling: + * it removes a mov operation that will be added again to handle clobbering. + * There are also some other issues that should with make testjit. + */ + mono_regstate_alloc_int (rs, 1 << ins->dreg); + val = rs->iassign [ins->sreg1] = ins->dreg; + //g_assert (val >= 0); + DEBUG (g_print ("\tfast assigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1)); + } else { + //g_assert (val == -1); /* source cannot be spilled */ + val = mono_regstate_alloc_int (rs, src1_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, src1_mask, ins->sreg1); + rs->iassign [ins->sreg1] = val; + DEBUG (g_print ("\tassigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1)); + } + if (spill) { + MonoInst *store = create_spilled_store (cfg, spill, val, prev_sreg1, NULL); + insert_before_ins (ins, tmp, store); + } + } + rs->isymbolic [val] = prev_sreg1; + ins->sreg1 = val; + } else { + prev_sreg1 = -1; + } + if (spec [MONO_INST_SRC2] != 'f' && ins->sreg2 >= MONO_MAX_IREGS) { + val = rs->iassign [ins->sreg2]; + prev_sreg2 = ins->sreg2; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, src2_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, src2_mask, ins->sreg2); + rs->iassign [ins->sreg2] = val; + DEBUG (g_print ("\tassigned sreg2 %s to R%d\n", mono_arch_regname (val), ins->sreg2)); + if (spill) + create_spilled_store (cfg, spill, val, prev_sreg2, ins); + } + rs->isymbolic [val] = prev_sreg2; + ins->sreg2 = val; + } else { + prev_sreg2 = -1; + } + + if (spec [MONO_INST_CLOB] == 'c') { + int j, s; + guint32 clob_mask = PPC_CALLER_REGS; + for (j = 0; j < MONO_MAX_IREGS; ++j) { + s = 1 << j; + if ((clob_mask & s) && !(rs->ifree_mask & s) && j != ins->sreg1) { + //g_warning ("register %s busy at call site\n", mono_arch_regname (j)); + } + } + } + /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) { + DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg1))); + mono_regstate_free_int (rs, ins->sreg1); + } + if (reg_is_freeable (ins->sreg2) && prev_sreg2 >= 0 && reginfo [prev_sreg2].born_in >= i) { + DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2))); + mono_regstate_free_int (rs, ins->sreg2); + }*/ + + //DEBUG (print_ins (i, ins)); + tmp = tmp->next; + } +} + +static guchar* +emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int size, gboolean is_signed) +{ + return code; +} + +static unsigned char* +mono_emit_stack_alloc (guchar *code, MonoInst* tree) +{ +#if 0 + int sreg = tree->sreg1; + x86_alu_reg_reg (code, X86_SUB, X86_ESP, tree->sreg1); + if (tree->flags & MONO_INST_INIT) { + int offset = 0; + if (tree->dreg != X86_EAX && sreg != X86_EAX) { + x86_push_reg (code, X86_EAX); + offset += 4; + } + if (tree->dreg != X86_ECX && sreg != X86_ECX) { + x86_push_reg (code, X86_ECX); + offset += 4; + } + if (tree->dreg != X86_EDI && sreg != X86_EDI) { + x86_push_reg (code, X86_EDI); + offset += 4; + } + + x86_shift_reg_imm (code, X86_SHR, sreg, 2); + if (sreg != X86_ECX) + x86_mov_reg_reg (code, X86_ECX, sreg, 4); + x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX); + + x86_lea_membase (code, X86_EDI, X86_ESP, offset); + x86_cld (code); + x86_prefix (code, X86_REP_PREFIX); + x86_stosl (code); + + if (tree->dreg != X86_EDI && sreg != X86_EDI) + x86_pop_reg (code, X86_EDI); + if (tree->dreg != X86_ECX && sreg != X86_ECX) + x86_pop_reg (code, X86_ECX); + if (tree->dreg != X86_EAX && sreg != X86_EAX) + x86_pop_reg (code, X86_EAX); + } +#endif + return code; +} + +void +ppc_patch (guchar *code, guchar *target) +{ + guint32 ins = *(guint32*)code; + guint32 prim = ins >> 26; + +// g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target); + if (prim == 18) { + // absolute address + if (ins & 2) { + guint32 li = (guint32)target; + ins = prim << 26 | (ins & 3); + ins |= li; + // FIXME: assert the top bits of li are 0 + } else { + gint diff = target - code; + ins = prim << 26 | (ins & 3); + diff &= ~3; + diff &= ~(63 << 26); + ins |= diff; + } + *(guint32*)code = ins; + } else if (prim == 16) { + // absolute address + if (ins & 2) { + guint32 li = (guint32)target; + ins = (ins & 0xffff0000) | (ins & 3); + li &= 0xffff; + ins |= li; + // FIXME: assert the top bits of li are 0 + } else { + gint diff = target - code; + ins = (ins & 0xffff0000) | (ins & 3); + diff &= 0xffff; + ins |= diff; + } + *(guint32*)code = ins; + } else { + g_assert_not_reached (); + } +// g_print ("patched with 0x%08x\n", ins); +} + +void +mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins; + MonoCallInst *call; + guint offset; + guint8 *code = cfg->native_code + cfg->code_len; + MonoInst *last_ins = NULL; + guint last_offset = 0; + int max_len, cpos; + + if (cfg->opt & MONO_OPT_PEEPHOLE) + peephole_pass (cfg, bb); + +#if 0 + /* + * various stratgies to align BBs. Using real loop detection or simply + * aligning every block leads to more consistent benchmark results, + * but usually slows down the code + * we should do the alignment outside this function or we should adjust + * bb->native offset as well or the code is effectively slowed down! + */ + /* align all blocks */ +// if ((pad = (cfg->code_len & (align - 1)))) { + /* poor man loop start detection */ +// if (bb->code && bb->in_count && bb->in_bb [0]->cil_code > bb->cil_code && (pad = (cfg->code_len & (align - 1)))) { + /* consider real loop detection and nesting level */ +// if (bb->loop_blocks && bb->nesting < 3 && (pad = (cfg->code_len & (align - 1)))) { + /* consider real loop detection */ + if (/*bb->loop_blocks &&*/ (pad = (cfg->code_len & (align - 1)))) { + pad = align - pad; + x86_padding (code, pad); + cfg->code_len += pad; + bb->native_offset = cfg->code_len; + } +#endif + + if (cfg->verbose_level > 2) + g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset); + + cpos = bb->max_offset; + + if (mono_trace_coverage) { + MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method); + g_assert (!mono_compile_aot); + cpos += 6; + if (bb->cil_code) + cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code; + /* this is not thread save, but good enough */ + /* fixme: howto handle overflows? */ + //x86_inc_mem (code, &cov->data [bb->dfn].count); + } + + ins = bb->code; + while (ins) { + offset = code - cfg->native_code; + + max_len = ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + + if (offset > (cfg->code_size - max_len - 16)) { + cfg->code_size *= 2; + cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + code = cfg->native_code + offset; + } + // if (ins->cil_code) + // g_print ("cil code\n"); + + switch (ins->opcode) { + case OP_STOREI1_MEMBASE_IMM: + ppc_li (code, ppc_r11, ins->inst_imm); + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_stb (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_STOREI2_MEMBASE_IMM: + ppc_li (code, ppc_r11, ins->inst_imm); + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_sth (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_STORE_MEMBASE_IMM: + case OP_STOREI4_MEMBASE_IMM: + ppc_load (code, ppc_r11, ins->inst_imm); + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_stw (code, ppc_r11, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_STOREI1_MEMBASE_REG: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_stb (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_STOREI2_MEMBASE_REG: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_sth (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_STORE_MEMBASE_REG: + case OP_STOREI4_MEMBASE_REG: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_stw (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg); + break; + case CEE_LDIND_I: + case CEE_LDIND_I4: + case CEE_LDIND_U4: + g_assert_not_reached (); + //x86_mov_reg_mem (code, ins->dreg, ins->inst_p0, 4); + break; + case OP_LOADU4_MEM: + g_assert_not_reached (); + //x86_mov_reg_imm (code, ins->dreg, ins->inst_p0); + //x86_mov_reg_membase (code, ins->dreg, ins->dreg, 0, 4); + break; + case OP_LOAD_MEMBASE: + case OP_LOADI4_MEMBASE: + case OP_LOADU4_MEMBASE: + if (ppc_is_imm16 (ins->inst_offset)) { + ppc_lwz (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + } else { + ppc_load (code, ppc_r11, ins->inst_offset); + ppc_lwz (code, ins->dreg, 0, ppc_r11); + } + break; + case OP_LOADU1_MEMBASE: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_lbz (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + break; + case OP_LOADI1_MEMBASE: + g_assert (ppc_is_imm16 (ins->inst_offset)); + // FIXME: sign extend + ppc_lbz (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + break; + case OP_LOADU2_MEMBASE: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_lhz (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + break; + case OP_LOADI2_MEMBASE: + g_assert (ppc_is_imm16 (ins->inst_offset)); + ppc_lha (code, ins->dreg, ins->inst_basereg, ins->inst_offset); + break; + case CEE_CONV_I1: + ppc_extsb (code, ins->dreg, ins->sreg1); + break; + case CEE_CONV_I2: + ppc_extsh (code, ins->dreg, ins->sreg1); + break; + case CEE_CONV_U1: + ppc_rlwinm (code, ins->dreg, ins->sreg1, 0, 24, 31); + break; + case CEE_CONV_U2: + ppc_rlwinm (code, ins->dreg, ins->sreg1, 0, 16, 31); + break; + case OP_COMPARE: + ppc_cmp (code, 0, 0, ins->sreg1, ins->sreg2); + break; + case OP_COMPARE_IMM: + if (ppc_is_imm16 (ins->inst_imm)) { + ppc_cmpi (code, 0, 0, ins->sreg1, (ins->inst_imm & 0xffff)); + } else { + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_cmp (code, 0, 0, ins->sreg1, ppc_r11); + } + break; + case OP_X86_TEST_NULL: + ppc_cmpi (code, 0, 0, ins->sreg1, 0); + break; + case CEE_BREAK: + ppc_break (code); + break; + case OP_ADDCC: + ppc_addc (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case CEE_ADD: + ppc_add (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_ADC: + ppc_adde (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_ADD_IMM: + if (ppc_is_imm16 (ins->inst_imm)) { + ppc_addi (code, ins->dreg, ins->sreg1, ins->inst_imm); + } else { + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_add (code, ins->dreg, ins->sreg1, ppc_r11); + } + break; + case OP_ADC_IMM: + g_assert_not_reached (); + //x86_alu_reg_imm (code, X86_ADC, ins->dreg, ins->inst_imm); + break; + case OP_SUBCC: + ppc_subfc (code, ins->dreg, ins->sreg2, ins->sreg1); + break; + case CEE_SUB: + ppc_subf (code, ins->dreg, ins->sreg2, ins->sreg1); + break; + case OP_SBB: + ppc_subfe (code, ins->dreg, ins->sreg2, ins->sreg1); + break; + case OP_SUB_IMM: + // we add the negated value + g_assert (ppc_is_imm16 (-ins->inst_imm)); + ppc_addi (code, ins->dreg, ins->sreg1, -ins->inst_imm); + break; + case OP_SBB_IMM: + g_assert_not_reached (); + //x86_alu_reg_imm (code, X86_SBB, ins->dreg, ins->inst_imm); + break; + case CEE_AND: + /* FIXME: the ppc macros as inconsistent here: put dest as the first arg! */ + ppc_and (code, ins->sreg1, ins->dreg, ins->sreg2); + break; + case OP_AND_IMM: + if (!(ins->inst_imm & 0xffff0000)) { + ppc_andid (code, ins->sreg1, ins->dreg, ins->inst_imm); + } else if (!(ins->inst_imm & 0xffff)) { + ppc_andisd (code, ins->sreg1, ins->dreg, ((guint32)ins->inst_imm >> 16)); + } else { + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_and (code, ins->sreg1, ins->dreg, ins->sreg2); + } + break; + case CEE_DIV: + ppc_divw (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case CEE_DIV_UN: + ppc_divwu (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_DIV_IMM: + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_divw (code, ins->dreg, ins->sreg1, ppc_r11); + break; + case CEE_REM: + ppc_divw (code, ppc_r11, ins->sreg1, ins->sreg2); + ppc_mullw (code, ppc_r11, ppc_r11, ins->sreg2); + ppc_subf (code, ins->dreg, ppc_r11, ins->sreg1); + break; + case CEE_REM_UN: + ppc_divwu (code, ppc_r11, ins->sreg1, ins->sreg2); + ppc_mullw (code, ppc_r11, ppc_r11, ins->sreg2); + ppc_subf (code, ins->dreg, ppc_r11, ins->sreg1); + break; + case OP_REM_IMM: + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_divw (code, ins->dreg, ins->sreg1, ppc_r11); + ppc_mullw (code, ins->dreg, ins->dreg, ppc_r11); + ppc_subf (code, ins->dreg, ins->dreg, ins->sreg1); + break; + case CEE_OR: + ppc_or (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_OR_IMM: + if (!(ins->inst_imm & 0xffff0000)) { + ppc_ori (code, ins->sreg1, ins->dreg, ins->inst_imm); + } else if (!(ins->inst_imm & 0xffff)) { + ppc_oris (code, ins->sreg1, ins->dreg, ((guint32)(ins->inst_imm) >> 16)); + } else { + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_or (code, ins->sreg1, ins->dreg, ins->sreg2); + } + break; + case CEE_XOR: + ppc_xor (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_XOR_IMM: + if (!(ins->inst_imm & 0xffff0000)) { + ppc_xori (code, ins->sreg1, ins->dreg, ins->inst_imm); + } else if (!(ins->inst_imm & 0xffff)) { + ppc_xoris (code, ins->sreg1, ins->dreg, ((guint32)(ins->inst_imm) >> 16)); + } else { + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_xor (code, ins->sreg1, ins->dreg, ins->sreg2); + } + break; + case CEE_SHL: + ppc_slw (code, ins->sreg1, ins->dreg, ins->sreg2); + break; + case OP_SHL_IMM: + ppc_rlwinm (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0xf), 0, (31 - (ins->inst_imm & 0xf))); + //ppc_load (code, ppc_r11, ins->inst_imm); + //ppc_slw (code, ins->sreg1, ins->dreg, ppc_r11); + break; + case CEE_SHR: + ppc_sraw (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_SHR_IMM: + // there is also ppc_srawi + //ppc_load (code, ppc_r11, ins->inst_imm); + //ppc_sraw (code, ins->dreg, ins->sreg1, ppc_r11); + ppc_srawi (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f)); + break; + case OP_SHR_UN_IMM: + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_srw (code, ins->dreg, ins->sreg1, ppc_r11); + //ppc_rlwinm (code, ins->dreg, ins->sreg1, (32 - (ins->inst_imm & 0xf)), (ins->inst_imm & 0xf), 31); + break; + case CEE_SHR_UN: + ppc_srw (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case CEE_NOT: + ppc_not (code, ins->dreg, ins->sreg1); + break; + case CEE_NEG: + ppc_neg (code, ins->dreg, ins->sreg1); + break; + case CEE_MUL: + ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_MUL_IMM: + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_mullw (code, ins->dreg, ins->sreg1, ppc_r11); + break; + case CEE_MUL_OVF: + ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2); + //g_assert_not_reached (); + //x86_imul_reg_reg (code, ins->sreg1, ins->sreg2); + //EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException"); + break; + case CEE_MUL_OVF_UN: + ppc_mullw (code, ins->dreg, ins->sreg1, ins->sreg2); + //FIXME: g_assert_not_reached (); + break; + case OP_ICONST: + case OP_SETREGIMM: + ppc_load (code, ins->dreg, ins->inst_c0); + break; + /*case OP_CLASS: + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_CLASS, (gpointer)ins->inst_c0); + ppc_load (code, ins->dreg, 0xff00ff00); + break; + case OP_IMAGE: + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_IMAGE, (gpointer)ins->inst_c0); + ppc_load (code, ins->dreg, 0xff00ff00); + break;*/ + case CEE_CONV_I4: + case CEE_CONV_U4: + case OP_MOVE: + case OP_SETREG: + ppc_mr (code, ins->dreg, ins->sreg1); + break; + case CEE_JMP: + g_assert_not_reached (); + break; + case OP_CHECK_THIS: + /* ensure ins->sreg1 is not NULL */ + g_assert_not_reached (); + //x86_alu_membase_imm (code, X86_CMP, ins->sreg1, 0, 0); + break; + case OP_FCALL: + case OP_LCALL: + case OP_VCALL: + case OP_VOIDCALL: + case CEE_CALL: + call = (MonoCallInst*)ins; + if (ins->flags & MONO_INST_HAS_METHOD) + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method); + else + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr); + ppc_bl (code, 0); + break; + case OP_FCALL_REG: + case OP_LCALL_REG: + case OP_VCALL_REG: + case OP_VOIDCALL_REG: + case OP_CALL_REG: + ppc_mtlr (code, ins->sreg1); + ppc_blrl (code); + break; + case OP_FCALL_MEMBASE: + case OP_LCALL_MEMBASE: + case OP_VCALL_MEMBASE: + case OP_VOIDCALL_MEMBASE: + case OP_CALL_MEMBASE: + ppc_lwz (code, ppc_r0, ins->inst_offset, ins->sreg1); + ppc_mtlr (code, ppc_r0); + ppc_blrl (code); + break; + case OP_OUTARG: + g_assert_not_reached (); + break; + case OP_LOCALLOC: + /* keep alignment */ +#define MONO_FRAME_ALIGNMENT 16 + g_assert_not_reached (); + break; + case CEE_RET: + ppc_blr (code); + break; + case CEE_THROW: { + ppc_mr (code, ppc_r3, ins->sreg1); + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"throw_exception"); + ppc_bl (code, 0); + break; + } + case OP_ENDFILTER: + if (ins->sreg1 != ppc_r3) + ppc_mr (code, ppc_r3, ins->sreg1); + ppc_blr (code); + break; + case CEE_ENDFINALLY: + ppc_blr (code); + break; + case OP_HANDLER: + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb); + ppc_bl (code, 0); + break; + case OP_LABEL: + ins->inst_c0 = code - cfg->native_code; + break; + case CEE_BR: + //g_print ("target: %p, next: %p, curr: %p, last: %p\n", ins->inst_target_bb, bb->next_bb, ins, bb->last_ins); + //if ((ins->inst_target_bb == bb->next_bb) && ins == bb->last_ins) + //break; + if (ins->flags & MONO_INST_BRLABEL) { + /*if (ins->inst_i0->inst_c0) { + ppc_b (code, 0); + //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0); + } else*/ { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0); + ppc_b (code, 0); + } + } else { + /*if (ins->inst_target_bb->native_offset) { + ppc_b (code, 0); + //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset); + } else*/ { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb); + ppc_b (code, 0); + } + } + break; + case OP_BR_REG: + ppc_mtctr (code, ins->sreg1); + ppc_bcctr (code, 20, 0); + break; + case OP_CEQ: + ppc_li (code, ins->dreg, 0); + ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 2); + ppc_li (code, ins->dreg, 1); + break; + case OP_CLT: + case OP_CLT_UN: + ppc_li (code, ins->dreg, 1); + ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2); + ppc_li (code, ins->dreg, 0); + break; + case OP_CGT: + case OP_CGT_UN: + ppc_li (code, ins->dreg, 1); + ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2); + ppc_li (code, ins->dreg, 0); + break; + case OP_COND_EXC_EQ: + case OP_COND_EXC_NE_UN: + case OP_COND_EXC_LT: + case OP_COND_EXC_LT_UN: + case OP_COND_EXC_GT: + case OP_COND_EXC_GT_UN: + case OP_COND_EXC_GE: + case OP_COND_EXC_GE_UN: + case OP_COND_EXC_LE: + case OP_COND_EXC_LE_UN: + case OP_COND_EXC_OV: + case OP_COND_EXC_NO: + case OP_COND_EXC_C: + case OP_COND_EXC_NC: + //EMIT_COND_SYSTEM_EXCEPTION (branch_cc_table [ins->opcode - OP_COND_EXC_EQ], + // (ins->opcode < OP_COND_EXC_NE_UN), ins->inst_p1); + break; + case CEE_BEQ: + case CEE_BNE_UN: + case CEE_BLT: + case CEE_BLT_UN: + case CEE_BGT: + case CEE_BGT_UN: + case CEE_BGE: + case CEE_BGE_UN: + case CEE_BLE: + case CEE_BLE_UN: + EMIT_COND_BRANCH (ins, ins->opcode - CEE_BEQ); + break; + + /* floating point opcodes */ + case OP_R8CONST: + ppc_load (code, ppc_r11, ins->inst_p0); + ppc_lfd (code, ins->dreg, 0, ppc_r11); + break; + case OP_R4CONST: + ppc_load (code, ppc_r11, ins->inst_p0); + ppc_lfs (code, ins->dreg, 0, ppc_r11); + break; + case OP_STORER8_MEMBASE_REG: + ppc_stfd (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_LOADR8_MEMBASE: + ppc_lfd (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + break; + case OP_STORER4_MEMBASE_REG: + ppc_stfs (code, ins->sreg1, ins->inst_offset, ins->inst_destbasereg); + break; + case OP_LOADR4_MEMBASE: + ppc_lfs (code, ins->dreg, ins->inst_offset, ins->inst_basereg); + break; + case CEE_CONV_R4: /* FIXME: change precision */ + case CEE_CONV_R8: + g_assert_not_reached (); + x86_push_reg (code, ins->sreg1); + x86_fild_membase (code, X86_ESP, 0, FALSE); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + break; + case OP_X86_FP_LOAD_I8: + g_assert_not_reached (); + x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, TRUE); + break; + case OP_X86_FP_LOAD_I4: + g_assert_not_reached (); + x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, FALSE); + break; + case OP_FCONV_TO_I1: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 1, TRUE); + break; + case OP_FCONV_TO_U1: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 1, FALSE); + break; + case OP_FCONV_TO_I2: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 2, TRUE); + break; + case OP_FCONV_TO_U2: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 2, FALSE); + break; + case OP_FCONV_TO_I4: + case OP_FCONV_TO_I: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 4, TRUE); + break; + case OP_FCONV_TO_U4: + case OP_FCONV_TO_U: + g_assert_not_reached (); + code = emit_float_to_int (cfg, code, ins->dreg, 4, FALSE); + break; + case OP_FCONV_TO_I8: + case OP_FCONV_TO_U8: + g_assert_not_reached (); + /*x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + x86_fnstcw_membase(code, X86_ESP, 0); + x86_mov_reg_membase (code, ins->inst_dreg_low, X86_ESP, 0, 2); + x86_alu_reg_imm (code, X86_OR, ins->inst_dreg_low, 0xc00); + x86_mov_membase_reg (code, X86_ESP, 2, ins->inst_dreg_low, 2); + x86_fldcw_membase (code, X86_ESP, 2); + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fist_pop_membase (code, X86_ESP, 0, TRUE); + x86_pop_reg (code, ins->inst_dreg_low); + x86_pop_reg (code, ins->inst_dreg_high); + x86_fldcw_membase (code, X86_ESP, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);*/ + break; + case OP_LCONV_TO_R_UN: { +#if 0 + static guint8 mn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x40 }; + guint8 *br; + + /* load 64bit integer to FP stack */ + x86_push_imm (code, 0); + x86_push_reg (code, ins->sreg2); + x86_push_reg (code, ins->sreg1); + x86_fild_membase (code, X86_ESP, 0, TRUE); + /* store as 80bit FP value */ + x86_fst80_membase (code, X86_ESP, 0); + + /* test if lreg is negative */ + x86_test_reg_reg (code, ins->sreg2, ins->sreg2); + br = code; x86_branch8 (code, X86_CC_GEZ, 0, TRUE); + + /* add correction constant mn */ + x86_fld80_mem (code, mn); + x86_fld80_membase (code, X86_ESP, 0); + x86_fp_op_reg (code, X86_FADD, 1, TRUE); + x86_fst80_membase (code, X86_ESP, 0); + + x86_patch (br, code); + + x86_fld80_membase (code, X86_ESP, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); +#endif + g_assert_not_reached (); + break; + } + case OP_LCONV_TO_OVF_I: { +#if 0 + guint8 *br [3], *label [1]; + + /* + * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000 + */ + x86_test_reg_reg (code, ins->sreg1, ins->sreg1); + + /* If the low word top bit is set, see if we are negative */ + br [0] = code; x86_branch8 (code, X86_CC_LT, 0, TRUE); + /* We are not negative (no top bit set, check for our top word to be zero */ + x86_test_reg_reg (code, ins->sreg2, ins->sreg2); + br [1] = code; x86_branch8 (code, X86_CC_EQ, 0, TRUE); + label [0] = code; + + /* throw exception */ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException"); + x86_jump32 (code, 0); + + x86_patch (br [0], code); + /* our top bit is set, check that top word is 0xfffffff */ + x86_alu_reg_imm (code, X86_CMP, ins->sreg2, 0xffffffff); + + x86_patch (br [1], code); + /* nope, emit exception */ + br [2] = code; x86_branch8 (code, X86_CC_NE, 0, TRUE); + x86_patch (br [2], label [0]); + + if (ins->dreg != ins->sreg1) + x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4); +#endif + g_assert_not_reached (); + break; + } + case OP_FADD: + ppc_fadd (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_FSUB: + ppc_fsub (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_FMUL: + ppc_fmul (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_FDIV: + ppc_fdiv (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + case OP_FNEG: + ppc_fneg (code, ins->dreg, ins->sreg1); + break; + case OP_FREM: + g_assert_not_reached (); + break; + case OP_FCOMPARE: + g_assert_not_reached (); + /* this overwrites EAX */ + EMIT_FPCOMPARE(code); + break; + case OP_FCEQ: + g_assert_not_reached (); + /*if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000); + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX);*/ + break; + case OP_FCLT: + case OP_FCLT_UN: + g_assert_not_reached (); + /*if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX);*/ + break; + case OP_FCGT: + case OP_FCGT_UN: + g_assert_not_reached (); + /*if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX);*/ + break; + case OP_FBEQ: + g_assert_not_reached (); + break; + case OP_FBNE_UN: + g_assert_not_reached (); + break; + case OP_FBLT: + case OP_FBLT_UN: + g_assert_not_reached (); + break; + case OP_FBGT: + case OP_FBGT_UN: + g_assert_not_reached (); + break; + case OP_FBGE: + case OP_FBGE_UN: + g_assert_not_reached (); + break; + case OP_FBLE: + case OP_FBLE_UN: + g_assert_not_reached (); + break; + case CEE_CKFINITE: { + g_assert_not_reached (); + x86_push_reg (code, X86_EAX); + x86_fxam (code); + x86_fnstsw (code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4100); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + x86_pop_reg (code, X86_EAX); + EMIT_COND_SYSTEM_EXCEPTION (X86_CC_EQ, FALSE, "ArithmeticException"); + break; + } + default: + g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__); + g_assert_not_reached (); + } + + if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) { + g_warning ("wrong maximal instruction length of instruction %s (exptected %d, got %d)", + mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset); + g_assert_not_reached (); + } + + cpos += max_len; + + last_ins = ins; + last_offset = offset; + + ins = ins->next; + } + + cfg->code_len = code - cfg->native_code; +} + +void +mono_arch_register_lowlevel_calls (void) +{ + mono_register_jit_icall (enter_method, "mono_enter_method", NULL, TRUE); + mono_register_jit_icall (leave_method, "mono_leave_method", NULL, TRUE); +} + +void +mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji) +{ + MonoJumpInfo *patch_info; + + for (patch_info = ji; patch_info; patch_info = patch_info->next) { + unsigned char *ip = patch_info->ip.i + code; + const unsigned char *target = NULL; + + switch (patch_info->type) { + case MONO_PATCH_INFO_BB: + target = patch_info->data.bb->native_offset + code; + break; + case MONO_PATCH_INFO_ABS: + target = patch_info->data.target; + break; + case MONO_PATCH_INFO_LABEL: + target = patch_info->data.inst->inst_c0 + code; + break; + case MONO_PATCH_INFO_IP: + *((gpointer *)(ip)) = ip; + continue; + case MONO_PATCH_INFO_INTERNAL_METHOD: + if (!strcmp (patch_info->data.name, "throw_exception")) { + target = mono_arch_get_throw_exception (); + } else if (!strcmp (patch_info->data.name, "throw_exception_by_name")) { + target = mono_arch_get_throw_exception_by_name (); + } else if (!strcmp (patch_info->data.name, "ldstr")) { + target = (gpointer)mono_ldstr; + } else if (!strcmp (patch_info->data.name, "domain_get")) { + target = (gpointer)mono_domain_get; + } else if (!strcmp (patch_info->data.name, "object_new")) { + target = (gpointer)mono_object_new; + } else if (!strcmp (patch_info->data.name, "get_lmf_addr")) { + target = (gpointer)mono_get_lmf_addr; + } else + g_assert_not_reached (); + break; + case MONO_PATCH_INFO_METHOD: + if (patch_info->data.method == method) { + target = code; + } else { + /* get the trampoline to the method from the domain */ + target = mono_arch_create_jit_trampoline (patch_info->data.method); + } + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.target; + int i; + + // FIXME: inspect code to get the register + ppc_load (ip, ppc_r11, patch_info->data.target); + //*((gconstpointer *)(ip + 2)) = patch_info->data.target; + + for (i = 0; i < patch_info->table_size; i++) { + table [i] = (int)patch_info->data.table [i] + code; + } + /* we put into the table the absolute address, no need for ppc_patch in this case */ + continue; + } + case MONO_PATCH_INFO_CLASS: + *((gconstpointer *)(ip + 1)) = patch_info->data.target; + continue; + case MONO_PATCH_INFO_IMAGE: + *((gconstpointer *)(ip + 1)) = patch_info->data.target; + continue; + default: + g_assert_not_reached (); + } + ppc_patch (ip, target); + } +} + +int +mono_arch_max_epilog_size (MonoCompile *cfg) +{ + int exc_count = 0, max_epilog_size = 16; + MonoJumpInfo *patch_info; + + if (cfg->method->save_lmf) + max_epilog_size += 128; + + if (mono_jit_trace_calls) + max_epilog_size += 50; + + if (mono_jit_profile) + max_epilog_size += 50; + + /* count the number of exception infos */ + + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if (patch_info->type == MONO_PATCH_INFO_EXC) + exc_count++; + } + + /* + * make sure we have enough space for exceptions + * 16 is the size of two push_imm instructions and a call + */ + max_epilog_size += exc_count*16; + + return max_epilog_size; +} + +guint8 * +mono_arch_emit_prolog (MonoCompile *cfg) +{ + MonoMethod *method = cfg->method; + MonoBasicBlock *bb; + MonoMethodSignature *sig; + MonoInst *inst; + int alloc_size, pos, max_offset, i; + guint8 *code; + CallInfo *cinfo; + + cfg->code_size = 256; + code = cfg->native_code = g_malloc (cfg->code_size); + + if (cfg->flags & MONO_CFG_HAS_CALLS) { + ppc_mflr (code, ppc_r0); + ppc_stw (code, ppc_r0, 8, ppc_sp); + } + + alloc_size = cfg->stack_offset; + pos = 0; + + if (method->save_lmf) { +#if 0 + pos += sizeof (MonoLMF); + + /* save the current IP */ + mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL); + x86_push_imm (code, 0); + + /* save all caller saved regs */ + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + x86_push_reg (code, X86_EBP); + + /* save method info */ + x86_push_imm (code, method); + + /* get the address of lmf for the current thread */ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"get_lmf_addr"); + x86_call_code (code, 0); + + /* push lmf */ + x86_push_reg (code, X86_EAX); + /* push *lfm (previous_lmf) */ + x86_push_membase (code, X86_EAX, 0); + /* *(lmf) = ESP */ + x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4); +#endif + } else { + + for (i = 13; i < 32; ++i) { + if (cfg->used_int_regs & (1 << i)) { + pos += 4; + ppc_stw (code, i, -pos, ppc_sp); + } + } + } + + alloc_size += pos; + // align to 16 bytes + if (alloc_size & (16 - 1)) + alloc_size += 16 - (alloc_size & (16 - 1)); + + cfg->stack_usage = alloc_size; + if (alloc_size) + ppc_stwu (code, ppc_sp, -alloc_size, ppc_sp); + + /* compute max_offset in order to use short forward jumps */ + max_offset = 0; + if (cfg->opt & MONO_OPT_BRANCH) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *ins = bb->code; + bb->max_offset = max_offset; + + if (mono_trace_coverage) + max_offset += 6; + + while (ins) { + max_offset += ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + ins = ins->next; + } + } + } + + if (mono_jit_trace_calls) + code = mono_arch_instrument_prolog (cfg, enter_method, code, TRUE); + + /* load arguments allocated to register from the stack */ + sig = method->signature; + pos = 0; + + cinfo = calculate_sizes (sig, sig->pinvoke); + + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { + ArgInfo *ainfo = cinfo->args + i; + inst = cfg->varinfo [pos]; + + if (inst->opcode == OP_REGVAR) { + g_assert (!ainfo->regtype); // fine for now + ppc_mr (code, inst->dreg, ainfo->reg); + x86_mov_reg_membase (code, inst->dreg, X86_EBP, inst->inst_offset, 4); + if (cfg->verbose_level > 2) + g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg)); + } else { + /* the argument should be put on the stack: FIXME handle size != word */ + ppc_stw (code, ainfo->reg, inst->inst_offset, inst->inst_basereg); + } + pos++; + } + + cfg->code_len = code - cfg->native_code; + + return code; +} + +void +mono_arch_emit_epilog (MonoCompile *cfg) +{ + MonoJumpInfo *patch_info; + MonoMethod *method = cfg->method; + int pos, i; + guint8 *code; + + code = cfg->native_code + cfg->code_len; + + if (mono_jit_trace_calls) + code = mono_arch_instrument_epilog (cfg, leave_method, code, TRUE); + + + pos = 0; + + if (method->save_lmf) { + pos = -sizeof (MonoLMF); + } + + if (method->save_lmf) { +#if 0 + /* ebx = previous_lmf */ + x86_pop_reg (code, X86_EBX); + /* edi = lmf */ + x86_pop_reg (code, X86_EDI); + /* *(lmf) = previous_lmf */ + x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4); + + /* discard method info */ + x86_pop_reg (code, X86_ESI); + + /* restore caller saved regs */ + x86_pop_reg (code, X86_EBP); + x86_pop_reg (code, X86_ESI); + x86_pop_reg (code, X86_EDI); + x86_pop_reg (code, X86_EBX); +#endif + } + + if (cfg->flags & MONO_CFG_HAS_CALLS) { + ppc_lwz (code, ppc_r0, cfg->stack_usage + 8, ppc_sp); + ppc_mtlr (code, ppc_r0); + } + ppc_addic (code, ppc_sp, ppc_sp, cfg->stack_usage); + for (i = 13; i < 32; ++i) { + if (cfg->used_int_regs & (1 << i)) { + pos += 4; + ppc_lwz (code, i, -pos, ppc_sp); + } + } + ppc_blr (code); + + /* add code to raise exceptions */ + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_EXC: + /*x86_patch (patch_info->ip.i + cfg->native_code, code); + x86_push_imm (code, patch_info->data.target); + x86_push_imm (code, patch_info->ip.i + cfg->native_code); + patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD; + patch_info->data.name = "throw_exception_by_name"; + patch_info->ip.i = code - cfg->native_code; + x86_jump_code (code, 0);*/ + break; + default: + /* do nothing */ + break; + } + } + + cfg->code_len = code - cfg->native_code; + + g_assert (cfg->code_len < cfg->code_size); + +} diff --git a/mono/mini/mini-ppc.h b/mono/mini/mini-ppc.h new file mode 100644 index 00000000000..31dbf566423 --- /dev/null +++ b/mono/mini/mini-ppc.h @@ -0,0 +1,26 @@ +#ifndef __MONO_MINI_X86_H__ +#define __MONO_MINI_X86_H__ + +#include +#include + +#define MONO_ARCH_FRAME_ALIGNMENT 4 + +/* fixme: align to 16byte instead of 32byte (we align to 32byte to get + * reproduceable results for benchmarks */ +#define MONO_ARCH_CODE_ALIGNMENT 32 + +#define MONO_ARCH_BASEREG X86_EBP +#define MONO_ARCH_RETREG1 ppc_r3 +#define MONO_ARCH_RETREG2 ppc_r4 +#define MONO_ARCH_EXC_REG ppc_r5 + +struct MonoLMF { + gpointer previous_lmf; + gpointer lmf_addr; + MonoMethod *method; + guint32 ebp; + guint32 eip; +}; + +#endif /* __MONO_MINI_X86_H__ */ diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c new file mode 100644 index 00000000000..34465f6e082 --- /dev/null +++ b/mono/mini/mini-x86.c @@ -0,0 +1,3141 @@ +/* + * mini-x86.c: x86 backend for the Mono code generator + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include "mini.h" +#include + +#include +#include + +#include "mini-x86.h" +#include "inssel.h" +#include "regset.h" +#include "cpu-pentium.h" + +int mono_exc_esp_offset = 0; + +const char* +mono_arch_regname (int reg) { + switch (reg) { + case X86_EAX: return "%eax"; + case X86_EBX: return "%ebx"; + case X86_ECX: return "%ecx"; + case X86_EDX: return "%edx"; + case X86_ESP: return "%esp"; + case X86_EBP: return "%ebp"; + case X86_EDI: return "%edi"; + case X86_ESI: return "%esi"; + } + return "unknown"; +} + +typedef struct { + guint16 size; + guint16 offset; + guint8 pad; +} MonoJitArgumentInfo; + +/* + * arch_get_argument_info: + * @csig: a method signature + * @param_count: the number of parameters to consider + * @arg_info: an array to store the result infos + * + * Gathers information on parameters such as size, alignment and + * padding. arg_info should be large enought to hold param_count + 1 entries. + * + * Returns the size of the activation frame. + */ +static int +arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info) +{ + int k, frame_size = 0; + int size, align, pad; + int offset = 8; + + if (MONO_TYPE_ISSTRUCT (csig->ret)) { + frame_size += sizeof (gpointer); + offset += 4; + } + + arg_info [0].offset = offset; + + if (csig->hasthis) { + frame_size += sizeof (gpointer); + offset += 4; + } + + arg_info [0].size = frame_size; + + for (k = 0; k < param_count; k++) { + + if (csig->pinvoke) + size = mono_type_native_stack_size (csig->params [k], &align); + else + size = mono_type_stack_size (csig->params [k], &align); + + /* ignore alignment for now */ + align = 1; + + frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); + arg_info [k].pad = pad; + frame_size += size; + arg_info [k + 1].pad = 0; + arg_info [k + 1].size = size; + offset += pad; + arg_info [k + 1].offset = offset; + offset += size; + } + + align = MONO_ARCH_FRAME_ALIGNMENT; + frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); + arg_info [k].pad = pad; + + return frame_size; +} + +static int indent_level = 0; + +static void indent (int diff) { + int v = indent_level; + while (v-- > 0) { + printf (". "); + } + indent_level += diff; +} + +static void +enter_method (MonoMethod *method, char *ebp) +{ + int i, j; + MonoClass *class; + MonoObject *o; + MonoJitArgumentInfo *arg_info; + MonoMethodSignature *sig; + char *fname; + + fname = mono_method_full_name (method, TRUE); + indent (1); + printf ("ENTER: %s(", fname); + g_free (fname); + + if (((int)ebp & (MONO_ARCH_FRAME_ALIGNMENT - 1)) != 0) { + g_error ("unaligned stack detected (%p)", ebp); + } + + sig = method->signature; + + arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1)); + + arch_get_argument_info (sig, sig->param_count, arg_info); + + if (MONO_TYPE_ISSTRUCT (method->signature->ret)) { + g_assert (!method->signature->ret->byref); + + printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8))); + } + + if (method->signature->hasthis) { + gpointer *this = (gpointer *)(ebp + arg_info [0].offset); + if (method->klass->valuetype) { + printf ("value:%p, ", *this); + } else { + o = *((MonoObject **)this); + + if (o) { + class = o->vtable->klass; + + if (class == mono_defaults.string_class) { + printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o)); + } else { + printf ("this:%p[%s.%s], ", o, class->name_space, class->name); + } + } else + printf ("this:NULL, "); + } + } + + for (i = 0; i < method->signature->param_count; ++i) { + gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset); + int size = arg_info [i + 1].size; + + MonoType *type = method->signature->params [i]; + + if (type->byref) { + printf ("[BYREF:%p], ", *cpos); + } else switch (type->type) { + + case MONO_TYPE_I: + case MONO_TYPE_U: + printf ("%p, ", (gpointer)*((int *)(cpos))); + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + printf ("%d, ", *((int *)(cpos))); + break; + case MONO_TYPE_STRING: { + MonoString *s = *((MonoString **)cpos); + if (s) { + g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class); + printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s)); + } else + printf ("[STRING:null], "); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: { + o = *((MonoObject **)cpos); + if (o) { + class = o->vtable->klass; + + if (class == mono_defaults.string_class) { + printf ("[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o)); + } else if (class == mono_defaults.int32_class) { + printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject))); + } else + printf ("[%s.%s:%p], ", class->name_space, class->name, o); + } else { + printf ("%p, ", *((gpointer *)(cpos))); + } + break; + } + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + printf ("%p, ", *((gpointer *)(cpos))); + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + printf ("0x%016llx, ", *((gint64 *)(cpos))); + break; + case MONO_TYPE_R4: + printf ("%f, ", *((float *)(cpos))); + break; + case MONO_TYPE_R8: + printf ("%f, ", *((double *)(cpos))); + break; + case MONO_TYPE_VALUETYPE: + printf ("["); + for (j = 0; j < size; j++) + printf ("%02x,", *((guint8*)cpos +j)); + printf ("], "); + break; + default: + printf ("XX, "); + } + } + + printf (")\n"); +} + +static void +leave_method (MonoMethod *method, ...) +{ + MonoType *type; + char *fname; + va_list ap; + + va_start(ap, method); + + fname = mono_method_full_name (method, TRUE); + indent (-1); + printf ("LEAVE: %s", fname); + g_free (fname); + + type = method->signature->ret; + +handle_enum: + switch (type->type) { + case MONO_TYPE_VOID: + break; + case MONO_TYPE_BOOLEAN: { + int eax = va_arg (ap, int); + if (eax) + printf ("TRUE:%d", eax); + else + printf ("FALSE"); + + break; + } + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: { + int eax = va_arg (ap, int); + printf ("EAX=%d", eax); + break; + } + case MONO_TYPE_STRING: { + MonoString *s = va_arg (ap, MonoString *); +; + if (s) { + g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class); + printf ("[STRING:%p:%s]", s, mono_string_to_utf8 (s)); + } else + printf ("[STRING:null], "); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: { + MonoObject *o = va_arg (ap, MonoObject *); + + if (o) { + if (o->vtable->klass == mono_defaults.boolean_class) { + printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject))); + } else if (o->vtable->klass == mono_defaults.int32_class) { + printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject)))); + } else if (o->vtable->klass == mono_defaults.int64_class) { + printf ("[INT64:%p:%lld]", o, *((gint64 *)((char *)o + sizeof (MonoObject)))); + } else + printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o); + } else + printf ("[OBJECT:%p]", o); + + break; + } + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + gpointer p = va_arg (ap, gpointer); + printf ("EAX=%p", p); + break; + } + case MONO_TYPE_I8: { + gint64 l = va_arg (ap, gint64); + printf ("EAX/EDX=0x%16llx", l); + break; + } + case MONO_TYPE_U8: { + gint64 l = va_arg (ap, gint64); + printf ("EAX/EDX=0x%16llx", l); + break; + } + case MONO_TYPE_R8: { + double f = va_arg (ap, double); + printf ("FP=%f\n", f); + break; + } + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + type = type->data.klass->enum_basetype; + goto handle_enum; + } else { + guint8 *p = va_arg (ap, gpointer); + int j, size, align; + size = mono_type_size (type, &align); + printf ("["); + for (j = 0; p && j < size; j++) + printf ("%02x,", p [j]); + printf ("]"); + } + break; + default: + printf ("(unknown return type %x)", method->signature->ret->type); + } + + printf ("\n"); +} + +static const guchar cpuid_impl [] = { + 0x55, /* push %ebp */ + 0x89, 0xe5, /* mov %esp,%ebp */ + 0x53, /* push %ebx */ + 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */ + 0x0f, 0xa2, /* cpuid */ + 0x50, /* push %eax */ + 0x8b, 0x45, 0x10, /* mov 0x10(%ebp),%eax */ + 0x89, 0x18, /* mov %ebx,(%eax) */ + 0x8b, 0x45, 0x14, /* mov 0x14(%ebp),%eax */ + 0x89, 0x08, /* mov %ecx,(%eax) */ + 0x8b, 0x45, 0x18, /* mov 0x18(%ebp),%eax */ + 0x89, 0x10, /* mov %edx,(%eax) */ + 0x58, /* pop %eax */ + 0x8b, 0x55, 0x0c, /* mov 0xc(%ebp),%edx */ + 0x89, 0x02, /* mov %eax,(%edx) */ + 0x5b, /* pop %ebx */ + 0xc9, /* leave */ + 0xc3, /* ret */ +}; + +typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx); + +static int +cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) +{ + int have_cpuid = 0; + __asm__ __volatile__ ( + "pushfl\n" + "popl %%eax\n" + "movl %%eax, %%edx\n" + "xorl $0x200000, %%eax\n" + "pushl %%eax\n" + "popfl\n" + "pushfl\n" + "popl %%eax\n" + "xorl %%edx, %%eax\n" + "andl $0x200000, %%eax\n" + "movl %%eax, %0" + : "=r" (have_cpuid) + : + : "%eax", "%edx" + ); + + if (have_cpuid) { + CpuidFunc func = (CpuidFunc)cpuid_impl; + func (id, p_eax, p_ebx, p_ecx, p_edx); + /* + * We use this approach because of issues with gcc and pic code, see: + * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329 + __asm__ __volatile__ ("cpuid" + : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx) + : "a" (id)); + */ + return 1; + } + return 0; +} + +/* + * This function returns the optimizations supported on this cpu. + */ +guint32 +mono_arch_cpu_optimizazions (void) +{ + int eax, ebx, ecx, edx; + guint32 opts = 0; + + /* Feature Flags function, flags returned in EDX. */ + if (cpuid (1, &eax, &ebx, &ecx, &edx)) { + if (edx & (1 << 15)) { + opts |= MONO_OPT_CMOV; + if (edx & 1) + opts |= MONO_OPT_FCMOV; + } + } + return opts; +} + +static gboolean +is_regsize_var (MonoType *t) { + if (t->byref) + return TRUE; + switch (t->type) { + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + return TRUE; + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return FALSE; + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) + return is_regsize_var (t->data.klass->enum_basetype); + return FALSE; + } + return FALSE; +} + +GList * +mono_arch_get_allocatable_int_vars (MonoCompile *cfg) +{ + GList *vars = NULL; + int i; + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoInst *ins = cfg->varinfo [i]; + MonoMethodVar *vmv = MONO_VARINFO (cfg, i); + + /* unused vars */ + if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos) + continue; + + if ((ins->flags & (MONO_INST_IS_DEAD|MONO_INST_VOLATILE|MONO_INST_INDIRECT)) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG)) + continue; + + /* we can only allocate 32 bit values */ + if (is_regsize_var (ins->inst_vtype)) { + g_assert (MONO_VARINFO (cfg, i)->reg == -1); + g_assert (i == vmv->idx); + vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE); + } + } + + return vars; +} + +GList * +mono_arch_get_global_int_regs (MonoCompile *cfg) +{ + GList *regs = NULL; + + /* we can use 3 registers for global allocation */ + regs = g_list_prepend (regs, (gpointer)X86_EBX); + regs = g_list_prepend (regs, (gpointer)X86_ESI); + regs = g_list_prepend (regs, (gpointer)X86_EDI); + + return regs; +} + +/* + * Set var information according to the calling convention. X86 version. + * The locals var stuff should most likely be split in another method. + */ +void +mono_arch_allocate_vars (MonoCompile *m) +{ + MonoMethodSignature *sig; + MonoMethodHeader *header; + MonoInst *inst; + int i, offset, size, align, curinst; + + header = ((MonoMethodNormal *)m->method)->header; + + sig = m->method->signature; + + offset = 8; + curinst = 0; + if (MONO_TYPE_ISSTRUCT (sig->ret)) { + m->ret->opcode = OP_REGOFFSET; + m->ret->inst_basereg = X86_EBP; + m->ret->inst_offset = offset; + offset += sizeof (gpointer); + } else { + /* FIXME: handle long and FP values */ + switch (sig->ret->type) { + case MONO_TYPE_VOID: + break; + default: + m->ret->opcode = OP_REGVAR; + m->ret->inst_c0 = X86_EAX; + break; + } + } + if (sig->hasthis) { + inst = m->varinfo [curinst]; + if (inst->opcode != OP_REGVAR) { + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = X86_EBP; + } + inst->inst_offset = offset; + offset += sizeof (gpointer); + curinst++; + } + + for (i = 0; i < sig->param_count; ++i) { + inst = m->varinfo [curinst]; + if (inst->opcode != OP_REGVAR) { + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = X86_EBP; + } + inst->inst_offset = offset; + size = mono_type_size (sig->params [i], &align); + size += 4 - 1; + size &= ~(4 - 1); + offset += size; + curinst++; + } + + offset = 0; + /* reserve space to save LMF and caller saved registers */ + offset += sizeof (MonoLMF); + + /* reserve space to store the esp */ + offset += sizeof (gpointer); + + /* this is a global constant */ + mono_exc_esp_offset = -offset; + + for (i = curinst; i < m->num_varinfo; ++i) { + inst = m->varinfo [i]; + + if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR) + continue; + + /* inst->unused indicates native sized value types, this is used by the + * pinvoke wrappers when they call functions returning structure */ + if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype)) + size = mono_class_native_size (inst->inst_vtype->data.klass, &align); + else + size = mono_type_size (inst->inst_vtype, &align); + + offset += size; + offset += align - 1; + offset &= ~(align - 1); + inst->opcode = OP_REGOFFSET; + inst->inst_basereg = X86_EBP; + inst->inst_offset = -offset; + //g_print ("allocating local %d to %d\n", i, -offset); + } + offset += 3; + offset &= ~3; + + + /* change sign? */ + m->stack_offset = -offset; +} + +/* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode, + * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info + */ + +/* + * take the arguments and generate the arch-specific + * instructions to properly call the function in call. + * This includes pushing, moving arguments to the right register + * etc. + * Issue: who does the spilling if needed, and when? + */ +MonoCallInst* +mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) { + MonoInst *arg, *in, **rev_args; + MonoMethodSignature *sig; + int i, n, stack_size, type; + MonoType *ptype; + + sig = call->signature; + n = sig->param_count + sig->hasthis; + rev_args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n); + + if (sig->ret && (sig->ret->type == MONO_TYPE_I8 || sig->ret->type == MONO_TYPE_U8)) { + //g_warning ("long value returned"); + } + if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) + stack_size = 4; + else + stack_size = 0; + for (i = 0; i < n; ++i) { + if (is_virtual && i == 0) { + /* the argument will be attached to the call instrucion */ + rev_args [n - 1] = arg = NULL; + in = call->args [i]; + stack_size += 4; + } else { + MONO_INST_NEW (cfg, arg, OP_OUTARG); + in = call->args [i]; + arg->cil_code = in->cil_code; + arg->inst_left = in; + arg->type = in->type; + rev_args [n - i - 1] = arg; + if (i >= sig->hasthis) { + ptype = sig->params [i - sig->hasthis]; + if (ptype->byref) + type = MONO_TYPE_U; + else + type = ptype->type; +handle_enum: + /* FIXME: validate arguments... */ + switch (type) { + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + stack_size += 4; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + stack_size += 8; + break; + case MONO_TYPE_R4: + stack_size += 4; + arg->opcode = OP_OUTARG_R4; + break; + case MONO_TYPE_R8: + stack_size += 8; + arg->opcode = OP_OUTARG_R8; + break; + case MONO_TYPE_VALUETYPE: + if (MONO_TYPE_ISSTRUCT (ptype)) { + int size; + if (sig->pinvoke) + size = mono_type_native_stack_size (&in->klass->byval_arg, NULL); + else + size = mono_type_stack_size (&in->klass->byval_arg, NULL); + stack_size += size; + arg->opcode = OP_OUTARG_VT; + arg->klass = in->klass; + arg->unused = sig->pinvoke; + arg->inst_imm = size; + } else { + type = ptype->data.klass->enum_basetype->type; + goto handle_enum; + } + break; + default: + g_warning ("unknown type 0x%02x\n", type); + g_assert_not_reached (); + } + } else { + /* the this argument */ + stack_size += 4; + } + } + } + /* they need to be pushed in reverse order */ + call->args = rev_args; + call->stack_usage = stack_size; + /* + * should set more info in call, such as the stack space + * used by the args that needs to be added back to esp + */ + + return call; +} + +/* + * Allow tracing to work with this interface (with an optional argument) + */ + +/* + * This may be needed on some archs or for debugging support. + */ +void +mono_arch_instrument_mem_needs (MonoMethod *method, int *stack, int *code) +{ + /* no stack room needed now (may be needed for FASTCALL-trace support) */ + *stack = 0; + /* split prolog-epilog requirements? */ + *code = 50; /* max bytes needed: check this number */ +} + +void* +mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) +{ + guchar *code = p; + + /* if some args are passed in registers, we need to save them here */ + x86_push_reg (code, X86_EBP); + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_METHODCONST, cfg->method); + x86_push_imm (code, cfg->method); + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); + x86_call_code (code, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); + + return code; +} + +enum { + SAVE_NONE, + SAVE_STRUCT, + SAVE_EAX, + SAVE_EAX_EDX, + SAVE_FP +}; + +void* +mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) +{ + guchar *code = p; + int arg_size = 0, save_mode = SAVE_NONE; + MonoMethod *method = cfg->method; + int rtype = method->signature->ret->type; + +handle_enum: + switch (rtype) { + case MONO_TYPE_VOID: + /* special case string .ctor icall */ + if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class) + save_mode = SAVE_EAX; + else + save_mode = SAVE_NONE; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + save_mode = SAVE_EAX_EDX; + break; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + save_mode = SAVE_FP; + break; + case MONO_TYPE_VALUETYPE: + if (method->signature->ret->data.klass->enumtype) { + rtype = method->signature->ret->data.klass->enum_basetype->type; + goto handle_enum; + } + save_mode = SAVE_STRUCT; + break; + default: + save_mode = SAVE_EAX; + break; + } + + switch (save_mode) { + case SAVE_EAX_EDX: + x86_push_reg (code, X86_EDX); + x86_push_reg (code, X86_EAX); + if (enable_arguments) { + x86_push_reg (code, X86_EDX); + x86_push_reg (code, X86_EAX); + arg_size = 8; + } + break; + case SAVE_EAX: + x86_push_reg (code, X86_EAX); + if (enable_arguments) { + x86_push_reg (code, X86_EAX); + arg_size = 4; + } + break; + case SAVE_FP: + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE); + if (enable_arguments) { + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE); + arg_size = 8; + } + break; + case SAVE_STRUCT: + if (enable_arguments) { + x86_push_membase (code, X86_EBP, 8); + arg_size = 4; + } + break; + case SAVE_NONE: + default: + break; + } + + + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_METHODCONST, method); + x86_push_imm (code, method); + mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); + x86_call_code (code, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, arg_size + 4); + + switch (save_mode) { + case SAVE_EAX_EDX: + x86_pop_reg (code, X86_EAX); + x86_pop_reg (code, X86_EDX); + break; + case SAVE_EAX: + x86_pop_reg (code, X86_EAX); + break; + case SAVE_FP: + x86_fld_membase (code, X86_ESP, 0, TRUE); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); + break; + case SAVE_NONE: + default: + break; + } + + return code; +} + +#define EMIT_COND_BRANCH(ins,cond,sign) \ +if (ins->flags & MONO_INST_BRLABEL) { \ + if (ins->inst_i0->inst_c0) { \ + x86_branch (code, cond, cfg->native_code + ins->inst_i0->inst_c0, sign); \ + } else { \ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \ + x86_branch32 (code, cond, 0, sign); \ + } \ +} else { \ + if (ins->inst_true_bb->native_offset) { \ + x86_branch (code, cond, cfg->native_code + ins->inst_true_bb->native_offset, sign); \ + } else { \ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \ + if ((cfg->opt & MONO_OPT_BRANCH) && \ + x86_is_imm8 (ins->inst_true_bb->max_offset - cpos)) \ + x86_branch8 (code, cond, 0, sign); \ + else \ + x86_branch32 (code, cond, 0, sign); \ + } \ +} + +/* emit an exception if condition is fail */ +#define EMIT_COND_SYSTEM_EXCEPTION(cond,signed,exc_name) \ + do { \ + mono_add_patch_info (cfg, code - cfg->native_code, \ + MONO_PATCH_INFO_EXC, exc_name); \ + x86_branch32 (code, cond, 0, signed); \ + } while (0); + +#define EMIT_FPCOMPARE(code) do { \ + x86_fcompp (code); \ + x86_fnstsw (code); \ +} while (0); + +static void +peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins, *last_ins = NULL; + ins = bb->code; + + while (ins) { + + switch (ins->opcode) { + case OP_ICONST: + /* reg = 0 -> XOR (reg, reg) */ + /* XOR sets cflags on x86, so we cant do it always */ + if (ins->inst_c0 == 0 && ins->next && + (ins->next->opcode == CEE_BR)) { + ins->opcode = CEE_XOR; + ins->sreg1 = ins->dreg; + ins->sreg2 = ins->dreg; + } + break; + case OP_MUL_IMM: + /* remove unnecessary multiplication with 1 */ + if (ins->inst_imm == 1) { + if (ins->dreg != ins->sreg1) { + ins->opcode = OP_MOVE; + } else { + last_ins->next = ins->next; + ins = ins->next; + continue; + } + } + break; + case OP_COMPARE_IMM: + /* OP_COMPARE_IMM (reg, 0) --> OP_X86_TEST_NULL (reg) */ + if (ins->inst_imm == 0 && ins->next && + (ins->next->opcode == CEE_BEQ || ins->next->opcode == CEE_BNE_UN || + ins->next->opcode == OP_CEQ)) { + ins->opcode = OP_X86_TEST_NULL; + } + break; + case OP_LOAD_MEMBASE: + case OP_LOADI4_MEMBASE: + /* + * OP_STORE_MEMBASE_REG reg, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg + */ + if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG + || last_ins->opcode == OP_STORE_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + + /* + * Note: reg1 must be different from the basereg in the second load + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_MOVE reg1, reg2 + */ + } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE + || last_ins->opcode == OP_LOAD_MEMBASE) && + ins->inst_basereg != last_ins->dreg && + ins->inst_basereg == last_ins->inst_basereg && + ins->inst_offset == last_ins->inst_offset) { + + if (ins->dreg == last_ins->dreg) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->dreg; + } + + //g_assert_not_reached (); + +#if 0 + /* + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg + * --> + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_ICONST reg, imm + */ + } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM + || last_ins->opcode == OP_STORE_MEMBASE_IMM) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_ICONST; + ins->inst_c0 = last_ins->inst_imm; + g_assert_not_reached (); // check this rule +#endif + } + break; + case OP_LOADU1_MEMBASE: + case OP_LOADI1_MEMBASE: + /* + * FIXME: Missing explanation + */ + if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + } + break; + case OP_LOADU2_MEMBASE: + case OP_LOADI2_MEMBASE: + /* + * FIXME: Missing explanation + */ + if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + } + break; + case CEE_CONV_I4: + case CEE_CONV_U4: + case OP_MOVE: + /* + * OP_MOVE reg, reg + */ + if (ins->dreg == ins->sreg1) { + if (last_ins) + last_ins->next = ins->next; + ins = ins->next; + continue; + } + /* + * OP_MOVE sreg, dreg + * OP_MOVE dreg, sreg + */ + if (last_ins && last_ins->opcode == OP_MOVE && + ins->sreg1 == last_ins->dreg && + ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } + break; + } + last_ins = ins; + ins = ins->next; + } + bb->last_ins = last_ins; +} + +static const int +branch_cc_table [] = { + X86_CC_EQ, X86_CC_GE, X86_CC_GT, X86_CC_LE, X86_CC_LT, + X86_CC_NE, X86_CC_GE, X86_CC_GT, X86_CC_LE, X86_CC_LT, + X86_CC_O, X86_CC_NO, X86_CC_C, X86_CC_NC +}; + +#define DEBUG(a) if (cfg->verbose_level > 1) a +//#define DEBUG(a) +#define reg_is_freeable(r) ((r) >= 0 && (r) <= 7 && X86_IS_CALLEE ((r))) + +typedef struct { + int born_in; + int killed_in; + int last_use; + int prev_use; +} RegTrack; + +static const char*const * ins_spec = pentium; + +static void +print_ins (int i, MonoInst *ins) +{ + const char *spec = ins_spec [ins->opcode]; + g_print ("\t%-2d %s", i, mono_inst_name (ins->opcode)); + if (spec [MONO_INST_DEST]) { + if (ins->dreg >= MONO_MAX_IREGS) + g_print (" R%d <-", ins->dreg); + else + g_print (" %s <-", mono_arch_regname (ins->dreg)); + } + if (spec [MONO_INST_SRC1]) { + if (ins->sreg1 >= MONO_MAX_IREGS) + g_print (" R%d", ins->sreg1); + else + g_print (" %s", mono_arch_regname (ins->sreg1)); + } + if (spec [MONO_INST_SRC2]) { + if (ins->sreg2 >= MONO_MAX_IREGS) + g_print (" R%d", ins->sreg2); + else + g_print (" %s", mono_arch_regname (ins->sreg2)); + } + if (spec [MONO_INST_CLOB]) + g_print (" clobbers: %c", spec [MONO_INST_CLOB]); + g_print ("\n"); +} + +static void +print_regtrack (RegTrack *t, int num) +{ + int i; + char buf [32]; + const char *r; + + for (i = 0; i < num; ++i) { + if (!t [i].born_in) + continue; + if (i >= MONO_MAX_IREGS) { + g_snprintf (buf, sizeof(buf), "R%d", i); + r = buf; + } else + r = mono_arch_regname (i); + g_print ("liveness: %s [%d - %d]\n", r, t [i].born_in, t[i].last_use); + } +} + +typedef struct InstList InstList; + +struct InstList { + InstList *prev; + InstList *next; + MonoInst *data; +}; + +static inline InstList* +inst_list_prepend (MonoMemPool *pool, InstList *list, MonoInst *data) +{ + InstList *item = mono_mempool_alloc (pool, sizeof (InstList)); + item->data = data; + item->prev = NULL; + item->next = list; + if (list) + list->prev = item; + return item; +} + +/* + * Force the spilling of the variable in the symbolic register 'reg'. + */ +static int +get_register_force_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, int reg) +{ + MonoInst *load; + int i, sel, spill; + + sel = cfg->rs->iassign [reg]; + /*i = cfg->rs->isymbolic [sel]; + g_assert (i == reg);*/ + i = reg; + spill = ++cfg->spill_count; + cfg->rs->iassign [i] = -spill - 1; + mono_regstate_free_int (cfg->rs, sel); + /* we need to create a spill var and insert a load to sel after the current instruction */ + MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE); + load->dreg = sel; + load->inst_basereg = X86_EBP; + load->inst_offset = mono_spillvar_offset (cfg, spill); + if (item->prev) { + while (ins->next != item->prev->data) + ins = ins->next; + } + load->next = ins->next; + ins->next = load; + DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%ebp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel))); + i = mono_regstate_alloc_int (cfg->rs, 1 << sel); + g_assert (i == sel); + + return sel; +} + +static int +get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32 regmask, int reg) +{ + MonoInst *load; + int i, sel, spill; + + DEBUG (g_print ("start regmask to assign R%d: 0x%08x (R%d <- R%d R%d)\n", reg, regmask, ins->dreg, ins->sreg1, ins->sreg2)); + /* exclude the registers in the current instruction */ + if (reg != ins->sreg1 && (reg_is_freeable (ins->sreg1) || (ins->sreg1 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg1] >= 0))) { + if (ins->sreg1 >= MONO_MAX_IREGS) + regmask &= ~ (1 << cfg->rs->iassign [ins->sreg1]); + else + regmask &= ~ (1 << ins->sreg1); + DEBUG (g_print ("excluding sreg1 %s\n", mono_arch_regname (ins->sreg1))); + } + if (reg != ins->sreg2 && (reg_is_freeable (ins->sreg2) || (ins->sreg2 >= MONO_MAX_IREGS && cfg->rs->iassign [ins->sreg2] >= 0))) { + if (ins->sreg2 >= MONO_MAX_IREGS) + regmask &= ~ (1 << cfg->rs->iassign [ins->sreg2]); + else + regmask &= ~ (1 << ins->sreg2); + DEBUG (g_print ("excluding sreg2 %s %d\n", mono_arch_regname (ins->sreg2), ins->sreg2)); + } + if (reg != ins->dreg && reg_is_freeable (ins->dreg)) { + regmask &= ~ (1 << ins->dreg); + DEBUG (g_print ("excluding dreg %s\n", mono_arch_regname (ins->dreg))); + } + + DEBUG (g_print ("available regmask: 0x%08x\n", regmask)); + g_assert (regmask); /* need at least a register we can free */ + sel = -1; + /* we should track prev_use and spill the register that's farther */ + for (i = 0; i < MONO_MAX_IREGS; ++i) { + if (regmask & (1 << i)) { + sel = i; + DEBUG (g_print ("selected register %s has assignment %d\n", mono_arch_regname (sel), cfg->rs->iassign [sel])); + break; + } + } + i = cfg->rs->isymbolic [sel]; + spill = ++cfg->spill_count; + cfg->rs->iassign [i] = -spill - 1; + mono_regstate_free_int (cfg->rs, sel); + /* we need to create a spill var and insert a load to sel after the current instruction */ + MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE); + load->dreg = sel; + load->inst_basereg = X86_EBP; + load->inst_offset = mono_spillvar_offset (cfg, spill); + if (item->prev) { + while (ins->next != item->prev->data) + ins = ins->next; + } + load->next = ins->next; + ins->next = load; + DEBUG (g_print ("SPILLED LOAD (%d at 0x%08x(%%ebp)) R%d (freed %s)\n", spill, load->inst_offset, i, mono_arch_regname (sel))); + i = mono_regstate_alloc_int (cfg->rs, 1 << sel); + g_assert (i == sel); + + return sel; +} + +static MonoInst* +create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins) +{ + MonoInst *copy; + MONO_INST_NEW (cfg, copy, OP_MOVE); + copy->dreg = dest; + copy->sreg1 = src; + if (ins) { + copy->next = ins->next; + ins->next = copy; + } + DEBUG (g_print ("\tforced copy from %s to %s\n", mono_arch_regname (src), mono_arch_regname (dest))); + return copy; +} + +static MonoInst* +create_spilled_store (MonoCompile *cfg, int spill, int reg, int prev_reg, MonoInst *ins) +{ + MonoInst *store; + MONO_INST_NEW (cfg, store, OP_STORE_MEMBASE_REG); + store->sreg1 = reg; + store->inst_destbasereg = X86_EBP; + store->inst_offset = mono_spillvar_offset (cfg, spill); + if (ins) { + store->next = ins->next; + ins->next = store; + } + DEBUG (g_print ("SPILLED STORE (%d at 0x%08x(%%ebp)) R%d (from %s)\n", spill, store->inst_offset, prev_reg, mono_arch_regname (reg))); + return store; +} + +static void +insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert) +{ + MonoInst *prev; + if (item->next) { + prev = item->next->data; + + while (prev->next != ins) + prev = prev->next; + to_insert->next = ins; + prev->next = to_insert; + } else { + to_insert->next = ins; + } + /* + * needed otherwise in the next instruction we can add an ins to the + * end and that would get past this instruction. + */ + item->data = to_insert; +} + +#if 0 +static int +alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, guint32 allow_mask) +{ + int val = cfg->rs->iassign [sym_reg]; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (cfg->rs, allow_mask); + if (val < 0) + val = get_register_spilling (cfg, curinst, ins, allow_mask, sym_reg); + cfg->rs->iassign [sym_reg] = val; + /* add option to store before the instruction for src registers */ + if (spill) + create_spilled_store (cfg, spill, val, sym_reg, ins); + } + cfg->rs->isymbolic [val] = sym_reg; + return val; +} +#endif + +#include "cprop.c" + +/* + * Local register allocation. + * We first scan the list of instructions and we save the liveness info of + * each register (when the register is first used, when it's value is set etc.). + * We also reverse the list of instructions (in the InstList list) because assigning + * registers backwards allows for more tricks to be used. + */ +void +mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins; + MonoRegState *rs = cfg->rs; + int i, val, fpcount; + RegTrack *reginfo, *reginfof; + RegTrack *reginfo1, *reginfo2, *reginfod; + InstList *tmp, *reversed = NULL; + const char *spec; + guint32 src1_mask, src2_mask, dest_mask; + + if (!bb->code) + return; + rs->next_vireg = bb->max_ireg; + rs->next_vfreg = bb->max_freg; + mono_regstate_assign (rs); + reginfo = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vireg); + reginfof = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vfreg); + rs->ifree_mask = X86_CALLEE_REGS; + + ins = bb->code; + + if (cfg->opt & MONO_OPT_COPYPROP) + local_copy_prop (cfg, ins); + + i = 1; + fpcount = 0; /* FIXME: track fp stack utilization */ + DEBUG (g_print ("LOCAL regalloc: basic block: %d\n", bb->block_num)); + /* forward pass on the instructions to collect register liveness info */ + while (ins) { + spec = ins_spec [ins->opcode]; + DEBUG (print_ins (i, ins)); + if (spec [MONO_INST_SRC1]) { + if (spec [MONO_INST_SRC1] == 'f') + reginfo1 = reginfof; + else + reginfo1 = reginfo; + reginfo1 [ins->sreg1].prev_use = reginfo1 [ins->sreg1].last_use; + reginfo1 [ins->sreg1].last_use = i; + } else { + ins->sreg1 = -1; + } + if (spec [MONO_INST_SRC2]) { + if (spec [MONO_INST_SRC2] == 'f') + reginfo2 = reginfof; + else + reginfo2 = reginfo; + reginfo2 [ins->sreg2].prev_use = reginfo2 [ins->sreg2].last_use; + reginfo2 [ins->sreg2].last_use = i; + } else { + ins->sreg2 = -1; + } + if (spec [MONO_INST_DEST]) { + if (spec [MONO_INST_DEST] == 'f') + reginfod = reginfof; + else + reginfod = reginfo; + if (spec [MONO_INST_DEST] != 'b') /* it's not just a base register */ + reginfod [ins->dreg].killed_in = i; + reginfod [ins->dreg].prev_use = reginfod [ins->dreg].last_use; + reginfod [ins->dreg].last_use = i; + if (reginfod [ins->dreg].born_in == 0 || reginfod [ins->dreg].born_in > i) + reginfod [ins->dreg].born_in = i; + if (spec [MONO_INST_DEST] == 'l') { + /* result in eax:edx, the virtual register is allocated sequentially */ + reginfod [ins->dreg + 1].prev_use = reginfod [ins->dreg + 1].last_use; + reginfod [ins->dreg + 1].last_use = i; + if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i) + reginfod [ins->dreg + 1].born_in = i; + } + } else { + ins->dreg = -1; + } + reversed = inst_list_prepend (cfg->mempool, reversed, ins); + ++i; + ins = ins->next; + } + + DEBUG (print_regtrack (reginfo, rs->next_vireg)); + DEBUG (print_regtrack (reginfof, rs->next_vfreg)); + tmp = reversed; + while (tmp) { + int prev_dreg, prev_sreg1, prev_sreg2; + dest_mask = src1_mask = src2_mask = X86_CALLEE_REGS; + --i; + ins = tmp->data; + spec = ins_spec [ins->opcode]; + DEBUG (g_print ("processing:")); + DEBUG (print_ins (i, ins)); + if (spec [MONO_INST_CLOB] == 's') { + if (rs->ifree_mask & (1 << X86_ECX)) { + DEBUG (g_print ("\tshortcut assignment of R%d to ECX\n", ins->sreg2)); + rs->iassign [ins->sreg2] = X86_ECX; + rs->isymbolic [X86_ECX] = ins->sreg2; + ins->sreg2 = X86_ECX; + rs->ifree_mask &= ~ (1 << X86_ECX); + } else { + int need_ecx_spill = TRUE; + /* + * we first check if src1/dreg is already assigned a register + * and then we force a spill of the var assigned to ECX. + */ + /* the destination register can't be ECX */ + dest_mask &= ~ (1 << X86_ECX); + src1_mask &= ~ (1 << X86_ECX); + val = rs->iassign [ins->dreg]; + /* + * the destination register is already assigned to ECX: + * we need to allocate another register for it and then + * copy from this to ECX. + */ + if (val == X86_ECX && ins->dreg != ins->sreg2) { + int new_dest = mono_regstate_alloc_int (rs, dest_mask); + if (new_dest < 0) + new_dest = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg); + g_assert (new_dest >= 0); + ins->dreg = new_dest; + create_copy_ins (cfg, X86_ECX, new_dest, ins); + need_ecx_spill = FALSE; + /*DEBUG (g_print ("\tforced spill of R%d\n", ins->dreg)); + val = get_register_force_spilling (cfg, tmp, ins, ins->dreg); + rs->iassign [ins->dreg] = val; + rs->isymbolic [val] = prev_dreg; + ins->dreg = val;*/ + } + val = rs->iassign [ins->sreg1]; + if (val == X86_ECX) { + g_assert_not_reached (); + } else if (val >= 0) { + /* + * the first src reg was already assigned to a register, + * we need to copy it to the dest register because the + * shift instruction clobbers the first operand. + */ + MonoInst *copy = create_copy_ins (cfg, ins->dreg, val, NULL); + insert_before_ins (ins, tmp, copy); + } + val = rs->iassign [ins->sreg2]; + if (val >= 0 && val != X86_ECX) { + MonoInst *move = create_copy_ins (cfg, X86_ECX, val, NULL); + DEBUG (g_print ("\tmoved arg from R%d (%d) to ECX\n", val, ins->sreg2)); + move->next = ins; + g_assert_not_reached (); + /* FIXME: where is move connected to the instruction list? */ + //tmp->prev->data->next = move; + } + if (need_ecx_spill && !(rs->ifree_mask & (1 << X86_ECX))) { + DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_ECX])); + get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_ECX]); + mono_regstate_free_int (rs, X86_ECX); + } + /* force-set sreg2 */ + rs->iassign [ins->sreg2] = X86_ECX; + rs->isymbolic [X86_ECX] = ins->sreg2; + ins->sreg2 = X86_ECX; + rs->ifree_mask &= ~ (1 << X86_ECX); + } + } else if (spec [MONO_INST_CLOB] == 'd') { /* division */ + int dest_reg = X86_EAX; + if (spec [MONO_INST_DEST] == 'd') + dest_reg = X86_EDX; /* reminder */ + val = rs->iassign [ins->dreg]; + if (0 && val >= 0 && val != dest_reg && !(rs->ifree_mask & (1 << dest_reg))) { + DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [dest_reg])); + get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [dest_reg]); + mono_regstate_free_int (rs, dest_reg); + } + if (val < 0) { + if (val < -1) { + /* the register gets spilled after this inst */ + int spill = -val -1; + dest_mask = 1 << (dest_reg == X86_EAX? X86_EDX: X86_EAX); + prev_dreg = ins->dreg; + val = mono_regstate_alloc_int (rs, dest_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg); + rs->iassign [ins->dreg] = val; + if (spill) + create_spilled_store (cfg, spill, val, prev_dreg, ins); + DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg)); + rs->isymbolic [val] = prev_dreg; + ins->dreg = val; + if (val != dest_reg) { /* force a copy */ + create_copy_ins (cfg, val, dest_reg, ins); + } + } else { + DEBUG (g_print ("\tshortcut assignment of R%d to %s\n", ins->dreg, mono_arch_regname (dest_reg))); + rs->iassign [ins->dreg] = dest_reg; + rs->isymbolic [dest_reg] = ins->dreg; + ins->dreg = dest_reg; + rs->ifree_mask &= ~ (1 << dest_reg); + } + } else { + //DEBUG (g_print ("dest reg in div assigned: %s\n", mono_arch_regname (val))); + if (val != dest_reg) { /* force a copy */ + create_copy_ins (cfg, val, dest_reg, ins); + } + } + src1_mask = 1 << X86_EAX; + src2_mask = 1 << X86_ECX; + } + if (spec [MONO_INST_DEST] == 'l') { + if (!(rs->ifree_mask & (1 << X86_EAX))) { + DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_EAX])); + get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_EAX]); + mono_regstate_free_int (rs, X86_EAX); + } + if (!(rs->ifree_mask & (1 << X86_EDX))) { + DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [X86_EDX])); + get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [X86_EDX]); + mono_regstate_free_int (rs, X86_EDX); + } + } + /* update for use with FP regs... */ + if (spec [MONO_INST_DEST] != 'f' && ins->dreg >= MONO_MAX_IREGS) { + val = rs->iassign [ins->dreg]; + prev_dreg = ins->dreg; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, dest_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, dest_mask, ins->dreg); + rs->iassign [ins->dreg] = val; + if (spill) + create_spilled_store (cfg, spill, val, prev_dreg, ins); + } + DEBUG (g_print ("\tassigned dreg %s to dest R%d\n", mono_arch_regname (val), ins->dreg)); + rs->isymbolic [val] = prev_dreg; + ins->dreg = val; + if (spec [MONO_INST_DEST] == 'l') { + int hreg = prev_dreg + 1; + val = rs->iassign [hreg]; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, dest_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, dest_mask, hreg); + rs->iassign [hreg] = val; + if (spill) + create_spilled_store (cfg, spill, val, hreg, ins); + } + DEBUG (g_print ("\tassigned hreg %s to dest R%d\n", mono_arch_regname (val), hreg)); + rs->isymbolic [val] = hreg; + /* FIXME:? ins->dreg = val; */ + if (ins->dreg == X86_EAX) { + if (val != X86_EDX) + create_copy_ins (cfg, val, X86_EDX, ins); + } else if (ins->dreg == X86_EDX) { + if (val == X86_EAX) { + /* swap */ + g_assert_not_reached (); + } else { + /* two forced copies */ + create_copy_ins (cfg, val, X86_EDX, ins); + create_copy_ins (cfg, ins->dreg, X86_EAX, ins); + } + } else { + if (val == X86_EDX) { + create_copy_ins (cfg, ins->dreg, X86_EAX, ins); + } else { + /* two forced copies */ + create_copy_ins (cfg, val, X86_EDX, ins); + create_copy_ins (cfg, ins->dreg, X86_EAX, ins); + } + } + if (reg_is_freeable (val) && hreg >= 0 && reginfo [hreg].born_in >= i) { + DEBUG (g_print ("\tfreeable %s (R%d)\n", mono_arch_regname (val), hreg)); + mono_regstate_free_int (rs, val); + } + } else if (spec [MONO_INST_DEST] == 'a' && ins->dreg != X86_EAX && spec [MONO_INST_CLOB] != 'd') { + /* this instruction only outputs to EAX, need to copy */ + create_copy_ins (cfg, ins->dreg, X86_EAX, ins); + } else if (spec [MONO_INST_DEST] == 'd' && ins->dreg != X86_EDX && spec [MONO_INST_CLOB] != 'd') { + create_copy_ins (cfg, ins->dreg, X86_EDX, ins); + } + } else { + prev_dreg = -1; + } + if (spec [MONO_INST_DEST] != 'f' && reg_is_freeable (ins->dreg) && prev_dreg >= 0 && reginfo [prev_dreg].born_in >= i) { + DEBUG (g_print ("\tfreeable %s (R%d) (born in %d)\n", mono_arch_regname (ins->dreg), prev_dreg, reginfo [prev_dreg].born_in)); + mono_regstate_free_int (rs, ins->dreg); + } + if (spec [MONO_INST_SRC1] != 'f' && ins->sreg1 >= MONO_MAX_IREGS) { + val = rs->iassign [ins->sreg1]; + prev_sreg1 = ins->sreg1; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + if (0 && ins->opcode == OP_MOVE) { + /* + * small optimization: the dest register is already allocated + * but the src one is not: we can simply assign the same register + * here and peephole will get rid of the instruction later. + * This optimization may interfere with the clobbering handling: + * it removes a mov operation that will be added again to handle clobbering. + * There are also some other issues that should with make testjit. + */ + mono_regstate_alloc_int (rs, 1 << ins->dreg); + val = rs->iassign [ins->sreg1] = ins->dreg; + //g_assert (val >= 0); + DEBUG (g_print ("\tfast assigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1)); + } else { + //g_assert (val == -1); /* source cannot be spilled */ + val = mono_regstate_alloc_int (rs, src1_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, src1_mask, ins->sreg1); + rs->iassign [ins->sreg1] = val; + DEBUG (g_print ("\tassigned sreg1 %s to R%d\n", mono_arch_regname (val), ins->sreg1)); + } + if (spill) { + MonoInst *store = create_spilled_store (cfg, spill, val, prev_sreg1, NULL); + insert_before_ins (ins, tmp, store); + } + } + rs->isymbolic [val] = prev_sreg1; + ins->sreg1 = val; + } else { + prev_sreg1 = -1; + } + /* handle clobbering of sreg1 */ + if ((spec [MONO_INST_CLOB] == '1' || spec [MONO_INST_CLOB] == 's') && ins->dreg != ins->sreg1) { + MonoInst *copy = create_copy_ins (cfg, ins->dreg, ins->sreg1, NULL); + DEBUG (g_print ("\tneed to copy sreg1 %s to dreg %s\n", mono_arch_regname (ins->sreg1), mono_arch_regname (ins->dreg))); + if (ins->sreg2 == -1 || spec [MONO_INST_CLOB] == 's') { + /* note: the copy is inserted before the current instruction! */ + insert_before_ins (ins, tmp, copy); + /* we set sreg1 to dest as well */ + prev_sreg1 = ins->sreg1 = ins->dreg; + } else { + /* inserted after the operation */ + copy->next = ins->next; + ins->next = copy; + } + } + if (spec [MONO_INST_SRC2] != 'f' && ins->sreg2 >= MONO_MAX_IREGS) { + val = rs->iassign [ins->sreg2]; + prev_sreg2 = ins->sreg2; + if (val < 0) { + int spill = 0; + if (val < -1) { + /* the register gets spilled after this inst */ + spill = -val -1; + } + val = mono_regstate_alloc_int (rs, src2_mask); + if (val < 0) + val = get_register_spilling (cfg, tmp, ins, src2_mask, ins->sreg2); + rs->iassign [ins->sreg2] = val; + DEBUG (g_print ("\tassigned sreg2 %s to R%d\n", mono_arch_regname (val), ins->sreg2)); + if (spill) + create_spilled_store (cfg, spill, val, prev_sreg2, ins); + } + rs->isymbolic [val] = prev_sreg2; + ins->sreg2 = val; + if (spec [MONO_INST_CLOB] == 's' && ins->sreg2 != X86_ECX) { + DEBUG (g_print ("\tassigned sreg2 %s to R%d, but ECX is needed (R%d)\n", mono_arch_regname (val), ins->sreg2, rs->iassign [X86_ECX])); + } + } else { + prev_sreg2 = -1; + } + + if (spec [MONO_INST_CLOB] == 'c') { + int j, s; + guint32 clob_mask = X86_CALLEE_REGS; + for (j = 0; j < MONO_MAX_IREGS; ++j) { + s = 1 << j; + if ((clob_mask & s) && !(rs->ifree_mask & s) && j != ins->sreg1) { + //g_warning ("register %s busy at call site\n", mono_arch_regname (j)); + } + } + } + /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) { + DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg1))); + mono_regstate_free_int (rs, ins->sreg1); + } + if (reg_is_freeable (ins->sreg2) && prev_sreg2 >= 0 && reginfo [prev_sreg2].born_in >= i) { + DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2))); + mono_regstate_free_int (rs, ins->sreg2); + }*/ + + //DEBUG (print_ins (i, ins)); + /* this may result from a insert_before call */ + if (!tmp->next) + bb->code = tmp->data; + tmp = tmp->next; + } +} + +static unsigned char* +emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int size, gboolean is_signed) +{ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + x86_fnstcw_membase(code, X86_ESP, 0); + x86_mov_reg_membase (code, dreg, X86_ESP, 0, 2); + x86_alu_reg_imm (code, X86_OR, dreg, 0xc00); + x86_mov_membase_reg (code, X86_ESP, 2, dreg, 2); + x86_fldcw_membase (code, X86_ESP, 2); + if (size == 8) { + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fist_pop_membase (code, X86_ESP, 0, TRUE); + x86_pop_reg (code, dreg); + /* FIXME: need the high register + * x86_pop_reg (code, dreg_high); + */ + } else { + x86_push_reg (code, X86_EAX); // SP = SP - 4 + x86_fist_pop_membase (code, X86_ESP, 0, FALSE); + x86_pop_reg (code, dreg); + } + x86_fldcw_membase (code, X86_ESP, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + + if (size == 1) + x86_widen_reg (code, dreg, dreg, is_signed, FALSE); + else if (size == 2) + x86_widen_reg (code, dreg, dreg, is_signed, TRUE); + return code; +} + +static unsigned char* +mono_emit_stack_alloc (guchar *code, MonoInst* tree) +{ + int sreg = tree->sreg1; +#ifdef PLATFORM_WIN32 + guint8* br[5]; + + /* + * Under Windows: + * If requested stack size is larger than one page, + * perform stack-touch operation + */ + /* + * Generate stack probe code. + * Under Windows, it is necessary to allocate one page at a time, + * "touching" stack after each successful sub-allocation. This is + * because of the way stack growth is implemented - there is a + * guard page before the lowest stack page that is currently commited. + * Stack normally grows sequentially so OS traps access to the + * guard page and commits more pages when needed. + */ + x86_test_reg_imm (code, sreg, ~0xFFF); + br[0] = code; x86_branch8 (code, X86_CC_Z, 0, FALSE); + + br[2] = code; /* loop */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x1000); + x86_test_membase_reg (code, X86_ESP, 0, X86_ESP); + x86_alu_reg_imm (code, X86_SUB, sreg, 0x1000); + x86_alu_reg_imm (code, X86_CMP, sreg, 0x1000); + br[3] = code; x86_branch8 (code, X86_CC_AE, 0, FALSE); + x86_patch (br[3], br[2]); + x86_test_reg_reg (code, sreg, sreg); + br[4] = code; x86_branch8 (code, X86_CC_Z, 0, FALSE); + x86_alu_reg_reg (code, X86_SUB, X86_ESP, sreg); + + br[1] = code; x86_jump8 (code, 0); + + x86_patch (br[0], code); + x86_alu_reg_reg (code, X86_SUB, X86_ESP, sreg); + x86_patch (br[1], code); + x86_patch (br[4], code); +#else /* PLATFORM_WIN32 */ + x86_alu_reg_reg (code, X86_SUB, X86_ESP, tree->sreg1); +#endif + if (tree->flags & MONO_INST_INIT) { + int offset = 0; + if (tree->dreg != X86_EAX && sreg != X86_EAX) { + x86_push_reg (code, X86_EAX); + offset += 4; + } + if (tree->dreg != X86_ECX && sreg != X86_ECX) { + x86_push_reg (code, X86_ECX); + offset += 4; + } + if (tree->dreg != X86_EDI && sreg != X86_EDI) { + x86_push_reg (code, X86_EDI); + offset += 4; + } + + x86_shift_reg_imm (code, X86_SHR, sreg, 2); + if (sreg != X86_ECX) + x86_mov_reg_reg (code, X86_ECX, sreg, 4); + x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX); + + x86_lea_membase (code, X86_EDI, X86_ESP, offset); + x86_cld (code); + x86_prefix (code, X86_REP_PREFIX); + x86_stosl (code); + + if (tree->dreg != X86_EDI && sreg != X86_EDI) + x86_pop_reg (code, X86_EDI); + if (tree->dreg != X86_ECX && sreg != X86_ECX) + x86_pop_reg (code, X86_ECX); + if (tree->dreg != X86_EAX && sreg != X86_EAX) + x86_pop_reg (code, X86_EAX); + } + return code; +} + +#define REAL_PRINT_REG(text,reg) \ +mono_assert (reg >= 0); \ +x86_push_reg (code, X86_EAX); \ +x86_push_reg (code, X86_EDX); \ +x86_push_reg (code, X86_ECX); \ +x86_push_reg (code, reg); \ +x86_push_imm (code, reg); \ +x86_push_imm (code, text " %d %p\n"); \ +x86_mov_reg_imm (code, X86_EAX, printf); \ +x86_call_reg (code, X86_EAX); \ +x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3*4); \ +x86_pop_reg (code, X86_ECX); \ +x86_pop_reg (code, X86_EDX); \ +x86_pop_reg (code, X86_EAX); + +void +mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins; + MonoCallInst *call; + guint offset; + guint8 *code = cfg->native_code + cfg->code_len; + MonoInst *last_ins = NULL; + guint last_offset = 0; + int max_len, cpos; + + if (cfg->opt & MONO_OPT_PEEPHOLE) + peephole_pass (cfg, bb); + +#if 0 + /* + * various stratgies to align BBs. Using real loop detection or simply + * aligning every block leads to more consistent benchmark results, + * but usually slows down the code + * we should do the alignment outside this function or we should adjust + * bb->native offset as well or the code is effectively slowed down! + */ + /* align all blocks */ +// if ((pad = (cfg->code_len & (align - 1)))) { + /* poor man loop start detection */ +// if (bb->code && bb->in_count && bb->in_bb [0]->cil_code > bb->cil_code && (pad = (cfg->code_len & (align - 1)))) { + /* consider real loop detection and nesting level */ +// if (bb->loop_blocks && bb->nesting < 3 && (pad = (cfg->code_len & (align - 1)))) { + /* consider real loop detection */ + if (bb->loop_blocks && (pad = (cfg->code_len & (align - 1)))) { + pad = align - pad; + x86_padding (code, pad); + cfg->code_len += pad; + bb->native_offset = cfg->code_len; + } +#endif + + if (cfg->verbose_level > 2) + g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset); + + cpos = bb->max_offset; + + if (mono_trace_coverage) { + MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method); + g_assert (!mono_compile_aot); + cpos += 6; + + // fixme: make this work with inlining + g_assert_not_reached (); + //if (bb->cil_code) + //cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code; + /* this is not thread save, but good enough */ + /* fixme: howto handle overflows? */ + x86_inc_mem (code, &cov->data [bb->dfn].count); + } + + offset = code - cfg->native_code; + + ins = bb->code; + while (ins) { + offset = code - cfg->native_code; + + max_len = ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + + if (offset > (cfg->code_size - max_len - 16)) { + cfg->code_size *= 2; + cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + code = cfg->native_code + offset; + mono_jit_stats.code_reallocs++; + } + + mono_debug_record_line_number (cfg, ins, offset); + + switch (ins->opcode) { + case OP_STOREI1_MEMBASE_IMM: + x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 1); + break; + case OP_STOREI2_MEMBASE_IMM: + x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 2); + break; + case OP_STORE_MEMBASE_IMM: + case OP_STOREI4_MEMBASE_IMM: + x86_mov_membase_imm (code, ins->inst_destbasereg, ins->inst_offset, ins->inst_imm, 4); + break; + case OP_STOREI1_MEMBASE_REG: + x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 1); + break; + case OP_STOREI2_MEMBASE_REG: + x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 2); + break; + case OP_STORE_MEMBASE_REG: + case OP_STOREI4_MEMBASE_REG: + x86_mov_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1, 4); + break; + case CEE_LDIND_I: + case CEE_LDIND_I4: + case CEE_LDIND_U4: + x86_mov_reg_mem (code, ins->dreg, ins->inst_p0, 4); + break; + case OP_LOADU4_MEM: + x86_mov_reg_imm (code, ins->dreg, ins->inst_p0); + x86_mov_reg_membase (code, ins->dreg, ins->dreg, 0, 4); + break; + case OP_LOAD_MEMBASE: + case OP_LOADI4_MEMBASE: + case OP_LOADU4_MEMBASE: + x86_mov_reg_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, 4); + break; + case OP_LOADU1_MEMBASE: + x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, FALSE, FALSE); + break; + case OP_LOADI1_MEMBASE: + x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, TRUE, FALSE); + break; + case OP_LOADU2_MEMBASE: + x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, FALSE, TRUE); + break; + case OP_LOADI2_MEMBASE: + x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, TRUE, TRUE); + break; + case CEE_CONV_I1: + x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, FALSE); + break; + case CEE_CONV_I2: + x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, TRUE); + break; + case CEE_CONV_U1: + x86_widen_reg (code, ins->dreg, ins->sreg1, FALSE, FALSE); + break; + case CEE_CONV_U2: + x86_widen_reg (code, ins->dreg, ins->sreg1, FALSE, TRUE); + break; + case OP_COMPARE: + x86_alu_reg_reg (code, X86_CMP, ins->sreg1, ins->sreg2); + break; + case OP_COMPARE_IMM: + x86_alu_reg_imm (code, X86_CMP, ins->sreg1, ins->inst_imm); + break; + case OP_X86_COMPARE_MEMBASE_REG: + x86_alu_membase_reg (code, X86_CMP, ins->inst_basereg, ins->inst_offset, ins->sreg2); + break; + case OP_X86_COMPARE_MEMBASE_IMM: + x86_alu_membase_imm (code, X86_CMP, ins->inst_basereg, ins->inst_offset, ins->inst_imm); + break; + case OP_X86_COMPARE_REG_MEMBASE: + x86_alu_reg_membase (code, X86_CMP, ins->sreg1, ins->sreg2, ins->inst_offset); + break; + case OP_X86_TEST_NULL: + x86_test_reg_reg (code, ins->sreg1, ins->sreg1); + break; + case OP_X86_ADD_MEMBASE_IMM: + x86_alu_membase_imm (code, X86_ADD, ins->inst_basereg, ins->inst_offset, ins->inst_imm); + break; + case OP_X86_SUB_MEMBASE_IMM: + x86_alu_membase_imm (code, X86_SUB, ins->inst_basereg, ins->inst_offset, ins->inst_imm); + break; + case OP_X86_INC_MEMBASE: + x86_inc_membase (code, ins->inst_basereg, ins->inst_offset); + break; + case OP_X86_INC_REG: + x86_inc_reg (code, ins->dreg); + break; + case OP_X86_DEC_MEMBASE: + x86_dec_membase (code, ins->inst_basereg, ins->inst_offset); + break; + case OP_X86_DEC_REG: + x86_dec_reg (code, ins->dreg); + break; + case CEE_BREAK: + x86_breakpoint (code); + break; + case OP_ADDCC: + case CEE_ADD: + x86_alu_reg_reg (code, X86_ADD, ins->sreg1, ins->sreg2); + break; + case OP_ADC: + x86_alu_reg_reg (code, X86_ADC, ins->sreg1, ins->sreg2); + break; + case OP_ADD_IMM: + x86_alu_reg_imm (code, X86_ADD, ins->dreg, ins->inst_imm); + break; + case OP_ADC_IMM: + x86_alu_reg_imm (code, X86_ADC, ins->dreg, ins->inst_imm); + break; + case OP_SUBCC: + case CEE_SUB: + x86_alu_reg_reg (code, X86_SUB, ins->sreg1, ins->sreg2); + break; + case OP_SBB: + x86_alu_reg_reg (code, X86_SBB, ins->sreg1, ins->sreg2); + break; + case OP_SUB_IMM: + x86_alu_reg_imm (code, X86_SUB, ins->dreg, ins->inst_imm); + break; + case OP_SBB_IMM: + x86_alu_reg_imm (code, X86_SBB, ins->dreg, ins->inst_imm); + break; + case CEE_AND: + x86_alu_reg_reg (code, X86_AND, ins->sreg1, ins->sreg2); + break; + case OP_AND_IMM: + x86_alu_reg_imm (code, X86_AND, ins->sreg1, ins->inst_imm); + break; + case CEE_DIV: + x86_cdq (code); + x86_div_reg (code, ins->sreg2, TRUE); + break; + case CEE_DIV_UN: + x86_alu_reg_reg (code, X86_XOR, X86_EDX, X86_EDX); + x86_div_reg (code, ins->sreg2, FALSE); + break; + case OP_DIV_IMM: + x86_mov_reg_imm (code, ins->sreg2, ins->inst_imm); + x86_cdq (code); + x86_div_reg (code, ins->sreg2, TRUE); + break; + case CEE_REM: + x86_cdq (code); + x86_div_reg (code, ins->sreg2, TRUE); + break; + case CEE_REM_UN: + x86_alu_reg_reg (code, X86_XOR, X86_EDX, X86_EDX); + x86_div_reg (code, ins->sreg2, FALSE); + break; + case OP_REM_IMM: + x86_mov_reg_imm (code, ins->sreg2, ins->inst_imm); + x86_cdq (code); + x86_div_reg (code, ins->sreg2, TRUE); + break; + case CEE_OR: + x86_alu_reg_reg (code, X86_OR, ins->sreg1, ins->sreg2); + break; + case OP_OR_IMM: + x86_alu_reg_imm (code, X86_OR, ins->sreg1, ins->inst_imm); + break; + case CEE_XOR: + x86_alu_reg_reg (code, X86_XOR, ins->sreg1, ins->sreg2); + break; + case OP_XOR_IMM: + x86_alu_reg_imm (code, X86_XOR, ins->sreg1, ins->inst_imm); + break; + case CEE_SHL: + g_assert (ins->sreg2 == X86_ECX); + x86_shift_reg (code, X86_SHL, ins->dreg); + break; + case CEE_SHR: + g_assert (ins->sreg2 == X86_ECX); + x86_shift_reg (code, X86_SAR, ins->dreg); + break; + case OP_SHR_IMM: + x86_shift_reg_imm (code, X86_SAR, ins->dreg, ins->inst_imm); + break; + case OP_SHR_UN_IMM: + x86_shift_reg_imm (code, X86_SHR, ins->dreg, ins->inst_imm); + break; + case CEE_SHR_UN: + g_assert (ins->sreg2 == X86_ECX); + x86_shift_reg (code, X86_SHR, ins->dreg); + break; + case OP_SHL_IMM: + x86_shift_reg_imm (code, X86_SHL, ins->dreg, ins->inst_imm); + break; + case CEE_NOT: + x86_not_reg (code, ins->sreg1); + break; + case CEE_NEG: + x86_neg_reg (code, ins->sreg1); + break; + case CEE_MUL: + x86_imul_reg_reg (code, ins->sreg1, ins->sreg2); + break; + case OP_MUL_IMM: + x86_imul_reg_reg_imm (code, ins->dreg, ins->sreg1, ins->inst_imm); + break; + case CEE_MUL_OVF: + x86_imul_reg_reg (code, ins->sreg1, ins->sreg2); + EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException"); + break; + case CEE_MUL_OVF_UN: { + /* the mul operation and the exception check should most likely be split */ + int non_eax_reg, saved_eax = FALSE, saved_edx = FALSE; + /*g_assert (ins->sreg2 == X86_EAX); + g_assert (ins->dreg == X86_EAX);*/ + if (ins->sreg2 == X86_EAX) { + non_eax_reg = ins->sreg1; + } else if (ins->sreg1 == X86_EAX) { + non_eax_reg = ins->sreg2; + } else { + /* no need to save since we're going to store to it anyway */ + if (ins->dreg != X86_EAX) { + saved_eax = TRUE; + x86_push_reg (code, X86_EAX); + } + x86_mov_reg_reg (code, X86_EAX, ins->sreg1, 4); + non_eax_reg = ins->sreg2; + } + if (ins->dreg == X86_EDX) { + if (!saved_eax) { + saved_eax = TRUE; + x86_push_reg (code, X86_EAX); + } + } else if (ins->dreg != X86_EAX) { + saved_edx = TRUE; + x86_push_reg (code, X86_EDX); + } + x86_mul_reg (code, non_eax_reg, FALSE); + /* save before the check since pop and mov don't change the flags */ + if (saved_edx) + x86_pop_reg (code, X86_EDX); + if (saved_eax) + x86_pop_reg (code, X86_EAX); + if (ins->dreg != X86_EAX) + x86_mov_reg_reg (code, ins->dreg, X86_EAX, 4); + EMIT_COND_SYSTEM_EXCEPTION (X86_CC_O, FALSE, "OverflowException"); + break; + } + case OP_ICONST: + x86_mov_reg_imm (code, ins->dreg, ins->inst_c0); + break; + case OP_AOTCONST: + mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0); + x86_mov_reg_imm (code, ins->dreg, 0); + break; + case CEE_CONV_I4: + case CEE_CONV_U4: + case OP_MOVE: + x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4); + break; + case CEE_JMP: { + /* + * Note: this 'frame destruction' logic is useful for tail calls, too. + */ + int pos = -4; + if (cfg->used_int_regs & (1 << X86_EBX)) { + x86_mov_reg_membase (code, X86_EBX, X86_EBP, pos, 4); + pos -= 4; + } + if (cfg->used_int_regs & (1 << X86_EDI)) { + x86_mov_reg_membase (code, X86_EDI, X86_EBP, pos, 4); + pos -= 4; + } + if (cfg->used_int_regs & (1 << X86_ESI)) { + x86_mov_reg_membase (code, X86_ESI, X86_EBP, pos, 4); + pos -= 4; + } + /* restore ESP/EBP */ + x86_leave (code); + offset = code - cfg->native_code; + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, ins->inst_p0); + x86_jump32 (code, 0); + break; + } + case OP_CHECK_THIS: + /* ensure ins->sreg1 is not NULL */ + x86_alu_membase_imm (code, X86_CMP, ins->sreg1, 0, 0); + break; + case OP_FCALL: + case OP_LCALL: + case OP_VCALL: + case OP_VOIDCALL: + case CEE_CALL: + call = (MonoCallInst*)ins; + if (ins->flags & MONO_INST_HAS_METHOD) + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method); + else { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr); + } + x86_call_code (code, 0); + if (call->stack_usage) + x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); + break; + case OP_FCALL_REG: + case OP_LCALL_REG: + case OP_VCALL_REG: + case OP_VOIDCALL_REG: + case OP_CALL_REG: + call = (MonoCallInst*)ins; + x86_call_reg (code, ins->sreg1); + if (call->stack_usage) + x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); + break; + case OP_FCALL_MEMBASE: + case OP_LCALL_MEMBASE: + case OP_VCALL_MEMBASE: + case OP_VOIDCALL_MEMBASE: + case OP_CALL_MEMBASE: + call = (MonoCallInst*)ins; + x86_call_membase (code, ins->sreg1, ins->inst_offset); + if (call->stack_usage) + x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); + break; + case OP_OUTARG: + case OP_X86_PUSH: + x86_push_reg (code, ins->sreg1); + break; + case OP_X86_PUSH_IMM: + x86_push_imm (code, ins->inst_imm); + break; + case OP_X86_PUSH_MEMBASE: + x86_push_membase (code, ins->inst_basereg, ins->inst_offset); + break; + case OP_X86_PUSH_OBJ: + x86_alu_reg_imm (code, X86_SUB, X86_ESP, ins->inst_imm); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + x86_push_reg (code, X86_ECX); + if (ins->inst_offset) + x86_lea_membase (code, X86_ESI, ins->inst_basereg, ins->inst_offset); + else + x86_mov_reg_reg (code, X86_ESI, ins->inst_basereg, 4); + x86_lea_membase (code, X86_EDI, X86_ESP, 12); + x86_mov_reg_imm (code, X86_ECX, (ins->inst_imm >> 2)); + x86_cld (code); + x86_prefix (code, X86_REP_PREFIX); + x86_movsd (code); + x86_pop_reg (code, X86_ECX); + x86_pop_reg (code, X86_ESI); + x86_pop_reg (code, X86_EDI); + break; + case OP_X86_LEA: + x86_lea_memindex (code, ins->dreg, ins->sreg1, ins->inst_imm, ins->sreg2, ins->unused); + break; + case OP_X86_XCHG: + x86_xchg_reg_reg (code, ins->sreg1, ins->sreg2, 4); + break; + case OP_LOCALLOC: + /* keep alignment */ + x86_alu_reg_imm (code, X86_ADD, ins->sreg1, MONO_ARCH_FRAME_ALIGNMENT - 1); + x86_alu_reg_imm (code, X86_AND, ins->sreg1, ~(MONO_ARCH_FRAME_ALIGNMENT - 1)); + code = mono_emit_stack_alloc (code, ins); + x86_mov_reg_reg (code, ins->dreg, X86_ESP, 4); + break; + case CEE_RET: + x86_ret (code); + break; + case CEE_THROW: { + x86_push_reg (code, ins->sreg1); + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"mono_arch_throw_exception"); + x86_call_code (code, 0); + break; + } + case OP_ENDFILTER: + if (ins->sreg1 != X86_EAX) + x86_mov_reg_reg (code, X86_EAX, ins->sreg1, 4); + x86_mov_reg_membase (code, X86_ESP, X86_EBP, mono_exc_esp_offset, 4); + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + x86_ret (code); + break; + case CEE_ENDFINALLY: + /* + * restore ESP - which can be modified when we allocate value types in the filter + */ + x86_mov_reg_membase (code, X86_ESP, X86_EBP, mono_exc_esp_offset, 4); + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + x86_ret (code); + break; + case OP_HANDLER: + x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4); + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb); + x86_call_imm (code, 0); + break; + case OP_LABEL: + ins->inst_c0 = code - cfg->native_code; + break; + case CEE_BR: + //g_print ("target: %p, next: %p, curr: %p, last: %p\n", ins->inst_target_bb, bb->next_bb, ins, bb->last_ins); + //if ((ins->inst_target_bb == bb->next_bb) && ins == bb->last_ins) + //break; + if (ins->flags & MONO_INST_BRLABEL) { + if (ins->inst_i0->inst_c0) { + x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0); + } else { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0); + x86_jump32 (code, 0); + } + } else { + if (ins->inst_target_bb->native_offset) { + x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset); + } else { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb); + if ((cfg->opt & MONO_OPT_BRANCH) && + x86_is_imm8 (ins->inst_target_bb->max_offset - cpos)) + x86_jump8 (code, 0); + else + x86_jump32 (code, 0); + } + } + break; + case OP_BR_REG: + x86_jump_reg (code, ins->sreg1); + break; + case OP_CEQ: + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + break; + case OP_CLT: + x86_set_reg (code, X86_CC_LT, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + break; + case OP_CLT_UN: + x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + break; + case OP_CGT: + x86_set_reg (code, X86_CC_GT, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + break; + case OP_CGT_UN: + x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + break; + case OP_COND_EXC_EQ: + case OP_COND_EXC_NE_UN: + case OP_COND_EXC_LT: + case OP_COND_EXC_LT_UN: + case OP_COND_EXC_GT: + case OP_COND_EXC_GT_UN: + case OP_COND_EXC_GE: + case OP_COND_EXC_GE_UN: + case OP_COND_EXC_LE: + case OP_COND_EXC_LE_UN: + case OP_COND_EXC_OV: + case OP_COND_EXC_NO: + case OP_COND_EXC_C: + case OP_COND_EXC_NC: + EMIT_COND_SYSTEM_EXCEPTION (branch_cc_table [ins->opcode - OP_COND_EXC_EQ], + (ins->opcode < OP_COND_EXC_NE_UN), ins->inst_p1); + break; + case CEE_BEQ: + case CEE_BNE_UN: + case CEE_BLT: + case CEE_BLT_UN: + case CEE_BGT: + case CEE_BGT_UN: + case CEE_BGE: + case CEE_BGE_UN: + case CEE_BLE: + case CEE_BLE_UN: + EMIT_COND_BRANCH (ins, branch_cc_table [ins->opcode - CEE_BEQ], (ins->opcode < CEE_BNE_UN)); + break; + + /* floating point opcodes */ + case OP_R8CONST: { + double d = *(double *)ins->inst_p0; + + if (d == 0.0) { + x86_fldz (code); + } else if (d == 1.0) { + x86_fld1 (code); + } else { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_R8, ins->inst_p0); + x86_fld (code, NULL, TRUE); + } + break; + } + case OP_R4CONST: { + float f = *(float *)ins->inst_p0; + + if (f == 0.0) { + x86_fldz (code); + } else if (f == 1.0) { + x86_fld1 (code); + } else { + mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_R4, ins->inst_p0); + x86_fld (code, NULL, FALSE); + } + break; + } + case OP_STORER8_MEMBASE_REG: + x86_fst_membase (code, ins->inst_destbasereg, ins->inst_offset, TRUE, TRUE); + break; + case OP_LOADR8_MEMBASE: + x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, TRUE); + break; + case OP_STORER4_MEMBASE_REG: + x86_fst_membase (code, ins->inst_destbasereg, ins->inst_offset, FALSE, TRUE); + break; + case OP_LOADR4_MEMBASE: + x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, FALSE); + break; + case CEE_CONV_R4: /* FIXME: change precision */ + case CEE_CONV_R8: + x86_push_reg (code, ins->sreg1); + x86_fild_membase (code, X86_ESP, 0, FALSE); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + break; + case OP_X86_FP_LOAD_I8: + x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, TRUE); + break; + case OP_X86_FP_LOAD_I4: + x86_fild_membase (code, ins->inst_basereg, ins->inst_offset, FALSE); + break; + case OP_FCONV_TO_I1: + code = emit_float_to_int (cfg, code, ins->dreg, 1, TRUE); + break; + case OP_FCONV_TO_U1: + code = emit_float_to_int (cfg, code, ins->dreg, 1, FALSE); + break; + case OP_FCONV_TO_I2: + code = emit_float_to_int (cfg, code, ins->dreg, 2, TRUE); + break; + case OP_FCONV_TO_U2: + code = emit_float_to_int (cfg, code, ins->dreg, 2, FALSE); + break; + case OP_FCONV_TO_I4: + case OP_FCONV_TO_I: + code = emit_float_to_int (cfg, code, ins->dreg, 4, TRUE); + break; + case OP_FCONV_TO_I8: + /* we defined this instruction to output only to eax:edx */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + x86_fnstcw_membase(code, X86_ESP, 0); + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 0, 2); + x86_alu_reg_imm (code, X86_OR, X86_EAX, 0xc00); + x86_mov_membase_reg (code, X86_ESP, 2, X86_EAX, 2); + x86_fldcw_membase (code, X86_ESP, 2); + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fist_pop_membase (code, X86_ESP, 0, TRUE); + x86_pop_reg (code, X86_EAX); + x86_pop_reg (code, X86_EDX); + x86_fldcw_membase (code, X86_ESP, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); + break; + case OP_LCONV_TO_R_UN: { + static guint8 mn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x40 }; + guint8 *br; + + /* load 64bit integer to FP stack */ + x86_push_imm (code, 0); + x86_push_reg (code, ins->sreg2); + x86_push_reg (code, ins->sreg1); + x86_fild_membase (code, X86_ESP, 0, TRUE); + /* store as 80bit FP value */ + x86_fst80_membase (code, X86_ESP, 0); + + /* test if lreg is negative */ + x86_test_reg_reg (code, ins->sreg2, ins->sreg2); + br = code; x86_branch8 (code, X86_CC_GEZ, 0, TRUE); + + /* add correction constant mn */ + x86_fld80_mem (code, mn); + x86_fld80_membase (code, X86_ESP, 0); + x86_fp_op_reg (code, X86_FADD, 1, TRUE); + x86_fst80_membase (code, X86_ESP, 0); + + x86_patch (br, code); + + x86_fld80_membase (code, X86_ESP, 0); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); + + break; + } + case OP_LCONV_TO_OVF_I: { + guint8 *br [3], *label [1]; + + /* + * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000 + */ + x86_test_reg_reg (code, ins->sreg1, ins->sreg1); + + /* If the low word top bit is set, see if we are negative */ + br [0] = code; x86_branch8 (code, X86_CC_LT, 0, TRUE); + /* We are not negative (no top bit set, check for our top word to be zero */ + x86_test_reg_reg (code, ins->sreg2, ins->sreg2); + br [1] = code; x86_branch8 (code, X86_CC_EQ, 0, TRUE); + label [0] = code; + + /* throw exception */ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException"); + x86_jump32 (code, 0); + + x86_patch (br [0], code); + /* our top bit is set, check that top word is 0xfffffff */ + x86_alu_reg_imm (code, X86_CMP, ins->sreg2, 0xffffffff); + + x86_patch (br [1], code); + /* nope, emit exception */ + br [2] = code; x86_branch8 (code, X86_CC_NE, 0, TRUE); + x86_patch (br [2], label [0]); + + if (ins->dreg != ins->sreg1) + x86_mov_reg_reg (code, ins->dreg, ins->sreg1, 4); + break; + } + case OP_FADD: + x86_fp_op_reg (code, X86_FADD, 1, TRUE); + break; + case OP_FSUB: + x86_fp_op_reg (code, X86_FSUB, 1, TRUE); + break; + case OP_FMUL: + x86_fp_op_reg (code, X86_FMUL, 1, TRUE); + break; + case OP_FDIV: + x86_fp_op_reg (code, X86_FDIV, 1, TRUE); + break; + case OP_FNEG: + x86_fchs (code); + break; + case OP_SIN: + x86_fsin (code); + break; + case OP_COS: + x86_fcos (code); + break; + case OP_ABS: + x86_fabs (code); + break; + case OP_TAN: + x86_fptan (code); + break; + case OP_ATAN: + x86_fpatan (code); + break; + case OP_SQRT: + x86_fsqrt (code); + break; + case OP_X86_FPOP: + x86_fstp (code, 0); + break; + case OP_FREM: { + guint8 *l1, *l2; + + x86_push_reg (code, X86_EAX); + /* we need to exchange ST(0) with ST(1) */ + x86_fxch (code, 1); + + /* this requires a loop, because fprem1 somtimes + * returns a partial remainder */ + l1 = code; + x86_fprem (code); + x86_fnstsw (code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x0400); + l2 = code + 2; + x86_branch8 (code, X86_CC_NE, l1 - l2, FALSE); + + /* pop result */ + x86_fstp (code, 1); + + x86_pop_reg (code, X86_EAX); + break; + } + case OP_FCOMPARE: + if (cfg->opt & MONO_OPT_FCMOV) { + x86_fcomip (code, 1); + x86_fstp (code, 0); + break; + } + /* this overwrites EAX */ + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); + break; + case OP_FCEQ: + if (cfg->opt & MONO_OPT_FCMOV) { + /* zeroing the register at the start results in + * shorter and faster code (we can also remove the widening op) + */ + guchar *unordered_check; + x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg); + x86_fcomip (code, 1); + x86_fstp (code, 0); + unordered_check = code; + x86_branch8 (code, X86_CC_P, 0, FALSE); + x86_set_reg (code, X86_CC_EQ, ins->dreg, FALSE); + x86_patch (unordered_check, code); + break; + } + if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000); + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX); + break; + case OP_FCLT: + case OP_FCLT_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + /* zeroing the register at the start results in + * shorter and faster code (we can also remove the widening op) + */ + x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg); + x86_fcomip (code, 1); + x86_fstp (code, 0); + if (ins->opcode == OP_FCLT_UN) { + guchar *unordered_check = code; + guchar *jump_to_end; + x86_branch8 (code, X86_CC_P, 0, FALSE); + x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE); + jump_to_end = code; + x86_jump8 (code, 0); + x86_patch (unordered_check, code); + x86_inc_reg (code, ins->dreg); + x86_patch (jump_to_end, code); + } else { + x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE); + } + break; + } + if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); + if (ins->opcode == OP_FCLT_UN) { + guchar *is_not_zero_check, *end_jump; + is_not_zero_check = code; + x86_branch8 (code, X86_CC_NZ, 0, TRUE); + end_jump = code; + x86_jump8 (code, 0); + x86_patch (is_not_zero_check, code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500); + + x86_patch (end_jump, code); + } + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX); + break; + case OP_FCGT: + case OP_FCGT_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + /* zeroing the register at the start results in + * shorter and faster code (we can also remove the widening op) + */ + guchar *unordered_check; + x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg); + x86_fcomip (code, 1); + x86_fstp (code, 0); + if (ins->opcode == OP_FCGT) { + unordered_check = code; + x86_branch8 (code, X86_CC_P, 0, FALSE); + x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE); + x86_patch (unordered_check, code); + } else { + x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE); + } + break; + } + if (ins->dreg != X86_EAX) + x86_push_reg (code, X86_EAX); + + EMIT_FPCOMPARE(code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4500); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + if (ins->opcode == OP_FCGT_UN) { + guchar *is_not_zero_check, *end_jump; + is_not_zero_check = code; + x86_branch8 (code, X86_CC_NZ, 0, TRUE); + end_jump = code; + x86_jump8 (code, 0); + x86_patch (is_not_zero_check, code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500); + + x86_patch (end_jump, code); + } + x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); + x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); + + if (ins->dreg != X86_EAX) + x86_pop_reg (code, X86_EAX); + break; + case OP_FBEQ: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE); + break; + } + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000); + EMIT_COND_BRANCH (ins, X86_CC_EQ, TRUE); + break; + case OP_FBNE_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_P, FALSE); + EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE); + break; + } + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4000); + EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE); + break; + case OP_FBLT: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_GT, FALSE); + break; + } + EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE); + break; + case OP_FBLT_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_P, FALSE); + EMIT_COND_BRANCH (ins, X86_CC_GT, FALSE); + break; + } + if (ins->opcode == OP_FBLT_UN) { + guchar *is_not_zero_check, *end_jump; + is_not_zero_check = code; + x86_branch8 (code, X86_CC_NZ, 0, TRUE); + end_jump = code; + x86_jump8 (code, 0); + x86_patch (is_not_zero_check, code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500); + + x86_patch (end_jump, code); + } + EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE); + break; + case OP_FBGT: + case OP_FBGT_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_LT, FALSE); + break; + } + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + if (ins->opcode == OP_FBGT_UN) { + guchar *is_not_zero_check, *end_jump; + is_not_zero_check = code; + x86_branch8 (code, X86_CC_NZ, 0, TRUE); + end_jump = code; + x86_jump8 (code, 0); + x86_patch (is_not_zero_check, code); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x4500); + + x86_patch (end_jump, code); + } + EMIT_COND_BRANCH (ins, X86_CC_EQ, FALSE); + break; + case OP_FBGE: + case OP_FBGE_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_LE, FALSE); + break; + } + EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE); + break; + case OP_FBLE: + case OP_FBLE_UN: + if (cfg->opt & MONO_OPT_FCMOV) { + EMIT_COND_BRANCH (ins, X86_CC_P, FALSE); + EMIT_COND_BRANCH (ins, X86_CC_GE, FALSE); + break; + } + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE); + break; + case CEE_CKFINITE: { + x86_push_reg (code, X86_EAX); + x86_fxam (code); + x86_fnstsw (code); + x86_alu_reg_imm (code, X86_AND, X86_EAX, 0x4100); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0x0100); + x86_pop_reg (code, X86_EAX); + EMIT_COND_SYSTEM_EXCEPTION (X86_CC_EQ, FALSE, "ArithmeticException"); + break; + } + default: + g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__); + g_assert_not_reached (); + } + + if ((code - cfg->native_code - offset) > max_len) { + g_warning ("wrong maximal instruction length of instruction %s (exptected %d, got %d)", + mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset); + g_assert_not_reached (); + } + + cpos += max_len; + + last_ins = ins; + last_offset = offset; + + ins = ins->next; + } + + cfg->code_len = code - cfg->native_code; +} + +void +mono_arch_register_lowlevel_calls (void) +{ + mono_register_jit_icall (enter_method, "mono_enter_method", NULL, TRUE); + mono_register_jit_icall (leave_method, "mono_leave_method", NULL, TRUE); +} + +void +mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji) +{ + MonoJumpInfo *patch_info; + + for (patch_info = ji; patch_info; patch_info = patch_info->next) { + unsigned char *ip = patch_info->ip.i + code; + const unsigned char *target = NULL; + + switch (patch_info->type) { + case MONO_PATCH_INFO_BB: + target = patch_info->data.bb->native_offset + code; + break; + case MONO_PATCH_INFO_ABS: + target = patch_info->data.target; + break; + case MONO_PATCH_INFO_LABEL: + target = patch_info->data.inst->inst_c0 + code; + break; + case MONO_PATCH_INFO_IP: + *((gpointer *)(ip)) = ip; + continue; + case MONO_PATCH_INFO_INTERNAL_METHOD: { + MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name); + if (!mi) { + g_warning ("unknown MONO_PATCH_INFO_INTERNAL_METHOD %s", patch_info->data.name); + g_assert_not_reached (); + } + target = mi->wrapper; + break; + } + case MONO_PATCH_INFO_METHOD: + if (patch_info->data.method == method) { + target = code; + } else { + /* get the trampoline to the method from the domain */ + target = mono_arch_create_jit_trampoline (patch_info->data.method); + } + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.target; + int i; + + *((gconstpointer *)(ip + 2)) = patch_info->data.target; + + for (i = 0; i < patch_info->table_size; i++) { + table [i] = (int)patch_info->data.table [i] + code; + } + /* we put into the table the absolute address, no need fo x86_patch in this case */ + continue; + } + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IMAGE: + case MONO_PATCH_INFO_FIELD: + *((gconstpointer *)(ip + 1)) = patch_info->data.target; + continue; + case MONO_PATCH_INFO_R4: + case MONO_PATCH_INFO_R8: + *((gconstpointer *)(ip + 2)) = patch_info->data.target; + continue; + default: + g_assert_not_reached (); + } + x86_patch (ip, target); + } +} + +int +mono_arch_max_epilog_size (MonoCompile *cfg) +{ + int exc_count = 0, max_epilog_size = 16; + MonoJumpInfo *patch_info; + + if (cfg->method->save_lmf) + max_epilog_size += 128; + + if (mono_jit_trace_calls) + max_epilog_size += 50; + + if (mono_jit_profile) + max_epilog_size += 50; + + /* count the number of exception infos */ + + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if (patch_info->type == MONO_PATCH_INFO_EXC) + exc_count++; + } + + /* + * make sure we have enough space for exceptions + * 16 is the size of two push_imm instructions and a call + */ + max_epilog_size += exc_count*16; + + return max_epilog_size; +} + +guint8 * +mono_arch_emit_prolog (MonoCompile *cfg) +{ + MonoMethod *method = cfg->method; + MonoBasicBlock *bb; + MonoMethodSignature *sig; + MonoInst *inst; + int alloc_size, pos, max_offset, i; + guint8 *code; + + cfg->code_size = 256; + code = cfg->native_code = g_malloc (cfg->code_size); + + x86_push_reg (code, X86_EBP); + x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4); + + alloc_size = - cfg->stack_offset; + pos = 0; + + if (method->save_lmf) { + pos += sizeof (MonoLMF); + + /* save the current IP */ + mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL); + x86_push_imm (code, 0); + + /* save all caller saved regs */ + x86_push_reg (code, X86_EBX); + x86_push_reg (code, X86_EDI); + x86_push_reg (code, X86_ESI); + x86_push_reg (code, X86_EBP); + + /* save method info */ + x86_push_imm (code, method); + + /* get the address of lmf for the current thread */ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"mono_get_lmf_addr"); + x86_call_code (code, 0); + + /* push lmf */ + x86_push_reg (code, X86_EAX); + /* push *lfm (previous_lmf) */ + x86_push_membase (code, X86_EAX, 0); + /* *(lmf) = ESP */ + x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4); + } else { + + if (cfg->used_int_regs & (1 << X86_EBX)) { + x86_push_reg (code, X86_EBX); + pos += 4; + } + + if (cfg->used_int_regs & (1 << X86_EDI)) { + x86_push_reg (code, X86_EDI); + pos += 4; + } + + if (cfg->used_int_regs & (1 << X86_ESI)) { + x86_push_reg (code, X86_ESI); + pos += 4; + } + } + + alloc_size -= pos; + + if (alloc_size) + x86_alu_reg_imm (code, X86_SUB, X86_ESP, alloc_size); + + /* compute max_offset in order to use short forward jumps */ + max_offset = 0; + if (cfg->opt & MONO_OPT_BRANCH) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *ins = bb->code; + bb->max_offset = max_offset; + + if (mono_trace_coverage) + max_offset += 6; + + while (ins) { + max_offset += ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + ins = ins->next; + } + } + } + + if (mono_jit_trace_calls) + code = mono_arch_instrument_prolog (cfg, enter_method, code, TRUE); + + /* load arguments allocated to register from the stack */ + sig = method->signature; + pos = 0; + + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { + inst = cfg->varinfo [pos]; + if (inst->opcode == OP_REGVAR) { + x86_mov_reg_membase (code, inst->dreg, X86_EBP, inst->inst_offset, 4); + if (cfg->verbose_level > 2) + g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg)); + } + pos++; + } + + cfg->code_len = code - cfg->native_code; + + return code; +} + +void +mono_arch_emit_epilog (MonoCompile *cfg) +{ + MonoJumpInfo *patch_info; + MonoMethod *method = cfg->method; + int pos; + guint8 *code; + + code = cfg->native_code + cfg->code_len; + + if (mono_jit_trace_calls) + code = mono_arch_instrument_epilog (cfg, leave_method, code, TRUE); + + + pos = 0; + + if (method->save_lmf) { + pos = -sizeof (MonoLMF); + } else { + if (cfg->used_int_regs & (1 << X86_EBX)) { + pos -= 4; + } + if (cfg->used_int_regs & (1 << X86_EDI)) { + pos -= 4; + } + if (cfg->used_int_regs & (1 << X86_ESI)) { + pos -= 4; + } + } + + if (pos) + x86_lea_membase (code, X86_ESP, X86_EBP, pos); + + if (method->save_lmf) { + /* ebx = previous_lmf */ + x86_pop_reg (code, X86_EBX); + /* edi = lmf */ + x86_pop_reg (code, X86_EDI); + /* *(lmf) = previous_lmf */ + x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4); + + /* discard method info */ + x86_pop_reg (code, X86_ESI); + + /* restore caller saved regs */ + x86_pop_reg (code, X86_EBP); + x86_pop_reg (code, X86_ESI); + x86_pop_reg (code, X86_EDI); + x86_pop_reg (code, X86_EBX); + + } else { + + if (cfg->used_int_regs & (1 << X86_ESI)) { + x86_pop_reg (code, X86_ESI); + } + if (cfg->used_int_regs & (1 << X86_EDI)) { + x86_pop_reg (code, X86_EDI); + } + if (cfg->used_int_regs & (1 << X86_EBX)) { + x86_pop_reg (code, X86_EBX); + } + } + + x86_leave (code); + x86_ret (code); + + /* add code to raise exceptions */ + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_EXC: + x86_patch (patch_info->ip.i + cfg->native_code, code); + x86_push_imm (code, patch_info->data.target); + x86_push_imm (code, patch_info->ip.i + cfg->native_code); + patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD; + patch_info->data.name = "mono_arch_throw_exception_by_name"; + patch_info->ip.i = code - cfg->native_code; + x86_jump_code (code, 0); + break; + default: + /* do nothing */ + break; + } + } + + cfg->code_len = code - cfg->native_code; + + g_assert (cfg->code_len < cfg->code_size); + +} diff --git a/mono/mini/mini-x86.h b/mono/mini/mini-x86.h new file mode 100644 index 00000000000..b99ef7e0359 --- /dev/null +++ b/mono/mini/mini-x86.h @@ -0,0 +1,37 @@ +#ifndef __MONO_MINI_X86_H__ +#define __MONO_MINI_X86_H__ + +#include + +#define MONO_ARCH_FRAME_ALIGNMENT 4 + +/* fixme: align to 16byte instead of 32byte (we align to 32byte to get + * reproduceable results for benchmarks */ +#define MONO_ARCH_CODE_ALIGNMENT 32 + +#define MONO_ARCH_BASEREG X86_EBP +#define MONO_ARCH_RETREG1 X86_EAX +#define MONO_ARCH_RETREG2 X86_EDX +#define MONO_ARCH_EXC_REG X86_ECX + +#define MONO_ARCH_ENCODE_LREG(r1,r2) (r1 | (r2<<3)) + +#define inst_dreg_low dreg&7 +#define inst_dreg_high dreg>>3 +#define inst_sreg1_low sreg1&7 +#define inst_sreg1_high sreg1>>3 +#define inst_sreg2_low sreg2&7 +#define inst_sreg2_high sreg2>>3 + +struct MonoLMF { + gpointer previous_lmf; + gpointer lmf_addr; + MonoMethod *method; + guint32 ebp; + guint32 esi; + guint32 edi; + guint32 ebx; + guint32 eip; +}; + +#endif /* __MONO_MINI_X86_H__ */ diff --git a/mono/mini/mini.c b/mono/mini/mini.c new file mode 100644 index 00000000000..076e90b2d72 --- /dev/null +++ b/mono/mini/mini.c @@ -0,0 +1,6182 @@ +/* + * mini.c: The new Mono code generator. + * + * Author: + * Paolo Molaro (lupus@ximian.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mono/metadata/profiler.h" +#include +#include +#include + +#include "mini.h" +#include +#include +#include "inssel.h" +#include "debug.h" + +#include "jit-icalls.c" + +#define MONO_CHECK_THIS(ins) (cfg->method->signature->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0) + +static gpointer mono_jit_compile_method (MonoMethod *method); + +static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, + const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native); + +extern guint8 mono_burg_arity []; +/* helper methods signature */ +static MonoMethodSignature *helper_sig_long_long_long = NULL; +static MonoMethodSignature *helper_sig_long_long_int = NULL; +static MonoMethodSignature *helper_sig_newarr = NULL; +static MonoMethodSignature *helper_sig_ldstr = NULL; +static MonoMethodSignature *helper_sig_domain_get = NULL; +static MonoMethodSignature *helper_sig_object_new = NULL; +static MonoMethodSignature *helper_sig_compile = NULL; +static MonoMethodSignature *helper_sig_compile_virt = NULL; +static MonoMethodSignature *helper_sig_obj_ptr = NULL; +static MonoMethodSignature *helper_sig_ptr_void = NULL; +static MonoMethodSignature *helper_sig_void_ptr = NULL; +static MonoMethodSignature *helper_sig_void_obj = NULL; +static MonoMethodSignature *helper_sig_void_ptr_ptr = NULL; +static MonoMethodSignature *helper_sig_void_ptr_ptr_ptr = NULL; +static MonoMethodSignature *helper_sig_ptr_ptr_ptr = NULL; +static MonoMethodSignature *helper_sig_ptr_obj = NULL; +static MonoMethodSignature *helper_sig_initobj = NULL; +static MonoMethodSignature *helper_sig_memcpy = NULL; +static MonoMethodSignature *helper_sig_memset = NULL; +static MonoMethodSignature *helper_sig_ulong_double = NULL; +static MonoMethodSignature *helper_sig_long_double = NULL; +static MonoMethodSignature *helper_sig_uint_double = NULL; +static MonoMethodSignature *helper_sig_int_double = NULL; + +static guint32 default_opt = MONO_OPT_PEEPHOLE; + +guint32 mono_jit_tls_id = 0; +gboolean mono_jit_trace_calls = FALSE; +gboolean mono_break_on_exc = FALSE; +gboolean mono_compile_aot = FALSE; +gboolean mono_trace_coverage = FALSE; +gboolean mono_jit_profile = FALSE; +MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE; + +CRITICAL_SECTION *metadata_section = NULL; + +static int mini_verbose = 0; + +#ifdef MONO_USE_EXC_TABLES +static gboolean +mono_type_blittable (MonoType *type) +{ + if (type->byref) + return FALSE; + + switch (type->type){ + case MONO_TYPE_VOID: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + return TRUE; + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_CLASS: + return type->data.klass->blittable; + break; + default: + break; + } + + return FALSE; +} + +gboolean +mono_method_blittable (MonoMethod *method) +{ + MonoMethodSignature *sig; + int i; + + if (!method->addr) + return FALSE; + + if (!mono_arch_has_unwind_info (method->addr)) { + return FALSE; + } + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + return TRUE; + + sig = method->signature; + + if (!mono_type_blittable (sig->ret)) + return FALSE; + + for (i = 0; i < sig->param_count; i++) + if (!mono_type_blittable (sig->params [i])) + return FALSE; + + return TRUE; +} +#endif + +#if 0 +/* debug function */ +static void +print_method_from_ip (void *ip) +{ + MonoJitInfo *ji; + char *method; + + ji = mono_jit_info_table_find (mono_domain_get (), ip); + if (!ji) { + g_print ("No method at %p\n", ip); + return; + } + method = mono_method_full_name (ji->method, TRUE); + g_print ("IP at offset 0x%x of method %s (%p %p)\n", (char*)ip - (char*)ji->code_start, method, ji->code_start, (char*)ji->code_start + ji->code_size); + g_free (method); + +} +#endif + +#define MONO_INIT_VARINFO(vi,id) do { \ + (vi)->range.first_use.pos.bid = 0xffff; \ + (vi)->reg = -1; \ + (vi)->idx = (id); \ +} while (0) + +/* + * Basic blocks have two numeric identifiers: + * dfn: Depth First Number + * block_num: unique ID assigned at bblock creation + */ +#define NEW_BBLOCK(cfg) (mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock))) +#define ADD_BBLOCK(cfg,bbhash,b) do { \ + g_hash_table_insert (bbhash, (b)->cil_code, (b)); \ + (b)->block_num = cfg->num_bblocks++; \ + (b)->real_offset = real_offset; \ + } while (0) + +#define GET_BBLOCK(cfg,bbhash,tblock,ip) do { \ + (tblock) = g_hash_table_lookup (bbhash, (ip)); \ + if (!(tblock)) { \ + if ((ip) >= end || (ip) < header->code) goto unverified; \ + (tblock) = NEW_BBLOCK (cfg); \ + (tblock)->cil_code = (ip); \ + ADD_BBLOCK (cfg, (bbhash), (tblock)); \ + } \ + (tblock)->real_offset = real_offset; \ + } while (0) + +#define CHECK_BBLOCK(target,ip,tblock) do { \ + if ((target) < (ip) && !(tblock)->code) { \ + bb_recheck = g_list_prepend (bb_recheck, (tblock)); \ + if (cfg->verbose_level > 2) g_print ("queued block %d for check at IL%04x from IL%04x\n", (tblock)->block_num, (target) - header->code, (ip) - header->code); \ + } \ + } while (0) + +#define NEW_ICONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_ICONST; \ + (dest)->inst_c0 = (val); \ + (dest)->type = STACK_I4; \ + } while (0) + +/* FIXME: have a different definition of NEW_PCONST for 64 bit systems */ +#define NEW_PCONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_ICONST; \ + (dest)->inst_p0 = (val); \ + (dest)->type = STACK_PTR; \ + } while (0) + +#define NEW_CLASSCONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST; \ + (dest)->inst_p0 = (val); \ + (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_CLASS; \ + (dest)->type = STACK_PTR; \ + } while (0) + +#define NEW_IMAGECONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST; \ + (dest)->inst_p0 = (val); \ + (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_IMAGE; \ + (dest)->type = STACK_PTR; \ + } while (0) + +#define NEW_FIELDCONST(cfg,dest,field) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST; \ + (dest)->inst_p0 = (field); \ + (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_FIELD; \ + (dest)->type = STACK_PTR; \ + } while (0) + +#define NEW_METHODCONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_ICONST; \ + (dest)->inst_p0 = (val); \ + (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_METHODCONST; \ + (dest)->type = STACK_PTR; \ + } while (0) + +#define NEW_DOMAINCONST(cfg,dest) do { \ + if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) { \ + NEW_TEMPLOAD (cfg, dest, mono_get_domainvar (cfg)->inst_c0); \ + } else { \ + NEW_PCONST (cfg, dest, (cfg)->domain); \ + } \ + } while (0) + +#define GET_VARINFO_INST(cfg,num) ((cfg)->varinfo [(num)]->inst) + +#define NEW_ARGLOAD(cfg,dest,num) do { \ + if (arg_array [(num)]->opcode == OP_ICONST) (dest) = arg_array [(num)]; else { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_LOAD; \ + (dest)->inst_i0 = arg_array [(num)]; \ + (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \ + type_to_eval_stack_type (param_types [(num)], (dest)); \ + (dest)->klass = (dest)->inst_i0->klass; \ + }} while (0) + +#define NEW_LOCLOAD(cfg,dest,num) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_LOAD; \ + (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \ + (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \ + type_to_eval_stack_type (header->locals [(num)], (dest)); \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + +#define NEW_LOCLOADA(cfg,dest,num) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \ + (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \ + (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \ + (dest)->opcode = OP_LDADDR; \ + (dest)->type = STACK_MP; \ + (dest)->klass = (dest)->inst_i0->klass; \ + (cfg)->disable_ssa = TRUE; \ + } while (0) + +#define NEW_RETLOADA(cfg,dest) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \ + (dest)->inst_i0 = (cfg)->ret; \ + (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \ + (dest)->opcode = CEE_LDIND_I; \ + (dest)->type = STACK_MP; \ + (dest)->klass = (dest)->inst_i0->klass; \ + (cfg)->disable_ssa = TRUE; \ + } while (0) + +#define NEW_ARGLOADA(cfg,dest,num) do { \ + if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \ + (dest)->inst_i0 = arg_array [(num)]; \ + (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \ + (dest)->opcode = OP_LDADDR; \ + (dest)->type = STACK_MP; \ + (dest)->klass = (dest)->inst_i0->klass; \ + (cfg)->disable_ssa = TRUE; \ + } while (0) + +#define NEW_TEMPLOAD(cfg,dest,num) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_LOAD; \ + (dest)->inst_i0 = (cfg)->varinfo [(num)]; \ + (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \ + type_to_eval_stack_type ((dest)->inst_i0->inst_vtype, (dest)); \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + +#define NEW_TEMPLOADA(cfg,dest,num) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \ + (dest)->inst_i0 = (cfg)->varinfo [(num)]; \ + (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \ + (dest)->opcode = OP_LDADDR; \ + (dest)->type = STACK_MP; \ + (dest)->klass = (dest)->inst_i0->klass; \ + (cfg)->disable_ssa = TRUE; \ + } while (0) + + +#define NEW_INDLOAD(cfg,dest,addr,vtype) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->inst_left = addr; \ + (dest)->opcode = mono_type_to_ldind (vtype); \ + type_to_eval_stack_type (vtype, (dest)); \ + /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/ \ + } while (0) + +#define NEW_INDSTORE(cfg,dest,addr,value,vtype) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->inst_i0 = addr; \ + (dest)->opcode = mono_type_to_stind (vtype); \ + (dest)->inst_i1 = (value); \ + /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/ \ + } while (0) + +#define NEW_TEMPSTORE(cfg,dest,num,inst) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->ssa_op = MONO_SSA_STORE; \ + (dest)->inst_i0 = (cfg)->varinfo [(num)]; \ + (dest)->opcode = mono_type_to_stind ((dest)->inst_i0->inst_vtype); \ + (dest)->inst_i1 = (inst); \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + +#define NEW_LOCSTORE(cfg,dest,num,inst) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_type_to_stind (header->locals [(num)]); \ + (dest)->ssa_op = MONO_SSA_STORE; \ + (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \ + (dest)->inst_i1 = (inst); \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + +#define NEW_ARGSTORE(cfg,dest,num,inst) do { \ + if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = mono_type_to_stind (param_types [(num)]); \ + (dest)->ssa_op = MONO_SSA_STORE; \ + (dest)->inst_i0 = arg_array [(num)]; \ + (dest)->inst_i1 = (inst); \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + +#define ADD_BINOP(op) do { \ + MONO_INST_NEW (cfg, ins, (op)); \ + ins->cil_code = ip; \ + sp -= 2; \ + ins->inst_i0 = sp [0]; \ + ins->inst_i1 = sp [1]; \ + *sp++ = ins; \ + type_from_op (ins); \ + CHECK_TYPE (ins); \ + } while (0) + +#define ADD_UNOP(op) do { \ + MONO_INST_NEW (cfg, ins, (op)); \ + ins->cil_code = ip; \ + sp--; \ + ins->inst_i0 = sp [0]; \ + *sp++ = ins; \ + type_from_op (ins); \ + CHECK_TYPE (ins); \ + } while (0) + +#define ADD_BINCOND(next_block) do { \ + MonoInst *cmp; \ + MONO_INST_NEW(cfg, cmp, OP_COMPARE); \ + sp -= 2; \ + cmp->inst_i0 = sp [0]; \ + cmp->inst_i1 = sp [1]; \ + cmp->cil_code = ins->cil_code; \ + type_from_op (cmp); \ + CHECK_TYPE (cmp); \ + ins->inst_i0 = cmp; \ + MONO_ADD_INS (bblock, ins); \ + ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \ + GET_BBLOCK (cfg, bbhash, tblock, target); \ + link_bblock (cfg, bblock, tblock); \ + ins->inst_true_bb = tblock; \ + CHECK_BBLOCK (target, ip, tblock); \ + if ((next_block)) { \ + link_bblock (cfg, bblock, (next_block)); \ + ins->inst_false_bb = (next_block); \ + start_new_bblock = 1; \ + } else { \ + GET_BBLOCK (cfg, bbhash, tblock, ip); \ + link_bblock (cfg, bblock, tblock); \ + ins->inst_false_bb = tblock; \ + start_new_bblock = 2; \ + } \ + } while (0) + +/* FIXME: handle float, long ... */ +#define ADD_UNCOND(istrue) do { \ + MonoInst *cmp; \ + MONO_INST_NEW(cfg, cmp, OP_COMPARE); \ + sp--; \ + cmp->inst_i0 = sp [0]; \ + switch (cmp->inst_i0->type) { \ + case STACK_I8: \ + cmp->inst_i1 = zero_int64; break; \ + case STACK_R8: \ + cmp->inst_i1 = zero_r8; break; \ + case STACK_PTR: \ + case STACK_MP: \ + cmp->inst_i1 = zero_ptr; break; \ + case STACK_OBJ: \ + cmp->inst_i1 = zero_obj; break; \ + default: \ + cmp->inst_i1 = zero_int32; \ + } \ + cmp->cil_code = ins->cil_code; \ + type_from_op (cmp); \ + CHECK_TYPE (cmp); \ + ins->inst_i0 = cmp; \ + ins->opcode = (istrue)? CEE_BNE_UN: CEE_BEQ; \ + MONO_ADD_INS (bblock, ins); \ + ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \ + GET_BBLOCK (cfg, bbhash, tblock, target); \ + link_bblock (cfg, bblock, tblock); \ + ins->inst_true_bb = tblock; \ + CHECK_BBLOCK (target, ip, tblock); \ + GET_BBLOCK (cfg, bbhash, tblock, ip); \ + link_bblock (cfg, bblock, tblock); \ + ins->inst_false_bb = tblock; \ + start_new_bblock = 2; \ + } while (0) + +#define NEW_LDELEMA(cfg,dest,sp,k) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = CEE_LDELEMA; \ + (dest)->inst_left = (sp) [0]; \ + (dest)->inst_right = (sp) [1]; \ + (dest)->type = STACK_MP; \ + (dest)->klass = (k); \ + } while (0) + +static GHashTable *coverage_hash = NULL; + +MonoCoverageInfo * +mono_allocate_coverage_info (MonoMethod *method, int size) +{ + MonoCoverageInfo *res; + + if (!coverage_hash) + coverage_hash = g_hash_table_new (NULL, NULL); + + res = g_malloc0 (sizeof (MonoCoverageInfo) + sizeof (int) * size * 2); + + res->entries = size; + + g_hash_table_insert (coverage_hash, method, res); + + return res; +} + +MonoCoverageInfo * +mono_get_coverage_info (MonoMethod *method) +{ + if (!coverage_hash) + return NULL; + + return g_hash_table_lookup (coverage_hash, method); +} + +#if 0 +static gint +compare_bblock (gconstpointer a, gconstpointer b) +{ + const MonoBasicBlock *b1 = a; + const MonoBasicBlock *b2 = b; + + return b2->cil_code - b1->cil_code; +} +#endif + +/* * + * link_bblock: Links two basic blocks + * + * links two basic blocks in the control flow graph, the 'from' + * argument is the starting block and the 'to' argument is the block + * the control flow ends to after 'from'. + */ +static void +link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to) +{ + MonoBasicBlock **newa; + int i, found; + +#if 0 + if (from->cil_code) { + if (to->cil_code) + g_print ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code); + else + g_print ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code); + } else { + if (to->cil_code) + g_print ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code); + else + g_print ("edge from entry to exit\n"); + } +#endif + found = FALSE; + for (i = 0; i < from->out_count; ++i) { + if (to == from->out_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1)); + for (i = 0; i < from->out_count; ++i) { + newa [i] = from->out_bb [i]; + } + newa [i] = to; + from->out_count++; + from->out_bb = newa; + } + + found = FALSE; + for (i = 0; i < to->in_count; ++i) { + if (from == to->in_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1)); + for (i = 0; i < to->in_count; ++i) { + newa [i] = to->in_bb [i]; + } + newa [i] = from; + to->in_count++; + to->in_bb = newa; + } +} + +/* + * We mark each basic block with a region ID. We use that to avoid BB + * optimizations when blocks are in different regions. + */ +static int +mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths) +{ + MonoMethod *method = cfg->method; + MonoMethodHeader *header = ((MonoMethodNormal *)method)->header; + MonoExceptionClause *clause; + int i; + + /* first search for handlers and filters */ + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->token_or_filter) && + (offset < (clause->token_or_filter + filter_lengths [i]))) + return (i << 8) | 128 | clause->flags; + + if (MONO_OFFSET_IN_HANDLER (clause, offset)) { + return (i << 8) | 64 | clause->flags; + } + } + + /* search the try blocks */ + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_CLAUSE (clause, offset)) + return (i << 8) | clause->flags; + } + + return -1; +} + +static MonoBasicBlock * +mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type) +{ + MonoMethod *method = cfg->method; + MonoMethodHeader *header = ((MonoMethodNormal *)method)->header; + MonoExceptionClause *clause; + MonoBasicBlock *handler; + int i; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) && + (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) { + if (clause->flags & type) { + handler = g_hash_table_lookup (cfg->bb_hash, header->code + clause->handler_offset); + g_assert (handler); + return handler; + } + } + } + return NULL; +} + + +static void +df_visit (MonoBasicBlock *start, int *dfn, MonoBasicBlock **array) +{ + int i; + + array [*dfn] = start; + /*g_print ("visit %d at %p\n", *dfn, start->cil_code);*/ + for (i = 0; i < start->out_count; ++i) { + if (start->out_bb [i]->dfn) + continue; + (*dfn)++; + start->out_bb [i]->dfn = *dfn; + start->out_bb [i]->df_parent = start; + array [*dfn] = start->out_bb [i]; + df_visit (start->out_bb [i], dfn, array); + } +} + +typedef struct { + const guchar *code; + MonoBasicBlock *best; +} PrevStruct; + +static void +previous_foreach (gconstpointer key, gpointer val, gpointer data) +{ + PrevStruct *p = data; + MonoBasicBlock *bb = val; + //printf ("FIDPREV %d %p %p %p %p %p %d %d %d\n", bb->block_num, p->code, bb, p->best, bb->cil_code, p->best->cil_code, + //bb->method == p->best->method, bb->cil_code < p->code, bb->cil_code > p->best->cil_code); + + if (bb->cil_code && bb->cil_code < p->code && bb->cil_code > p->best->cil_code) + p->best = bb; +} + +static MonoBasicBlock* +find_previous (GHashTable *bb_hash, MonoBasicBlock *start, const guchar *code) { + PrevStruct p; + + p.code = code; + p.best = start; + + g_hash_table_foreach (bb_hash, (GHFunc)previous_foreach, &p); + return p.best; +} + +static void +split_bblock (MonoCompile *cfg, MonoBasicBlock *first, MonoBasicBlock *second) { + int i, j; + MonoInst *inst; + MonoBasicBlock *bb; + + if (second->code) + return; + + /* + * FIXME: take into account all the details: + * second may have been the target of more than one bblock + */ + second->out_count = first->out_count; + second->out_bb = first->out_bb; + + for (i = 0; i < first->out_count; ++i) { + bb = first->out_bb [i]; + for (j = 0; j < bb->in_count; ++j) { + if (bb->in_bb [j] == first) + bb->in_bb [j] = second; + } + } + + first->out_count = 0; + first->out_bb = NULL; + link_bblock (cfg, first, second); + + second->last_ins = first->last_ins; + + /*g_print ("start search at %p for %p\n", first->cil_code, second->cil_code);*/ + for (inst = first->code; inst && inst->next; inst = inst->next) { + /*char *code = mono_disasm_code_one (NULL, cfg->method, inst->next->cil_code, NULL); + g_print ("found %p: %s", inst->next->cil_code, code); + g_free (code);*/ + if (inst->cil_code < second->cil_code && inst->next->cil_code >= second->cil_code) { + second->code = inst->next; + inst->next = NULL; + first->last_ins = inst; + second->next_bb = first->next_bb; + first->next_bb = second; + return; + } + } + if (!second->code) { + g_warning ("bblock split failed in %s::%s\n", cfg->method->klass->name, cfg->method->name); + //G_BREAKPOINT (); + } +} + +guint +mono_type_to_ldind (MonoType *type) +{ + int t = type->type; + + if (type->byref) + return CEE_LDIND_I; + +handle_enum: + switch (t) { + case MONO_TYPE_I1: + return CEE_LDIND_I1; + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return CEE_LDIND_U1; + case MONO_TYPE_I2: + return CEE_LDIND_I2; + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return CEE_LDIND_U2; + case MONO_TYPE_I4: + return CEE_LDIND_I4; + case MONO_TYPE_U4: + return CEE_LDIND_U4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + return CEE_LDIND_I; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return CEE_LDIND_REF; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return CEE_LDIND_I8; + case MONO_TYPE_R4: + return CEE_LDIND_R4; + case MONO_TYPE_R8: + return CEE_LDIND_R8; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + t = type->data.klass->enum_basetype->type; + goto handle_enum; + } + return CEE_LDOBJ; + default: + g_error ("unknown type 0x%02x in type_to_ldind", type->type); + } + return -1; +} + +guint +mono_type_to_stind (MonoType *type) +{ + int t = type->type; + + if (type->byref) + return CEE_STIND_I; + +handle_enum: + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + return CEE_STIND_I1; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + return CEE_STIND_I2; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + return CEE_STIND_I4; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + return CEE_STIND_I; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return CEE_STIND_REF; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return CEE_STIND_I8; + case MONO_TYPE_R4: + return CEE_STIND_R4; + case MONO_TYPE_R8: + return CEE_STIND_R8; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + t = type->data.klass->enum_basetype->type; + goto handle_enum; + } + return CEE_STOBJ; + /* fail right now */ + default: + g_error ("unknown type %02x in type_to_stind", type->type); + } + return -1; +} + +/* + * Returns the type used in the eval stack when @type is loaded. + * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases. + */ +static void +type_to_eval_stack_type (MonoType *type, MonoInst *inst) { + int t = type->type; + + if (type->byref) { + inst->type = STACK_MP; + return; + } + +handle_enum: + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + inst->type = STACK_I4; + return; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + inst->type = STACK_PTR; + return; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + inst->type = STACK_OBJ; + return; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + inst->type = STACK_I8; + return; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + inst->type = STACK_R8; + return; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + t = type->data.klass->enum_basetype->type; + goto handle_enum; + } else { + inst->klass = type->data.klass; + inst->type = STACK_VTYPE; + return; + } + default: + g_error ("unknown type 0x%02x in eval stack type", type->type); + } +} + +/* + * The following tables are used to quickly validate the IL code in type_from_op (). + */ +static const char +bin_num_table [STACK_MAX] [STACK_MAX] = { + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV} +}; + +static const char +neg_table [] = { + STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV +}; + +/* reduce the size of this table */ +static const char +bin_int_table [STACK_MAX] [STACK_MAX] = { + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV} +}; + +static const char +bin_comp_table [STACK_MAX] [STACK_MAX] = { + {0}, + {0, 1, 0, 1, 0, 0, 4, 0}, + {0, 0, 1, 0, 0, 0, 0, 0}, + {0, 1, 0, 1, 0, 2, 0, 0}, + {0, 0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 2, 0, 1, 0, 0}, + {0, 4, 0, 0, 0, 0, 3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, +}; + +/* reduce the size of this table */ +static const char +shift_table [STACK_MAX] [STACK_MAX] = { + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV} +}; + +/* + * Tables to map from the non-specific opcode to the matching + * type-specific opcode. + */ +/* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */ +static const guint16 +binops_op_map [STACK_MAX] = { + 0, 0, OP_LADD-CEE_ADD, OP_PADD-CEE_ADD, OP_FADD-CEE_ADD, 0 +}; + +/* handles from CEE_NEG to CEE_CONV_U8 */ +static const guint16 +unops_op_map [STACK_MAX] = { + 0, 0, OP_LNEG-CEE_NEG, OP_PNEG-CEE_NEG, OP_FNEG-CEE_NEG, 0 +}; + +/* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */ +static const guint16 +ovfops_op_map [STACK_MAX] = { + 0, 0, OP_LCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_FCONV_TO_U2-CEE_CONV_U2, 0 +}; + +/* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */ +static const guint16 +ovf2ops_op_map [STACK_MAX] = { + 0, 0, OP_LCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_FCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, 0 +}; + +/* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */ +static const guint16 +ovf3ops_op_map [STACK_MAX] = { + 0, 0, OP_LCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_FCONV_TO_OVF_I1-CEE_CONV_OVF_I1, 0 +}; + +/* handles from CEE_CEQ to CEE_CLT_UN */ +static const guint16 +ceqops_op_map [STACK_MAX] = { + 0, 0, OP_LCEQ-CEE_CEQ, OP_PCEQ-CEE_CEQ, OP_FCEQ-CEE_CEQ, 0 +}; + +/* + * Sets ins->type (the type on the eval stack) according to the + * type of the opcode and the arguments to it. + * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV. + * + * FIXME: this function sets ins->type unconditionally in some cases, but + * it should set it to invalid for some types (a conv.x on an object) + */ +static void +type_from_op (MonoInst *ins) { + switch (ins->opcode) { + /* binops */ + case CEE_ADD: + case CEE_SUB: + case CEE_MUL: + case CEE_DIV: + case CEE_REM: + /* FIXME: check unverifiable args for STACK_MP */ + ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type]; + ins->opcode += binops_op_map [ins->type]; + return; + case CEE_DIV_UN: + case CEE_REM_UN: + case CEE_AND: + case CEE_OR: + case CEE_XOR: + ins->type = bin_int_table [ins->inst_i0->type] [ins->inst_i1->type]; + ins->opcode += binops_op_map [ins->type]; + return; + case CEE_SHL: + case CEE_SHR: + case CEE_SHR_UN: + ins->type = shift_table [ins->inst_i0->type] [ins->inst_i1->type]; + ins->opcode += binops_op_map [ins->type]; + return; + case OP_COMPARE: + /* FIXME: handle some specifics with ins->next->type */ + ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV; + return; + case 256+CEE_CEQ: + case 256+CEE_CGT: + case 256+CEE_CGT_UN: + case 256+CEE_CLT: + case 256+CEE_CLT_UN: + ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV; + ins->opcode += ceqops_op_map [ins->inst_i0->type]; + return; + /* unops */ + case CEE_NEG: + ins->type = neg_table [ins->inst_i0->type]; + ins->opcode += unops_op_map [ins->type]; + return; + case CEE_NOT: + if (ins->inst_i0->type >= STACK_I4 && ins->inst_i0->type <= STACK_PTR) + ins->type = ins->inst_i0->type; + else + ins->type = STACK_INV; + ins->opcode += unops_op_map [ins->type]; + return; + case CEE_CONV_I1: + case CEE_CONV_I2: + case CEE_CONV_I4: + case CEE_CONV_U4: + ins->type = STACK_I4; + ins->opcode += unops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_R_UN: + ins->type = STACK_R8; + switch (ins->inst_i0->type) { + case STACK_I4: + case STACK_PTR: + break; + case STACK_I8: + ins->opcode = OP_LCONV_TO_R_UN; + break; + } + return; + case CEE_CONV_OVF_I1: + case CEE_CONV_OVF_U1: + case CEE_CONV_OVF_I2: + case CEE_CONV_OVF_U2: + case CEE_CONV_OVF_I4: + case CEE_CONV_OVF_U4: + ins->type = STACK_I4; + ins->opcode += ovf3ops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_OVF_I_UN: + case CEE_CONV_OVF_U_UN: + ins->type = STACK_PTR; + ins->opcode += ovf2ops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_OVF_I1_UN: + case CEE_CONV_OVF_I2_UN: + case CEE_CONV_OVF_I4_UN: + case CEE_CONV_OVF_U1_UN: + case CEE_CONV_OVF_U2_UN: + case CEE_CONV_OVF_U4_UN: + ins->type = STACK_I4; + ins->opcode += ovf2ops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_U: + ins->type = STACK_PTR; + switch (ins->inst_i0->type) { + case STACK_I4: + case STACK_PTR: + case STACK_MP: + break; + case STACK_I8: + ins->opcode = OP_LCONV_TO_U; + break; + case STACK_R8: + ins->opcode = OP_FCONV_TO_U; + break; + } + return; + case CEE_CONV_I8: + case CEE_CONV_U8: + ins->type = STACK_I8; + ins->opcode += unops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_OVF_I8: + case CEE_CONV_OVF_U8: + ins->type = STACK_I8; + ins->opcode += ovf3ops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_OVF_U8_UN: + case CEE_CONV_OVF_I8_UN: + ins->type = STACK_I8; + ins->opcode += ovf2ops_op_map [ins->inst_i0->type]; + return; + case CEE_CONV_R4: + case CEE_CONV_R8: + ins->type = STACK_R8; + ins->opcode += unops_op_map [ins->inst_i0->type]; + return; + case CEE_CKFINITE: + ins->type = STACK_R8; + return; + case CEE_CONV_U2: + case CEE_CONV_U1: + ins->type = STACK_I4; + ins->opcode += ovfops_op_map [ins->inst_i0->type]; + break; + case CEE_CONV_I: + case CEE_CONV_OVF_I: + case CEE_CONV_OVF_U: + ins->type = STACK_PTR; + ins->opcode += ovfops_op_map [ins->inst_i0->type]; + return; + case CEE_ADD_OVF: + case CEE_ADD_OVF_UN: + case CEE_MUL_OVF: + case CEE_MUL_OVF_UN: + case CEE_SUB_OVF: + case CEE_SUB_OVF_UN: + ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type]; + ins->opcode += ovfops_op_map [ins->inst_i0->type]; + return; + default: + g_error ("opcode 0x%04x not handled in type from op", ins->opcode); + break; + } +} + +static const char +ldind_type [] = { + STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_MP, STACK_R8, STACK_R8, STACK_OBJ +}; + +/* map ldelem.x to the matching ldind.x opcode */ +static const guchar +ldelem_to_ldind [] = { + CEE_LDIND_I1, + CEE_LDIND_U1, + CEE_LDIND_I2, + CEE_LDIND_U2, + CEE_LDIND_I4, + CEE_LDIND_U4, + CEE_LDIND_I8, + CEE_LDIND_I, + CEE_LDIND_R4, + CEE_LDIND_R8, + CEE_LDIND_REF +}; + +/* map stelem.x to the matching stind.x opcode */ +static const guchar +stelem_to_stind [] = { + CEE_STIND_I, + CEE_STIND_I1, + CEE_STIND_I2, + CEE_STIND_I4, + CEE_STIND_I8, + CEE_STIND_R4, + CEE_STIND_R8, + CEE_STIND_REF +}; + +#if 0 + +static const char +param_table [STACK_MAX] [STACK_MAX] = { + {0}, +}; + +static int +check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) { + int i; + + if (sig->hasthis) { + switch (args->type) { + case STACK_I4: + case STACK_I8: + case STACK_R8: + case STACK_VTYPE: + case STACK_INV: + return 0; + } + args++; + } + for (i = 0; i < sig->param_count; ++i) { + switch (args [i].type) { + case STACK_INV: + return 0; + case STACK_MP: + if (!sig->params [i]->byref) + return 0; + continue; + case STACK_OBJ: + if (sig->params [i]->byref) + return 0; + switch (sig->params [i]->type) { + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + break; + default: + return 0; + } + continue; + case STACK_R8: + if (sig->params [i]->byref) + return 0; + if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8) + return 0; + continue; + case STACK_PTR: + case STACK_I4: + case STACK_I8: + case STACK_VTYPE: + break; + } + /*if (!param_table [args [i].type] [sig->params [i]->type]) + return 0;*/ + } + return 1; +} +#endif + +/* + * When we need a pointer to the current domain many times in a method, we + * call mono_domain_get() once and we store the result in a local variable. + * This function returns the variable that represents the MonoDomain*. + */ +inline static MonoInst * +mono_get_domainvar (MonoCompile *cfg) +{ + if (!cfg->domainvar) + cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + return cfg->domainvar; +} + +static void +realloc_var_info (MonoCompile *cfg, int count) +{ + gpointer data; + int num = cfg->varinfo_count; + + g_assert (count > num); + + cfg->varinfo_count = count; + + data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * cfg->varinfo_count); + if (num) + memcpy (data, cfg->varinfo, sizeof (MonoInst*) * num); + cfg->varinfo = (MonoInst **)data; + + data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar*) * cfg->varinfo_count); + if (num) + memcpy (data, cfg->vars, sizeof (MonoMethodVar*) * num); + cfg->vars = (MonoMethodVar **)data; +} + +MonoInst* +mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) +{ + MonoInst *inst; + int num = cfg->num_varinfo; + + if ((num + 1) >= cfg->varinfo_count) + realloc_var_info (cfg, cfg->varinfo_count + 16); + + mono_jit_stats.allocate_var++; + + MONO_INST_NEW (cfg, inst, opcode); + inst->inst_c0 = num; + inst->inst_vtype = type; + inst->klass = mono_class_from_mono_type (type); + /* if set to 1 the variable is native */ + inst->unused = 0; + + cfg->varinfo [num] = inst; + + cfg->vars [num] = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar)); + MONO_INIT_VARINFO (cfg->vars [num], num); + + cfg->num_varinfo++; + //g_print ("created temp %d of type %s\n", num, mono_type_get_name (type)); + return inst; +} + +static MonoType* +type_from_stack_type (MonoInst *ins) { + switch (ins->type) { + case STACK_I4: return &mono_defaults.int32_class->byval_arg; + case STACK_I8: return &mono_defaults.int64_class->byval_arg; + case STACK_PTR: return &mono_defaults.int_class->byval_arg; + case STACK_R8: return &mono_defaults.double_class->byval_arg; + case STACK_MP: return &mono_defaults.int_class->byval_arg; + case STACK_OBJ: return &mono_defaults.object_class->byval_arg; + case STACK_VTYPE: return &ins->klass->byval_arg; + default: + g_error ("stack type %d to montype not handled\n", ins->type); + } + return NULL; +} + +static MonoClass* +array_access_to_klass (int opcode) +{ + switch (opcode) { + case CEE_LDELEM_U1: + return mono_defaults.byte_class; + case CEE_LDELEM_U2: + return mono_defaults.uint16_class; + case CEE_LDELEM_I: + case CEE_STELEM_I: + return mono_defaults.int_class; + case CEE_LDELEM_I1: + case CEE_STELEM_I1: + return mono_defaults.sbyte_class; + case CEE_LDELEM_I2: + case CEE_STELEM_I2: + return mono_defaults.int16_class; + case CEE_LDELEM_I4: + case CEE_STELEM_I4: + return mono_defaults.int32_class; + case CEE_LDELEM_U4: + return mono_defaults.uint32_class; + case CEE_LDELEM_I8: + case CEE_STELEM_I8: + return mono_defaults.int64_class; + case CEE_LDELEM_R4: + case CEE_STELEM_R4: + return mono_defaults.single_class; + case CEE_LDELEM_R8: + case CEE_STELEM_R8: + return mono_defaults.double_class; + case CEE_LDELEM_REF: + case CEE_STELEM_REF: + return mono_defaults.object_class; + default: + g_assert_not_reached (); + } + return NULL; +} + +static void +mono_add_ins_to_end (MonoBasicBlock *bb, MonoInst *inst) +{ + MonoInst *prev; + if (!bb->code) { + MONO_ADD_INS (bb, inst); + return; + } + switch (bb->last_ins->opcode) { + case CEE_BEQ: + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BNE_UN: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + case CEE_BR: + prev = bb->code; + while (prev->next && prev->next != bb->last_ins) + prev = prev->next; + if (prev == bb->code) { + if (bb->last_ins == bb->code) { + inst->next = bb->code; + bb->code = inst; + } else { + inst->next = prev->next; + prev->next = inst; + } + } else { + inst->next = bb->last_ins; + prev->next = inst; + } + break; + // g_warning ("handle conditional jump in add_ins_to_end ()\n"); + default: + MONO_ADD_INS (bb, inst); + break; + } +} + +void +mono_add_varcopy_to_end (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest) +{ + MonoInst *inst, *load; + + NEW_TEMPLOAD (cfg, load, src); + + NEW_TEMPSTORE (cfg, inst, dest, load); + if (inst->opcode == CEE_STOBJ) { + NEW_TEMPLOADA (cfg, inst, dest); + handle_stobj (cfg, bb, inst, load, NULL, inst->klass, TRUE, FALSE); + } else { + inst->cil_code = NULL; + mono_add_ins_to_end (bb, inst); + } +} + +/* + * This function is called to handle items that are left on the evaluation stack + * at basic block boundaries. What happens is that we save the values to local variables + * and we reload them later when first entering the target basic block (with the + * handle_loaded_temps () function). + * A single joint point will use the same variables (stored in the array bb->out_stack or + * bb->in_stack, if the basic block is before or after the joint point). + */ +static int +handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int count) { + int i; + MonoBasicBlock *outb; + MonoInst *inst, **locals; + + if (!count) + return 0; + if (cfg->verbose_level > 3) + g_print ("%d item(s) on exit from B%d\n", count, bb->block_num); + if (!bb->out_scount) { + int found = 0; + bb->out_scount = count; + //g_print ("bblock %d has out:", bb->block_num); + for (i = 0; i < bb->out_count; ++i) { + outb = bb->out_bb [i]; + //g_print (" %d", outb->block_num); + if (outb->in_stack) { + found = 1; + bb->out_stack = outb->in_stack; + break; + } + } + //g_print ("\n"); + if (!found) { + bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count); + for (i = 0; i < count; ++i) { + /* + * dietmar suggests that we can reuse temps already allocated + * for this purpouse, if they occupy the same stack slot and if + * they are of the same type. + */ + bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL); + } + } + } + locals = bb->out_stack; + for (i = 0; i < count; ++i) { + /* add store ops at the end of the bb, before the branch */ + NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]); + if (inst->opcode == CEE_STOBJ) { + NEW_TEMPLOADA (cfg, inst, locals [i]->inst_c0); + handle_stobj (cfg, bb, inst, sp [i], sp [i]->cil_code, inst->klass, TRUE, FALSE); + } else { + inst->cil_code = sp [i]->cil_code; + mono_add_ins_to_end (bb, inst); + } + if (cfg->verbose_level > 3) + g_print ("storing %d to temp %d\n", i, locals [i]->inst_c0); + } + + for (i = 0; i < bb->out_count; ++i) { + outb = bb->out_bb [i]; + if (outb->in_scount) + continue; /* check they are the same locals */ + outb->in_scount = count; + outb->in_stack = locals; + } + return 0; +} + +static int +ret_type_to_call_opcode (MonoType *type, int calli, int virt) +{ + int t = type->type; + + if (type->byref) + return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL; + +handle_enum: + switch (t) { + case MONO_TYPE_VOID: + return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALLVIRT: OP_VOIDCALL; + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return calli? OP_LCALL_REG: virt? OP_LCALLVIRT: OP_LCALL; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + return calli? OP_FCALL_REG: virt? OP_FCALLVIRT: OP_FCALL; + case MONO_TYPE_VALUETYPE: + if (type->data.klass->enumtype) { + t = type->data.klass->enum_basetype->type; + goto handle_enum; + } else + return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL; + default: + g_error ("unknown type %02x in ret_type_to_call_opcode", type->type); + } + return -1; +} + +void +mono_create_jump_table (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks) +{ + MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo)); + + ji->ip.label = label; + ji->type = MONO_PATCH_INFO_SWITCH; + ji->data.table = bbs; + ji->next = cfg->patch_info; + ji->table_size = num_blocks; + cfg->patch_info = ji; +} + +/* + * When we add a tree of instructions, we need to ensure the instructions currently + * on the stack are executed before (like, if we load a value from a local). + * We ensure this by saving the currently loaded values to temps and rewriting the + * instructions to load the values. + * This is not done for opcodes that terminate a basic block (because it's handled already + * by handle_stack_args ()) and for opcodes that can't change values, like POP. + */ +static void +handle_loaded_temps (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst **stack, MonoInst **sp) +{ + MonoInst *load, *store, *temp, *ins; + + while (stack < sp) { + ins = *stack; + /* handle also other constants */ + if (ins->opcode != OP_ICONST) { + temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = ins->cil_code; + if (store->opcode == CEE_STOBJ) { + NEW_TEMPLOADA (cfg, store, temp->inst_c0); + handle_stobj (cfg, bblock, store, ins, ins->cil_code, temp->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, store); + NEW_TEMPLOAD (cfg, load, temp->inst_c0); + load->cil_code = ins->cil_code; + *stack = load; + } + stack++; + } +} + +inline static int +mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, MonoMethodSignature *sig, gboolean ret_object, + const guint8 *ip, gboolean to_end) +{ + MonoInst *temp, *store, *ins = (MonoInst*)call; + MonoType *ret = sig->ret; + + if (!MONO_TYPE_IS_VOID (ret) || ret_object) { + if (ret_object) { + call->inst.type = STACK_OBJ; + call->inst.opcode = CEE_CALL; + temp = mono_compile_create_var (cfg, &mono_defaults.string_class->byval_arg, OP_LOCAL); + } else { + type_to_eval_stack_type (ret, ins); + temp = mono_compile_create_var (cfg, ret, OP_LOCAL); + } + + if (MONO_TYPE_ISSTRUCT (ret)) { + MonoInst *loada; + + /* we use this to allocate native sized structs */ + temp->unused = sig->pinvoke; + + NEW_TEMPLOADA (cfg, loada, temp->inst_c0); + if (call->inst.opcode == OP_VCALL) + ins->inst_left = loada; + else + ins->inst_right = loada; /* a virtual or indirect call */ + + if (to_end) + mono_add_ins_to_end (bblock, ins); + else + MONO_ADD_INS (bblock, ins); + } else { + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = ip; + if (to_end) + mono_add_ins_to_end (bblock, store); + else + MONO_ADD_INS (bblock, store); + } + return temp->inst_c0; + } else { + if (to_end) + mono_add_ins_to_end (bblock, ins); + else + MONO_ADD_INS (bblock, ins); + return -1; + } +} + +inline static MonoCallInst * +mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, + MonoInst **args, int calli, int virtual, const guint8 *ip, gboolean to_end) +{ + MonoCallInst *call; + int i; + + MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (sig->ret, calli, virtual)); + + call->inst.cil_code = ip; + call->args = args; + call->signature = sig; + call = mono_arch_call_opcode (cfg, bblock, call, virtual); + + for (i = 0; i < (sig->param_count + sig->hasthis); ++i) { + if (call->args [i]) { + if (to_end) + mono_add_ins_to_end (bblock, call->args [i]); + else + MONO_ADD_INS (bblock, call->args [i]); + } + } + return call; +} + +inline static int +mono_emit_calli (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, + MonoInst **args, MonoInst *addr, const guint8 *ip) +{ + MonoCallInst *call = mono_emit_call_args (cfg, bblock, sig, args, TRUE, FALSE, ip, FALSE); + + call->inst.inst_i0 = addr; + + return mono_spill_call (cfg, bblock, call, sig, FALSE, ip, FALSE); +} + +static MonoCallInst* +mono_emit_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig, + MonoInst **args, const guint8 *ip, MonoInst *this) +{ + gboolean virtual = this != NULL; + MonoCallInst *call; + + call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, virtual, ip, FALSE); + + if (this && sig->hasthis && + (method->klass->marshalbyref || method->klass == mono_defaults.object_class) && + !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !MONO_CHECK_THIS (this)) { + call->method = mono_marshal_get_remoting_invoke_with_check (method); + } else { + call->method = method; + } + call->inst.flags |= MONO_INST_HAS_METHOD; + call->inst.inst_left = this; + + return call; +} + +inline static int +mono_emit_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, + MonoInst **args, const guint8 *ip, MonoInst *this) +{ + MonoCallInst *call = mono_emit_method_call (cfg, bblock, method, method->signature, args, ip, this); + + return mono_spill_call (cfg, bblock, call, method->signature, method->string_ctor, ip, FALSE); +} + +inline static int +mono_emit_native_call (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoMethodSignature *sig, + MonoInst **args, const guint8 *ip, gboolean to_end) +{ + MonoCallInst *call; + + g_assert (sig); + + call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, FALSE, ip, to_end); + call->fptr = func; + return mono_spill_call (cfg, bblock, call, sig, func == mono_array_new_va, ip, to_end); +} + +inline static int +mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoInst **args, const guint8 *ip) +{ + MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func); + + if (!info) { + g_warning ("unregistered JIT ICall"); + g_assert_not_reached (); + } + + return mono_emit_native_call (cfg, bblock, info->wrapper, info->sig, args, ip, FALSE); +} + +static void +mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info) +{ + MonoInst *ins, *temp = NULL, *store, *load; + int i, nargs; + MonoCallInst *call; + + /*g_print ("emulating: "); + mono_print_tree (tree); + g_print ("\n");*/ + MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (info->sig->ret, FALSE, FALSE)); + ins = (MonoInst*)call; + + call->inst.cil_code = tree->cil_code; + call->args = iargs; + call->signature = info->sig; + + call = mono_arch_call_opcode (cfg, cfg->cbb, call, FALSE); + + if (!MONO_TYPE_IS_VOID (info->sig->ret)) { + temp = mono_compile_create_var (cfg, info->sig->ret, OP_LOCAL); + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = tree->cil_code; + } else { + store = ins; + } + + nargs = info->sig->param_count + info->sig->hasthis; + + for (i = 1; i < nargs; i++) { + call->args [i - 1]->next = call->args [i]; + } + + if (nargs) + call->args [nargs - 1]->next = store; + + if (cfg->prev_ins) { + store->next = cfg->prev_ins->next; + if (nargs) + cfg->prev_ins->next = call->args [0]; + else + cfg->prev_ins->next = store; + } else { + store->next = cfg->cbb->code; + if (nargs) + cfg->cbb->code = call->args [0]; + else + cfg->cbb->code = store; + } + + + call->fptr = info->wrapper; + + if (!MONO_TYPE_IS_VOID (info->sig->ret)) { + NEW_TEMPLOAD (cfg, load, temp->inst_c0); + *tree = *load; + } +} + +static MonoMethodSignature * +mono_get_element_address_signature (int arity) +{ + static GHashTable *sighash = NULL; + MonoMethodSignature *res; + int i; + + if (!sighash) + sighash = g_hash_table_new (NULL, NULL); + + + if ((res = g_hash_table_lookup (sighash, (gpointer)arity))) + return res; + + res = mono_metadata_signature_alloc (mono_defaults.corlib, arity + 1); + + res->params [0] = &mono_defaults.array_class->byval_arg; + + for (i = 1; i <= arity; i++) + res->params [i] = &mono_defaults.int_class->byval_arg; + + res->ret = &mono_defaults.int_class->byval_arg; + + g_hash_table_insert (sighash, (gpointer)arity, res); + + return res; +} + +static void +handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native) { + MonoInst *iargs [3]; + int n; + + g_assert (klass); + /* + * This check breaks with spilled vars... need to handle it during verification anyway. + * g_assert (klass && klass == src->klass && klass == dest->klass); + */ + + if (native) + n = mono_class_native_size (klass, NULL); + else + n = mono_class_value_size (klass, NULL); + + iargs [0] = dest; + iargs [1] = src; + NEW_ICONST (cfg, iargs [2], n); + + mono_emit_native_call (cfg, bblock, helper_memcpy, helper_sig_memcpy, iargs, ip, to_end); +} + +static void +handle_initobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, const guchar *ip, MonoClass *klass, MonoInst **stack_start, MonoInst **sp) +{ + MonoInst *iargs [2]; + MonoInst *ins, *zero_int32; + int n; + + NEW_ICONST (cfg, zero_int32, 0); + + mono_class_init (klass); + n = mono_class_value_size (klass, NULL); + MONO_INST_NEW (cfg, ins, 0); + ins->cil_code = ip; + ins->inst_left = dest; + ins->inst_right = zero_int32; + switch (n) { + case 1: + ins->opcode = CEE_STIND_I1; + MONO_ADD_INS (bblock, ins); + break; + case 2: + ins->opcode = CEE_STIND_I2; + MONO_ADD_INS (bblock, ins); + break; + case 4: + ins->opcode = CEE_STIND_I4; + MONO_ADD_INS (bblock, ins); + break; + default: + handle_loaded_temps (cfg, bblock, stack_start, sp); + NEW_ICONST (cfg, ins, n); + iargs [0] = dest; + iargs [1] = ins; + mono_emit_jit_icall (cfg, bblock, helper_initobj, iargs, ip); + break; + } +} + +#define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S)) + +static gboolean +mono_method_check_inlining (MonoMethod *method) +{ + MonoMethodHeader *header = ((MonoMethodNormal *)method)->header; + MonoMethodSignature *signature = method->signature; + int i; + + /* fixme: we should inline wrappers */ + if (method->wrapper_type != MONO_WRAPPER_NONE) + return FALSE; + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->klass->marshalbyref) || + !header || header->num_clauses || + /* fixme: why cant we inline valuetype returns? */ + MONO_TYPE_ISSTRUCT (signature->ret)) + return FALSE; + + /* its not worth to inline methods with valuetype arguments?? */ + for (i = 0; i < signature->param_count; i++) { + if (MONO_TYPE_ISSTRUCT (signature->params [i])) { + return FALSE; + } + } + + //if (!MONO_TYPWE_IS_VOID (signature->ret)) return FALSE; + + /* also consider num_locals? */ + if (header->code_size < 20) + return TRUE; + + return FALSE; +} + +static void +mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, MonoInst **sp, MonoInst **args) +{ + MonoInst *store, *temp; + int i; + + g_assert (!MONO_TYPE_ISSTRUCT (sig->ret)); + + if (!sig->hasthis && sig->param_count == 0) + return; + + if (sig->hasthis) { + if (sp [0]->opcode == OP_ICONST) { + *args++ = sp [0]; + //printf ("REUSABLE CONST\n"); + } else if (sp [0]->ssa_op == MONO_SSA_LOAD && + (sp [0]->inst_i0->opcode == OP_LOCAL || sp [0]->inst_i0->opcode == OP_ARG)) { + *args++ = sp [0]->inst_i0; + //printf ("REUSABLE ARG\n"); + } else { + //printf ("NONREUSABLE ARG0 %s\n", mono_inst_name (sp [0]->opcode)); + temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL); + *args++ = temp; + NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp); + store->cil_code = sp [0]->cil_code; + MONO_ADD_INS (bblock, store); + //printf ("SAVE THIS %s\n", mono_inst_name (store->opcode)); + } + sp++; + } + + for (i = 0; i < sig->param_count; ++i) { + if (sp [0]->opcode == OP_ICONST) { + *args++ = sp [0]; + //printf ("REUSABLE CONST\n"); + } else if (sp [0]->ssa_op == MONO_SSA_LOAD && + (sp [0]->inst_i0->opcode == OP_LOCAL || sp [0]->inst_i0->opcode == OP_ARG)) { + *args++ = sp [0]->inst_i0; + //printf ("REUSABLE ARG\n"); + } else { + //printf ("NONREUSABLE ARG1 %s\n", mono_inst_name (sp [0]->opcode)); + temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL); + *args++ = temp; + NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp); + store->cil_code = sp [0]->cil_code; + if (store->opcode == CEE_STOBJ) { + NEW_TEMPLOADA (cfg, store, temp->inst_c0); + handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, temp->klass, FALSE, FALSE); + //printf ("SAVE OBJARGS %s\n", mono_inst_name (store->opcode)); + } else { + MONO_ADD_INS (bblock, store); + //printf ("SAVE ARGS %s\n", mono_inst_name (store->opcode)); + } + } + sp++; + } +} + +/* + * Some of these comments may well be out-of-date. + * Design decisions: we do a single pass over the IL code (and we do bblock + * splitting/merging in the few cases when it's required: a back jump to an IL + * address that was not already seen as bblock starting point). + * Code is validated as we go (full verification is still better left to metadata/verify.c). + * Complex operations are decomposed in simpler ones right away. We need to let the + * arch-specific code peek and poke inside this process somehow (except when the + * optimizations can take advantage of the full semantic info of coarse opcodes). + * All the opcodes of the form opcode.s are 'normalized' to opcode. + * MonoInst->opcode initially is the IL opcode or some simplification of that + * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific + * opcode with value bigger than OP_LAST. + * At this point the IR can be handed over to an interpreter, a dumb code generator + * or to the optimizing code generator that will translate it to SSA form. + * + * Profiling directed optimizations. + * We may compile by default with few or no optimizations and instrument the code + * or the user may indicate what methods to optimize the most either in a config file + * or through repeated runs where the compiler applies offline the optimizations to + * each method and then decides if it was worth it. + * + * TODO: + * * consider using an array instead of an hash table (bb_hash) + */ + +#define CHECK_TYPE(ins) if (!(ins)->type) goto unverified +#define CHECK_STACK(num) if ((sp - stack_start) < (num)) goto unverified +#define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) goto unverified + +/* offset from br.s -> br like opcodes */ +#define BIG_BRANCH_OFFSET 13 + +/* + * mono_method_to_ir: translates IL into basic blocks containing trees + */ +static int +mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, + int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, + guint inline_offset) +{ + MonoInst *zero_int32, *zero_int64, *zero_ptr, *zero_obj, *zero_r8; + MonoInst *ins, **sp, **stack_start; + MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL; + GHashTable *bbhash; + MonoMethod *cmethod; + MonoInst **arg_array; + MonoMethodHeader *header; + MonoImage *image; + guint32 token, ins_flag; + MonoClass *klass; + unsigned char *ip, *end, *target; + static double r8_0 = 0.0; + MonoMethodSignature *sig; + MonoType **param_types; + GList *bb_recheck = NULL, *tmp; + int i, n, start_new_bblock, align; + int num_calls = 0, inline_costs = 0; + int *filter_lengths = NULL; + guint real_offset; + + image = method->klass->image; + header = ((MonoMethodNormal *)method)->header; + sig = method->signature; + ip = (unsigned char*)header->code; + end = ip + header->code_size; + mono_jit_stats.cil_code_size += header->code_size; + + if (cfg->method == method) { + real_offset = 0; + bbhash = cfg->bb_hash; + } else { + real_offset = inline_offset; + bbhash = g_hash_table_new (g_direct_hash, NULL); + } + + if (cfg->method == method) { + + /* ENTRY BLOCK */ + cfg->bb_entry = start_bblock = NEW_BBLOCK (cfg); + start_bblock->cil_code = NULL; + start_bblock->cil_length = 0; + start_bblock->block_num = cfg->num_bblocks++; + + /* EXIT BLOCK */ + cfg->bb_exit = end_bblock = NEW_BBLOCK (cfg); + end_bblock->cil_code = NULL; + end_bblock->cil_length = 0; + end_bblock->block_num = cfg->num_bblocks++; + g_assert (cfg->num_bblocks == 2); + + arg_array = alloca (sizeof (MonoInst *) * (sig->hasthis + sig->param_count)); + for (i = sig->hasthis + sig->param_count - 1; i >= 0; i--) + arg_array [i] = cfg->varinfo [i]; + + if (mono_compile_aot) + cfg->opt |= MONO_OPT_SAHRED; + + if (header->num_clauses) { + int size = sizeof (int) * header->num_clauses; + filter_lengths = alloca (size); + memset (filter_lengths, 0, size); + } + /* handle exception clauses */ + for (i = 0; i < header->num_clauses; ++i) { + //unsigned char *p = ip; + MonoExceptionClause *clause = &header->clauses [i]; + GET_BBLOCK (cfg, bbhash, tblock, ip + clause->try_offset); + tblock->real_offset = clause->try_offset; + GET_BBLOCK (cfg, bbhash, tblock, ip + clause->handler_offset); + tblock->real_offset = clause->handler_offset; + /*g_print ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len); + while (p < end) { + g_print ("%s", mono_disasm_code_one (NULL, method, p, &p)); + }*/ + /* catch and filter blocks get the exception object on the stack */ + if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || + clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + /* mostly like handle_stack_args (), but just sets the input args */ + /* g_print ("handling clause at IL_%04x\n", clause->handler_offset); */ + if (!cfg->exvar) { + cfg->exvar = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL); + /* prevent it from being register allocated */ + cfg->exvar->flags |= MONO_INST_INDIRECT; + } + tblock->in_scount = 1; + tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); + tblock->in_stack [0] = cfg->exvar; + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + GET_BBLOCK (cfg, bbhash, tblock, ip + clause->token_or_filter); + tblock->real_offset = clause->token_or_filter; + tblock->in_scount = 1; + tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); + tblock->in_stack [0] = cfg->exvar; + } + } + } + + } else { + arg_array = alloca (sizeof (MonoInst *) * (sig->hasthis + sig->param_count)); + mono_save_args (cfg, start_bblock, sig, inline_args, arg_array); + } + + /* FIRST CODE BLOCK */ + bblock = NEW_BBLOCK (cfg); + bblock->cil_code = ip; + + ADD_BBLOCK (cfg, bbhash, bblock); + + if (cfg->method == method) { + if (mono_method_has_breakpoint (method, FALSE)) { + MONO_INST_NEW (cfg, ins, CEE_BREAK); + MONO_ADD_INS (bblock, ins); + } + } + + if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SAHRED)))) { + /* we use a separate basic block for the initialization code */ + cfg->bb_init = init_localsbb = NEW_BBLOCK (cfg); + init_localsbb->real_offset = real_offset; + start_bblock->next_bb = init_localsbb; + init_localsbb->next_bb = bblock; + link_bblock (cfg, start_bblock, init_localsbb); + link_bblock (cfg, init_localsbb, bblock); + init_localsbb->block_num = cfg->num_bblocks++; + } else { + start_bblock->next_bb = bblock; + link_bblock (cfg, start_bblock, bblock); + } + + mono_debug_init_method (cfg, bblock); + + param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * (sig->hasthis + sig->param_count)); + if (sig->hasthis) + param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg; + for (n = 0; n < sig->param_count; ++n) + param_types [n + sig->hasthis] = sig->params [n]; + + /* do this somewhere outside - not here */ + NEW_ICONST (cfg, zero_int32, 0); + NEW_ICONST (cfg, zero_int64, 0); + zero_int64->type = STACK_I8; + NEW_PCONST (cfg, zero_ptr, 0); + NEW_PCONST (cfg, zero_obj, 0); + zero_obj->type = STACK_OBJ; + + MONO_INST_NEW (cfg, zero_r8, OP_R8CONST); + zero_r8->type = STACK_R8; + zero_r8->inst_p0 = &r8_0; + + /* add a check for this != NULL to inlined methods */ + if (cfg->method != method && sig->hasthis) { + MONO_INST_NEW (cfg, ins, OP_CHECK_THIS); + NEW_ARGLOAD (cfg, ins->inst_left, 0); + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + } + + /* we use a spare stack slot in SWITCH and NEWOBJ and others */ + stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1)); + + ins_flag = 0; + start_new_bblock = 0; + while (ip < end) { + + if (cfg->method == method) + real_offset = ip - header->code; + else + real_offset = inline_offset; + + if (start_new_bblock) { + bblock->cil_length = ip - bblock->cil_code; + if (start_new_bblock == 2) { + g_assert (ip == tblock->cil_code); + } else { + GET_BBLOCK (cfg, bbhash, tblock, ip); + } + bblock->next_bb = tblock; + bblock = tblock; + start_new_bblock = 0; + for (i = 0; i < bblock->in_scount; ++i) { + NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0); + *sp++ = ins; + } + } else { + if ((tblock = g_hash_table_lookup (bbhash, ip)) && (tblock != bblock)) { + link_bblock (cfg, bblock, tblock); + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + bblock->next_bb = tblock; + bblock = tblock; + for (i = 0; i < bblock->in_scount; ++i) { + NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0); + *sp++ = ins; + } + } + } + + if (cfg->verbose_level > 3) + g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, sp-stack_start, mono_disasm_code_one (NULL, method, ip, NULL)); + + switch (*ip) { + case CEE_NOP: + ++ip; + break; + case CEE_BREAK: + MONO_INST_NEW (cfg, ins, CEE_BREAK); + ins->cil_code = ip++; + MONO_ADD_INS (bblock, ins); + break; + case CEE_LDARG_0: + case CEE_LDARG_1: + case CEE_LDARG_2: + case CEE_LDARG_3: + CHECK_STACK_OVF (1); + n = (*ip)-CEE_LDARG_0; + NEW_ARGLOAD (cfg, ins, n); + ins->cil_code = ip++; + *sp++ = ins; + break; + case CEE_LDLOC_0: + case CEE_LDLOC_1: + case CEE_LDLOC_2: + case CEE_LDLOC_3: + CHECK_STACK_OVF (1); + n = (*ip)-CEE_LDLOC_0; + NEW_LOCLOAD (cfg, ins, n); + ins->cil_code = ip++; + *sp++ = ins; + break; + case CEE_STLOC_0: + case CEE_STLOC_1: + case CEE_STLOC_2: + case CEE_STLOC_3: + CHECK_STACK (1); + n = (*ip)-CEE_STLOC_0; + --sp; + handle_loaded_temps (cfg, bblock, stack_start, sp); + NEW_LOCSTORE (cfg, ins, n, *sp); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + NEW_LOCLOADA (cfg, ins, n); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, ins); + ++ip; + inline_costs += 1; + break; + case CEE_LDARG_S: + CHECK_STACK_OVF (1); + NEW_ARGLOAD (cfg, ins, ip [1]); + ins->cil_code = ip; + *sp++ = ins; + ip += 2; + break; + case CEE_LDARGA_S: + CHECK_STACK_OVF (1); + NEW_ARGLOADA (cfg, ins, ip [1]); + ins->cil_code = ip; + *sp++ = ins; + ip += 2; + break; + case CEE_STARG_S: + CHECK_STACK (1); + --sp; + NEW_ARGSTORE (cfg, ins, ip [1], *sp); + handle_loaded_temps (cfg, bblock, stack_start, sp); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + NEW_ARGLOADA (cfg, ins, ip [1]); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, ins); + ip += 2; + break; + case CEE_LDLOC_S: + CHECK_STACK_OVF (1); + NEW_LOCLOAD (cfg, ins, ip [1]); + ins->cil_code = ip; + *sp++ = ins; + ip += 2; + break; + case CEE_LDLOCA_S: + CHECK_STACK_OVF (1); + NEW_LOCLOADA (cfg, ins, ip [1]); + ins->cil_code = ip; + *sp++ = ins; + ip += 2; + break; + case CEE_STLOC_S: + CHECK_STACK (1); + --sp; + handle_loaded_temps (cfg, bblock, stack_start, sp); + NEW_LOCSTORE (cfg, ins, ip [1], *sp); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + NEW_LOCLOADA (cfg, ins, ip [1]); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, ins); + ip += 2; + inline_costs += 1; + break; + case CEE_LDNULL: + CHECK_STACK_OVF (1); + NEW_PCONST (cfg, ins, NULL); + ins->cil_code = ip; + ins->type = STACK_OBJ; + ++ip; + *sp++ = ins; + break; + case CEE_LDC_I4_M1: + CHECK_STACK_OVF (1); + NEW_ICONST (cfg, ins, -1); + ins->cil_code = ip; + ++ip; + *sp++ = ins; + break; + case CEE_LDC_I4_0: + case CEE_LDC_I4_1: + case CEE_LDC_I4_2: + case CEE_LDC_I4_3: + case CEE_LDC_I4_4: + case CEE_LDC_I4_5: + case CEE_LDC_I4_6: + case CEE_LDC_I4_7: + case CEE_LDC_I4_8: + CHECK_STACK_OVF (1); + NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0); + ins->cil_code = ip; + ++ip; + *sp++ = ins; + break; + case CEE_LDC_I4_S: + CHECK_STACK_OVF (1); + ++ip; + NEW_ICONST (cfg, ins, *((signed char*)ip)); + ins->cil_code = ip; + ++ip; + *sp++ = ins; + break; + case CEE_LDC_I4: + CHECK_STACK_OVF (1); + NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1)); + ins->cil_code = ip; + ip += 5; + *sp++ = ins; + break; + case CEE_LDC_I8: + CHECK_STACK_OVF (1); + MONO_INST_NEW (cfg, ins, OP_I8CONST); + ins->cil_code = ip; + ins->type = STACK_I8; + ++ip; + ins->inst_l = (gint64)read64 (ip); + ip += 8; + *sp++ = ins; + break; + case CEE_LDC_R4: { + float *f = g_malloc (sizeof (float)); + CHECK_STACK_OVF (1); + MONO_INST_NEW (cfg, ins, OP_R4CONST); + ins->type = STACK_R8; + ++ip; + readr4 (ip, f); + ins->inst_p0 = f; + ip += 4; + *sp++ = ins; + break; + } + case CEE_LDC_R8: { + double *d = g_malloc (sizeof (double)); + CHECK_STACK_OVF (1); + MONO_INST_NEW (cfg, ins, OP_R8CONST); + ins->type = STACK_R8; + ++ip; + readr8 (ip, d); + ins->inst_p0 = d; + ip += 8; + *sp++ = ins; + break; + } + case CEE_DUP: { + MonoInst *temp, *store; + CHECK_STACK (1); + CHECK_STACK_OVF (1); + sp--; + ins = *sp; + + /* + * small optimization: if the loaded value was from a local already, + * just load it twice. + */ + if (ins->ssa_op == MONO_SSA_LOAD && + (ins->inst_i0->opcode == OP_LOCAL || ins->inst_i0->opcode == OP_ARG)) { + sp++; + MONO_INST_NEW (cfg, temp, 0); + *temp = *ins; + temp->cil_code = ip; + *sp++ = temp; + } else { + temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + temp->cil_code = ip; + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = ip; + MONO_ADD_INS (bblock, store); + NEW_TEMPLOAD (cfg, ins, temp->inst_c0); + *sp++ = ins; + ins->cil_code = ip; + NEW_TEMPLOAD (cfg, ins, temp->inst_c0); + *sp++ = ins; + ins->cil_code = ip; + } + ++ip; + inline_costs += 2; + break; + } + case CEE_POP: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, CEE_POP); + MONO_ADD_INS (bblock, ins); + ins->cil_code = ip++; + --sp; + ins->inst_i0 = *sp; + break; + case CEE_JMP: + if (stack_start != sp) + goto unverified; + MONO_INST_NEW (cfg, ins, CEE_JMP); + token = read32 (ip + 1); + /* FIXME: check the signature matches */ + cmethod = mono_get_method (image, token, NULL); + /* + * The current magic trampoline can't handle this + * apparently, so we compile the method right away. + * Later, we may need to fix the trampoline or use a different one. + */ + ins->inst_p0 = mono_compile_method (cmethod); + MONO_ADD_INS (bblock, ins); + ip += 5; + start_new_bblock = 1; + break; + case CEE_CALLI: + case CEE_CALL: + case CEE_CALLVIRT: { + MonoInst *addr = NULL; + MonoMethodSignature *fsig = NULL; + MonoMethodHeader *cheader; + int mop, temp, array_rank = 0; + int virtual = *ip == CEE_CALLVIRT; + + token = read32 (ip + 1); + + if (*ip == CEE_CALLI) { + cmethod = NULL; + cheader = NULL; + CHECK_STACK (1); + --sp; + addr = *sp; + if (method->wrapper_type != MONO_WRAPPER_NONE) + fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token); + else + fsig = mono_metadata_parse_signature (image, token); + + n = fsig->param_count + fsig->hasthis; + + } else { + cmethod = mono_get_method (image, token, NULL); + cheader = ((MonoMethodNormal *)cmethod)->header; + + if (!cmethod->klass->inited) + mono_class_init (cmethod->klass); + + if (cmethod->signature->pinvoke) { +#ifdef MONO_USE_EXC_TABLES + if (mono_method_blittable (cmethod)) { + fsig = cmethod->signature; + } else { +#endif + MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod); + fsig = wrapper->signature; +#ifdef MONO_USE_EXC_TABLES + } +#endif + } else { + fsig = cmethod->signature; + } + + n = fsig->param_count + fsig->hasthis; + + if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && + cmethod->klass->parent == mono_defaults.array_class) { + array_rank = cmethod->klass->rank; + } + + if (cmethod->string_ctor) + g_assert_not_reached (); + + } + + CHECK_STACK (n); + + //g_assert (!virtual || fsig->hasthis); + + sp -= n; + + if (cmethod && (mop = mono_find_method_opcode (cmethod))) { + + MONO_INST_NEW (cfg, ins, mop); + ins->cil_code = ip; + g_assert (n <= 2); + + if (fsig->param_count > 0) { + ins->inst_i0 = sp [0]; + if (fsig->param_count > 1) + ins->inst_i1 = sp [1]; + } + + if (MONO_TYPE_IS_VOID (fsig->ret)) { + MONO_ADD_INS (bblock, ins); + } else { + type_to_eval_stack_type (fsig->ret, ins); + *sp = ins; + sp++; + } + + ip += 5; + break; + } + + if ((cfg->opt & MONO_OPT_INLINE) && + (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) && + cmethod && cheader && mono_method_check_inlining (cmethod) && + method != cmethod && !g_list_find (dont_inline, cmethod)) { + MonoInst *rvar = NULL; + MonoBasicBlock *ebblock, *sbblock; + int costs, new_locals_offset; + + if (cfg->verbose_level > 2) + g_print ("INLINE START %p %s\n", cmethod, mono_method_full_name (cmethod, TRUE)); + + mono_jit_stats.inlineable_methods++; + + /* allocate space to store the return value */ + if (!MONO_TYPE_IS_VOID (fsig->ret)) + rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL); + + /* allocate local variables */ + new_locals_offset = cfg->num_varinfo; + for (i = 0; i < cheader->num_locals; ++i) + mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL); + + /* allocate starte and end blocks */ + sbblock = NEW_BBLOCK (cfg); + sbblock->block_num = cfg->num_bblocks++; + sbblock->real_offset = real_offset; + + ebblock = NEW_BBLOCK (cfg); + ebblock->block_num = cfg->num_bblocks++; + ebblock->real_offset = real_offset; + + dont_inline = g_list_prepend (dont_inline, method); + costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset); + dont_inline = g_list_remove (dont_inline, method); + + if (costs >= 0 && costs < 60) { + + mono_jit_stats.inlined_methods++; + + /* always add some code to avoid block split failures */ + MONO_INST_NEW (cfg, ins, CEE_NOP); + MONO_ADD_INS (bblock, ins); + ins->cil_code = ip; + + ip += 5; + real_offset += 5; + + bblock->next_bb = sbblock; + link_bblock (cfg, bblock, sbblock); + + GET_BBLOCK (cfg, bbhash, bblock, ip); + ebblock->next_bb = bblock; + link_bblock (cfg, ebblock, bblock); + + if (rvar) { + NEW_TEMPLOAD (cfg, ins, rvar->inst_c0); + *sp++ = ins; + } + if (cfg->verbose_level > 2) + g_print ("INLINE END %s\n", mono_method_full_name (cmethod, TRUE)); + + // { static int c = 0; printf ("ICOUNT %d %d %s\n", c++, costs, mono_method_full_name (cmethod, TRUE)); } + + inline_costs += costs; + break; + } else { + + if (cfg->verbose_level > 2) + g_print ("INLINE ABORTED %s\n", mono_method_full_name (cmethod, TRUE)); + + } + } + + inline_costs += 10 * num_calls++; + handle_loaded_temps (cfg, bblock, stack_start, sp); + + /* tail recursion elimination */ + if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == cfg->method && ip [5] == CEE_RET) { + gboolean has_vtargs = FALSE; + int i; + + /* keep it simple */ + for (i = fsig->param_count - 1; i >= 0; i--) { + if (MONO_TYPE_ISSTRUCT (cmethod->signature->params [i])) + has_vtargs = TRUE; + } + + if (!has_vtargs) { + for (i = 0; i < n; ++i) { + NEW_ARGSTORE (cfg, ins, i, sp [i]); + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + } + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + tblock = start_bblock->out_bb [0]; + link_bblock (cfg, bblock, tblock); + ins->inst_target_bb = tblock; + start_new_bblock = 1; + ip += 5; + + if (!MONO_TYPE_IS_VOID (fsig->ret)) { + /* just create a dummy - the value is never used */ + ins = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL); + NEW_TEMPLOAD (cfg, *sp, ins->inst_c0); + sp++; + } + + break; + } + } + + if (*ip == CEE_CALLI) { + + if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) { + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + } + + } else if (array_rank) { + MonoMethodSignature *esig; + MonoInst *addr; + + if (strcmp (cmethod->name, "Set") == 0) { /* array Set */ + esig = mono_get_element_address_signature (fsig->param_count - 1); + + temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); + NEW_TEMPLOAD (cfg, addr, temp); + NEW_INDSTORE (cfg, ins, addr, sp [fsig->param_count], fsig->params [fsig->param_count - 1]); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + handle_stobj (cfg, bblock, addr, sp [fsig->param_count], ip, mono_class_from_mono_type (fsig->params [fsig->param_count-1]), FALSE, FALSE); + } else { + MONO_ADD_INS (bblock, ins); + } + + } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */ + esig = mono_get_element_address_signature (fsig->param_count); + + temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); + NEW_TEMPLOAD (cfg, addr, temp); + NEW_INDLOAD (cfg, ins, addr, fsig->ret); + ins->cil_code = ip; + + *sp++ = ins; + } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */ + /* implement me */ + esig = mono_get_element_address_signature (fsig->param_count); + + temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + } else { + g_assert_not_reached (); + } + + } else { + if (0 && CODE_IS_STLOC (ip + 5) && (!MONO_TYPE_ISSTRUCT (fsig->ret)) && (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor)) { + /* no need to spill */ + ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL); + *sp++ = ins; + } else { + if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, virtual ? sp [0] : NULL)) != -1) { + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + } + } + } + + ip += 5; + break; + } + case CEE_RET: + if (cfg->method != method) { + /* return from inlined methode */ + if (return_var) { + MonoInst *store; + CHECK_STACK (1); + --sp; + //g_assert (returnvar != -1); + NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp); + store->cil_code = sp [0]->cil_code; + if (store->opcode == CEE_STOBJ) { + g_assert_not_reached (); + NEW_TEMPLOADA (cfg, store, return_var->inst_c0); + handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, return_var->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, store); + } + } else { + if (cfg->ret) { + g_assert (!return_var); + CHECK_STACK (1); + --sp; + MONO_INST_NEW (cfg, ins, CEE_NOP); + ins->opcode = mono_type_to_stind (method->signature->ret); + if (ins->opcode == CEE_STOBJ) { + NEW_RETLOADA (cfg, ins); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else { + ins->opcode = OP_SETRET; + ins->cil_code = ip; + ins->inst_i0 = *sp;; + ins->inst_i1 = NULL; + MONO_ADD_INS (bblock, ins); + } + } + } + if (sp != stack_start) + goto unverified; + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip++; + ins->inst_target_bb = end_bblock; + MONO_ADD_INS (bblock, ins); + link_bblock (cfg, bblock, end_bblock); + start_new_bblock = 1; + break; + case CEE_BR_S: + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip++; + MONO_ADD_INS (bblock, ins); + target = ip + 1 + (signed char)(*ip); + ++ip; + GET_BBLOCK (cfg, bbhash, tblock, target); + link_bblock (cfg, bblock, tblock); + CHECK_BBLOCK (target, ip, tblock); + ins->inst_target_bb = tblock; + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + start_new_bblock = 1; + inline_costs += 10; + break; + case CEE_BRFALSE_S: + case CEE_BRTRUE_S: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET); + ins->cil_code = ip++; + target = ip + 1 + *(signed char*)ip; + ip++; + ADD_UNCOND (ins->opcode == CEE_BRTRUE); + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + inline_costs += 10; + break; + case CEE_BEQ_S: + case CEE_BGE_S: + case CEE_BGT_S: + case CEE_BLE_S: + case CEE_BLT_S: + case CEE_BNE_UN_S: + case CEE_BGE_UN_S: + case CEE_BGT_UN_S: + case CEE_BLE_UN_S: + case CEE_BLT_UN_S: + CHECK_STACK (2); + MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET); + ins->cil_code = ip++; + target = ip + 1 + *(signed char*)ip; + ip++; + ADD_BINCOND (NULL); + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + inline_costs += 10; + break; + case CEE_BR: + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip++; + MONO_ADD_INS (bblock, ins); + target = ip + 4 + (gint32)read32(ip); + ip += 4; + GET_BBLOCK (cfg, bbhash, tblock, target); + link_bblock (cfg, bblock, tblock); + CHECK_BBLOCK (target, ip, tblock); + ins->inst_target_bb = tblock; + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + start_new_bblock = 1; + inline_costs += 10; + break; + case CEE_BRFALSE: + case CEE_BRTRUE: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip++; + target = ip + 4 + (gint32)read32(ip); + ip += 4; + ADD_UNCOND(ins->opcode == CEE_BRTRUE); + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + inline_costs += 10; + break; + case CEE_BEQ: + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BNE_UN: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + CHECK_STACK (2); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip++; + target = ip + 4 + (gint32)read32(ip); + ip += 4; + ADD_BINCOND(NULL); + if (sp != stack_start) { + handle_stack_args (cfg, bblock, stack_start, sp - stack_start); + sp = stack_start; + } + inline_costs += 10; + break; + case CEE_SWITCH: + CHECK_STACK (1); + n = read32 (ip + 1); + MONO_INST_NEW (cfg, ins, *ip); + --sp; + ins->inst_left = *sp; + if (ins->inst_left->type != STACK_I4) goto unverified; + ins->cil_code = ip; + ip += 5; + target = ip + n * sizeof (guint32); + MONO_ADD_INS (bblock, ins); + GET_BBLOCK (cfg, bbhash, tblock, target); + link_bblock (cfg, bblock, tblock); + ins->klass = GUINT_TO_POINTER (n); + ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (n + 1)); + ins->inst_many_bb [n] = tblock; + + for (i = 0; i < n; ++i) { + GET_BBLOCK (cfg, bbhash, tblock, target + (gint32)read32(ip)); + link_bblock (cfg, bblock, tblock); + ins->inst_many_bb [i] = tblock; + ip += 4; + } + /* FIXME: handle stack args */ + inline_costs += 20; + break; + case CEE_LDIND_I1: + case CEE_LDIND_U1: + case CEE_LDIND_I2: + case CEE_LDIND_U2: + case CEE_LDIND_I4: + case CEE_LDIND_U4: + case CEE_LDIND_I8: + case CEE_LDIND_I: + case CEE_LDIND_R4: + case CEE_LDIND_R8: + case CEE_LDIND_REF: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip; + --sp; + ins->inst_i0 = *sp; + *sp++ = ins; + ins->type = ldind_type [*ip - CEE_LDIND_I1]; + ins->flags |= ins_flag; + ins_flag = 0; + ++ip; + break; + case CEE_STIND_REF: + case CEE_STIND_I1: + case CEE_STIND_I2: + case CEE_STIND_I4: + case CEE_STIND_I8: + case CEE_STIND_R4: + case CEE_STIND_R8: + CHECK_STACK (2); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip++; + sp -= 2; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, ins); + ins->inst_i0 = sp [0]; + ins->inst_i1 = sp [1]; + ins->flags |= ins_flag; + ins_flag = 0; + inline_costs += 1; + break; + case CEE_ADD: + case CEE_SUB: + case CEE_MUL: + case CEE_DIV: + case CEE_DIV_UN: + case CEE_REM: + case CEE_REM_UN: + case CEE_AND: + case CEE_OR: + case CEE_XOR: + case CEE_SHL: + case CEE_SHR: + case CEE_SHR_UN: + CHECK_STACK (2); + ADD_BINOP (*ip); + ip++; + break; + case CEE_NEG: + case CEE_NOT: + case CEE_CONV_I1: + case CEE_CONV_I2: + case CEE_CONV_I4: + case CEE_CONV_I8: + case CEE_CONV_R4: + case CEE_CONV_R8: + case CEE_CONV_U4: + case CEE_CONV_U8: + case CEE_CONV_OVF_I8: + case CEE_CONV_OVF_U8: + case CEE_CONV_R_UN: + CHECK_STACK (1); + ADD_UNOP (*ip); + ip++; + break; + case CEE_CONV_OVF_I4: + case CEE_CONV_OVF_I1: + case CEE_CONV_OVF_I2: + case CEE_CONV_OVF_I: + case CEE_CONV_OVF_U: + CHECK_STACK (1); + + if (sp [-1]->type == STACK_R8) { + ADD_UNOP (CEE_CONV_OVF_I8); + ADD_UNOP (*ip); + } else { + ADD_UNOP (*ip); + } + + ip++; + break; + case CEE_CONV_OVF_U1: + case CEE_CONV_OVF_U2: + case CEE_CONV_OVF_U4: + CHECK_STACK (1); + + if (sp [-1]->type == STACK_R8) { + ADD_UNOP (CEE_CONV_OVF_U8); + ADD_UNOP (*ip); + } else { + ADD_UNOP (*ip); + } + + ip++; + break; + case CEE_CONV_OVF_I1_UN: + case CEE_CONV_OVF_I2_UN: + case CEE_CONV_OVF_I4_UN: + case CEE_CONV_OVF_I8_UN: + case CEE_CONV_OVF_U1_UN: + case CEE_CONV_OVF_U2_UN: + case CEE_CONV_OVF_U4_UN: + case CEE_CONV_OVF_U8_UN: + case CEE_CONV_OVF_I_UN: + case CEE_CONV_OVF_U_UN: + CHECK_STACK (1); + ADD_UNOP (*ip); + ip++; + break; + case CEE_CPOBJ: + g_error ("opcode 0x%02x not handled", *ip); + break; + case CEE_LDOBJ: { + MonoInst *iargs [3]; + CHECK_STACK (1); + --sp; + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + mono_class_init (klass); + n = mono_class_value_size (klass, NULL); + ins = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL); + NEW_TEMPLOADA (cfg, iargs [0], ins->inst_c0); + iargs [1] = *sp; + NEW_ICONST (cfg, iargs [2], n); + mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + NEW_TEMPLOAD (cfg, *sp, ins->inst_c0); + ++sp; + ip += 5; + inline_costs += 1; + break; + } + case CEE_LDSTR: + CHECK_STACK_OVF (1); + n = read32 (ip + 1); + + if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) { + int temp; + MonoInst *iargs [3]; + NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0); + NEW_IMAGECONST (cfg, iargs [1], image); + NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n)); + temp = mono_emit_jit_icall (cfg, bblock, mono_ldstr, iargs, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + } else { + NEW_PCONST (cfg, ins, NULL); + ins->cil_code = ip; + ins->type = STACK_OBJ; + ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n)); + *sp = ins; + } + sp++; + ip += 5; + break; + case CEE_NEWOBJ: { + MonoInst *iargs [2]; + int temp; + + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) { + cmethod = mono_method_get_wrapper_data (method, token); + } else + cmethod = mono_get_method (image, token, NULL); + + mono_class_init (cmethod->klass); + + n = cmethod->signature->param_count; + CHECK_STACK (n); + + /* move the args to allow room for 'this' in the first position */ + while (n--) { + --sp; + sp [1] = sp [0]; + } + + handle_loaded_temps (cfg, bblock, stack_start, sp); + + + if (cmethod->klass->parent == mono_defaults.array_class) { + NEW_METHODCONST (cfg, *sp, cmethod); + temp = mono_emit_native_call (cfg, bblock, mono_array_new_va, cmethod->signature, sp, ip, FALSE); + + } else if (cmethod->string_ctor) { + /* we simply pass a null pointer */ + NEW_PCONST (cfg, *sp, NULL); + /* now call the string ctor */ + temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL); + } else { + if (cmethod->klass->valuetype) { + iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL); + temp = iargs [0]->inst_c0; + NEW_TEMPLOADA (cfg, *sp, temp); + } else { + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], cmethod->klass); + + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + } + + /* now call the actual ctor */ + mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL); + } + + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + + ip += 5; + inline_costs += 5; + break; + } + case CEE_ISINST: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + --sp; + klass = mono_class_get (image, read32 (ip + 1)); + mono_class_init (klass); + ins->type = STACK_OBJ; + ins->inst_left = *sp; + ins->inst_newa_class = klass; + ins->cil_code = ip; + ip += 5; + *sp++ = ins; + break; + case CEE_UNBOX: { + MonoInst *add, *vtoffset; + /* FIXME: need to check class: move to inssel.brg? */ + CHECK_STACK (1); + --sp; + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = (MonoClass *)mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + mono_class_init (klass); + MONO_INST_NEW (cfg, add, CEE_ADD); + NEW_ICONST (cfg, vtoffset, sizeof (MonoObject)); + add->inst_left = *sp; + add->inst_right = vtoffset; + add->type = STACK_MP; + *sp++ = add; + ip += 5; + inline_costs += 1; + break; + } + case CEE_CASTCLASS: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + --sp; + klass = mono_class_get (image, read32 (ip + 1)); + mono_class_init (klass); + ins->type = STACK_OBJ; + ins->inst_left = *sp; + ins->klass = klass; + ins->inst_newa_class = klass; + ins->cil_code = ip; + ip += 5; + *sp++ = ins; + break; + case CEE_THROW: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + --sp; + ins->inst_left = *sp; + ins->cil_code = ip++; + MONO_ADD_INS (bblock, ins); + sp = stack_start; + start_new_bblock = 1; + break; + case CEE_LDFLD: + case CEE_LDFLDA: + case CEE_STFLD: { + MonoInst *offset_ins; + MonoClassField *field; + guint foffset; + + if (*ip == CEE_STFLD) { + CHECK_STACK (2); + sp -= 2; + } else { + CHECK_STACK (1); + --sp; + } + // FIXME: enable this test later. + //if (sp [0]->type != STACK_OBJ && sp [0]->type != STACK_MP) + // goto unverified; + token = read32 (ip + 1); + field = mono_field_from_token (image, token, &klass); + mono_class_init (klass); + foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset; + /* FIXME: mark instructions for use in SSA */ + if (*ip == CEE_STFLD) { + if (klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) { + /* fixme: we need to inline that call somehow */ + MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type); + MonoInst *iargs [5]; + iargs [0] = sp [0]; + NEW_CLASSCONST (cfg, iargs [1], klass); + NEW_FIELDCONST (cfg, iargs [2], field); + NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset); + iargs [4] = sp [1]; + mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, iargs, ip, NULL); + } else { + MonoInst *store; + NEW_ICONST (cfg, offset_ins, foffset); + MONO_INST_NEW (cfg, ins, CEE_ADD); + ins->cil_code = ip; + ins->inst_left = *sp; + ins->inst_right = offset_ins; + ins->type = STACK_MP; + + MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type)); + store->cil_code = ip; + store->inst_left = ins; + store->inst_right = sp [1]; + handle_loaded_temps (cfg, bblock, stack_start, sp); + store->flags |= ins_flag; + ins_flag = 0; + if (store->opcode == CEE_STOBJ) { + handle_stobj (cfg, bblock, ins, sp [1], ip, + mono_class_from_mono_type (field->type), FALSE, FALSE); + } else + MONO_ADD_INS (bblock, store); + } + } else { + if (klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) { + /* fixme: we need to inline that call somehow */ + MonoMethod *ldfld_wrapper = mono_marshal_get_ldfld_wrapper (field->type); + MonoInst *iargs [4]; + int temp; + iargs [0] = sp [0]; + NEW_CLASSCONST (cfg, iargs [1], klass); + NEW_FIELDCONST (cfg, iargs [2], field); + NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset); + temp = mono_emit_method_call_spilled (cfg, bblock, ldfld_wrapper, iargs, ip, NULL); + if (*ip == CEE_LDFLDA) { + /* not sure howto handle this */ + NEW_TEMPLOADA (cfg, *sp, temp); + } else { + NEW_TEMPLOAD (cfg, *sp, temp); + } + sp++; + } else { + NEW_ICONST (cfg, offset_ins, foffset); + MONO_INST_NEW (cfg, ins, CEE_ADD); + ins->cil_code = ip; + ins->inst_left = *sp; + ins->inst_right = offset_ins; + ins->type = STACK_MP; + + if (*ip == CEE_LDFLDA) { + *sp++ = ins; + } else { + MonoInst *load; + MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type)); + type_to_eval_stack_type (field->type, load); + load->cil_code = ip; + load->inst_left = ins; + load->flags |= ins_flag; + ins_flag = 0; + *sp++ = load; + } + } + } + ip += 5; + break; + } + case CEE_LDSFLD: + case CEE_LDSFLDA: + case CEE_STSFLD: { + MonoClassField *field; + MonoVTable *vtable; + + token = read32 (ip + 1); + + field = mono_field_from_token (image, token, &klass); + mono_class_init (klass); + + handle_loaded_temps (cfg, bblock, stack_start, sp); + + if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) { + int temp; + MonoInst *iargs [2]; + g_assert (field->parent); + NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0); + NEW_FIELDCONST (cfg, iargs [1], field); + temp = mono_emit_jit_icall (cfg, bblock, mono_class_static_field_address, iargs, ip); + NEW_TEMPLOAD (cfg, ins, temp); + } else { + vtable = mono_class_vtable (cfg->domain, klass); + NEW_PCONST (cfg, ins, (char*)vtable->data + field->offset); + ins->cil_code = ip; + } + + /* FIXME: mark instructions for use in SSA */ + if (*ip == CEE_LDSFLDA) { + *sp++ = ins; + } else if (*ip == CEE_STSFLD) { + MonoInst *store; + CHECK_STACK (1); + sp--; + MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type)); + store->cil_code = ip; + store->inst_left = ins; + store->inst_right = sp [0]; + store->flags |= ins_flag; + ins_flag = 0; + + if (store->opcode == CEE_STOBJ) { + handle_stobj (cfg, bblock, ins, sp [0], ip, mono_class_from_mono_type (field->type), FALSE, FALSE); + } else + MONO_ADD_INS (bblock, store); + } else { + MonoInst *load; + CHECK_STACK_OVF (1); + MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type)); + type_to_eval_stack_type (field->type, load); + load->cil_code = ip; + load->inst_left = ins; + *sp++ = load; + load->flags |= ins_flag; + ins_flag = 0; + /* fixme: dont see the problem why this does not work */ + //cfg->disable_aot = TRUE; + } + ip += 5; + break; + } + case CEE_STOBJ: + CHECK_STACK (2); + sp -= 2; + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + mono_class_init (klass); + handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE); + ip += 5; + inline_costs += 1; + break; + case CEE_BOX: { + MonoInst *iargs [2]; + MonoInst *load, *vtoffset, *add, *val, *vstore; + int temp; + CHECK_STACK (1); + --sp; + val = *sp; + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + mono_class_init (klass); + + /* much like NEWOBJ */ + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], klass); + + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + NEW_TEMPLOAD (cfg, load, temp); + NEW_ICONST (cfg, vtoffset, sizeof (MonoObject)); + MONO_INST_NEW (cfg, add, CEE_ADD); + add->inst_left = load; + add->inst_right = vtoffset; + add->cil_code = ip; + add->klass = klass; + MONO_INST_NEW (cfg, vstore, CEE_STIND_I); + vstore->opcode = mono_type_to_stind (&klass->byval_arg); + vstore->cil_code = ip; + vstore->inst_left = add; + vstore->inst_right = val; + + if (vstore->opcode == CEE_STOBJ) { + handle_stobj (cfg, bblock, add, val, ip, klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, vstore); + + NEW_TEMPLOAD (cfg, load, temp); + *sp++ = load; + ip += 5; + inline_costs += 1; + break; + } + case CEE_NEWARR: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip; + --sp; + + token = read32 (ip + 1); + + /* allocate the domainvar - becaus this is used in decompose_foreach */ + if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) + mono_get_domainvar (cfg); + + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = (MonoClass *)mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + + mono_class_init (klass); + ins->inst_newa_class = klass; + ins->inst_newa_len = *sp; + ins->type = STACK_OBJ; + ip += 5; + *sp++ = ins; + inline_costs += 1; + break; + case CEE_LDLEN: + CHECK_STACK (1); + MONO_INST_NEW (cfg, ins, *ip); + ins->cil_code = ip++; + --sp; + ins->inst_left = *sp; + ins->type = STACK_PTR; + *sp++ = ins; + break; + case CEE_LDELEMA: + CHECK_STACK (2); + sp -= 2; + klass = mono_class_get (image, read32 (ip + 1)); + mono_class_init (klass); + NEW_LDELEMA (cfg, ins, sp, klass); + ins->cil_code = ip; + *sp++ = ins; + ip += 5; + break; + case CEE_LDELEM_I1: + case CEE_LDELEM_U1: + case CEE_LDELEM_I2: + case CEE_LDELEM_U2: + case CEE_LDELEM_I4: + case CEE_LDELEM_U4: + case CEE_LDELEM_I8: + case CEE_LDELEM_I: + case CEE_LDELEM_R4: + case CEE_LDELEM_R8: + case CEE_LDELEM_REF: { + MonoInst *load; + /* + * translate to: + * ldind.x (ldelema (array, index)) + * ldelema does the bounds check + */ + CHECK_STACK (2); + sp -= 2; + klass = array_access_to_klass (*ip); + NEW_LDELEMA (cfg, load, sp, klass); + load->cil_code = ip; + MONO_INST_NEW (cfg, ins, ldelem_to_ldind [*ip - CEE_LDELEM_I1]); + ins->cil_code = ip; + ins->inst_left = load; + *sp++ = ins; + ins->type = ldind_type [ins->opcode - CEE_LDIND_I1]; + ++ip; + break; + } + case CEE_STELEM_I: + case CEE_STELEM_I1: + case CEE_STELEM_I2: + case CEE_STELEM_I4: + case CEE_STELEM_I8: + case CEE_STELEM_R4: + case CEE_STELEM_R8: + case CEE_STELEM_REF: { + MonoInst *load; + /* + * translate to: + * stind.x (ldelema (array, index), val) + * ldelema does the bounds check + */ + CHECK_STACK (3); + sp -= 3; + klass = array_access_to_klass (*ip); + NEW_LDELEMA (cfg, load, sp, klass); + load->cil_code = ip; + MONO_INST_NEW (cfg, ins, stelem_to_stind [*ip - CEE_STELEM_I]); + ins->cil_code = ip; + ins->inst_left = load; + ins->inst_right = sp [2]; + ++ip; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, ins); + /* FIXME: add the implicit STELEM_REF castclass */ + inline_costs += 1; + cfg->disable_ssa = TRUE; + break; + } + case CEE_CKFINITE: { + MonoInst *store, *temp; + CHECK_STACK (1); + + /* this instr. can throw exceptions as side effect, + * so we cant eliminate dead code which contains CKFINITE opdodes. + * Spilling to memory makes sure that we always perform + * this check */ + + + MONO_INST_NEW (cfg, ins, CEE_CKFINITE); + ins->cil_code = ip; + ins->inst_left = sp [-1]; + temp = mono_compile_create_var (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL); + + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = ip; + MONO_ADD_INS (bblock, store); + + NEW_TEMPLOAD (cfg, sp [-1], temp->inst_c0); + + ++ip; + break; + } + case CEE_REFANYVAL: + case CEE_MKREFANY: + g_error ("opcode 0x%02x not handled", *ip); + break; + case CEE_LDTOKEN: { + gpointer handle; + MonoClass *handle_class; + + CHECK_STACK_OVF (1); + + n = read32 (ip + 1); + + handle = mono_ldtoken (image, n, &handle_class); + mono_class_init (handle_class); + + if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) { + int temp; + MonoInst *res, *store, *addr, *vtvar, *iargs [2]; + + vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL); + + NEW_IMAGECONST (cfg, iargs [0], image); + NEW_ICONST (cfg, iargs [1], n); + temp = mono_emit_jit_icall (cfg, bblock, mono_ldtoken_wrapper, iargs, ip); + NEW_TEMPLOAD (cfg, res, temp); + NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0); + NEW_INDSTORE (cfg, store, addr, res, &mono_defaults.int_class->byval_arg); + MONO_ADD_INS (bblock, store); + NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0); + } else { + if ((ip [5] == CEE_CALL) && (cmethod = mono_get_method (image, read32 (ip + 6), NULL)) && + (cmethod->klass == mono_defaults.monotype_class->parent) && + (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) { + MonoClass *tclass = mono_class_from_mono_type (handle); + mono_class_init (tclass); + NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle)); + ins->type = STACK_OBJ; + ins->klass = cmethod->klass; + ip += 5; + } else { + NEW_PCONST (cfg, ins, handle); + ins->type = STACK_VTYPE; + ins->klass = handle_class; + } + } + + *sp++ = ins; + ip += 5; + break; + } + case CEE_CONV_U2: + case CEE_CONV_U1: + case CEE_CONV_I: + CHECK_STACK (1); + ADD_UNOP (*ip); + ip++; + break; + case CEE_ADD_OVF: + case CEE_ADD_OVF_UN: + case CEE_MUL_OVF: + case CEE_MUL_OVF_UN: + case CEE_SUB_OVF: + case CEE_SUB_OVF_UN: + CHECK_STACK (2); + ADD_BINOP (*ip); + ip++; + break; + case CEE_ENDFINALLY: + /* FIXME: check stack state */ + MONO_INST_NEW (cfg, ins, *ip); + MONO_ADD_INS (bblock, ins); + ins->cil_code = ip++; + start_new_bblock = 1; + break; + case CEE_LEAVE: + case CEE_LEAVE_S: + if (*ip == CEE_LEAVE) { + target = ip + 5 + (gint32)read32(ip + 1); + } else { + target = ip + 2 + (signed char)(ip [1]); + } + + /* empty the stack */ + while (sp != stack_start) { + MONO_INST_NEW (cfg, ins, CEE_POP); + ins->cil_code = ip; + sp--; + ins->inst_i0 = *sp; + MONO_ADD_INS (bblock, ins); + } + + /* fixme: call fault handler ? */ + + if ((tblock = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) { + link_bblock (cfg, bblock, tblock); + MONO_INST_NEW (cfg, ins, OP_HANDLER); + ins->cil_code = ip; + ins->inst_target_bb = tblock; + MONO_ADD_INS (bblock, ins); + } + + + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + GET_BBLOCK (cfg, bbhash, tblock, target); + link_bblock (cfg, bblock, tblock); + CHECK_BBLOCK (target, ip, tblock); + ins->inst_target_bb = tblock; + start_new_bblock = 1; + + if (*ip == CEE_LEAVE) + ip += 5; + else + ip += 2; + + + break; + case CEE_STIND_I: + CHECK_STACK (2); + MONO_INST_NEW (cfg, ins, *ip); + sp -= 2; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, ins); + ins->cil_code = ip++; + ins->inst_i0 = sp [0]; + ins->inst_i1 = sp [1]; + inline_costs += 1; + break; + case CEE_CONV_U: + CHECK_STACK (1); + ADD_UNOP (*ip); + ip++; + break; + /* trampoline mono specific opcodes */ + case MONO_CUSTOM_PREFIX: { + + g_assert (method->wrapper_type != MONO_WRAPPER_NONE); + + switch (ip [1]) { + + case CEE_MONO_FUNC1: { + int temp; + gpointer func = NULL; + CHECK_STACK (1); + sp--; + + switch (ip [2]) { + case MONO_MARSHAL_CONV_STR_LPWSTR: + func = mono_string_to_utf16; + break; + case MONO_MARSHAL_CONV_LPWSTR_STR: + func = mono_string_from_utf16; + break; + case MONO_MARSHAL_CONV_LPSTR_STR: + func = mono_string_new_wrapper; + break; + case MONO_MARSHAL_CONV_STR_LPTSTR: + case MONO_MARSHAL_CONV_STR_LPSTR: + func = mono_string_to_utf8; + break; + case MONO_MARSHAL_CONV_STR_BSTR: + func = mono_string_to_bstr; + break; + case MONO_MARSHAL_CONV_STR_TBSTR: + case MONO_MARSHAL_CONV_STR_ANSIBSTR: + func = mono_string_to_ansibstr; + break; + case MONO_MARSHAL_CONV_SB_LPSTR: + func = mono_string_builder_to_utf8; + break; + case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY: + func = mono_array_to_savearray; + break; + case MONO_MARSHAL_CONV_ARRAY_LPARRAY: + func = mono_array_to_lparray; + break; + case MONO_MARSHAL_CONV_DEL_FTN: + func = mono_delegate_to_ftnptr; + break; + case MONO_MARSHAL_CONV_STRARRAY_STRLPARRAY: + func = mono_marshal_string_array; + break; + default: + g_warning ("unknown conversion %d\n", ip [2]); + g_assert_not_reached (); + } + + temp = mono_emit_jit_icall (cfg, bblock, func, sp, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + + ip += 3; + inline_costs += 10 * num_calls++; + break; + } + case CEE_MONO_PROC2: { + gpointer func = NULL; + CHECK_STACK (2); + sp -= 2; + + switch (ip [2]) { + case MONO_MARSHAL_CONV_LPSTR_SB: + func = mono_string_utf8_to_builder; + break; + case MONO_MARSHAL_FREE_ARRAY: + func = mono_marshal_free_array; + break; + default: + g_assert_not_reached (); + } + + mono_emit_jit_icall (cfg, bblock, func, sp, ip); + ip += 3; + inline_costs += 10 * num_calls++; + break; + } + case CEE_MONO_PROC3: { + gpointer func = NULL; + CHECK_STACK (3); + sp -= 3; + + switch (ip [2]) { + case MONO_MARSHAL_CONV_STR_BYVALSTR: + func = mono_string_to_byvalstr; + break; + case MONO_MARSHAL_CONV_STR_BYVALWSTR: + func = mono_string_to_byvalwstr; + break; + default: + g_assert_not_reached (); + } + + mono_emit_jit_icall (cfg, bblock, func, sp, ip); + ip += 3; + inline_costs += 10 * num_calls++; + break; + } + case CEE_MONO_FREE: + CHECK_STACK (1); + sp -= 1; + mono_emit_jit_icall (cfg, bblock, g_free, sp, ip); + ip += 2; + inline_costs += 10 * num_calls++; + break; + case CEE_MONO_LDPTR: + CHECK_STACK_OVF (1); + token = read32 (ip + 2); + NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, token)); + ins->cil_code = ip; + *sp++ = ins; + ip += 6; + inline_costs += 10 * num_calls++; + break; + case CEE_MONO_VTADDR: + CHECK_STACK (1); + --sp; + MONO_INST_NEW (cfg, ins, OP_VTADDR); + ins->cil_code = ip; + ins->type = STACK_MP; + ins->inst_left = *sp; + *sp++ = ins; + ip += 2; + break; + case CEE_MONO_NEWOBJ: { + MonoInst *iargs [2]; + int temp; + CHECK_STACK_OVF (1); + token = read32 (ip + 2); + klass = (MonoClass *)mono_method_get_wrapper_data (method, token); + mono_class_init (klass); + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], klass); + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + ip += 6; + inline_costs += 10 * num_calls++; + break; + } + case CEE_MONO_OBJADDR: + CHECK_STACK (1); + --sp; + MONO_INST_NEW (cfg, ins, OP_OBJADDR); + ins->cil_code = ip; + ins->type = STACK_MP; + ins->inst_left = *sp; + *sp++ = ins; + ip += 2; + break; + case CEE_MONO_LDNATIVEOBJ: + CHECK_STACK (1); + token = read32 (ip + 2); + klass = mono_method_get_wrapper_data (method, token); + g_assert (klass->valuetype); + mono_class_init (klass); + NEW_INDLOAD (cfg, ins, sp [-1], &klass->byval_arg); + sp [-1] = ins; + ip += 6; + break; + case CEE_MONO_RETOBJ: + g_assert (cfg->ret); + g_assert (method->signature->pinvoke); + CHECK_STACK (1); + --sp; + + token = read32 (ip + 2); + klass = (MonoClass *)mono_method_get_wrapper_data (method, token); + + NEW_RETLOADA (cfg, ins); + handle_stobj (cfg, bblock, ins, *sp, ip, klass, FALSE, TRUE); + + if (sp != stack_start) + goto unverified; + + MONO_INST_NEW (cfg, ins, CEE_BR); + ins->cil_code = ip; + ins->inst_target_bb = end_bblock; + MONO_ADD_INS (bblock, ins); + link_bblock (cfg, bblock, end_bblock); + start_new_bblock = 1; + ip += 6; + break; + default: + g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]); + break; + } + break; + } + case CEE_PREFIX1: { + switch (ip [1]) { + case CEE_ARGLIST: + g_error ("opcode 0xfe 0x%02x not handled", ip [1]); + break; + case CEE_CEQ: + case CEE_CGT: + case CEE_CGT_UN: + case CEE_CLT: + case CEE_CLT_UN: { + MonoInst *cmp; + CHECK_STACK (2); + MONO_INST_NEW (cfg, cmp, 256 + ip [1]); + MONO_INST_NEW (cfg, ins, cmp->opcode); + sp -= 2; + cmp->inst_i0 = sp [0]; + cmp->inst_i1 = sp [1]; + cmp->cil_code = ip; + type_from_op (cmp); + CHECK_TYPE (cmp); + cmp->opcode = OP_COMPARE; + ins->cil_code = ip; + ins->type = STACK_I4; + ins->inst_i0 = cmp; + *sp++ = ins; + ip += 2; + break; + } + case CEE_LDFTN: { + MonoInst *argconst; + int temp; + + CHECK_STACK_OVF (1); + n = read32 (ip + 2); + if (method->wrapper_type != MONO_WRAPPER_NONE) + cmethod = mono_method_get_wrapper_data (method, n); + else + cmethod = mono_get_method (image, n, NULL); + + mono_class_init (cmethod->klass); + handle_loaded_temps (cfg, bblock, stack_start, sp); + + NEW_METHODCONST (cfg, argconst, cmethod); + temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn, &argconst, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + sp ++; + + ip += 6; + inline_costs += 10 * num_calls++; + break; + } + case CEE_LDVIRTFTN: { + MonoInst *args [2]; + int temp; + + CHECK_STACK (1); + n = read32 (ip + 2); + if (method->wrapper_type != MONO_WRAPPER_NONE) + cmethod = mono_method_get_wrapper_data (method, n); + else + cmethod = mono_get_method (image, n, NULL); + + mono_class_init (cmethod->klass); + handle_loaded_temps (cfg, bblock, stack_start, sp); + + --sp; + args [0] = *sp; + NEW_METHODCONST (cfg, args [1], cmethod); + temp = mono_emit_jit_icall (cfg, bblock, mono_ldvirtfn, args, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + sp ++; + + ip += 6; + inline_costs += 10 * num_calls++; + break; + } + case CEE_LDARG: + CHECK_STACK_OVF (1); + NEW_ARGLOAD (cfg, ins, read16 (ip + 2)); + ins->cil_code = ip; + *sp++ = ins; + ip += 4; + break; + case CEE_LDARGA: + CHECK_STACK_OVF (1); + NEW_ARGLOADA (cfg, ins, read16 (ip + 2)); + ins->cil_code = ip; + *sp++ = ins; + ip += 4; + break; + case CEE_STARG: + CHECK_STACK (1); + --sp; + handle_loaded_temps (cfg, bblock, stack_start, sp); + n = read16 (ip + 2); + NEW_ARGSTORE (cfg, ins, n, *sp); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + NEW_ARGLOADA (cfg, ins, n); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, ins); + ip += 4; + break; + case CEE_LDLOC: + CHECK_STACK_OVF (1); + NEW_LOCLOAD (cfg, ins, read16 (ip + 2)); + ins->cil_code = ip; + *sp++ = ins; + ip += 4; + break; + case CEE_LDLOCA: + CHECK_STACK_OVF (1); + NEW_LOCLOADA (cfg, ins, read16 (ip + 2)); + ins->cil_code = ip; + *sp++ = ins; + ip += 4; + break; + case CEE_STLOC: + CHECK_STACK (1); + --sp; + n = read16 (ip + 2); + handle_loaded_temps (cfg, bblock, stack_start, sp); + NEW_LOCSTORE (cfg, ins, n, *sp); + ins->cil_code = ip; + if (ins->opcode == CEE_STOBJ) { + NEW_LOCLOADA (cfg, ins, n); + handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE); + } else + MONO_ADD_INS (bblock, ins); + ip += 4; + inline_costs += 1; + break; + case CEE_LOCALLOC: + CHECK_STACK (1); + --sp; + if (sp != stack_start) + goto unverified; + MONO_INST_NEW (cfg, ins, 256 + ip [1]); + ins->inst_left = *sp; + ins->cil_code = ip; + + if (header->init_locals) + ins->flags |= MONO_INST_INIT; + + *sp++ = ins; + ip += 2; + /* FIXME: set init flag if locals init is set in this method */ + break; + case CEE_ENDFILTER: { + MonoExceptionClause *clause, *nearest; + int cc, nearest_num; + + CHECK_STACK (1); + --sp; + if ((sp != stack_start) || (sp [0]->type != STACK_I4)) + goto unverified; + MONO_INST_NEW (cfg, ins, OP_ENDFILTER); + ins->inst_left = *sp; + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + start_new_bblock = 1; + ip += 2; + + nearest = NULL; + for (cc = 0; cc < header->num_clauses; ++cc) { + clause = &header->clauses [cc]; + if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) && + (!nearest || (clause->token_or_filter > nearest->token_or_filter))) { + nearest = clause; + nearest_num = cc; + } + } + g_assert (nearest); + filter_lengths [nearest_num] = (ip - header->code) - nearest->token_or_filter; + + break; + } + case CEE_UNALIGNED_: + ins_flag |= MONO_INST_UNALIGNED; + ip += 3; + break; + case CEE_VOLATILE_: + ins_flag |= MONO_INST_VOLATILE; + ip += 2; + break; + case CEE_TAIL_: + ins_flag |= MONO_INST_TAILCALL; + ip += 2; + break; + case CEE_INITOBJ: + CHECK_STACK (1); + --sp; + token = read32 (ip + 2); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + handle_initobj (cfg, bblock, *sp, NULL, klass, stack_start, sp); + ip += 6; + inline_costs += 1; + break; + case CEE_CPBLK: + case CEE_INITBLK: { + MonoInst *iargs [3]; + CHECK_STACK (3); + sp -= 3; + iargs [0] = sp [0]; + iargs [1] = sp [1]; + iargs [2] = sp [2]; + handle_loaded_temps (cfg, bblock, stack_start, sp); + if (ip [1] == CEE_CPBLK) { + mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + } else { + mono_emit_jit_icall (cfg, bblock, helper_memset, iargs, ip); + } + ip += 2; + inline_costs += 1; + break; + } + case CEE_RETHROW: { + MonoInst *load; + /* FIXME: check we are in a catch handler */ + NEW_TEMPLOAD (cfg, load, cfg->exvar->inst_c0); + load->cil_code = ip; + MONO_INST_NEW (cfg, ins, CEE_THROW); + ins->inst_left = load; + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); + sp = stack_start; + start_new_bblock = 1; + ip += 2; + break; + } + case CEE_SIZEOF: + CHECK_STACK_OVF (1); + token = read32 (ip + 2); + if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) { + MonoType *type = mono_type_create_from_typespec (image, token); + token = mono_type_size (type, &align); + mono_metadata_free_type (type); + } else { + MonoClass *szclass = mono_class_get (image, token); + mono_class_init (szclass); + token = mono_class_value_size (szclass, &align); + } + NEW_ICONST (cfg, ins, token); + ins->cil_code = ip; + *sp++= ins; + ip += 6; + break; + case CEE_REFANYTYPE: + g_error ("opcode 0xfe 0x%02x not handled", ip [1]); + break; + default: + g_error ("opcode 0xfe 0x%02x not handled", ip [1]); + } + break; + } + default: + g_error ("opcode 0x%02x not handled", *ip); + } + } + if (start_new_bblock != 1) + goto unverified; + + bblock->cil_length = ip - bblock->cil_code; + bblock->next_bb = end_bblock; + link_bblock (cfg, bblock, end_bblock); + + if (cfg->method == method && cfg->domainvar) { + MonoCallInst *call; + MonoInst *store; + + MONO_INST_NEW_CALL (cfg, call, CEE_CALL); + call->signature = helper_sig_domain_get; + call->inst.type = STACK_PTR; + call->fptr = mono_domain_get; + NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, (MonoInst*)call); + + MONO_ADD_INS (init_localsbb, store); + } + + if (header->init_locals) { + MonoInst *store; + for (i = 0; i < header->num_locals; ++i) { + int t = header->locals [i]->type; + if (t == MONO_TYPE_VALUETYPE && header->locals [i]->data.klass->enumtype) + t = header->locals [i]->data.klass->enum_basetype->type; + /* FIXME: use initobj for valuetypes, handle pointers, long, float. */ + if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) { + NEW_ICONST (cfg, ins, 0); + NEW_LOCSTORE (cfg, store, i, ins); + MONO_ADD_INS (init_localsbb, store); + } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) { + MONO_INST_NEW (cfg, ins, OP_I8CONST); + ins->type = STACK_I8; + ins->inst_l = 0; + NEW_LOCSTORE (cfg, store, i, ins); + MONO_ADD_INS (init_localsbb, store); + } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) { + MONO_INST_NEW (cfg, ins, OP_R8CONST); + ins->type = STACK_R8; + ins->inst_p0 = (void*)&r8_0; + NEW_LOCSTORE (cfg, store, i, ins); + MONO_ADD_INS (init_localsbb, store); + } else if (t == MONO_TYPE_VALUETYPE) { + NEW_LOCLOADA (cfg, ins, i); + handle_initobj (cfg, init_localsbb, ins, NULL, mono_class_from_mono_type (header->locals [i]), NULL, NULL); + break; + } else { + NEW_PCONST (cfg, ins, NULL); + NEW_LOCSTORE (cfg, store, i, ins); + MONO_ADD_INS (init_localsbb, store); + } + } + } + + + /* resolve backward branches in the middle of an existing basic block */ + for (tmp = bb_recheck; tmp; tmp = tmp->next) { + bblock = tmp->data; + /*g_print ("need recheck in %s at IL_%04x\n", method->name, bblock->cil_code - header->code);*/ + tblock = find_previous (bbhash, start_bblock, bblock->cil_code); + if (tblock != start_bblock) { + int l; + split_bblock (cfg, tblock, bblock); + l = bblock->cil_code - header->code; + bblock->cil_length = tblock->cil_length - l; + tblock->cil_length = l; + } else { + g_print ("recheck failed.\n"); + } + } + + /* we compute regions here, because the length of filter clauses is not known in advance. + * It is computed in the CEE_ENDFILTER case in the above switch statement*/ + if (cfg->method == method) { + MonoBasicBlock *bb; + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + bb->region = mono_find_block_region (cfg, bb->real_offset, filter_lengths); + if (cfg->verbose_level > 2) + g_print ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region); + } + } else { + g_hash_table_destroy (bbhash); + } + + return inline_costs; + + inline_failure: + if (cfg->method != method) + g_hash_table_destroy (bbhash); + return -1; + + unverified: + if (cfg->method != method) + g_hash_table_destroy (bbhash); + g_error ("Invalid IL code at IL%04x in %s: %s\n", ip - header->code, + mono_method_full_name (method, TRUE), mono_disasm_code_one (NULL, method, ip, NULL)); + return -1; +} + +void +mono_print_tree (MonoInst *tree) { + int arity; + + if (!tree) + return; + + arity = mono_burg_arity [tree->opcode]; + + printf (" %s%s", arity?"(":"", mono_inst_name (tree->opcode)); + + switch (tree->opcode) { + case OP_ICONST: + printf ("[%d]", tree->inst_c0); + break; + case OP_I8CONST: + printf ("[%lld]", tree->inst_l); + break; + case OP_R8CONST: + printf ("[%f]", *(double*)tree->inst_p0); + break; + case OP_R4CONST: + printf ("[%f]", *(float*)tree->inst_p0); + break; + case OP_ARG: + case OP_LOCAL: + printf ("[%d]", tree->inst_c0); + break; + case OP_REGOFFSET: + printf ("[0x%x(%s)]", tree->inst_offset, mono_arch_regname (tree->inst_basereg)); + break; + case OP_REGVAR: + printf ("[%s]", mono_arch_regname (tree->dreg)); + break; + case CEE_NEWARR: + printf ("[%s]", tree->inst_newa_class->name); + mono_print_tree (tree->inst_newa_len); + break; + case CEE_CALL: + case CEE_CALLVIRT: + case OP_FCALL: + case OP_FCALLVIRT: + case OP_LCALL: + case OP_LCALLVIRT: + case OP_VCALL: + case OP_VCALLVIRT: + case OP_VOIDCALL: + case OP_VOIDCALLVIRT: { + MonoCallInst *call = (MonoCallInst*)tree; + if (call->method) + printf ("[%s]", call->method->name); + break; + } + case OP_PHI: { + int i; + printf ("[%d (", tree->inst_c0); + for (i = 0; i < tree->inst_phi_args [0]; i++) { + if (i) + printf (", "); + printf ("%d", tree->inst_phi_args [i + 1]); + } + printf (")]"); + break; + } + case OP_RENAME: + case OP_RETARG: + case CEE_NOP: + case CEE_JMP: + case CEE_BREAK: + break; + case CEE_BR: + printf ("[B%d]", tree->inst_target_bb->block_num); + break; + case CEE_SWITCH: + case CEE_ISINST: + case CEE_CASTCLASS: + case OP_OUTARG: + case OP_CALL_REG: + case OP_FCALL_REG: + case OP_LCALL_REG: + case OP_VCALL_REG: + case OP_VOIDCALL_REG: + mono_print_tree (tree->inst_left); + break; + case CEE_BNE_UN: + case CEE_BEQ: + case CEE_BLT: + case CEE_BLT_UN: + case CEE_BGT: + case CEE_BGT_UN: + case CEE_BGE: + case CEE_BGE_UN: + case CEE_BLE: + case CEE_BLE_UN: + printf ("[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num); + mono_print_tree (tree->inst_left); + break; + default: + if (arity) { + mono_print_tree (tree->inst_left); + if (arity > 1) + mono_print_tree (tree->inst_right); + } + break; + } + + if (arity) + printf (")"); +} + +static void +create_helper_signature (void) +{ + /* FIXME: set call conv */ + /* MonoArray * mono_array_new (MonoDomain *domain, MonoClass *klass, gint32 len) */ + helper_sig_newarr = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_newarr->params [0] = helper_sig_newarr->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_newarr->ret = &mono_defaults.object_class->byval_arg; + helper_sig_newarr->params [2] = &mono_defaults.int32_class->byval_arg; + helper_sig_newarr->pinvoke = 1; + + /* MonoObject * mono_object_new (MonoDomain *domain, MonoClass *klass) */ + helper_sig_object_new = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_object_new->params [0] = helper_sig_object_new->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_object_new->ret = &mono_defaults.object_class->byval_arg; + helper_sig_object_new->pinvoke = 1; + + /* void* mono_method_compile (MonoMethod*) */ + helper_sig_compile = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_compile->params [0] = helper_sig_compile->ret = &mono_defaults.int_class->byval_arg; + helper_sig_compile->pinvoke = 1; + + /* void* mono_ldvirtfn (MonoObject *, MonoMethod*) */ + helper_sig_compile_virt = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_compile_virt->params [0] = &mono_defaults.object_class->byval_arg; + helper_sig_compile_virt->params [1] = helper_sig_compile_virt->ret = &mono_defaults.int_class->byval_arg; + helper_sig_compile_virt->pinvoke = 1; + + /* MonoString* mono_ldstr (MonoDomain *domain, MonoImage *image, guint32 str_index) */ + helper_sig_ldstr = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_ldstr->params [0] = helper_sig_ldstr->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_ldstr->params [2] = &mono_defaults.int32_class->byval_arg; + helper_sig_ldstr->ret = &mono_defaults.object_class->byval_arg; + helper_sig_ldstr->pinvoke = 1; + + /* MonoDomain *mono_domain_get (void) */ + helper_sig_domain_get = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + helper_sig_domain_get->ret = &mono_defaults.int_class->byval_arg; + helper_sig_domain_get->pinvoke = 1; + + /* long amethod (long, long) */ + helper_sig_long_long_long = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_long_long_long->params [0] = helper_sig_long_long_long->params [1] = + &mono_defaults.int64_class->byval_arg; + helper_sig_long_long_long->ret = &mono_defaults.int64_class->byval_arg; + helper_sig_long_long_long->pinvoke = 1; + + /* object amethod (intptr) */ + helper_sig_obj_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_obj_ptr->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_obj_ptr->ret = &mono_defaults.object_class->byval_arg; + helper_sig_obj_ptr->pinvoke = 1; + + /* void amethod (intptr) */ + helper_sig_void_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_void_ptr->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr->ret = &mono_defaults.void_class->byval_arg; + helper_sig_void_ptr->pinvoke = 1; + + /* void amethod (MonoObject *obj) */ + helper_sig_void_obj = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_void_obj->params [0] = &mono_defaults.object_class->byval_arg; + helper_sig_void_obj->ret = &mono_defaults.void_class->byval_arg; + helper_sig_void_obj->pinvoke = 1; + + /* intptr amethod (void) */ + helper_sig_ptr_void = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + helper_sig_ptr_void->ret = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_void->pinvoke = 1; + + /* void amethod (intptr, intptr) */ + helper_sig_void_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_void_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr_ptr->ret = &mono_defaults.void_class->byval_arg; + helper_sig_void_ptr_ptr->pinvoke = 1; + + /* void amethod (intptr, intptr, intptr) */ + helper_sig_void_ptr_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_void_ptr_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr_ptr_ptr->params [2] = &mono_defaults.int_class->byval_arg; + helper_sig_void_ptr_ptr_ptr->ret = &mono_defaults.void_class->byval_arg; + helper_sig_void_ptr_ptr_ptr->pinvoke = 1; + + /* intptr amethod (intptr, intptr) */ + helper_sig_ptr_ptr_ptr = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_ptr_ptr_ptr->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_ptr_ptr->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_ptr_ptr->ret = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_ptr_ptr->pinvoke = 1; + + /* IntPtr amethod (object) */ + helper_sig_ptr_obj = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_ptr_obj->params [0] = &mono_defaults.object_class->byval_arg; + helper_sig_ptr_obj->ret = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_obj->pinvoke = 1; + + /* long amethod (long, guint32) */ + helper_sig_long_long_int = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_long_long_int->params [0] = &mono_defaults.int64_class->byval_arg; + helper_sig_long_long_int->params [1] = &mono_defaults.int32_class->byval_arg; + helper_sig_long_long_int->ret = &mono_defaults.int64_class->byval_arg; + helper_sig_long_long_int->pinvoke = 1; + + /* ulong amethod (double) */ + helper_sig_ulong_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_ulong_double->params [0] = &mono_defaults.double_class->byval_arg; + helper_sig_ulong_double->ret = &mono_defaults.uint64_class->byval_arg; + helper_sig_ulong_double->pinvoke = 1; + + /* long amethod (double) */ + helper_sig_long_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_long_double->params [0] = &mono_defaults.double_class->byval_arg; + helper_sig_long_double->ret = &mono_defaults.int64_class->byval_arg; + helper_sig_long_double->pinvoke = 1; + + /* uint amethod (double) */ + helper_sig_uint_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_uint_double->params [0] = &mono_defaults.double_class->byval_arg; + helper_sig_uint_double->ret = &mono_defaults.uint32_class->byval_arg; + helper_sig_uint_double->pinvoke = 1; + + /* int amethod (double) */ + helper_sig_int_double = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_int_double->params [0] = &mono_defaults.double_class->byval_arg; + helper_sig_int_double->ret = &mono_defaults.int32_class->byval_arg; + helper_sig_int_double->pinvoke = 1; + + /* void initobj (intptr, int size) */ + helper_sig_initobj = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_initobj->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_initobj->params [1] = &mono_defaults.int32_class->byval_arg; + helper_sig_initobj->ret = &mono_defaults.void_class->byval_arg; + helper_sig_initobj->pinvoke = 1; + + /* void memcpy (intptr, intptr, int size) */ + helper_sig_memcpy = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_memcpy->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_memcpy->params [1] = &mono_defaults.int_class->byval_arg; + helper_sig_memcpy->params [2] = &mono_defaults.int32_class->byval_arg; + helper_sig_memcpy->ret = &mono_defaults.void_class->byval_arg; + helper_sig_memcpy->pinvoke = 1; + + /* void memset (intptr, int val, int size) */ + helper_sig_memset = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_memset->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_memset->params [1] = &mono_defaults.int32_class->byval_arg; + helper_sig_memset->params [2] = &mono_defaults.int32_class->byval_arg; + helper_sig_memset->ret = &mono_defaults.void_class->byval_arg; + helper_sig_memset->pinvoke = 1; +} + +static GHashTable *method_opcode_hash = NULL; + +static void +mono_register_method_opcode (MonoMethod *method, int opcode) +{ + + if (!method_opcode_hash) + method_opcode_hash = g_hash_table_new (NULL, NULL); + + g_hash_table_insert (method_opcode_hash, method, (gpointer)opcode); + +} + +int +mono_find_method_opcode (MonoMethod *method) +{ + g_assert (method_opcode_hash); + + return (int)g_hash_table_lookup (method_opcode_hash, method); +} + +static GHashTable *jit_icall_hash_name = NULL; +static GHashTable *jit_icall_hash_addr = NULL; + +MonoJitICallInfo * +mono_find_jit_icall_by_name (const char *name) +{ + g_assert (jit_icall_hash_name); + + //printf ("lookup addr %s %p\n", name, g_hash_table_lookup (jit_icall_hash_name, name)); + return g_hash_table_lookup (jit_icall_hash_name, name); +} + +MonoJitICallInfo * +mono_find_jit_icall_by_addr (gconstpointer addr) +{ + g_assert (jit_icall_hash_addr); + + return g_hash_table_lookup (jit_icall_hash_addr, (gpointer)addr); +} + +MonoJitICallInfo * +mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save) +{ + MonoJitICallInfo *info; + MonoMethod *wrapper; + char *n; + + g_assert (func); + g_assert (name); + + if (!jit_icall_hash_name) { + jit_icall_hash_name = g_hash_table_new (g_str_hash, g_str_equal); + jit_icall_hash_addr = g_hash_table_new (NULL, NULL); + } + + if (g_hash_table_lookup (jit_icall_hash_name, name)) { + g_warning ("jit icall already defined \"%s\"\n", name); + g_assert_not_reached (); + } + + info = g_new (MonoJitICallInfo, 1); + + info->name = g_strdup (name); + info->func = func; + info->sig = sig; + + if (is_save +#ifdef MONO_USE_EXC_TABLES + || mono_arch_has_unwind_info (func) +#endif + ) { + info->wrapper = func; + } else { + g_assert (sig); + n = g_strdup_printf ("__icall_wrapper_%s", name); + wrapper = mono_marshal_get_icall_wrapper (sig, n, func); + info->wrapper = mono_jit_compile_method (wrapper); + g_free (n); + } + + g_hash_table_insert (jit_icall_hash_name, info->name, info); + g_hash_table_insert (jit_icall_hash_addr, (gpointer)func, info); + if (func != info->wrapper) + g_hash_table_insert (jit_icall_hash_addr, (gpointer)info->wrapper, info); + + return info; +} + +static GHashTable *emul_opcode_hash = NULL; + +static MonoJitICallInfo * +mono_find_jit_opcode_emulation (int opcode) +{ + if (emul_opcode_hash) + return g_hash_table_lookup (emul_opcode_hash, (gpointer)opcode); + else + return NULL; +} + +void +mono_register_opcode_emulation (int opcode, MonoMethodSignature *sig, gpointer func) +{ + MonoJitICallInfo *info; + char *name; + + if (!emul_opcode_hash) + emul_opcode_hash = g_hash_table_new (NULL, NULL); + + g_assert (!sig->hasthis); + g_assert (sig->param_count < 3); + + name = g_strdup_printf ("__emulate_%s", mono_inst_name (opcode)); + + info = mono_register_jit_icall (func, name, sig, FALSE); + + g_free (name); + + g_hash_table_insert (emul_opcode_hash, (gpointer)opcode, info); +} + +static void +decompose_foreach (MonoInst *tree, gpointer data) +{ + static MonoJitICallInfo *newarr_info = NULL; + + switch (tree->opcode) { + case CEE_NEWARR: { + MonoCompile *cfg = data; + MonoInst *iargs [3]; + + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], tree->inst_newa_class); + iargs [2] = tree->inst_newa_len; + + if (!newarr_info) { + newarr_info = mono_find_jit_icall_by_addr (mono_array_new); + g_assert (newarr_info); + } + + mono_emulate_opcode (cfg, tree, iargs, newarr_info); + break; + } + + default: + break; + } +} + +void +mono_inst_foreach (MonoInst *tree, MonoInstFunc func, gpointer data) { + + switch (mono_burg_arity [tree->opcode]) { + case 0: break; + case 1: + mono_inst_foreach (tree->inst_left, func, data); + break; + case 2: + mono_inst_foreach (tree->inst_left, func, data); + mono_inst_foreach (tree->inst_right, func, data); + break; + default: + g_assert_not_reached (); + } + func (tree, data); +} + +#if 0 +static void +mono_print_bb_code (MonoBasicBlock *bb) { + if (bb->code) { + MonoInst *c = bb->code; + while (c) { + mono_print_tree (c); + g_print ("\n"); + c = c->next; + } + } +} +#endif + +static void +print_dfn (MonoCompile *cfg) { + int i, j; + char *code; + MonoBasicBlock *bb; + + g_print ("IR code for method %s\n", mono_method_full_name (cfg->method, TRUE)); + + for (i = 0; i < cfg->num_bblocks; ++i) { + bb = cfg->bblocks [i]; + if (bb->cil_code) { + char* code1, *code2; + code1 = mono_disasm_code_one (NULL, cfg->method, bb->cil_code, NULL); + if (bb->last_ins->cil_code) + code2 = mono_disasm_code_one (NULL, cfg->method, bb->last_ins->cil_code, NULL); + else + code2 = g_strdup (""); + + code1 [strlen (code1) - 1] = 0; + code = g_strdup_printf ("%s -> %s", code1, code2); + g_free (code1); + g_free (code2); + } else + code = g_strdup ("\n"); + g_print ("\nBB%d DFN%d (len: %d): %s", bb->block_num, i, bb->cil_length, code); + if (bb->code) { + MonoInst *c = bb->code; + while (c) { + mono_print_tree (c); + g_print ("\n"); + c = c->next; + } + } else { + + } + + g_print ("\tprev:"); + for (j = 0; j < bb->in_count; ++j) { + g_print (" BB%d", bb->in_bb [j]->block_num); + } + g_print ("\t\tsucc:"); + for (j = 0; j < bb->out_count; ++j) { + g_print (" BB%d", bb->out_bb [j]->block_num); + } + g_print ("\n\tidom: BB%d\n", bb->idom? bb->idom->block_num: -1); + + if (bb->idom) + g_assert (mono_bitset_test_fast (bb->dominators, bb->idom->dfn)); + + if (bb->dominators) + mono_blockset_print (cfg, bb->dominators, "\tdominators", bb->idom? bb->idom->dfn: -1); + if (bb->dfrontier) + mono_blockset_print (cfg, bb->dfrontier, "\tdfrontier", -1); + g_free (code); + } + + g_print ("\n"); +} + +/* + * returns the offset used by spillvar. It allocates a new + * spill variable if necessary. + */ +int +mono_spillvar_offset (MonoCompile *cfg, int spillvar) +{ + MonoSpillInfo **si, *info; + int i = 0; + + si = &cfg->spill_info; + + while (i <= spillvar) { + + if (!*si) { + *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo)); + info->next = NULL; + cfg->stack_offset -= sizeof (gpointer); + info->offset = cfg->stack_offset; + } + + if (i == spillvar) + return (*si)->offset; + + i++; + si = &(*si)->next; + } + + g_assert_not_reached (); + return 0; +} + +void +mono_bblock_add_inst (MonoBasicBlock *bb, MonoInst *inst) +{ + inst->next = NULL; + if (bb->last_ins) { + g_assert (bb->code); + bb->last_ins->next = inst; + bb->last_ins = inst; + } else { + bb->last_ins = bb->code = inst; + } +} + +void +mono_destroy_compile (MonoCompile *cfg) +{ + + //mono_mempool_stats (cfg->mempool); + g_hash_table_destroy (cfg->bb_hash); + mono_mempool_destroy (cfg->mempool); + g_free (cfg); +} + +gpointer +mono_get_lmf_addr (void) +{ + MonoJitTlsData *jit_tls; + + if ((jit_tls = TlsGetValue (mono_jit_tls_id))) + return &jit_tls->lmf; + + g_assert_not_reached (); + return NULL; +} + +/** + * mono_thread_abort: + * @obj: exception object + * + * abort the thread, print exception information and stack trace + */ +static void +mono_thread_abort (MonoObject *obj) +{ + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + + g_free (jit_tls); + + ExitThread (-1); +} + +static void +mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func) +{ + MonoJitTlsData *jit_tls; + MonoLMF *lmf; + + jit_tls = g_new0 (MonoJitTlsData, 1); + + TlsSetValue (mono_jit_tls_id, jit_tls); + + jit_tls->abort_func = mono_thread_abort; + jit_tls->end_of_stack = stack_start; + + lmf = g_new0 (MonoLMF, 1); + lmf->ebp = -1; + + jit_tls->lmf = lmf; +} + +void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL; + +static void +mono_thread_abort_dummy (MonoObject *obj) +{ + if (mono_thread_attach_aborted_cb) + mono_thread_attach_aborted_cb (obj); + else + mono_thread_abort (obj); +} + +static void +mono_thread_attach_cb (guint32 tid, gpointer stack_start) +{ + MonoJitTlsData *jit_tls; + MonoLMF *lmf; + + jit_tls = g_new0 (MonoJitTlsData, 1); + + TlsSetValue (mono_jit_tls_id, jit_tls); + + jit_tls->abort_func = mono_thread_abort_dummy; + jit_tls->end_of_stack = stack_start; + + lmf = g_new0 (MonoLMF, 1); + lmf->ebp = -1; + + jit_tls->lmf = lmf; +} + +void +mono_add_patch_info (MonoCompile *cfg, int ip, MonoJumpInfoType type, gconstpointer target) +{ + MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo)); + + ji->ip.i = ip; + ji->type = type; + ji->data.target = target; + ji->next = cfg->patch_info; + + cfg->patch_info = ji; +} + +void +mono_remove_patch_info (MonoCompile *cfg, int ip) +{ + MonoJumpInfo **ji = &cfg->patch_info; + + while (*ji) { + if ((*ji)->ip.i == ip) + *ji = (*ji)->next; + else + ji = &((*ji)->next); + } +} + +static void +dec_foreach (MonoInst *tree, MonoCompile *cfg) { + MonoJitICallInfo *info; + + switch (mono_burg_arity [tree->opcode]) { + case 0: break; + case 1: + dec_foreach (tree->inst_left, cfg); + + if ((info = mono_find_jit_opcode_emulation (tree->opcode))) { + MonoInst *iargs [2]; + + iargs [0] = tree->inst_left; + + mono_emulate_opcode (cfg, tree, iargs, info); + return; + } + + break; + case 2: + if ((info = mono_find_jit_opcode_emulation (tree->opcode))) { + MonoInst *iargs [2]; + + iargs [0] = tree->inst_i0; + iargs [1] = tree->inst_i1; + + mono_emulate_opcode (cfg, tree, iargs, info); + + dec_foreach (iargs [0], cfg); + dec_foreach (iargs [1], cfg); + return; + } else { + dec_foreach (tree->inst_left, cfg); + dec_foreach (tree->inst_right, cfg); + } + break; + default: + g_assert_not_reached (); + } + decompose_foreach (tree, cfg); +} + +static void +decompose_pass (MonoCompile *cfg) { + MonoBasicBlock *bb; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *tree; + cfg->cbb = bb; + cfg->prev_ins = NULL; + for (tree = cfg->cbb->code; tree; tree = tree->next) { + dec_foreach (tree, cfg); + cfg->prev_ins = tree; + } + } +} + +static void +nullify_basic_block (MonoBasicBlock *bb) +{ + bb->in_count = 0; + bb->out_count = 0; + bb->in_bb = NULL; + bb->out_bb = NULL; + bb->next_bb = NULL; + bb->code = bb->last_ins = NULL; +} + +static void +replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + int i, j; + + for (i = 0; i < bb->out_count; i++) { + MonoBasicBlock *ob = bb->out_bb [i]; + for (j = 0; j < ob->in_count; j++) { + if (ob->in_bb [j] == orig) + ob->in_bb [j] = repl; + } + } + +} + +static void +merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) +{ + bb->out_count = bbn->out_count; + bb->out_bb = bbn->out_bb; + + replace_basic_block (bb, bbn, bb); + + if (bb->last_ins) { + if (bbn->code) { + bb->last_ins->next = bbn->code; + bb->last_ins = bbn->last_ins; + } + } else { + bb->code = bbn->code; + bb->last_ins = bbn->last_ins; + } + bb->next_bb = bbn->next_bb; + nullify_basic_block (bbn); +} + +static void +optimize_branches (MonoCompile *cfg) { + int changed = FALSE; + MonoBasicBlock *bb, *bbn; + + do { + changed = FALSE; + + /* we skip the entry block (exit is handled specially instead ) */ + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + + if (bb->out_count == 1) { + bbn = bb->out_bb [0]; + + if (bb->region == bbn->region && bb->next_bb == bbn) { + /* the block are in sequence anyway ... */ + + /* + * miguel: I do not understand what the test below does, could we + * use a macro, or a comment here? opcode > CEE_BEQ && <= BLT_UN + * + * It could also test for bb->last_in only once, and the value + * could be cached (last_ins->opcode) + */ + if (bb->last_ins && (bb->last_ins->opcode == CEE_BR || ( + (bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN)))) { + bb->last_ins->opcode = CEE_NOP; + changed = TRUE; + if (cfg->verbose_level > 2) + g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num); + } + /* fixme: this causes problems with inlining */ + if (bbn->in_count == 1) { + + if (bbn != cfg->bb_exit) { + if (cfg->verbose_level > 2) + g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num); + merge_basic_blocks (bb, bbn); + changed = TRUE; + } + + //mono_print_bb_code (bb); + } + } else { + if (bb->last_ins && bb->last_ins->opcode == CEE_BR) { + bbn = bb->last_ins->inst_target_bb; + if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) { + /* + if (cfg->verbose_level > 2) + g_print ("in %s branch to branch triggered %d -> %d\n", cfg->method->name, bb->block_num, bbn->block_num); + bb->out_bb [0] = bb->last_ins->inst_target_bb = bbn->code->inst_target_bb; + changed = TRUE;*/ + } + } + } + } else if (bb->out_count == 2) { + /* fixme: this does not correctly unlink the blocks, so we get serious problems in idom code */ + if (0 && bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN) { + bbn = bb->last_ins->inst_true_bb; + if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) { + if (cfg->verbose_level > 2) + g_print ("cbranch to branch triggered %d -> %d (0x%02x)\n", bb->block_num, + bbn->block_num, bbn->code->opcode); + + if (bb->out_bb [0] == bbn) { + bb->out_bb [0] = bbn->code->inst_target_bb; + } else if (bb->out_bb [1] == bbn) { + bb->out_bb [1] = bbn->code->inst_target_bb; + } + bb->last_ins->inst_true_bb = bbn->code->inst_target_bb; + changed = TRUE; + } + } + } + } + } while (changed); +} + +static void +mono_compile_create_vars (MonoCompile *cfg) +{ + MonoMethodSignature *sig; + MonoMethodHeader *header; + int i; + + header = ((MonoMethodNormal *)cfg->method)->header; + + sig = cfg->method->signature; + + if (!MONO_TYPE_IS_VOID (sig->ret)) { + cfg->ret = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst)); + cfg->ret->opcode = OP_RETARG; + cfg->ret->inst_vtype = sig->ret; + cfg->ret->klass = mono_class_from_mono_type (sig->ret); + } + + if (sig->hasthis) + mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG); + + for (i = 0; i < sig->param_count; ++i) + mono_compile_create_var (cfg, sig->params [i], OP_ARG); + + cfg->locals_start = cfg->num_varinfo; + + for (i = 0; i < header->num_locals; ++i) + mono_compile_create_var (cfg, header->locals [i], OP_LOCAL); +} + +#if 0 +static void +mono_print_code (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *tree = bb->code; + + if (!tree) + continue; + + g_print ("CODE BLOCK %d (nesting %d):\n", bb->block_num, bb->nesting); + + for (; tree; tree = tree->next) { + mono_print_tree (tree); + g_print ("\n"); + } + + if (bb->last_ins) + bb->last_ins->next = NULL; + } +} +#endif + +extern const char * const mono_burg_rule_string []; + +static void +emit_state (MonoCompile *cfg, MBState *state, int goal) +{ + MBState *kids [10]; + int ern = mono_burg_rule (state, goal); + const guint16 *nts = mono_burg_nts [ern]; + MBEmitFunc emit; + + //g_print ("rule: %s\n", mono_burg_rule_string [ern]); + switch (goal) { + case MB_NTERM_reg: + //if (state->reg2) + // state->reg1 = state->reg2; /* chain rule */ + //else + state->reg1 = mono_regstate_next_int (cfg->rs); + //g_print ("alloc symbolic R%d (reg2: R%d) in block %d\n", state->reg1, state->reg2, cfg->cbb->block_num); + break; + case MB_NTERM_lreg: + state->reg1 = mono_regstate_next_int (cfg->rs); + state->reg2 = mono_regstate_next_int (cfg->rs); + break; + case MB_NTERM_freg: + state->reg1 = mono_regstate_next_float (cfg->rs); + break; + default: + /* do nothing */ + break; + } + if (nts [0]) { + mono_burg_kids (state, ern, kids); + + emit_state (cfg, kids [0], nts [0]); + if (nts [1]) { + emit_state (cfg, kids [1], nts [1]); + if (nts [2]) { + g_assert (!nts [3]); + emit_state (cfg, kids [2], nts [2]); + } + } + } + +// g_print ("emit: %s (%p)\n", mono_burg_rule_string [ern], state); + if ((emit = mono_burg_func [ern])) + emit (state, state->tree, cfg); +} + +#define DEBUG_SELECTION + +static void +mini_select_instructions (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + cfg->state_pool = mono_mempool_new (); + cfg->rs = mono_regstate_new (); + +#ifdef DEBUG_SELECTION + if (cfg->verbose_level >= 4) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *tree = bb->code; + g_print ("DUMP BLOCK %d:\n", bb->block_num); + if (!tree) + continue; + for (; tree; tree = tree->next) { + mono_print_tree (tree); + g_print ("\n"); + } + } + } +#endif + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *tree = bb->code, *next; + MBState *mbstate; + + if (!tree) + continue; + bb->code = NULL; + bb->last_ins = NULL; + + cfg->cbb = bb; + mono_regstate_reset (cfg->rs); + +#ifdef DEBUG_SELECTION + if (cfg->verbose_level >= 3) + g_print ("LABEL BLOCK %d:\n", bb->block_num); +#endif + for (; tree; tree = next) { + next = tree->next; +#ifdef DEBUG_SELECTION + if (cfg->verbose_level >= 3) { + mono_print_tree (tree); + g_print ("\n"); + } +#endif + + if (!(mbstate = mono_burg_label (tree, cfg))) { + g_warning ("unabled to label tree %p", tree); + mono_print_tree (tree); + g_print ("\n"); + g_assert_not_reached (); + } + emit_state (cfg, mbstate, MB_NTERM_stmt); + } + bb->max_ireg = cfg->rs->next_vireg; + bb->max_freg = cfg->rs->next_vfreg; + + if (bb->last_ins) + bb->last_ins->next = NULL; + + mono_mempool_empty (cfg->state_pool); + } + mono_mempool_destroy (cfg->state_pool); +} + +void +mono_codegen (MonoCompile *cfg) +{ + MonoJumpInfo *patch_info; + MonoBasicBlock *bb; + int i, max_epilog_size; + guint8 *code; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + cfg->spill_count = 0; + /* we reuse dfn here */ + /* bb->dfn = bb_count++; */ + mono_arch_local_regalloc (cfg, bb); + } + + if (mono_trace_coverage) + mono_allocate_coverage_info (cfg->method, cfg->num_bblocks); + + code = mono_arch_emit_prolog (cfg); + + if (mono_jit_profile) + code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE); + + cfg->code_len = code - cfg->native_code; + cfg->prolog_end = cfg->code_len; + + mono_debug_open_method (cfg); + + /* emit code all basic blocks */ + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + bb->native_offset = cfg->code_len; + mono_arch_output_basic_block (cfg, bb); + } + cfg->bb_exit->native_offset = cfg->code_len; + + code = cfg->native_code + cfg->code_len; + + max_epilog_size = mono_arch_max_epilog_size (cfg); + + /* we always allocate code in cfg->domain->code_mp to increase locality */ + cfg->code_size = cfg->code_len + max_epilog_size; + /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */ + code = mono_mempool_alloc (cfg->domain->code_mp, cfg->code_size); + memcpy (code, cfg->native_code, cfg->code_len); + g_free (cfg->native_code); + cfg->native_code = code; + code = cfg->native_code + cfg->code_len; + + /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */ + + cfg->epilog_begin = cfg->code_len; + + if (mono_jit_profile) + code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE); + + cfg->code_len = code - cfg->native_code; + + mono_arch_emit_epilog (cfg); + + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + switch (patch_info->type) { + case MONO_PATCH_INFO_ABS: { + MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target); + if (info) { + //printf ("TEST %s %p\n", info->name, patch_info->data.target); + patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD; + patch_info->data.name = info->name; + } + break; + } + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = g_new (gpointer, patch_info->table_size); + patch_info->ip.i = patch_info->ip.label->inst_c0; + for (i = 0; i < patch_info->table_size; i++) { + table [i] = (gpointer)patch_info->data.table [i]->native_offset; + } + patch_info->data.target = table; + break; + } + default: + /* do nothing */ + break; + } + } + + if (cfg->verbose_level > 1) + g_print ("Method %s::%s emmitted at %p to %p\n", cfg->method->klass->name, + cfg->method->name, cfg->native_code, cfg->native_code + cfg->code_len); + + mono_arch_patch_code (cfg->method, cfg->domain, cfg->native_code, cfg->patch_info); + + mono_debug_close_method (cfg); +} + +static void +mono_cprop_copy_values (MonoCompile *cfg, MonoInst *tree, MonoInst **acp) +{ + MonoInst *cp; + int arity; + + if (tree->ssa_op == MONO_SSA_LOAD && (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG) && + (cp = acp [tree->inst_i0->inst_c0]) && !tree->inst_i0->flags) { + + if (cp->opcode == OP_ICONST) { + if (cfg->opt & MONO_OPT_CONSPROP) { + //{ static int c = 0; printf ("CCOPY %d %d %s\n", c++, cp->inst_c0, mono_method_full_name (cfg->method, TRUE)); } + *tree = *cp; + } + } else { + if (tree->inst_i0->inst_vtype->type == cp->inst_vtype->type) { + if (cfg->opt & MONO_OPT_COPYPROP) { + //{ static int c = 0; printf ("VCOPY %d\n", ++c); } + tree->inst_i0 = cp; + } + } + } + } else { + arity = mono_burg_arity [tree->opcode]; + + if (arity) { + mono_cprop_copy_values (cfg, tree->inst_i0, acp); + if (cfg->opt & MONO_OPT_CFOLD) + mono_constant_fold_inst (tree, NULL); + if (arity > 1) { + mono_cprop_copy_values (cfg, tree->inst_i1, acp); + if (cfg->opt & MONO_OPT_CFOLD) + mono_constant_fold_inst (tree, NULL); + } + mono_constant_fold_inst (tree, NULL); + } + } +} + +static void +mono_cprop_invalidate_values (MonoInst *tree, MonoInst **acp, int acp_size) +{ + int arity; + + switch (tree->opcode) { + case CEE_STIND_I: + case CEE_STIND_I1: + case CEE_STIND_I2: + case CEE_STIND_I4: + case CEE_STIND_REF: + case CEE_STIND_I8: + case CEE_STIND_R4: + case CEE_STIND_R8: + case CEE_STOBJ: + if (tree->ssa_op == MONO_SSA_NOP) { + memset (acp, 0, sizeof (MonoInst *) * acp_size); + return; + } + + break; + case CEE_CALL: + case OP_CALL_REG: + case CEE_CALLVIRT: + case OP_LCALL_REG: + case OP_LCALLVIRT: + case OP_LCALL: + case OP_FCALL_REG: + case OP_FCALLVIRT: + case OP_FCALL: + case OP_VCALL_REG: + case OP_VCALLVIRT: + case OP_VCALL: + case OP_VOIDCALL_REG: + case OP_VOIDCALLVIRT: + case OP_VOIDCALL: { + MonoCallInst *call = (MonoCallInst *)tree; + MonoMethodSignature *sig = call->signature; + int i, byref = FALSE; + + for (i = 0; i < sig->param_count; i++) { + if (sig->params [i]->byref) { + byref = TRUE; + break; + } + } + + if (byref) + memset (acp, 0, sizeof (MonoInst *) * acp_size); + + return; + } + default: + break; + } + + arity = mono_burg_arity [tree->opcode]; + + switch (arity) { + case 0: + break; + case 1: + mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size); + break; + case 2: + mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size); + mono_cprop_invalidate_values (tree->inst_i1, acp, acp_size); + break; + default: + g_assert_not_reached (); + } +} + +static void +mono_local_cprop_bb (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size) +{ + MonoInst *tree = bb->code; + int i; + + if (!tree) + return; + + for (; tree; tree = tree->next) { + + mono_cprop_copy_values (cfg, tree, acp); + + mono_cprop_invalidate_values (tree, acp, acp_size); + + if (tree->ssa_op == MONO_SSA_STORE && + (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG)) { + MonoInst *i1 = tree->inst_i1; + + acp [tree->inst_i0->inst_c0] = NULL; + + for (i = 0; i < acp_size; i++) { + if (acp [i] && acp [i]->opcode != OP_ICONST && + acp [i]->inst_c0 == tree->inst_i0->inst_c0) { + acp [i] = NULL; + } + } + + if (i1->opcode == OP_ICONST) { + acp [tree->inst_i0->inst_c0] = i1; + //printf ("DEF1 BB%d %d\n", bb->block_num,tree->inst_i0->inst_c0); + } + if (i1->ssa_op == MONO_SSA_LOAD && + (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG) && + (i1->inst_i0->inst_c0 != tree->inst_i0->inst_c0)) { + acp [tree->inst_i0->inst_c0] = i1->inst_i0; + //printf ("DEF2 BB%d %d %d\n", bb->block_num,tree->inst_i0->inst_c0,i1->inst_i0->inst_c0); + } + } + + /* + if (tree->opcode == CEE_BEQ) { + g_assert (tree->inst_i0->opcode == OP_COMPARE); + if (tree->inst_i0->inst_i0->opcode == OP_ICONST && + tree->inst_i0->inst_i1->opcode == OP_ICONST) { + + tree->opcode = CEE_BR; + if (tree->inst_i0->inst_i0->opcode == tree->inst_i0->inst_i1->opcode) { + tree->inst_target_bb = tree->inst_true_bb; + } else { + tree->inst_target_bb = tree->inst_false_bb; + } + } + } + */ + } +} + +static void +mono_local_cprop (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + MonoInst **acp; + + acp = alloca (sizeof (MonoInst *) * cfg->num_varinfo); + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + memset (acp, 0, sizeof (MonoInst *) * cfg->num_varinfo); + mono_local_cprop_bb (cfg, bb, acp, cfg->num_varinfo); + } +} + +MonoCompile* +mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int parts) +{ + MonoMethodHeader *header = ((MonoMethodNormal *)method)->header; + guint8 *ip = (guint8 *)header->code; + MonoCompile *cfg; + MonoJitInfo *jinfo; + int dfn = 0, i, code_size_ratio; + + mono_jit_stats.methods_compiled++; + + cfg = g_new0 (MonoCompile, 1); + cfg->method = method; + cfg->mempool = mono_mempool_new (); + cfg->opt = opts; + cfg->bb_hash = g_hash_table_new (g_direct_hash, NULL); + cfg->domain = domain; + cfg->verbose_level = mini_verbose; + + /* + * create MonoInst* which represents arguments and local variables + */ + mono_compile_create_vars (cfg); + + if (cfg->verbose_level > 2) + g_print ("converting method %s\n", mono_method_full_name (method, TRUE)); + + if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0)) < 0) { + mono_destroy_compile (cfg); + return NULL; + } + + mono_jit_stats.basic_blocks += cfg->num_bblocks; + mono_jit_stats.max_basic_blocks = MAX (cfg->num_bblocks, mono_jit_stats.max_basic_blocks); + + /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/ + + /* Depth-first ordering on basic blocks */ + cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1)); + + if (cfg->opt & MONO_OPT_BRANCH) + optimize_branches (cfg); + + df_visit (cfg->bb_entry, &dfn, cfg->bblocks); + if (cfg->num_bblocks != dfn + 1) { + if (cfg->verbose_level > 1) + g_print ("unreachable code?\n"); + cfg->num_bblocks = dfn + 1; + } + + if (cfg->opt & MONO_OPT_LOOP) { + mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM); + mono_compute_natural_loops (cfg); + } + + + /* after method_to_ir */ + if (parts == 1) + return cfg; + +//#define DEBUGSSA "logic_run" +#define DEBUGSSA_CLASS "Tests" +#ifdef DEBUGSSA + + + if (!header->num_clauses && !cfg->disable_ssa) { + mono_local_cprop (cfg); + mono_ssa_compute (cfg); + } +#else + + /* fixme: add all optimizations which requires SSA */ + if (cfg->opt & (MONO_OPT_DEADCE)) { + if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) { + mono_local_cprop (cfg); + mono_ssa_compute (cfg); + + if (cfg->verbose_level >= 2) { + print_dfn (cfg); + } + } + } +#endif + + /* after SSA translation */ + if (parts == 2) + return cfg; + + if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) { + if (cfg->comp_done & MONO_COMP_SSA) { + mono_ssa_cprop (cfg); + } else { + mono_local_cprop (cfg); + } + } + + if (cfg->comp_done & MONO_COMP_SSA) { + mono_ssa_deadce (cfg); + + //mono_ssa_strength_reduction (cfg); + + mono_ssa_remove (cfg); + + if (cfg->opt & MONO_OPT_BRANCH) + optimize_branches (cfg); + } + + /* after SSA removal */ + if (parts == 3) + return cfg; + + decompose_pass (cfg); + + if (cfg->opt & MONO_OPT_LINEARS) { + GList *vars, *regs; + + /* fixme: maybe we can avoid to compute livenesss here if already computed ? */ + cfg->comp_done &= ~MONO_COMP_LIVENESS; + if (!(cfg->comp_done & MONO_COMP_LIVENESS)) + mono_analyze_liveness (cfg); + + if ((vars = mono_arch_get_allocatable_int_vars (cfg))) { + regs = mono_arch_get_global_int_regs (cfg); + mono_linear_scan (cfg, vars, regs, &cfg->used_int_regs); + } + } + + //mono_print_code (cfg); + + //print_dfn (cfg); + + /* variables are allocated after decompose, since decompose could create temps */ + mono_arch_allocate_vars (cfg); + + if (cfg->opt & MONO_OPT_CFOLD) + mono_constant_fold (cfg); + + mini_select_instructions (cfg); + + mono_codegen (cfg); + if (cfg->verbose_level >= 2) { + char *id = mono_method_full_name (cfg->method, FALSE); + mono_disassemble_code (cfg->native_code, cfg->code_len, id + 3); + g_free (id); + } + + jinfo = mono_mempool_alloc0 (cfg->domain->mp, sizeof (MonoJitInfo)); + + jinfo = g_new0 (MonoJitInfo, 1); + jinfo->method = method; + jinfo->code_start = cfg->native_code; + jinfo->code_size = cfg->code_len; + jinfo->used_regs = cfg->used_int_regs; + + if (header->num_clauses) { + int i; + + jinfo->exvar_offset = cfg->exvar? cfg->exvar->inst_offset: 0; + jinfo->num_clauses = header->num_clauses; + jinfo->clauses = mono_mempool_alloc0 (cfg->domain->mp, + sizeof (MonoJitExceptionInfo) * header->num_clauses); + + for (i = 0; i < header->num_clauses; i++) { + MonoExceptionClause *ec = &header->clauses [i]; + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + MonoBasicBlock *tblock; + + ei->flags = ec->flags; + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->token_or_filter); + g_assert (tblock); + ei->data.filter = cfg->native_code + tblock->native_offset; + } else { + ei->data.token = ec->token_or_filter; + } + + tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset); + g_assert (tblock); + ei->try_start = cfg->native_code + tblock->native_offset; + tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset + ec->try_len); + g_assert (tblock); + ei->try_end = cfg->native_code + tblock->native_offset; + tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->handler_offset); + g_assert (tblock); + ei->handler_start = cfg->native_code + tblock->native_offset; + + } + } + + mono_jit_info_table_add (cfg->domain, jinfo); + + /* collect statistics */ + mono_jit_stats.allocated_code_size += cfg->code_len; + code_size_ratio = cfg->code_len; + if (code_size_ratio > mono_jit_stats.biggest_method_size) { + mono_jit_stats.biggest_method_size = code_size_ratio; + mono_jit_stats.biggest_method = method; + } + code_size_ratio = (code_size_ratio * 100) / ((MonoMethodNormal *)method)->header->code_size; + if (code_size_ratio > mono_jit_stats.max_code_size_ratio) { + mono_jit_stats.max_code_size_ratio = code_size_ratio; + mono_jit_stats.max_ratio_method = method; + } + mono_jit_stats.native_code_size += cfg->code_len; + + return cfg; +} + +static gpointer +mono_jit_compile_method (MonoMethod *method) +{ + /* FIXME: later copy the code from mono */ + MonoDomain *target_domain, *domain = mono_domain_get (); + MonoCompile *cfg; + GHashTable *jit_code_hash; + gpointer code; + + if (default_opt & MONO_OPT_SAHRED) + target_domain = mono_root_domain; + else + target_domain = domain; + + jit_code_hash = target_domain->jit_code_hash; + + if ((code = g_hash_table_lookup (jit_code_hash, method))) { + mono_jit_stats.methods_lookups++; + return code; + } + +#ifdef MONO_USE_AOT_COMPILER + if (!mono_compile_aot) { + mono_class_init (method->klass); + if ((code = mono_aot_get_method (method))) { + g_hash_table_insert (jit_code_hash, method, code); + return code; + } + } +#endif + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + if (!method->info) { + MonoMethod *nm; + + if (!method->addr && (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + mono_lookup_pinvoke_call (method); +#ifdef MONO_USE_EXC_TABLES + if (mono_method_blittable (method)) { + method->info = method->addr; + } else { +#endif + nm = mono_marshal_get_native_wrapper (method); + method->info = mono_compile_method (nm); + + //if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) + //mono_debug_add_wrapper (method, nm); +#ifdef MONO_USE_EXC_TABLES + } +#endif + } + return method->info; + } else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) { + const char *name = method->name; + MonoMethod *nm; + + if (method->klass->parent == mono_defaults.multicastdelegate_class) { + if (*name == '.' && (strcmp (name, ".ctor") == 0)) { + /* FIXME: uhm, we need a wrapper to handle exceptions? */ + return (gpointer)mono_delegate_ctor; + } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) { + nm = mono_marshal_get_delegate_invoke (method); + return mono_jit_compile_method (nm); + } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) { + nm = mono_marshal_get_delegate_begin_invoke (method); + return mono_jit_compile_method (nm); + } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) { + nm = mono_marshal_get_delegate_end_invoke (method); + return mono_jit_compile_method (nm); + } + } + return NULL; + } + + cfg = mini_method_compile (method, default_opt, target_domain, 0); + code = cfg->native_code; + mono_destroy_compile (cfg); + + g_hash_table_insert (jit_code_hash, method, code); + + /* make sure runtime_init is called */ + mono_class_vtable (target_domain, method->klass); + + return code; +} + +/** + * mono_jit_runtime_invoke: + * @method: the method to invoke + * @obj: this pointer + * @params: array of parameter values. + * @exc: used to catch exceptions objects + */ +static MonoObject* +mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc) +{ + MonoMethod *invoke; + MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc); + invoke = mono_marshal_get_runtime_invoke (method); + runtime_invoke = mono_jit_compile_method (invoke); + return runtime_invoke (obj, params, exc); +} + +#ifdef PLATFORM_WIN32 +#define GET_CONTEXT \ + struct sigcontext *ctx = (struct sigcontext*)_dummy; +#else +#define GET_CONTEXT \ + void **_p = (void **)&_dummy; \ + struct sigcontext *ctx = (struct sigcontext *)++_p; +#endif + +static void +sigfpe_signal_handler (int _dummy) +{ + MonoException *exc; + GET_CONTEXT + + exc = mono_get_exception_divide_by_zero (); + + mono_arch_handle_exception (ctx, exc, FALSE); +} + +static void +sigill_signal_handler (int _dummy) +{ + MonoException *exc; + GET_CONTEXT + exc = mono_get_exception_execution_engine ("SIGILL"); + + mono_arch_handle_exception (ctx, exc, FALSE); +} + +static void +sigsegv_signal_handler (int _dummy) +{ + MonoException *exc; + GET_CONTEXT + + exc = mono_get_exception_null_reference (); + + mono_arch_handle_exception (ctx, exc, FALSE); +} + +static void +sigusr1_signal_handler (int _dummy) +{ + MonoThread *thread; + GET_CONTEXT + + thread = mono_thread_current (); + + g_assert (thread->abort_exc); + + mono_arch_handle_exception (ctx, thread->abort_exc, FALSE); +} + +static void +mono_runtime_install_handlers (void) +{ +#ifndef PLATFORM_WIN32 + struct sigaction sa; +#endif + +#ifdef PLATFORM_WIN32 + win32_seh_init(); + win32_seh_set_handler(SIGFPE, sigfpe_signal_handler); + win32_seh_set_handler(SIGILL, sigill_signal_handler); + win32_seh_set_handler(SIGSEGV, sigsegv_signal_handler); +#else /* !PLATFORM_WIN32 */ + + /* libpthreads has its own implementation of sigaction(), + * but it seems to work well with our current exception + * handlers. If not we must call syscall directly instead + * of sigaction */ + + /* catch SIGFPE */ + sa.sa_handler = sigfpe_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + //g_assert (syscall (SYS_sigaction, SIGFPE, &sa, NULL) != -1); + g_assert (sigaction (SIGFPE, &sa, NULL) != -1); + + /* catch SIGILL */ + sa.sa_handler = sigill_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + //g_assert (syscall (SYS_sigaction, SIGILL, &sa, NULL) != -1); + g_assert (sigaction (SIGILL, &sa, NULL) != -1); + + /* catch thread abort signal */ + sa.sa_handler = sigusr1_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + //g_assert (syscall (SYS_sigaction, SIGILL, &sa, NULL) != -1); + g_assert (sigaction (mono_thread_get_abort_signal (), &sa, NULL) != -1); + +#if 1 + /* catch SIGSEGV */ + sa.sa_handler = sigsegv_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + //g_assert (syscall (SYS_sigaction, SIGSEGV, &sa, NULL) != -1); + g_assert (sigaction (SIGSEGV, &sa, NULL) != -1); +#endif +#endif /* PLATFORM_WIN32 */ +} + +/* mono_jit_create_remoting_trampoline: + * @method: pointer to the method info + * + * Creates a trampoline which calls the remoting functions. This + * is used in the vtable of transparent proxies. + * + * Returns: a pointer to the newly created code + */ +static gpointer +mono_jit_create_remoting_trampoline (MonoMethod *method) +{ + MonoMethod *nm; + guint8 *addr = NULL; + + if (method->signature->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) { + nm = mono_marshal_get_remoting_invoke (method); + addr = mono_compile_method (nm); + } else { + addr = mono_compile_method (method); + } + return addr; +} + +static MonoMethod * +mono_find_unique_method (MonoClass *klass, const char *name, int param_count) +{ + MonoMethod *rval = NULL; + int i; + + mono_class_init (klass); + + for (i = 0; i < klass->method.count; ++i) { + if (!strcmp (name, klass->methods [i]->name) && + klass->methods [i]->signature->param_count == param_count) { + g_assert (rval == NULL); + rval = klass->methods [i]; + g_assert (!(rval->flags & METHOD_ATTRIBUTE_VIRTUAL)); + } + } + + return rval; +} + + +static CRITICAL_SECTION ms; + +MonoDomain * +mini_init (const char *filename) +{ + MonoDomain *domain; + MonoMethod *m; + MonoMethodDesc *desc; + + metadata_section = &ms; + InitializeCriticalSection (metadata_section); + + mono_jit_tls_id = TlsAlloc (); + mono_thread_start_cb (GetCurrentThreadId (), (gpointer)-1, NULL); + + mono_burg_init (); + + mono_runtime_install_handlers (); + + mono_install_compile_method (mono_jit_compile_method); + mono_install_trampoline (mono_arch_create_jit_trampoline); + mono_install_remoting_trampoline (mono_jit_create_remoting_trampoline); + mono_install_runtime_invoke (mono_jit_runtime_invoke); + mono_install_handler (mono_arch_get_throw_exception ()); + mono_install_stack_walk (mono_jit_walk_stack); + mono_install_get_config_dir (); + + domain = mono_init (filename); + mono_init_icall (); + + mono_add_internal_call ("System.Diagnostics.StackFrame::get_frame_info", + ves_icall_get_frame_info); + mono_add_internal_call ("System.Diagnostics.StackTrace::get_trace", + ves_icall_get_trace); + mono_add_internal_call ("Mono.Runtime::mono_runtime_install_handlers", + mono_runtime_install_handlers); + + + create_helper_signature (); + + mono_arch_register_lowlevel_calls (); + mono_register_jit_icall (mono_profiler_method_enter, "mono_profiler_method_enter", NULL, TRUE); + mono_register_jit_icall (mono_profiler_method_leave, "mono_profiler_method_leave", NULL, TRUE); + + mono_register_jit_icall (mono_get_lmf_addr, "mono_get_lmf_addr", helper_sig_ptr_void, TRUE); + mono_register_jit_icall (mono_domain_get, "mono_domain_get", helper_sig_domain_get, TRUE); + + /* fixme: we cant hanlde vararg methods this way, because the signature is not constant */ + //mono_register_jit_icall (ves_array_element_address, "ves_array_element_address", NULL); + //mono_register_jit_icall (mono_array_new_va, "mono_array_new_va", NULL); + + mono_register_jit_icall (mono_arch_get_throw_exception (), "mono_arch_throw_exception", helper_sig_void_obj, TRUE); + mono_register_jit_icall (mono_arch_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", + helper_sig_void_ptr, TRUE); + + + g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sin", 1))); + mono_register_method_opcode (m, OP_SIN); + g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Cos", 1))); + mono_register_method_opcode (m, OP_COS); + g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Tan", 1))); + mono_register_method_opcode (m, OP_TAN); + g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Atan", 1))); + mono_register_method_opcode (m, OP_ATAN); + g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sqrt", 1))); + mono_register_method_opcode (m, OP_SQRT); + + g_assert ((desc = mono_method_desc_new ("System.Math:Abs(double)", 0))); + g_assert ((m = mono_method_desc_search_in_image (desc, mono_defaults.corlib))); + mono_register_method_opcode (m, OP_ABS); + + /* + * NOTE, NOTE, NOTE, NOTE: + * when adding emulation for some opcodes, remember to also add a dummy + * rule to the burg files, because we need the arity information to be correct. + */ + mono_register_opcode_emulation (OP_LMUL, helper_sig_long_long_long, mono_llmult); + mono_register_opcode_emulation (OP_LMUL_OVF_UN, helper_sig_long_long_long, mono_llmult_ovf_un); + mono_register_opcode_emulation (OP_LMUL_OVF, helper_sig_long_long_long, mono_llmult_ovf); + mono_register_opcode_emulation (OP_LDIV, helper_sig_long_long_long, mono_lldiv); + mono_register_opcode_emulation (OP_LDIV_UN, helper_sig_long_long_long, mono_lldiv_un); + mono_register_opcode_emulation (OP_LREM, helper_sig_long_long_long, mono_llrem); + mono_register_opcode_emulation (OP_LREM_UN, helper_sig_long_long_long, mono_llrem_un); + + mono_register_opcode_emulation (OP_LSHL, helper_sig_long_long_int, mono_lshl); + mono_register_opcode_emulation (OP_LSHR, helper_sig_long_long_int, mono_lshr); + mono_register_opcode_emulation (OP_LSHR_UN, helper_sig_long_long_int, mono_lshr_un); + + mono_register_opcode_emulation (OP_FCONV_TO_U8, helper_sig_ulong_double, mono_fconv_u8); + mono_register_opcode_emulation (OP_FCONV_TO_U4, helper_sig_uint_double, mono_fconv_u4); + mono_register_opcode_emulation (OP_FCONV_TO_OVF_I8, helper_sig_long_double, mono_fconv_ovf_i8); + mono_register_opcode_emulation (OP_FCONV_TO_OVF_U8, helper_sig_ulong_double, mono_fconv_ovf_u8); + +#if SIZEOF_VOID_P == 4 + mono_register_opcode_emulation (OP_FCONV_TO_U, helper_sig_uint_double, mono_fconv_u4); +#else +#warning "fixme: add opcode emulation" +#endif + + /* other jit icalls */ + mono_register_jit_icall (mono_class_static_field_address , "mono_class_static_field_address", + helper_sig_ptr_ptr_ptr, FALSE); + mono_register_jit_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", helper_sig_ptr_ptr_ptr, FALSE); + mono_register_jit_icall (mono_ldstr, "mono_ldstr", helper_sig_ldstr, FALSE); + mono_register_jit_icall (helper_memcpy, "helper_memcpy", helper_sig_memcpy, FALSE); + mono_register_jit_icall (helper_memset, "helper_memset", helper_sig_memset, FALSE); + mono_register_jit_icall (helper_initobj, "helper_initobj", helper_sig_initobj, FALSE); + mono_register_jit_icall (mono_object_new, "mono_object_new", helper_sig_object_new, FALSE); + mono_register_jit_icall (mono_array_new, "mono_array_new", helper_sig_newarr, FALSE); + mono_register_jit_icall (mono_string_to_utf16, "mono_string_to_utf16", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_string_from_utf16, "mono_string_from_utf16", helper_sig_obj_ptr, FALSE); + mono_register_jit_icall (mono_string_new_wrapper, "mono_string_new_wrapper", helper_sig_obj_ptr, FALSE); + mono_register_jit_icall (mono_string_to_utf8, "mono_string_to_utf8", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_string_to_bstr, "mono_string_to_bstr", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_string_to_ansibstr, "mono_string_to_ansibstr", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_string_builder_to_utf8, "mono_string_builder_to_utf8", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_array_to_savearray, "mono_array_to_savearray", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_array_to_lparray, "mono_array_to_lparray", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_delegate_to_ftnptr, "mono_delegate_to_ftnptr", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_marshal_string_array, "mono_marshal_string_array", helper_sig_ptr_obj, FALSE); + mono_register_jit_icall (mono_string_utf8_to_builder, "mono_string_utf8_to_builder", helper_sig_void_ptr_ptr, FALSE); + mono_register_jit_icall (mono_marshal_free_array, "mono_marshal_free_array", helper_sig_void_ptr_ptr, FALSE); + mono_register_jit_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", helper_sig_void_ptr_ptr_ptr, FALSE); + mono_register_jit_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", helper_sig_void_ptr_ptr_ptr, FALSE); + mono_register_jit_icall (g_free, "g_free", helper_sig_void_ptr, FALSE); + mono_register_jit_icall (mono_ldftn, "mono_ldftn", helper_sig_compile, FALSE); + mono_register_jit_icall (mono_ldvirtfn, "mono_ldvirtfn", helper_sig_compile_virt, FALSE); + + mono_runtime_init (domain, mono_thread_start_cb, + mono_thread_attach_cb); + + //mono_thread_attach (domain); + return domain; +} + +MonoJitStats mono_jit_stats = {0}; + +static void +print_jit_stats (void) +{ + if (mono_jit_stats.enabled) { + g_print ("Mono Jit statistics\n"); + g_print ("Compiled methods: %ld\n", mono_jit_stats.methods_compiled); + g_print ("Methods from AOT: %ld\n", mono_jit_stats.methods_aot); + g_print ("Methods cache lookup: %ld\n", mono_jit_stats.methods_lookups); + g_print ("Method trampolines: %ld\n", mono_jit_stats.method_trampolines); + g_print ("Basic blocks: %ld\n", mono_jit_stats.basic_blocks); + g_print ("Max basic blocks: %ld\n", mono_jit_stats.max_basic_blocks); + g_print ("Allocated vars: %ld\n", mono_jit_stats.allocate_var); + g_print ("Analyze stack repeat: %ld\n", mono_jit_stats.analyze_stack_repeat); + g_print ("Compiled CIL code size: %ld\n", mono_jit_stats.cil_code_size); + g_print ("Native code size: %ld\n", mono_jit_stats.native_code_size); + g_print ("Max code size ratio: %.2f (%s::%s)\n", mono_jit_stats.max_code_size_ratio/100.0, + mono_jit_stats.max_ratio_method->klass->name, mono_jit_stats.max_ratio_method->name); + g_print ("Biggest method: %ld (%s::%s)\n", mono_jit_stats.biggest_method_size, + mono_jit_stats.biggest_method->klass->name, mono_jit_stats.biggest_method->name); + g_print ("Code reallocs: %ld\n", mono_jit_stats.code_reallocs); + g_print ("Allocated code size: %ld\n", mono_jit_stats.allocated_code_size); + g_print ("Inlineable methods: %ld\n", mono_jit_stats.inlineable_methods); + g_print ("Inlined methods: %ld\n", mono_jit_stats.inlined_methods); + + g_print ("\nCreated object count: %ld\n", mono_stats.new_object_count); + g_print ("Initialized classes: %ld\n", mono_stats.initialized_class_count); + g_print ("Used classes: %ld\n", mono_stats.used_class_count); + g_print ("Static data size: %ld\n", mono_stats.class_static_data_size); + g_print ("VTable data size: %ld\n", mono_stats.class_vtable_size); + } +} + +void +mini_cleanup (MonoDomain *domain) +{ + /* + * mono_runtime_cleanup() needs to be called early since + * it needs the execution engine still fully working (it will + * wait for other threads to finish). + */ + mono_runtime_cleanup (domain); + + mono_domain_finalize (domain); + + mono_profiler_shutdown (); + + mono_debug_cleanup (); +#ifdef PLATFORM_WIN32 + win32_seh_cleanup(); +#endif + + mono_domain_unload (domain, TRUE); + + print_jit_stats (); + DeleteCriticalSection (metadata_section); +} + +void +mini_set_defaults (int verbose_level, guint32 opts) +{ + mini_verbose = verbose_level; + default_opt = opts; +} + diff --git a/mono/mini/mini.h b/mono/mini/mini.h new file mode 100644 index 00000000000..889cc0412e6 --- /dev/null +++ b/mono/mini/mini.h @@ -0,0 +1,662 @@ +#ifndef __MONO_MINI_H__ +#define __MONO_MINI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "regalloc.h" + +/* fixme: configure should set this */ +#define SIZEOF_VOID_P 4 + +#define MONO_USE_AOT_COMPILER + +#if 1 +#define mono_bitset_test_fast(set,n) (((guint32*)set)[2+(n)/32] & (1 << ((n) % 32))) +#else +#define mono_bitset_test_fast(set,n) mono_bitset_test(set,n) +#endif + +#if 0 +#define mono_bitset_foreach_bit(set,b,n) \ + for (b = 0; b < n; b++)\ + if (mono_bitset_test_fast(set,b)) +#define mono_bitset_foreach_bit_rev(set,b,n) \ + for (b = n - 1; b >= 0; b--)\ + if (mono_bitset_test_fast(set,b)) +#else +#define mono_bitset_foreach_bit(set,b,n) \ + for (b = mono_bitset_find_first (set, -1); b < n && b >= 0; b = mono_bitset_find_first (set, b)) +#define mono_bitset_foreach_bit_rev(set,b,n) \ + for (b = mono_bitset_find_last (set, n - 1); b >= 0; b = b ? mono_bitset_find_last (set, b) : -1) + +#endif + +/* + * Pull the list of opcodes + */ +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + CEE_LASTOP +}; +#undef OPDEF + +#define MONO_VARINFO(cfg,varnum) ((cfg)->vars [varnum]) + +#define MONO_INST_NEW(cfg,dest,op) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = (op); \ + } while (0) + +#define MONO_INST_NEW_CALL(cfg,dest,op) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoCallInst)); \ + (dest)->inst.opcode = (op); \ + } while (0) + +#define MONO_ADD_INS(b,inst) do { \ + if ((b)->last_ins) { \ + (b)->last_ins->next = (inst); \ + (b)->last_ins = (inst); \ + } else { \ + (b)->code = (b)->last_ins = (inst); \ + } \ + } while (0) + +typedef struct MonoInst MonoInst; +typedef struct MonoCallInst MonoCallInst; +typedef struct MonoEdge MonoEdge; +typedef struct MonoMethodVar MonoMethodVar; +typedef struct MonoBasicBlock MonoBasicBlock; +typedef struct MonoLMF MonoLMF; +typedef struct MonoSpillInfo MonoSpillInfo; + +extern guint32 mono_jit_tls_id; +extern gboolean mono_jit_trace_calls; +extern gboolean mono_break_on_exc; +extern int mono_exc_esp_offset; +extern gboolean mono_compile_aot; +extern gboolean mono_trace_coverage; +extern gboolean mono_jit_profile; + +extern CRITICAL_SECTION *metadata_section; + +struct MonoEdge { + MonoEdge *next; + MonoBasicBlock *bb; + /* add edge type? */ +}; + +struct MonoSpillInfo { + MonoSpillInfo *next; + int offset; +}; + +/* + * The IR-level basic block. + * + * A basic block can have multiple exits just fine, as long as the point of + * 'departure' is the last instruction in the basic block. Extended basic + * blocks, on the other hand, may have instructions that leave the block + * midstream. The important thing is that they cannot be _entered_ + * midstream, ie, execution of a basic block (or extened bb) always start + * at the beginning of the block, never in the middle. + */ +struct MonoBasicBlock { + MonoInst *last_ins; + + /* Points to the start of the CIL code that initiated this BB */ + unsigned char* cil_code; + + /* Length of the CIL block */ + gint32 cil_length; + + /* The address of the generated code, used for fixups */ + int native_offset; + int max_offset; + + gint32 dfn; + + /* unique block number identification */ + gint32 block_num; + + /* Visited and reachable flags */ + guint32 flags; + + /* Basic blocks: incoming and outgoing counts and pointers */ + gint16 out_count, in_count; + MonoBasicBlock **in_bb; + MonoBasicBlock **out_bb; + + /* the next basic block in the order it appears in IL */ + MonoBasicBlock *next_bb; + + /* + * Before instruction selection it is the first tree in the + * forest and the first item in the list of trees. After + * instruction selection it is the first instruction and the + * first item in the list of instructions. + */ + MonoInst *code; + + /* + * SSA and loop based flags + */ + MonoBitSet *dominators; + MonoBitSet *dfrontier; + MonoBasicBlock *idom; + GList *dominated; + /* fast dominator algorithm */ + MonoBasicBlock *df_parent, *ancestor, *child, *label; + MonoEdge *bucket; + int size, sdom, idomn; + + /* loop nesting and recognition */ + GList *loop_blocks; + gint8 nesting; + + /* use for liveness analysis */ + MonoBitSet *gen_set; + MonoBitSet *kill_set; + MonoBitSet *live_in_set; + MonoBitSet *live_out_set; + + /* fields to deal with non-empty stack slots at bb boundary */ + guint16 out_scount, in_scount; + MonoInst **out_stack; + MonoInst **in_stack; + + /* we use that to prevent merging of bblock covered by different clauses*/ + guint real_offset; + guint region; + + /* The current symbolic register number, used in local register allocation. */ + guint16 max_ireg, max_freg; +}; + +/* BBlock flags */ +#define BB_VISITED 1 +#define BB_REACHABLE 2 + +struct MonoInst { + union { + union { + MonoInst *src; + MonoMethodVar *var; + gint32 const_val; + gpointer p; + MonoMethod *method; + MonoMethodSignature *signature; + MonoBasicBlock **many_blocks; + MonoBasicBlock *target_block; + MonoInst **args; + MonoType *vtype; + MonoClass *klass; + int *phi_args; + } op [2]; + gint64 i8const; + double r8const; + } data; + guint16 opcode; + guint8 type; /* stack type */ + guint ssa_op : 3; + guint8 flags : 5; + + /* used by the register allocator */ + gint16 dreg, sreg1, sreg2, unused; + + MonoInst *next; + MonoClass *klass; + const unsigned char* cil_code; /* for debugging and bblock splitting */ +}; + +struct MonoCallInst { + MonoInst inst; + MonoMethodSignature *signature; + MonoMethod *method; + MonoInst **args; + gconstpointer fptr; + guint stack_usage; + guint32 used_iregs; + guint32 used_fregs; +}; + +/* + * flags for MonoInst + * Note: some of the values overlap, because they can't appear + * in the same MonoInst. + */ +enum { + MONO_INST_HAS_METHOD = 1, + /* temp local created by a DUP: used only within a BB */ + MONO_INST_IS_TEMP = 1, + MONO_INST_INIT = 1, /* in localloc */ + MONO_INST_IS_DEAD = 2, + MONO_INST_TAILCALL = 4, + MONO_INST_VOLATILE = 4, + MONO_INST_BRLABEL = 4, + MONO_INST_UNALIGNED = 8, + /* the address of the variable has been taken */ + MONO_INST_INDIRECT = 16 +}; + +#define inst_c0 data.op[0].const_val +#define inst_c1 data.op[1].const_val +#define inst_i0 data.op[0].src +#define inst_i1 data.op[1].src +#define inst_p0 data.op[0].p +#define inst_p1 data.op[1].p +#define inst_l data.i8const +#define inst_r data.r8const +#define inst_left data.op[0].src +#define inst_right data.op[1].src + +#define inst_newa_len data.op[0].src +#define inst_newa_class data.op[1].klass + +#define inst_switch data.op[0].switch_blocks +#define inst_var data.op[0].var +#define inst_vtype data.op[1].vtype +/* in branch instructions */ +#define inst_many_bb data.op[1].many_blocks +#define inst_target_bb data.op[0].target_block +#define inst_true_bb data.op[1].many_blocks[0] +#define inst_false_bb data.op[1].many_blocks[1] + +#define inst_basereg sreg1 +#define inst_indexreg sreg2 +#define inst_destbasereg dreg +#define inst_offset data.op[0].const_val +#define inst_imm data.op[1].const_val + +#define inst_phi_args data.op[1].phi_args + +/* instruction description for use in regalloc/scheduling */ +enum { + MONO_INST_DEST, + MONO_INST_SRC1, + MONO_INST_SRC2, + MONO_INST_FLAGS, + MONO_INST_CLOB, + MONO_INST_COST, + MONO_INST_DELAY, + MONO_INST_RES, + MONO_INST_LEN, + MONO_INST_MAX +}; + +typedef union { + struct { + guint16 tid; /* tree number */ + guint16 bid; /* block number */ + } pos ; + guint32 abs_pos; +} MonoPosition; + +typedef struct { + MonoPosition first_use, last_use; +} MonoLiveRange; + +/* + * Additional information about a variable + */ +struct MonoMethodVar { + guint idx; /* inside cfg->varinfo, cfg->vars */ + guint last_name; + MonoBitSet *dfrontier; + MonoLiveRange range; /* generated by liveness analysis */ + int reg; /* != -1 if allocated into a register */ + int spill_costs; + MonoBitSet *def_in; /* used by SSA */ + MonoInst *def; /* used by SSA */ + MonoBasicBlock *def_bb; /* used by SSA */ + GList *uses; /* used by SSA */ + char cpstate; /* used by SSA conditional constant propagation */ +}; + +typedef struct { + gpointer end_of_stack; + MonoLMF *lmf; + void (*abort_func) (MonoObject *object); +} MonoJitTlsData; + +typedef enum { + MONO_PATCH_INFO_BB, + MONO_PATCH_INFO_ABS, + MONO_PATCH_INFO_LABEL, + MONO_PATCH_INFO_METHOD, + MONO_PATCH_INFO_METHODCONST, + MONO_PATCH_INFO_INTERNAL_METHOD, + MONO_PATCH_INFO_SWITCH, + MONO_PATCH_INFO_EXC, + MONO_PATCH_INFO_CLASS, + MONO_PATCH_INFO_IMAGE, + MONO_PATCH_INFO_FIELD, + MONO_PATCH_INFO_R4, + MONO_PATCH_INFO_R8, + MONO_PATCH_INFO_IP +} MonoJumpInfoType; + +typedef struct MonoJumpInfo MonoJumpInfo; +struct MonoJumpInfo { + MonoJumpInfo *next; + union { + int i; + guint8 *p; + MonoInst *label; + } ip; + + MonoJumpInfoType type; + union { + gconstpointer target; + int offset; + MonoBasicBlock *bb; + MonoBasicBlock **table; + MonoInst *inst; + MonoMethod *method; + MonoClass *klass; + MonoClassField *field; + MonoImage *image; + const char *name; + } data; + + int table_size; /* use by switch */ +}; + +/* optimization flags: keep up to date with the name array in mini.c */ +enum { + MONO_OPT_PEEPHOLE = 1 << 0, + MONO_OPT_BRANCH = 1 << 1, + MONO_OPT_INLINE = 1 << 2, + MONO_OPT_CFOLD = 1 << 3, + MONO_OPT_CONSPROP = 1 << 4, + MONO_OPT_COPYPROP = 1 << 5, + MONO_OPT_DEADCE = 1 << 6, + MONO_OPT_LINEARS = 1 << 7, + MONO_OPT_CMOV = 1 << 8, + MONO_OPT_SAHRED = 1 << 9, + MONO_OPT_SCHED = 1 << 10, + MONO_OPT_INTRINS = 1 << 11, + MONO_OPT_TAILC = 1 << 12, + MONO_OPT_LOOP = 1 << 13, + MONO_OPT_FCMOV = 1 << 14 +}; + +/* + * Control Flow Graph and compilation unit information + */ +typedef struct { + MonoMethod *method; + MonoMemPool *mempool; + MonoInst **varinfo; + MonoMethodVar **vars; + MonoInst *ret; + MonoBasicBlock *bb_entry; + MonoBasicBlock *bb_exit; + MonoBasicBlock *bb_init; + MonoBasicBlock **bblocks; + GHashTable *bb_hash; + MonoMemPool *state_pool; /* used by instruction selection */ + MonoBasicBlock *cbb; /* used by instruction selection */ + MonoInst *prev_ins; /* in decompose */ + MonoJumpInfo *patch_info; + guint num_bblocks; + guint locals_start; + guint num_varinfo; /* used items in varinfo */ + guint varinfo_count; /* total storage in varinfo */ + gint stack_offset; + MonoRegState *rs; + MonoSpillInfo *spill_info; + gint spill_count; + // unsigned char *cil_code; + + MonoInst *exvar; /* the exception object passed to catch/filter blocks */ + MonoInst *domainvar; /* a cache for the current domain */ + + MonoDomain *domain; + + unsigned char *native_code; + guint code_size; + guint code_len; + guint prolog_end; + guint epilog_begin; + guint32 used_int_regs; + guint32 opt; + guint32 flags; + guint32 comp_done; + guint32 verbose_level; + guint32 stack_usage; + guint32 param_area; + gboolean disable_aot; + gboolean disable_ssa; + gpointer debug_info; +} MonoCompile; + +typedef enum { + MONO_CFG_HAS_ALLOCA = 1 << 0, + MONO_CFG_HAS_CALLS = 1 << 1 +} MonoCompileFlags; + +typedef struct { + int entries; + struct { + int iloffset; + int count; + } data [0]; +} MonoCoverageInfo; + +typedef struct { + gulong methods_compiled; + gulong methods_aot; + gulong methods_lookups; + gulong method_trampolines; + gulong allocate_var; + gulong analyze_stack_repeat; + gulong cil_code_size; + gulong native_code_size; + gulong code_reallocs; + gulong max_code_size_ratio; + gulong biggest_method_size; + gulong allocated_code_size; + gulong inlineable_methods; + gulong inlined_methods; + gulong basic_blocks; + gulong max_basic_blocks; + MonoMethod *max_ratio_method; + MonoMethod *biggest_method; + gboolean enabled; +} MonoJitStats; + +extern MonoJitStats mono_jit_stats; + +/* values for MonoInst.ssa_op */ +enum { + MONO_SSA_NOP, + MONO_SSA_LOAD, + MONO_SSA_STORE, + MONO_SSA_MAYBE_LOAD, + MONO_SSA_MAYBE_STORE +}; + +#define OP_CEQ (256+CEE_CEQ) +#define OP_CLT (256+CEE_CLT) +#define OP_CLT_UN (256+CEE_CLT_UN) +#define OP_CGT (256+CEE_CGT) +#define OP_CGT_UN (256+CEE_CGT_UN) +#define OP_LOCALLOC (256+CEE_LOCALLOC) + +/* opcodes: value assigned after all the CIL opcodes */ +#ifdef MINI_OP +#undef MINI_OP +#endif +#define MINI_OP(a,b) a, +enum { + OP_START = MONO_CEE_LAST, +#include "mini-ops.h" + OP_LAST +}; +#undef MINI_OP + +/* make this depend on 32bit platform (use OP_LADD otherwise) */ +#define OP_PADD CEE_ADD +#define OP_PNEG CEE_NEG +#define OP_PCONV_TO_U2 CEE_CONV_U2 +#define OP_PCONV_TO_OVF_I1_UN CEE_CONV_OVF_I1_UN +#define OP_PCONV_TO_OVF_I1 CEE_CONV_OVF_I1 +#define OP_PCEQ CEE_CEQ + +enum { + STACK_INV, + STACK_I4, + STACK_I8, + STACK_PTR, + STACK_R8, + STACK_MP, + STACK_OBJ, + STACK_VTYPE, + STACK_MAX +}; + +typedef struct { + union { + double r8; + gint32 i4; + gint64 i8; + gpointer p; + MonoClass *klass; + } data; + int type; +} StackSlot; + +enum { + MONO_COMP_DOM = 1, + MONO_COMP_IDOM = 2, + MONO_COMP_DFRONTIER = 4, + MONO_COMP_DOM_REV = 8, + MONO_COMP_LIVENESS = 16, + MONO_COMP_SSA = 32, + MONO_COMP_SSA_DEF_USE = 64, + MONO_COMP_REACHABILITY = 128, + MONO_COMP_LOOPS = 256 +}; + +typedef enum { + MONO_GRAPH_CFG = 1, + MONO_GRAPH_DTREE = 2, + MONO_GRAPH_CFG_CODE = 4, + MONO_GRAPH_CFG_SSA = 8, + MONO_GRAPH_CFG_OPTCODE = 16 +} MonoGraphOptions; + +typedef struct { + char *name; + gconstpointer func; + gconstpointer wrapper; + MonoMethodSignature *sig; +} MonoJitICallInfo; + +typedef void (*MonoInstFunc) (MonoInst *tree, gpointer data); + +/* main function */ +int mini_main (int argc, char* argv[]); +void mini_set_defaults (int verbose_level, guint32 opts); +MonoDomain* mini_init (const char *filename); +void mini_cleanup (MonoDomain *domain); + +/* helper methods */ +int mini_parse_default_optimizations (const char* p); +void mono_bblock_add_inst (MonoBasicBlock *bb, MonoInst *inst); +void mono_constant_fold (MonoCompile *cfg); +void mono_constant_fold_inst (MonoInst *inst, gpointer data); +void mono_cprop_local (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size); +MonoInst* mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode); +void mono_blockset_print (MonoCompile *cfg, MonoBitSet *set, const char *name, guint idom); +void mono_print_tree (MonoInst *tree); +int mono_spillvar_offset (MonoCompile *cfg, int spillvar); +void mono_select_instructions (MonoCompile *cfg); +const char* mono_inst_name (int op); +void mono_inst_foreach (MonoInst *tree, MonoInstFunc func, gpointer data); +void mono_disassemble_code (guint8 *code, int size, char *id); +guint mono_type_to_ldind (MonoType *t); +guint mono_type_to_stind (MonoType *t); +void mono_add_patch_info (MonoCompile *cfg, int ip, MonoJumpInfoType type, gconstpointer target); +void mono_remove_patch_info (MonoCompile *cfg, int ip); +gpointer mono_get_lmf_addr (void); +GList *mono_varlist_insert_sorted (MonoCompile *cfg, GList *list, MonoMethodVar *mv, gboolean sort_end); +void mono_analyze_liveness (MonoCompile *cfg); +void mono_linear_scan (MonoCompile *cfg, GList *vars, GList *regs, guint32 *used_mask); +void mono_create_jump_table (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks); +int mono_compile_assembly (MonoAssembly *ass, guint32 opts); +MonoCompile *mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int parts); +void mono_destroy_compile (MonoCompile *cfg); +gpointer mono_aot_get_method (MonoMethod *method); +gboolean mono_method_blittable (MonoMethod *method); +void mono_register_opcode_emulation (int opcode, MonoMethodSignature *sig, gpointer func); +void mono_arch_register_lowlevel_calls (void); +void mono_draw_graph (MonoCompile *cfg, MonoGraphOptions draw_options); +void mono_add_varcopy_to_end (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest); + +int mono_find_method_opcode (MonoMethod *method); +MonoJitICallInfo *mono_find_jit_icall_by_name (const char *name); +MonoJitICallInfo *mono_find_jit_icall_by_addr (gconstpointer addr); +MonoJitICallInfo *mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save); + +MonoCoverageInfo *mono_allocate_coverage_info (MonoMethod *method, int size); +MonoCoverageInfo *mono_get_coverage_info (MonoMethod *method); + +/* methods that must be provided by the arch-specific port */ +guint32 mono_arch_cpu_optimizazions (void); +void mono_arch_instrument_mem_needs (MonoMethod *method, int *stack, int *code); +void *mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments); +void *mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments); +MonoCallInst *mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual); +void mono_codegen (MonoCompile *cfg); +const char *mono_arch_regname (int reg); +gpointer mono_arch_get_throw_exception (void); +gpointer mono_arch_get_throw_exception_by_name (void); +gpointer mono_arch_create_jit_trampoline (MonoMethod *method); +GList *mono_arch_get_allocatable_int_vars (MonoCompile *cfg); +GList *mono_arch_get_global_int_regs (MonoCompile *cfg); +gboolean mono_arch_handle_exception (struct sigcontext *ctx, gpointer obj, gboolean test_only); +void mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji); +void mono_arch_flush_icache (guint8 *code, gint size); +int mono_arch_max_epilog_size (MonoCompile *cfg); +guint8 *mono_arch_emit_prolog (MonoCompile *cfg); +void mono_arch_emit_epilog (MonoCompile *cfg); +void mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb); +void mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb); +gboolean mono_arch_has_unwind_info (gconstpointer addr); +void mono_arch_allocate_vars (MonoCompile *m); +void mono_jit_walk_stack (MonoStackWalk func, gpointer user_data); +MonoArray *ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info); +MonoBoolean ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, + MonoReflectionMethod **method, + gint32 *iloffset, gint32 *native_offset, + MonoString **file, gint32 *line, gint32 *column); + +/* Dominator/SSA methods */ +void mono_compile_dominator_info (MonoCompile *cfg, int dom_flags); +void mono_compute_natural_loops (MonoCompile *cfg); +MonoBitSet* mono_compile_iterated_dfrontier (MonoCompile *cfg, MonoBitSet *set); +void mono_ssa_compute (MonoCompile *cfg); +void mono_ssa_remove (MonoCompile *cfg); +void mono_ssa_cprop (MonoCompile *cfg); +void mono_ssa_deadce (MonoCompile *cfg); +void mono_ssa_strength_reduction (MonoCompile *cfg); + +/* debugging support */ +void mono_debug_init_method (MonoCompile *cfg, MonoBasicBlock *start_block); +void mono_debug_open_method (MonoCompile *cfg); +void mono_debug_close_method (MonoCompile *cfg); +void mono_debug_open_block (MonoCompile *cfg, MonoBasicBlock *bb, guint32 address); +void mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address); + +#endif /* __MONO_MINI_H__ */ diff --git a/mono/mini/mini.prj b/mono/mini/mini.prj new file mode 100644 index 00000000000..b9bd7c25f61 --- /dev/null +++ b/mono/mini/mini.prj @@ -0,0 +1,28 @@ +;; -*- Prcs -*- +(Created-By-Prcs-Version 1 3 2) +(Project-Description "The mono SSA-based JIT.") +(Project-Version mini 0 3) +(Parent-Version mini 0 2) +(Version-Log "") +(New-Version-Log "") +(Checkin-Time "Sat, 21 Sep 2002 12:11:29 +0200") +(Checkin-Login lupus) +(Populate-Ignore ()) +(Project-Keywords) +(Files +;; This is a comment. Fill in files here. +;; For example: (prcs/checkout.cc ()) + +;; Files added by populate at Sat, 08 Jun 2002 17:27:56 +0200, +;; to version 0.0(w), by lupus: + + (regalloc.c (mini/0_regalloc.c 1.1 664)) + (cfold.c (mini/1_cfold.c 1.1 664)) + (mini-x86.c (mini/2_mini-x86.c 1.2 664)) + (mini.h (mini/3_mini.h 1.3 664)) + (makefile (mini/4_makefile 1.3 664)) + (test.cs (mini/5_test.cs 1.1 664)) + (mini.c (mini/6_mini.c 1.3 664)) +) +(Merge-Parents) +(New-Merge-Parents) diff --git a/mono/mini/objects.cs b/mono/mini/objects.cs new file mode 100644 index 00000000000..e3b15cf09a1 --- /dev/null +++ b/mono/mini/objects.cs @@ -0,0 +1,415 @@ +using System; +using System.Reflection; + +/* + * Regression tests for the mono JIT. + * + * Each test needs to be of the form: + * + * static int test__ (); + * + * where is an integer (the value that needs to be returned by + * the method to make it pass. + * is a user-displayed name used to identify the test. + * + * The tests can be driven in two ways: + * *) running the program directly: Main() uses reflection to find and invoke + * the test methods (this is useful mostly to check that the tests are correct) + * *) with the --regression switch of the jit (this is the preferred way since + * all the tests will be run with optimizations on and off) + * + * The reflection logic could be moved to a .dll since we need at least another + * regression test file written in IL code to have better control on how + * the IL code looks. + */ + +struct Simple { + public int a; + public byte b; + public short c; + public long d; +} + +class Sample { + public int a; + public Sample (int v) { + a = v; + } +} + +class Tests { + + static int Main () { + return TestDriver.RunTests (typeof (Tests)); + } + + static int test_0_return () { + Simple s; + s.a = 1; + s.b = 2; + s.c = (short)(s.a + s.b); + s.d = 4; + return s.a - 1; + } + + static int test_0_string_access () { + string s = "Hello"; + if (s [1] != 'e') + return 1; + return 0; + } + + static int test_0_string_virtual_call () { + string s = "Hello"; + string s2 = s.ToString (); + if (s2 [1] != 'e') + return 1; + return 0; + } + + static int test_0_iface_call () { + string s = "Hello"; + object o = ((ICloneable)s).Clone (); + return 0; + } + + static int test_5_newobj () { + Sample s = new Sample (5); + return s.a; + } + + static int test_4_box () { + object obj = 4; + return (int)obj; + } + + static Simple get_simple (int v) { + Simple r = new Simple (); + r.a = v; + r.b = (byte)(v + 1); + r.c = (short)(v + 2); + r.d = v + 3; + + return r; + } + + static int test_3_return_struct () { + Simple v = get_simple (1); + + if (v.a != 1) + return 0; + if (v.b != 2) + return 0; + if (v.c != 3) + return 0; + if (v.d != 4) + return 0; + return 3; + } + + public virtual Simple v_get_simple (int v) + { + return get_simple (v); + } + + static int test_2_return_struct_virtual () { + Tests t = new Tests (); + Simple v = t.v_get_simple (2); + + if (v.a != 2) + return 0; + if (v.b != 3) + return 0; + if (v.c != 4) + return 0; + if (v.d != 5) + return 0; + return 2; + } + + static int receive_simple (int a, Simple v, int b) { + if (v.a != 1) + return 0; + if (v.b != 2) + return 0; + if (v.c != 3) + return 0; + if (v.d != 4) + return 0; + if (a != 7) + return 0; + if (b != 9) + return 0; + return 1; + } + + static int test_5_pass_struct () { + Simple v = get_simple (1); + if (receive_simple (7, v, 9) != 1) + return 0; + if (receive_simple (7, get_simple (1), 9) != 1) + return 1; + return 5; + } + + class TestRegA { + + long buf_start; + int buf_length, buf_offset; + + public long Seek (long position) { + long pos = position; + /* interaction between the register allocator and + * allocating arguments to registers */ + if (pos >= buf_start && pos <= buf_start + buf_length) { + buf_offset = (int) (pos - buf_start); + return pos; + } + return buf_start; + } + + } + + static int test_0_seektest () { + TestRegA t = new TestRegA (); + return (int)t.Seek (0); + } + + class Super : ICloneable { + public virtual object Clone () { + return null; + } + } + class Duper: Super { + } + + static int test_0_super_cast () { + Duper d = new Duper (); + Super sup = d; + Object o = d; + + if (!(o is Super)) + return 1; + try { + d = (Duper)sup; + } catch { + return 2; + } + if (!(d is Object)) + return 3; + try { + d = (Duper)(object)sup; + } catch { + return 4; + } + return 0; + } + + static int test_0_super_cast_array () { + Duper[] d = new Duper [0]; + Super[] sup = d; + Object[] o = d; + + if (!(o is Super[])) + return 1; + try { + d = (Duper[])sup; + } catch { + return 2; + } + if (!(d is Object[])) + return 3; + try { + d = (Duper[])(object[])sup; + } catch { + return 4; + } + return 0; + } + + static int test_0_enum_array_cast () { + TypeCode[] tc = new TypeCode [0]; + object[] oa; + ValueType[] vta; + int[] inta; + Array a = tc; + bool ok; + + if (a is object[]) + return 1; + if (a is ValueType[]) + return 2; + if (a is Enum[]) + return 3; + try { + ok = false; + oa = (object[])a; + } catch { + ok = true; + } + if (!ok) + return 4; + try { + ok = false; + vta = (ValueType[])a; + } catch { + ok = true; + } + if (!ok) + return 5; + try { + ok = true; + inta = (int[])a; + } catch { + ok = false; + } + if (!ok) + return 6; + return 0; + } + + static int test_0_more_cast_corner_cases () { + ValueType[] vta = new ValueType [0]; + Enum[] ea = new Enum [0]; + Array a = vta; + object[] oa; + bool ok; + + if (!(a is object[])) + return 1; + if (!(a is ValueType[])) + return 2; + if (a is Enum[]) + return 3; + a = ea; + if (!(a is object[])) + return 4; + if (!(a is ValueType[])) + return 5; + if (!(a is Enum[])) + return 6; + + try { + ok = true; + oa = (object[])a; + } catch { + ok = false; + } + if (!ok) + return 7; + + try { + ok = true; + oa = (Enum[])a; + } catch { + ok = false; + } + if (!ok) + return 8; + + try { + ok = true; + oa = (ValueType[])a; + } catch { + ok = false; + } + if (!ok) + return 9; + + a = vta; + try { + ok = true; + oa = (object[])a; + } catch { + ok = false; + } + if (!ok) + return 10; + + try { + ok = true; + oa = (ValueType[])a; + } catch { + ok = false; + } + if (!ok) + return 11; + + try { + ok = false; + vta = (Enum[])a; + } catch { + ok = true; + } + if (!ok) + return 12; + return 0; + } + + static int test_0_cast_iface_array () { + object o = new ICloneable [0]; + object o2 = new Duper [0]; + object t; + bool ok; + + if (!(o is object[])) + return 1; + if (!(o2 is ICloneable[])) + return 2; + + try { + ok = true; + t = (object[])o; + } catch { + ok = false; + } + if (!ok) + return 3; + + try { + ok = true; + t = (ICloneable[])o2; + } catch { + ok = false; + } + if (!ok) + return 4; + + try { + ok = true; + t = (ICloneable[])o; + } catch { + ok = false; + } + if (!ok) + return 5; + + /* add tests for interfaces that 'inherit' interfaces */ + return 0; + } + + private static int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + private static int AbsoluteDays (int year, int month, int day) + { + int temp = 0, m = 1; + int[] days = daysmonthleap; + while (m < month) + temp += days[m++]; + return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400)); + } + + static int test_719162_complex_div () { + int adays = AbsoluteDays (1970, 1, 1); + return adays; + } + + static int test_1_store_decimal () { + decimal[,] a = {{1}}; + + if (a[0,0] != 1m) + return 0; + return 1; + } +} + diff --git a/mono/mini/opcode-decomp.txt b/mono/mini/opcode-decomp.txt new file mode 100644 index 00000000000..48968d17ab9 --- /dev/null +++ b/mono/mini/opcode-decomp.txt @@ -0,0 +1,113 @@ + +* How to handle complex IL opcodes in an arch-independent way + + Many IL opcodes are very simple: add, ldind etc. + Such opcodes can be implemented with a single cpu instruction + in most architectures (on some, a group of IL instructions + can be converted to a single cpu op). + There are many IL opcodes, though, that are more complex, but + can be expressed as a series of trees or a single tree of + simple operations. Such simple operations are architecture-independent. + It makes sense to decompose such complex IL instructions in their + simpler equivalent so that we gain in several ways: + *) porting effort is easier, because only the simple instructions + need to be implemented in arch-specific code + *) we could apply BURG rules to the trees and do pattern matching + on them to optimize the expressions according to the host cpu + + The issue is: where do we do such conversion from coarse opcodes to + simple expressions? + +* Doing the conversion in method_to_ir () + + Some of these conversions can certainly be done in method_to_ir (), + but it's not always easy to decide which are better done there and + which in a different pass. + For example, let's take ldlen: in the mono implementation, ldlen + can be simply implemented with a load from a fixed position in the + array object: + + len = [reg + maxlen_offset] + + However, ldlen carries also semantics information: the result is the + length of the array, and since in the CLR arrays are of fixed size, + this information can be useful to later do bounds check removal. + If we convert this opcode in method_to_ir () we lost some useful + information for further optimizations. + + In some other ways, decomposing an opcode in method_to_ir() may + allow for better optimizations later on (need to come up with an + example here ...). + +* Doing the conversion in inssel.brg + + Some conversion may be done inside the burg rules: this has the + disadvantage that the instruction selector is not run again on + the resulting expression tree and we could miss some optimization + (this is what effectively happens with the coarse opcodes in the old + jit). This may also interfere with an efficient local register allocator. + It may be possible to add an extension in monoburg that allows a rule + such as: + + recheck: LDLEN (reg) { + create an expression tree representing LDLEN + and return it + } + + When the monoburg label process gets back a recheck, it will run + the labeling again on the resulting expression tree. + If this is possible at all (and in an efficient way) is a + question for dietmar:-) + It should be noted, though, that this may not always work, since + some complex IL opcodes may require a series of expression trees + and handling such cases in monoburg could become quite hairy. + For example, think of opcode that need to do multiple actions on the + same object: this basically means a DUP... + On the other end, if a complex opcode needs a DUP, monoburg doesn't + actually need to create trees if it emits the instructions in + the correct sequence and maintains the right values in the registers + (usually the values that need a DUP are not changed...). How + this integrates with the current register allocator is not clear, since + that assigns registers based on the rule, but the instructions emitted + by the rules may be different (this already happens with the current JIT + where a MULT is replaced with lea etc...). + +* Doing it in a separate pass. + + Doing the conversion in a separate pass over the instructions + is another alternative. This can be done right after method_to_ir () + or after the SSA pass (since the IR after the SSA pass should look + almost like the IR we get back from method_to_ir ()). + + This has the following advantages: + *) monoburg will handle only the simple opcodes (makes porting easier) + *) the instruction selection will be run on all the additional trees + *) it's easier to support coarse opcodes that produce multiple expression + trees (and apply the monoburg selector on all of them) + *) the SSA optimizer will see the original opcodes and will be able to use + the semantic info associated with them + + The disadvantage is that this is a separate pass on the code and + it takes time (how much has not been measured yet, though). + + With this approach, we may also be able to have C implementations + of some of the opcodes: this pass would insert a function call to + the C implementation (for example in the cases when first porting + to a new arch and implemenating some stuff may be too hard in asm). + +* Extended basic blocks + + IL code needs a lot of checks, bounds checks, overflow checks, + type checks and so on. This potentially increases by a lot + the number of basic blocks in a control flow graph. However, + all such blocks end up with a throw opcode that gives control to the + exception handling mechanism. + After method_to_ir () a MonoBasicBlock can be considered a sort + of extended basic block where the additional exits don't point + to basic blocks in the same procedure (at least when the method + doesn't have exception tables). + We need to make sure the passes following method_to_ir () can cope + with such kinds of extended basic blocks (especially the passes + that we need to apply to all the methods: as a start, we could + skip SSA optimizations for methods with exception clauses...) + diff --git a/mono/mini/regalloc.c b/mono/mini/regalloc.c new file mode 100644 index 00000000000..26be7b0f4e4 --- /dev/null +++ b/mono/mini/regalloc.c @@ -0,0 +1,72 @@ +/* + * regalloc.c: register state class + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include "mini.h" + +MonoRegState* +mono_regstate_new (void) +{ + MonoRegState* rs = g_new0 (MonoRegState, 1); + + mono_regstate_reset (rs); + + return rs; +} + +void +mono_regstate_reset (MonoRegState *rs) { + rs->next_vireg = MONO_MAX_IREGS; + rs->next_vfreg = MONO_MAX_FREGS; +} + +void +mono_regstate_assign (MonoRegState *rs) { + int i; + g_free (rs->iassign); + rs->iassign = g_malloc (MAX (MONO_MAX_IREGS, rs->next_vireg)); + for (i = 0; i < MONO_MAX_IREGS; ++i) { + rs->iassign [i] = i; + rs->isymbolic [i] = 0; + } + for (; i < rs->next_vireg; ++i) + rs->iassign [i] = -1; +} + +int +mono_regstate_alloc_int (MonoRegState *rs, guint32 allow) +{ + int i; + guint32 mask = allow & rs->ifree_mask; + for (i = 0; i < MONO_MAX_IREGS; ++i) { + if (mask & (1 << i)) { + rs->ifree_mask &= ~ (1 << i); + return i; + } + } + return -1; +} + +void +mono_regstate_free_int (MonoRegState *rs, int reg) +{ + if (reg >= 0) { + rs->ifree_mask |= 1 << reg; + rs->isymbolic [reg] = 0; + } +} + +inline int +mono_regstate_next_long (MonoRegState *rs) +{ + int rval = rs->next_vireg; + + rs->next_vireg += 2; + + return rval; +} + diff --git a/mono/mini/regalloc.h b/mono/mini/regalloc.h new file mode 100644 index 00000000000..a344c7c4032 --- /dev/null +++ b/mono/mini/regalloc.h @@ -0,0 +1,53 @@ + +enum { + MONO_REG_FREE, + MONO_REG_FREEABLE, + MONO_REG_MOVEABLE, + MONO_REG_BUSY, + MONO_REG_RESERVED +}; + +enum { + MONO_REG_INT, + MONO_REG_DOUBLE +}; + +/* make this arch-dependent */ +#define MONO_MAX_IREGS 8 +#define MONO_MAX_FREGS 7 + +typedef struct { + /* symbolic registers */ + int next_vireg; + int next_vfreg; + + /* hard registers */ + int num_iregs; + int num_fregs; + + guint32 ifree_mask; + guint32 ffree_mask; + + /* symbolic -> hard register assignment */ + char *iassign; + char *fassign; + + /* hard -> symbolic */ + int isymbolic [MONO_MAX_IREGS]; + int fsymbolic [MONO_MAX_FREGS]; + + int ispills; +} MonoRegState; + +#define mono_regstate_next_int(rs) ((rs)->next_vireg++) +#define mono_regstate_next_float(rs) ((rs)->next_vfreg++) + + +MonoRegState* mono_regstate_new (void); + +void mono_regstate_reset (MonoRegState *rs); +void mono_regstate_assign (MonoRegState *rs); +int mono_regstate_alloc_int (MonoRegState *rs, guint32 allow); +void mono_regstate_free_int (MonoRegState *rs, int reg); +inline int mono_regstate_next_long (MonoRegState *rs); + diff --git a/mono/mini/regset.c b/mono/mini/regset.c new file mode 100644 index 00000000000..619b390bd5e --- /dev/null +++ b/mono/mini/regset.c @@ -0,0 +1,114 @@ +/* + * regset.c: register set abstraction + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#include "regset.h" + +MonoRegSet * +mono_regset_new (int max_regs) +{ + MonoRegSet *rs; + + g_return_val_if_fail (max_regs > 0 && max_regs <= 32, NULL); + + rs = g_new0 (MonoRegSet, 1); + + rs->max_regs = max_regs; + rs->used_mask = 0; + rs->free_mask = ~rs->used_mask; + rs->reserved_mask = 0; + + return rs; +} + +void +mono_regset_free (MonoRegSet *rs) +{ + g_free (rs); +} + +void +mono_regset_reserve_reg (MonoRegSet *rs, int regnum) +{ + guint32 ind; + + g_return_if_fail (rs != NULL); + g_return_if_fail (rs->max_regs > regnum); + + ind = 1 << regnum; + + rs->reserved_mask |= ind; +} + +int +mono_regset_alloc_reg (MonoRegSet *rs, int regnum, guint32 exclude_mask) +{ + guint32 i, ind; + + g_return_val_if_fail (rs != NULL, -1); + g_return_val_if_fail (rs->max_regs > regnum, -1); + + if (regnum < 0) { + for (i = 0, ind = 1; i < rs->max_regs; i++, ind = ind << 1) { + if (exclude_mask & ind) + continue; + if ((rs->free_mask & ind) && !(rs->reserved_mask & ind)) { + rs->free_mask &= ~ind; + rs->used_mask |= ind; + return i; + } + } + return -1; + } else { + ind = 1 << regnum; + + if (exclude_mask & ind) + return -1; + + if ((rs->free_mask & ind) && !(rs->reserved_mask & ind)) { + rs->free_mask &= ~ind; + rs->used_mask |= ind; + return regnum; + } + return -1; + } +} + +void +mono_regset_free_reg (MonoRegSet *rs, int regnum) +{ + guint32 ind; + + g_return_if_fail (rs != NULL); + g_return_if_fail (rs->max_regs > regnum); + + if (regnum < 0) + return; + + ind = 1 << regnum; + + g_return_if_fail (rs->free_mask && ind); + + rs->free_mask |= ind; +} + +gboolean +mono_regset_reg_used (MonoRegSet *rs, int regnum) +{ + guint32 ind; + + g_return_val_if_fail (rs != NULL, FALSE); + g_return_val_if_fail (rs->max_regs > regnum, FALSE); + g_return_val_if_fail (regnum >= 0, FALSE); + + ind = 1 << regnum; + + return rs->used_mask & ind; +} + + diff --git a/mono/mini/regset.h b/mono/mini/regset.h new file mode 100644 index 00000000000..0fb15c624da --- /dev/null +++ b/mono/mini/regset.h @@ -0,0 +1,38 @@ +/* + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#ifndef _MONO_JIT_REGSET_H_ +#define _MONO_JIT_REGSET_H_ + +#include + +typedef struct { + int max_regs; + guint32 free_mask; + guint32 used_mask; + guint32 reserved_mask; +} MonoRegSet; + +MonoRegSet * +mono_regset_new (int max_regs); + +void +mono_regset_free (MonoRegSet *rs); + +int +mono_regset_alloc_reg (MonoRegSet *rs, int regnum, guint32 exclude_mask); + +void +mono_regset_free_reg (MonoRegSet *rs, int regnum); + +void +mono_regset_reserve_reg (MonoRegSet *rs, int regnum); + +gboolean +mono_regset_reg_used (MonoRegSet *rs, int regnum); + +#endif diff --git a/mono/mini/ssa.c b/mono/mini/ssa.c new file mode 100644 index 00000000000..8dff2a40c55 --- /dev/null +++ b/mono/mini/ssa.c @@ -0,0 +1,1099 @@ +/* + * ssa.c: Static single assign form support for the JIT compiler. + * + * Author: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2003 Ximian, Inc. + */ +#include +#include + +#include "mini.h" + +extern guint8 mono_burg_arity []; + +#define USE_ORIGINAL_VARS +#define CREATE_PRUNED_SSA + +//#define DEBUG_SSA 1 + +#define NEW_PHI(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_PHI; \ + (dest)->inst_c0 = (val); \ + } while (0) + +#define NEW_ICONST(cfg,dest,val) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_ICONST; \ + (dest)->inst_c0 = (val); \ + (dest)->type = STACK_I4; \ + } while (0) + + +static void +unlink_target (MonoBasicBlock *bb, MonoBasicBlock *target) +{ + int i; + + for (i = 0; i < bb->out_count; i++) { + if (bb->out_bb [i] == target) { + bb->out_bb [i] = bb->out_bb [--bb->out_count]; + break; + } + } + for (i = 0; i < target->in_count; i++) { + if (target->in_bb [i] == bb) { + target->in_bb [i] = target->in_bb [--target->in_count]; + break; + + } + } +} + +static void +unlink_unused_bblocks (MonoCompile *cfg) +{ + int i, j; + MonoBasicBlock *bb; + + g_assert (cfg->comp_done & MONO_COMP_REACHABILITY); + + for (bb = cfg->bb_entry; bb && bb->next_bb;) { + if (!(bb->next_bb->flags & BB_REACHABLE)) { + bb->next_bb = bb->next_bb->next_bb; + } else + bb = bb->next_bb; + } + + for (i = 1; i < cfg->num_bblocks; i++) { + bb = cfg->bblocks [i]; + + if (!(bb->flags & BB_REACHABLE)) { + for (j = 0; j < bb->in_count; j++) { + unlink_target (bb->in_bb [j], bb); + } + for (j = 0; j < bb->out_count; j++) { + unlink_target (bb, bb->out_bb [j]); + } + } + + } +} + + + +static void +replace_usage (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **stack) +{ + int arity; + + if (!inst) + return; + + arity = mono_burg_arity [inst->opcode]; + + if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) { + MonoInst *new_var; + int idx = inst->inst_i0->inst_c0; + + if (stack [idx]) { + new_var = stack [idx]; + } else { + new_var = cfg->varinfo [idx]; + + if (new_var->opcode != OP_ARG) { + /* uninitialized variable ? */ + g_warning ("using uninitialized variables %d in BB%d (%s)", idx, bb->block_num, + mono_method_full_name (cfg->method, TRUE)); + //g_assert_not_reached (); + } + } +#ifdef DEBUG_SSA + printf ("REPLACE BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0); +#endif + inst->inst_i0 = new_var; + } else { + + if (arity) { + if (inst->ssa_op != MONO_SSA_STORE) + replace_usage (cfg, bb, inst->inst_left, stack); + if (arity > 1) + replace_usage (cfg, bb, inst->inst_right, stack); + } + } +} + +static void +mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, MonoInst **stack) +{ + MonoInst *inst, *new_var; + int i, j, idx; + GList *tmp; + MonoInst *new_stack [max_vars]; + +#ifdef DEBUG_SSA + printf ("RENAME VARS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE)); +#endif + + for (inst = bb->code; inst; inst = inst->next) { + if (inst->opcode != OP_PHI) + replace_usage (cfg, bb, inst, stack); + + if (inst->ssa_op == MONO_SSA_STORE && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) { + idx = inst->inst_i0->inst_c0; + g_assert (idx < max_vars); + + if (!stack [idx] && bb == cfg->bb_init) { + new_var = cfg->varinfo [idx]; + } else { + new_var = mono_compile_create_var (cfg, inst->inst_i0->inst_vtype, inst->inst_i0->opcode); + new_var->flags = inst->inst_i0->flags; + } +#ifdef DEBUG_SSA + printf ("DEF %d %d\n", idx, new_var->inst_c0); +#endif + inst->inst_i0 = new_var; + +#ifdef USE_ORIGINAL_VARS + cfg->vars [new_var->inst_c0]->reg = idx; +#endif + + stack [idx] = new_var; + } + } + + for (i = 0; i < bb->out_count; i++) { + MonoBasicBlock *n = bb->out_bb [i]; + + for (j = 0; j < n->in_count; j++) + if (n->in_bb [j] == bb) + break; + + for (inst = n->code; inst; inst = inst->next) { + if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) { + idx = inst->inst_i1->inst_c0; + if (stack [idx]) + new_var = stack [idx]; + else + new_var = cfg->varinfo [idx]; +#ifdef DEBUG_SSA + printf ("FOUND PHI %d (%d, %d)\n", idx, j, new_var->inst_c0); +#endif + inst->inst_i1->inst_phi_args [j + 1] = new_var->inst_c0; + + } + } + } + + for (tmp = bb->dominated; tmp; tmp = tmp->next) { + memcpy (new_stack, stack, sizeof (MonoInst *) * max_vars); + mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, new_stack); + } +} + +void +mono_ssa_compute (MonoCompile *cfg) +{ + int i, idx; + MonoBitSet *set; + MonoMethodVar *vinfo = g_new0 (MonoMethodVar, cfg->num_varinfo); + MonoInst *inst, *store, **stack; + + g_assert (!(cfg->comp_done & MONO_COMP_SSA)); + + /* we dont support methods containing exception clauses */ + g_assert (((MonoMethodNormal *)cfg->method)->header->num_clauses == 0); + g_assert (!cfg->disable_ssa); + + //printf ("COMPUTS SSA %s %d\n", mono_method_full_name (cfg->method, TRUE), cfg->num_varinfo); + +#ifdef CREATE_PRUNED_SSA + /* we need liveness for pruned SSA */ + if (!(cfg->comp_done & MONO_COMP_LIVENESS)) + mono_analyze_liveness (cfg); +#endif + + mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM | MONO_COMP_DFRONTIER); + + for (i = 0; i < cfg->num_varinfo; ++i) { + vinfo [i].def_in = mono_bitset_new (cfg->num_bblocks, 0); + vinfo [i].idx = i; + /* implizit reference at start */ + mono_bitset_set (vinfo [i].def_in, 0); + } + for (i = 0; i < cfg->num_bblocks; ++i) { + for (inst = cfg->bblocks [i]->code; inst; inst = inst->next) { + if (inst->ssa_op == MONO_SSA_STORE) { + idx = inst->inst_i0->inst_c0; + g_assert (idx < cfg->num_varinfo); + mono_bitset_set (vinfo [idx].def_in, i); + } + } + } + + /* insert phi functions */ + for (i = 0; i < cfg->num_varinfo; ++i) { + set = mono_compile_iterated_dfrontier (cfg, vinfo [i].def_in); + vinfo [i].dfrontier = set; + mono_bitset_foreach_bit (set, idx, cfg->num_bblocks) { + MonoBasicBlock *bb = cfg->bblocks [idx]; + + /* fixme: create pruned SSA? we would need liveness information for that */ + + if (bb == cfg->bb_exit) + continue; + + if ((cfg->comp_done & MONO_COMP_LIVENESS) && !mono_bitset_test_fast (bb->live_in_set, i)) { + //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE)); + continue; + } + + NEW_PHI (cfg, inst, i); + + inst->inst_phi_args = mono_mempool_alloc0 (cfg->mempool, sizeof (int) * (cfg->bblocks [idx]->in_count + 1)); + inst->inst_phi_args [0] = cfg->bblocks [idx]->in_count; + + store = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); + if (!cfg->varinfo [i]->inst_vtype->type) + g_assert_not_reached (); + store->opcode = mono_type_to_stind (cfg->varinfo [i]->inst_vtype); + store->ssa_op = MONO_SSA_STORE; + store->inst_i0 = cfg->varinfo [i]; + store->inst_i1 = inst; + store->klass = store->inst_i0->klass; + + store->next = bb->code; + bb->code = store; + +#ifdef DEBUG_SSA + printf ("ADD PHI BB%d %s\n", cfg->bblocks [idx]->block_num, mono_method_full_name (cfg->method, TRUE)); +#endif + } + } + + /* free the stuff */ + for (i = 0; i < cfg->num_varinfo; ++i) + mono_bitset_free (vinfo [i].def_in); + g_free (vinfo); + + + stack = alloca (sizeof (MonoInst *) * cfg->num_varinfo); + + for (i = 0; i < cfg->num_varinfo; i++) + stack [i] = NULL; + + mono_ssa_rename_vars (cfg, cfg->num_varinfo, cfg->bb_entry, stack); + + cfg->comp_done |= MONO_COMP_SSA; +} + +#ifndef USE_ORIGINAL_VARS +static GPtrArray * +mono_ssa_get_allocatable_vars (MonoCompile *cfg) +{ + GHashTable *type_hash; + GPtrArray *varlist_array = g_ptr_array_new (); + int tidx, i; + + g_assert (cfg->comp_done & MONO_COMP_LIVENESS); + + type_hash = g_hash_table_new (NULL, NULL); + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoInst *ins = cfg->varinfo [i]; + MonoMethodVar *vmv = MONO_VARINFO (cfg, i); + + /* unused vars */ + if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos) + continue; + + if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || + (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG) || vmv->reg != -1) + continue; + + g_assert (ins->inst_vtype); + g_assert (vmv->reg == -1); + g_assert (i == vmv->idx); + + if (!(tidx = (int)g_hash_table_lookup (type_hash, ins->inst_vtype))) { + GList *vars = g_list_append (NULL, vmv); + g_ptr_array_add (varlist_array, vars); + g_hash_table_insert (type_hash, ins->inst_vtype, (gpointer)varlist_array->len); + } else { + tidx--; + g_ptr_array_index (varlist_array, tidx) = + mono_varlist_insert_sorted (cfg, g_ptr_array_index (varlist_array, tidx), vmv, FALSE); + } + } + + g_hash_table_destroy (type_hash); + + return varlist_array; +} +#endif + +static void +mono_ssa_replace_copies (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, char *is_live) +{ + int arity; + + if (!inst) + return; + + arity = mono_burg_arity [inst->opcode]; + + if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD || inst->ssa_op == MONO_SSA_STORE) && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) { + MonoInst *new_var; + int idx = inst->inst_i0->inst_c0; + MonoMethodVar *mv = cfg->vars [idx]; + + if (mv->reg != -1 && mv->reg != mv->idx) { + + is_live [mv->reg] = 1; + + new_var = cfg->varinfo [mv->reg]; + +#if 0 + printf ("REPLACE COPY BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0); + g_assert (cfg->varinfo [mv->reg]->inst_vtype == cfg->varinfo [idx]->inst_vtype); +#endif + inst->inst_i0 = new_var; + } else { + is_live [mv->idx] = 1; + } + } + + + if (arity) { + mono_ssa_replace_copies (cfg, bb, inst->inst_left, is_live); + if (arity > 1) + mono_ssa_replace_copies (cfg, bb, inst->inst_right, is_live); + } + + if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->ssa_op == MONO_SSA_LOAD && + inst->inst_i0->inst_c0 == inst->inst_i1->inst_i0->inst_c0) { + inst->ssa_op = MONO_SSA_NOP; + inst->opcode = CEE_NOP; + } + +} + +void +mono_ssa_remove (MonoCompile *cfg) +{ + MonoInst *inst, *phi; + char *is_live; + int i, j; +#ifndef USE_ORIGINAL_VARS + GPtrArray *varlist_array; + GList *active; +#endif + g_assert (cfg->comp_done & MONO_COMP_SSA); + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *bb = cfg->bblocks [i]; + for (inst = bb->code; inst; inst = inst->next) { + if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) { + + phi = inst->inst_i1; + g_assert (phi->inst_phi_args [0] == bb->in_count); + + for (j = 0; j < bb->in_count; j++) { + MonoBasicBlock *pred = bb->in_bb [j]; + int idx = phi->inst_phi_args [j + 1]; + MonoMethodVar *mv = cfg->vars [idx]; + + if (mv->reg != -1 && mv->reg != mv->idx) { + //printf ("PHICOPY %d %d -> %d\n", idx, mv->reg, inst->inst_i0->inst_c0); + idx = mv->reg; + } + + + if (idx != inst->inst_i0->inst_c0) { +#ifdef DEBUG_SSA + printf ("MOVE %d to %d in BB%d\n", idx, inst->inst_i0->inst_c0, pred->block_num); +#endif + mono_add_varcopy_to_end (cfg, pred, idx, inst->inst_i0->inst_c0); + } + } + + /* remove the phi functions */ + inst->opcode = CEE_NOP; + inst->ssa_op = MONO_SSA_NOP; + } + } + } + +#ifndef USE_ORIGINAL_VARS + /* we compute liveness again */ + cfg->comp_done &= ~MONO_COMP_LIVENESS; + mono_analyze_liveness (cfg); + + varlist_array = mono_ssa_get_allocatable_vars (cfg); + + for (i = 0; i < varlist_array->len; i++) { + GList *l, *t, *regs, *vars = g_ptr_array_index (varlist_array, i); + MonoMethodVar *vmv, *amv; + + if (g_list_length (vars) <= 1) { + continue; + } + + active = NULL; + regs = NULL; + + for (l = vars; l; l = l->next) { + vmv = l->data; + + /* expire old intervals in active */ + while (active) { + amv = (MonoMethodVar *)active->data; + + if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos) + break; + + active = g_list_remove_link (active, active); + regs = g_list_prepend (regs, (gpointer)amv->reg); + } + + if (!regs) + regs = g_list_prepend (regs, vmv->idx); + + vmv->reg = (int)regs->data; + regs = g_list_remove_link (regs, regs); + active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE); + } + + g_list_free (active); + g_list_free (regs); + g_list_free (vars); + } + + g_ptr_array_free (varlist_array, TRUE); + +#endif + + is_live = alloca (cfg->num_varinfo); + memset (is_live, 0, cfg->num_varinfo); + + for (i = 0; i < cfg->num_bblocks; ++i) { + MonoBasicBlock *bb = cfg->bblocks [i]; + + for (inst = bb->code; inst; inst = inst->next) + mono_ssa_replace_copies (cfg, bb, inst, is_live); + } + + for (i = 0; i < cfg->num_varinfo; ++i) { + cfg->vars [i]->reg = -1; + if (!is_live [i]) { + cfg->varinfo [i]->flags |= MONO_INST_IS_DEAD; + } + } + + if (cfg->comp_done & MONO_COMP_REACHABILITY) + unlink_unused_bblocks (cfg); + + cfg->comp_done &= ~MONO_COMP_SSA; +} + + +#define IS_CALL(op) (op == CEE_CALLI || op == CEE_CALL || op == CEE_CALLVIRT || (op >= OP_VOIDCALL && op <= OP_CALL_MEMBASE)) + +typedef struct { + MonoBasicBlock *bb; + MonoInst *inst; +} MonoVarUsageInfo; + +static void +analyze_dev_use (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *root, MonoInst *inst) +{ + MonoMethodVar *info; + int i, idx, arity; + + if (!inst) + return; + + arity = mono_burg_arity [inst->opcode]; + + if ((inst->ssa_op == MONO_SSA_STORE) && + (inst->inst_i0->opcode == OP_LOCAL /*|| inst->inst_i0->opcode == OP_ARG */)) { + idx = inst->inst_i0->inst_c0; + info = cfg->vars [idx]; + //printf ("%d defined in BB%d %p\n", idx, bb->block_num, root); + if (info->def) { + g_warning ("more than one definition of variable %d in %s", idx, + mono_method_full_name (cfg->method, TRUE)); + g_assert_not_reached (); + } + if (!IS_CALL (inst->inst_i1->opcode) /* && inst->inst_i1->opcode == OP_ICONST */) { + g_assert (inst == root); + info->def = root; + info->def_bb = bb; + } + + if (inst->inst_i1->opcode == OP_PHI) { + for (i = inst->inst_i1->inst_phi_args [0]; i > 0; i--) { + MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo)); + idx = inst->inst_i1->inst_phi_args [i]; + info = cfg->vars [idx]; + //printf ("FOUND %d\n", idx); + ui->bb = bb; + ui->inst = root; + info->uses = g_list_prepend (info->uses, ui); + } + } + } + + if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) { + MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo)); + idx = inst->inst_i0->inst_c0; + info = cfg->vars [idx]; + //printf ("FOUND %d\n", idx); + ui->bb = bb; + ui->inst = root; + info->uses = g_list_prepend (info->uses, ui); + } else { + if (arity) { + //if (inst->ssa_op != MONO_SSA_STORE) + analyze_dev_use (cfg, bb, root, inst->inst_left); + if (arity > 1) + analyze_dev_use (cfg, bb, root, inst->inst_right); + } + } +} + +/* avoid unnecessary copies of variables: + * Y <= X; Z = Y; is translated to Z = X; + */ +static void +mono_ssa_avoid_copies (MonoCompile *cfg) +{ + MonoInst *inst, *next; + MonoBasicBlock *bb; + MonoMethodVar *i1, *i2; + + g_assert ((cfg->comp_done & MONO_COMP_SSA_DEF_USE)); + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + for (inst = bb->code; inst; inst = inst->next) { + if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i0->opcode == OP_LOCAL) { + i1 = cfg->vars [inst->inst_i0->inst_c0]; + + if ((next = inst->next) && next->ssa_op == MONO_SSA_STORE && next->inst_i0->opcode == OP_LOCAL && + next->inst_i1->ssa_op == MONO_SSA_LOAD && next->inst_i1->inst_i0->opcode == OP_LOCAL && + next->inst_i1->inst_i0->inst_c0 == inst->inst_i0->inst_c0 && g_list_length (i1->uses) == 1 && + inst->opcode == next->opcode && inst->inst_i0->type == next->inst_i0->type) { + i2 = cfg->vars [next->inst_i0->inst_c0]; + //printf ("ELIM. COPY in BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE)); + inst->inst_i0 = next->inst_i0; + i2->def = inst; + i1->def = NULL; + g_list_free (i1->uses); + i1->uses = NULL; + next->opcode = CEE_NOP; + next->ssa_op = MONO_SSA_NOP; + } + } + } + } +} + +static void +mono_ssa_create_def_use (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + + g_assert (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE)); + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *inst; + for (inst = bb->code; inst; inst = inst->next) { + analyze_dev_use (cfg, bb, inst, inst); + } + } + + cfg->comp_done |= MONO_COMP_SSA_DEF_USE; +} + +static int +simulate_compare (int opcode, int a, int b) +{ + switch (opcode) { + case CEE_BEQ: + return a == b; + case CEE_BGE: + return a >= b; + case CEE_BGT: + return a > b; + case CEE_BLE: + return a <= b; + case CEE_BLT: + return a < b; + case CEE_BNE_UN: + return a != b; + case CEE_BGE_UN: + return (unsigned)a >= (unsigned)b; + case CEE_BGT_UN: + return (unsigned)a > (unsigned)b; + case CEE_BLE_UN: + return (unsigned)a <= (unsigned)b; + case CEE_BLT_UN: + return (unsigned)a < (unsigned)b; + default: + g_assert_not_reached (); + } + + return 0; +} + +#define EVAL_CXX(name,op,cast) \ + case name: \ + if (inst->inst_i0->opcode == OP_COMPARE) { \ + r1 = evaluate_const_tree (cfg, inst->inst_i0->inst_i0, &a, carray); \ + r2 = evaluate_const_tree (cfg, inst->inst_i0->inst_i1, &b, carray); \ + if (r1 == 1 && r2 == 1) { \ + *res = ((cast)a op (cast)b); \ + return 1; \ + } else { \ + return MAX (r1, r2); \ + } \ + } \ + break; + +#define EVAL_BINOP(name,op) \ + case name: \ + r1 = evaluate_const_tree (cfg, inst->inst_i0, &a, carray); \ + r2 = evaluate_const_tree (cfg, inst->inst_i1, &b, carray); \ + if (r1 == 1 && r2 == 1) { \ + *res = (a op b); \ + return 1; \ + } else { \ + return MAX (r1, r2); \ + } \ + break; + + +static int +evaluate_const_tree (MonoCompile *cfg, MonoInst *inst, int *res, MonoInst **carray) +{ + MonoInst *c0; + int a, b, r1, r2; + + if (!inst) + return 0; + + if (inst->ssa_op == MONO_SSA_LOAD && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) && + (c0 = carray [inst->inst_i0->inst_c0])) { + *res = c0->inst_c0; + return 1; + } + + switch (inst->opcode) { + case OP_ICONST: + *res = inst->inst_c0; + return 1; + + EVAL_CXX (OP_CEQ,==,gint32) + EVAL_CXX (OP_CGT,>,gint32) + EVAL_CXX (OP_CGT_UN,>,guint32) + EVAL_CXX (OP_CLT,<,gint32) + EVAL_CXX (OP_CLT_UN,<,guint32) + + EVAL_BINOP (CEE_ADD,+) + EVAL_BINOP (CEE_SUB,-) + EVAL_BINOP (CEE_MUL,*) + EVAL_BINOP (CEE_AND,&) + EVAL_BINOP (CEE_OR,|) + EVAL_BINOP (CEE_XOR,^) + EVAL_BINOP (CEE_SHL,<<) + EVAL_BINOP (CEE_SHR,>>) + + default: + return 2; + } + + return 2; +} + +static void +fold_tree (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **carray) +{ + MonoInst *c0; + int arity, a, b; + + if (!inst) + return; + + arity = mono_burg_arity [inst->opcode]; + + if (inst->ssa_op == MONO_SSA_STORE && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) && + inst->inst_i1->opcode == OP_PHI && (c0 = carray [inst->inst_i0->inst_c0])) { + //{static int cn = 0; printf ("PHICONST %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));} + *inst->inst_i1 = *c0; + } else if (inst->ssa_op == MONO_SSA_LOAD && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) && + (c0 = carray [inst->inst_i0->inst_c0])) { + //{static int cn = 0; printf ("YCCOPY %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));} + *inst = *c0; + } else { + + if (arity) { + fold_tree (cfg, bb, inst->inst_left, carray); + if (arity > 1) + fold_tree (cfg, bb, inst->inst_right, carray); + mono_constant_fold_inst (inst, NULL); + } + } + + if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) && + inst->inst_i0->opcode == OP_COMPARE) { + MonoInst *v0 = inst->inst_i0->inst_i0; + MonoInst *v1 = inst->inst_i0->inst_i1; + + if (evaluate_const_tree (cfg, v0, &a, carray) == 1 && + evaluate_const_tree (cfg, v1, &b, carray) == 1) { + MonoBasicBlock *target; + + if (simulate_compare (inst->opcode, a, b)) { + //unlink_target (bb, inst->inst_false_bb); + target = inst->inst_true_bb; + } else { + //unlink_target (bb, inst->inst_true_bb); + target = inst->inst_false_bb; + } + + bb->out_bb [0] = target; + bb->out_count = 1; + inst->opcode = CEE_BR; + inst->inst_target_bb = target; + } + } else if (inst->opcode == CEE_SWITCH && evaluate_const_tree (cfg, inst->inst_left, &a, carray) == 1) { + bb->out_bb [0] = inst->inst_many_bb [a]; + bb->out_count = 1; + inst->inst_target_bb = bb->out_bb [0]; + inst->opcode = CEE_BR; + } + +} + +static void +change_varstate (MonoCompile *cfg, GList **cvars, MonoMethodVar *info, int state, MonoInst *c0, MonoInst **carray) +{ + if (info->cpstate >= state) + return; + + info->cpstate = state; + + //printf ("SETSTATE %d to %d\n", info->idx, info->cpstate); + + if (state == 1) + carray [info->idx] = c0; + else + carray [info->idx] = NULL; + + if (!g_list_find (*cvars, info)) { + *cvars = g_list_prepend (*cvars, info); + } +} + +static void +visit_inst (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, GList **cvars, GList **bblist, MonoInst **carray) +{ + g_assert (inst); + + if (inst->opcode == CEE_SWITCH) { + int r1, i, a; + + r1 = evaluate_const_tree (cfg, inst->inst_left, &a, carray); + if (r1 == 1) { + MonoBasicBlock *tb = inst->inst_many_bb [a]; + if (!(tb->flags & BB_REACHABLE)) { + tb->flags |= BB_REACHABLE; + *bblist = g_list_prepend (*bblist, tb); + } + } else if (r1 == 2) { + for (i = (int)inst->klass; i >= 0; i--) { + MonoBasicBlock *tb = inst->inst_many_bb [i]; + if (!(tb->flags & BB_REACHABLE)) { + tb->flags |= BB_REACHABLE; + *bblist = g_list_prepend (*bblist, tb); + } + } + } + } else if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) && + inst->inst_i0->opcode == OP_COMPARE) { + int a, b, r1, r2; + MonoInst *v0 = inst->inst_i0->inst_i0; + MonoInst *v1 = inst->inst_i0->inst_i1; + + r1 = evaluate_const_tree (cfg, v0, &a, carray); + r2 = evaluate_const_tree (cfg, v1, &b, carray); + + if (r1 == 1 && r2 == 1) { + MonoBasicBlock *target; + + if (simulate_compare (inst->opcode, a, b)) { + target = inst->inst_true_bb; + } else { + target = inst->inst_false_bb; + } + if (!(target->flags & BB_REACHABLE)) { + target->flags |= BB_REACHABLE; + *bblist = g_list_prepend (*bblist, target); + } + } else if (r1 == 2 || r2 == 2) { + if (!(inst->inst_true_bb->flags & BB_REACHABLE)) { + inst->inst_true_bb->flags |= BB_REACHABLE; + *bblist = g_list_prepend (*bblist, inst->inst_true_bb); + } + if (!(inst->inst_false_bb->flags & BB_REACHABLE)) { + inst->inst_false_bb->flags |= BB_REACHABLE; + *bblist = g_list_prepend (*bblist, inst->inst_false_bb); + } + } + } else if (inst->ssa_op == MONO_SSA_STORE && + (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) { + MonoMethodVar *info = cfg->vars [inst->inst_i0->inst_c0]; + MonoInst *i1 = inst->inst_i1; + int res; + + if (info->cpstate < 2) { + if (i1->opcode == OP_ICONST) { + change_varstate (cfg, cvars, info, 1, i1, carray); + } else if (i1->opcode == OP_PHI) { + MonoInst *c0 = NULL; + int j; + + for (j = 1; j <= i1->inst_phi_args [0]; j++) { + MonoMethodVar *mv = cfg->vars [i1->inst_phi_args [j]]; + MonoInst *src = mv->def; + + if (mv->def_bb && !(mv->def_bb->flags & BB_REACHABLE)) { + continue; + } + + if (!mv->def || !src || src->ssa_op != MONO_SSA_STORE || + !(src->inst_i0->opcode == OP_LOCAL || src->inst_i0->opcode == OP_ARG) || + mv->cpstate == 2) { + change_varstate (cfg, cvars, info, 2, NULL, carray); + break; + } + + if (mv->cpstate == 0) + continue; + + //g_assert (src->inst_i1->opcode == OP_ICONST); + g_assert (carray [mv->idx]); + + if (!c0) { + c0 = carray [mv->idx]; + } + + if (carray [mv->idx]->inst_c0 != c0->inst_c0) { + change_varstate (cfg, cvars, info, 2, NULL, carray); + break; + } + } + + if (c0 && info->cpstate < 1) { + change_varstate (cfg, cvars, info, 1, c0, carray); + } + } else { + int state = evaluate_const_tree (cfg, i1, &res, carray); + if (state == 1) { + NEW_ICONST (cfg, i1, res); + change_varstate (cfg, cvars, info, 1, i1, carray); + } else { + change_varstate (cfg, cvars, info, 2, NULL, carray); + } + } + } + } +} + +void +mono_ssa_cprop (MonoCompile *cfg) +{ + MonoInst *carray [cfg->num_varinfo]; + MonoBasicBlock *bb; + GList *bblock_list, *cvars; + GList *tmp; + int i; + //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE)); + + if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE)) + mono_ssa_create_def_use (cfg); + + bblock_list = g_list_prepend (NULL, cfg->bb_entry); + cfg->bb_entry->flags |= BB_REACHABLE; + + memset (carray, 0, sizeof (MonoInst *) * cfg->num_varinfo); + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoMethodVar *info = cfg->vars [i]; + if (!info->def) + info->cpstate = 2; + } + + cvars = NULL; + + while (bblock_list) { + MonoInst *inst; + + bb = (MonoBasicBlock *)bblock_list->data; + + bblock_list = g_list_remove_link (bblock_list, bblock_list); + + g_assert (bb->flags & BB_REACHABLE); + + if (bb->out_count == 1) { + if (!(bb->out_bb [0]->flags & BB_REACHABLE)) { + bb->out_bb [0]->flags |= BB_REACHABLE; + bblock_list = g_list_prepend (bblock_list, bb->out_bb [0]); + } + } + + for (inst = bb->code; inst; inst = inst->next) { + visit_inst (cfg, bb, inst, &cvars, &bblock_list, carray); + } + + while (cvars) { + MonoMethodVar *info = (MonoMethodVar *)cvars->data; + cvars = g_list_remove_link (cvars, cvars); + + for (tmp = info->uses; tmp; tmp = tmp->next) { + MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data; + if (!(ui->bb->flags & BB_REACHABLE)) + continue; + visit_inst (cfg, ui->bb, ui->inst, &cvars, &bblock_list, carray); + } + } + } + + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + MonoInst *inst; + for (inst = bb->code; inst; inst = inst->next) { + fold_tree (cfg, bb, inst, carray); + } + } + + cfg->comp_done |= MONO_COMP_REACHABILITY; +} + +static void +add_to_dce_worklist (MonoCompile *cfg, MonoMethodVar *var, MonoMethodVar *use, GList **wl) +{ + GList *tmp; + + *wl = g_list_prepend (*wl, use); + + for (tmp = use->uses; tmp; tmp = tmp->next) { + MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data; + if (ui->inst == var->def) { + use->uses = g_list_remove_link (use->uses, tmp); + break; + } + } +} + +void +mono_ssa_deadce (MonoCompile *cfg) +{ + int i; + GList *work_list; + + g_assert (cfg->comp_done & MONO_COMP_SSA); + + //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE)); + + /* fixme: we should update usage infos during cprop, instead of computing it again */ + cfg->comp_done &= ~MONO_COMP_SSA_DEF_USE; + for (i = 0; i < cfg->num_varinfo; i++) { + MonoMethodVar *info = cfg->vars [i]; + info->def = NULL; + info->uses = NULL; + } + + if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE)) + mono_ssa_create_def_use (cfg); + + mono_ssa_avoid_copies (cfg); + + work_list = NULL; + for (i = 0; i < cfg->num_varinfo; i++) { + MonoMethodVar *info = cfg->vars [i]; + work_list = g_list_prepend (work_list, info); + } + + while (work_list) { + MonoMethodVar *info = (MonoMethodVar *)work_list->data; + work_list = g_list_remove_link (work_list, work_list); + + if (!info->uses && info->def) { + MonoInst *i1; + //printf ("ELIMINATE %s: ", mono_method_full_name (cfg->method, TRUE)); mono_print_tree (info->def); printf ("\n"); + + i1 = info->def->inst_i1; + if (i1->opcode == OP_PHI) { + int j; + for (j = i1->inst_phi_args [0]; j > 0; j--) { + MonoMethodVar *u = cfg->vars [i1->inst_phi_args [j]]; + add_to_dce_worklist (cfg, info, u, &work_list); + } + } else if (i1->ssa_op == MONO_SSA_LOAD && + (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG)) { + MonoMethodVar *u = cfg->vars [i1->inst_i0->inst_c0]; + add_to_dce_worklist (cfg, info, u, &work_list); + } + + info->def->opcode = CEE_NOP; + info->def->ssa_op = MONO_SSA_NOP; + } + + } +} + +#if 0 +void +mono_ssa_strength_reduction (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + int i; + + g_assert (cfg->comp_done & MONO_COMP_SSA); + g_assert (cfg->comp_done & MONO_COMP_LOOPS); + g_assert (cfg->comp_done & MONO_COMP_SSA_DEF_USE); + + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + GList *lp = bb->loop_blocks; + + if (lp) { + MonoBasicBlock *h = (MonoBasicBlock *)lp->data; + + /* we only consider loops with 2 in bblocks */ + if (!h->in_count == 2) + continue; + + for (i = 0; i < cfg->num_varinfo; i++) { + MonoMethodVar *info = cfg->vars [i]; + + if (info->def && info->def->ssa_op == MONO_SSA_STORE && + info->def->inst_i0->opcode == OP_LOCAL && g_list_find (lp, info->def_bb)) { + MonoInst *v = info->def->inst_i1; + + + printf ("FOUND %d in %s\n", info->idx, mono_method_full_name (cfg->method, TRUE)); + } + } + } + } +} +#endif diff --git a/mono/mini/test.cs b/mono/mini/test.cs new file mode 100644 index 00000000000..49e17ad14dd --- /dev/null +++ b/mono/mini/test.cs @@ -0,0 +1,348 @@ +using System; + +namespace SSA { + class Test { + + static void empty () { + } + static int ret_int () { + return 1; + } + static int simple_add (int a) { + int b = 5; + return a + b; + } + + static int cmov (int a) { + return a >= 10? 1: 2; + } + + static int many_shifts (int a, int b, int c) { + return a << b << c << 1; + } + + static void test2 (int a) { + int x, y, z; + + z = 1; + if (z > a) { + x = 1; + if (z > 2) { + y = x + 1; + return; + } + } else { + x = 2; + } + z = x -3; + x = 4; + goto next; + next: + z = x + 7; + } + + static int rfib (int n) { + if (n < 2) + return 1; + return rfib (n - 2) + rfib (n - 1); + } + + static int test1 (int v) { + int x, y; + + x = 1; + if (v != 0) { + y = 2; + } else { + y = x + 1; + } + return y; + } + + static int for_loop () { + int j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + return j; + } + + static int many_bb2 () { + int j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + return j; + } + + static int many_bb4 () { + int j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + return j; + } + + static int many_bb8 () { + int j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + return j; + } + + static int many_bb16 () { + int j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + j = 0; + for (int i = 0; i < 5; i++) { + j += i; + } + return j; + } + + static int many_bb32 () { + int j; + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + j = 0; for (int i = 0; i < 5; i++) { j += i; } + return j; + } + + /*static int fib (int n) { + int f0 = 0, f1 = 1, f2 = 0, i; + + if (n <= 1) goto L3; + i = 2; + L1: + if (i <= n) goto L2; + return f2; + L2: + f2 = f0 + f1; + f0 = f1; + f1 = f2; + i++; + goto L1; + L3: + return n; + }*/ + + static int nested_loops (int n) { + int m = 1000; + int a = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + a++; + } + } + return a; + } + + static int Main() { + if (test1 (1) != 2) + return 1; + return 0; + } + } +} + diff --git a/mono/mini/tramp-ppc.c b/mono/mini/tramp-ppc.c new file mode 100644 index 00000000000..039943799ed --- /dev/null +++ b/mono/mini/tramp-ppc.c @@ -0,0 +1,748 @@ +/* + * trampoline.c: JIT trampoline code + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * Paolo Molaro (lupus@ximian.com) + * Carlos Valiente + * + * (C) 2001 Ximian, Inc. + */ + +#include +#include + +#include +#include +#include + +#include "mini.h" +#include "mini-ppc.h" + +/* + * define for the (broken) debugger breakpoint interface: + * The debugger should use the hw registers to set the breakpoints. + */ +#define mono_method_has_breakpoint(a,b) (0) +#define mono_remove_breakpoint(a) + +/* adapt to mini later... */ +#define mono_jit_share_code (1) + +/* + * Address of the x86 trampoline code. This is used by the debugger to check + * whether a method is a trampoline. + */ +guint8 *mono_generic_trampoline_code = NULL; + +/* + * Address of a special breakpoint trampoline code for the debugger. + */ +guint8 *mono_breakpoint_trampoline_code = NULL; + +/* + * get_unbox_trampoline: + * @m: method pointer + * @addr: pointer to native code for @m + * + * when value type methods are called through the vtable we need to unbox the + * this argument. This method returns a pointer to a trampoline which does + * unboxing before calling the method + */ +static gpointer +get_unbox_trampoline (MonoMethod *m, gpointer addr) +{ + guint8 *code, *start; + int this_pos = 3; + + if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret)) + this_pos = 4; + + start = code = g_malloc (20); + + ppc_load (code, ppc_r11, addr); + ppc_mtctr (code, ppc_r11); + ppc_addi (code, this_pos, this_pos, sizeof (MonoObject)); + ppc_bcctr (code, 20, 0); + g_assert ((code - start) <= 20); + + return start; +} + +/* + * get_breakpoint_trampoline: + * @m: method pointer + * @addr: pointer to native code for @m + * + * creates a special trampoline for the debugger which is used to get + * a breakpoint after compiling a method. + */ +static gpointer +get_breakpoint_trampoline (MonoMethod *m, guint32 breakpoint_id, gpointer addr) +{ + guint8 *code, *start, *buf; + + if (!mono_breakpoint_trampoline_code) { + mono_breakpoint_trampoline_code = buf = g_malloc (8); + + ppc_break (buf); + /* x86_breakpoint (buf); + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8); + x86_ret (buf);*/ + + g_assert ((buf - mono_breakpoint_trampoline_code) <= 8); + } + + start = code = g_malloc (22); + ppc_break (code); +/* x86_push_imm (code, addr); + x86_push_imm (code, breakpoint_id); + x86_push_imm (code, m); + x86_jump_code (code, mono_breakpoint_trampoline_code);*/ + g_assert ((code - start) <= 22); + + return start; +} + +/* Stack size for trampoline function */ +#define STACK 144 + +/* Method-specific trampoline code framgment size */ +#define METHOD_TRAMPOLINE_SIZE 64 + +/** + * ppc_magic_trampoline: + * @code: pointer into caller code + * @method: the method to translate + * @sp: stack pointer + * + * This method is called by the function 'arch_create_jit_trampoline', which in + * turn is called by the trampoline functions for virtual methods. + * After having called the JIT compiler to compile the method, it inspects the + * caller code to find the address of the method-specific part of the + * trampoline vtable slot for this method, updates it with a fragment that calls + * the newly compiled code and returns this address of the compiled code to + * 'arch_create_jit_trampoline' + */ +static gpointer +ppc_magic_trampoline (MonoMethod *method, guint32 *code, char *sp) +{ + char *o, *start; + gpointer addr; + int reg; + + EnterCriticalSection(metadata_section); + addr = mono_compile_method(method); + LeaveCriticalSection(metadata_section); + g_assert(addr); + + /* Locate the address of the method-specific trampoline. The call using + the vtable slot that took the processing flow to 'arch_create_jit_trampoline' + looks something like this: + + mtlr rA ; Move rA (a register containing the + ; target address) to LR + blrl ; Call function at LR + + PowerPC instructions are 32-bit long, which means that a 32-bit target + address cannot be encoded as an immediate value (because we already + have spent some bits to encode the branch instruction!). That's why a + 'b'ranch to the contents of the 'l'ink 'r'egister (with 'l'ink register + update) is needed, instead of a simpler 'branch immediate'. This + complicates our purpose here, because 'blrl' overwrites LR, which holds + the value we're interested in. + + Therefore, we need to locate the 'mtlr rA' instruction to know which + register LR was loaded from, and then retrieve the value from that + register */ + + /* This is the 'blrl' instruction */ + --code; + + /* + * Note that methods are called also with the bl opcode. + */ + if (((*code) >> 26) == 18) { + ppc_patch (code, addr); + mono_arch_flush_icache (code, 4); + return addr; + } + + /* Sanity check: instruction must be 'blrl' */ + g_assert(*code == 0x4e800021); + + /* OK, we're now at the 'blrl' instruction. Now walk backwards + till we get to a 'mtlr rA' */ + for(; --code;) { + if((*code & 0x7c0803a6) == 0x7c0803a6) { + /* Here we are: we reached the 'mtlr rA'. + Extract the register from the instruction */ + reg = (*code & 0x03e00000) >> 21; + switch(reg) { + case 0 : o = *((int *) (sp + STACK - 8)); break; + case 11: o = *((int *) (sp + STACK - 24)); break; + case 12: o = *((int *) (sp + STACK - 28)); break; + case 13: o = *((int *) (sp + STACK - 32)); break; + case 14: o = *((int *) (sp + STACK - 36)); break; + case 15: o = *((int *) (sp + STACK - 40)); break; + case 16: o = *((int *) (sp + STACK - 44)); break; + case 17: o = *((int *) (sp + STACK - 48)); break; + case 18: o = *((int *) (sp + STACK - 52)); break; + case 19: o = *((int *) (sp + STACK - 56)); break; + case 20: o = *((int *) (sp + STACK - 60)); break; + case 21: o = *((int *) (sp + STACK - 64)); break; + case 22: o = *((int *) (sp + STACK - 68)); break; + case 23: o = *((int *) (sp + STACK - 72)); break; + case 24: o = *((int *) (sp + STACK - 76)); break; + case 25: o = *((int *) (sp + STACK - 80)); break; + case 26: o = *((int *) (sp + STACK - 84)); break; + case 27: o = *((int *) (sp + STACK - 88)); break; + case 28: o = *((int *) (sp + STACK - 92)); break; + case 29: o = *((int *) (sp + STACK - 96)); break; + case 30: o = *((int *) (sp + STACK - 100)); break; + case 31: o = *((int *) (sp + STACK - 4)); break; + default: + printf("%s: Unexpected register %d\n", + __FUNCTION__, reg); + g_assert_not_reached(); + } + break; + } + } + + /* this is not done for non-virtual calls, because in that case + we won't have an object, but the actual pointer to the + valuetype as the this argument + */ + if (method->klass->valuetype) + addr = get_unbox_trampoline (method, addr); + + /* Finally, replace the method-specific trampoline code (which called + the generic trampoline code) with a fragment that calls directly the + compiled method */ + + start = o; + ppc_stwu (o, ppc_r1, -16, ppc_r1); + ppc_mflr (o, ppc_r0); + ppc_stw (o, ppc_r31, 12, ppc_r1); + ppc_stw (o, ppc_r0, 20, ppc_r1); + ppc_mr (o, ppc_r31, ppc_r1); + + ppc_lis (o, ppc_r0, (guint32) addr >> 16); + ppc_ori (o, ppc_r0, ppc_r0, (guint32) addr & 0xffff); + ppc_mtlr (o, ppc_r0); + ppc_blrl (o); + + ppc_lwz (o, ppc_r11, 0, ppc_r1); + ppc_lwz (o, ppc_r0, 4, ppc_r11); + ppc_mtlr (o, ppc_r0); + ppc_lwz (o, ppc_r31, -4, ppc_r11); + ppc_mr (o, ppc_r1, ppc_r11); + ppc_blr (o); + + mono_arch_flush_icache (start, o - start); + g_assert(o - start < METHOD_TRAMPOLINE_SIZE); + + return addr; +} + +/** + * arch_create_jit_trampoline: + * @method: pointer to the method info + * + * Creates a trampoline function for virtual methods. If the created + * code is called it first starts JIT compilation of method, + * and then calls the newly created method. It also replaces the + * corresponding vtable entry (see ppc_magic_trampoline). + * + * A trampoline consists of two parts: a main fragment, shared by all method + * trampolines, and some code specific to each method, which hard-codes a + * reference to that method and then calls the main fragment. + * + * The main fragment contains a call to 'ppc_magic_trampoline', which performs + * call to the JIT compiler and substitutes the method-specific fragment with + * some code that directly calls the JIT-compiled method. + * + * Returns: a pointer to the newly created code + */ +gpointer +mono_arch_create_jit_trampoline (MonoMethod *method) +{ + guint8 *code, *buf; + static guint8 *vc = NULL; + + /* previously created trampoline code */ + if (method->info) + return method->info; + + /* we immediately compile runtime provided functions */ + if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) { + method->info = mono_compile_method (method); + return method->info; + } + + /* icalls use method->addr */ + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + MonoMethod *nm; + + nm = mono_marshal_get_native_wrapper (method); + method->info = mono_compile_method (nm); + return method->info; + } + + if(!vc) { + /* Now we'll create in 'buf' the PowerPC trampoline code. This + is the trampoline code common to all methods */ + + vc = buf = g_malloc(512); + + /*----------------------------------------------------------- + STEP 0: First create a non-standard function prologue with a + stack size big enough to save our registers: + + lr (We'll be calling functions here, so we + must save it) + r0 (See ppc_magic_trampoline) + r1 (sp) (Stack pointer - must save) + r3-r10 Function arguments. + r11-r31 (See ppc_magic_trampoline) + method in r11 (See ppc_magic_trampoline) + + This prologue is non-standard because r0 is not saved here - it + was saved in the method-specific trampoline code + -----------------------------------------------------------*/ + + ppc_stwu (buf, ppc_r1, -STACK, ppc_r1); + + /* Save r0 before modifying it - we will need its contents in + 'ppc_magic_trampoline' */ + ppc_stw (buf, ppc_r0, STACK - 8, ppc_r1); + + ppc_stw (buf, ppc_r31, STACK - 4, ppc_r1); + ppc_mr (buf, ppc_r31, ppc_r1); + + /* Now save our registers. */ + ppc_stw (buf, ppc_r3, STACK - 12, ppc_r1); + ppc_stw (buf, ppc_r4, STACK - 16, ppc_r1); + ppc_stw (buf, ppc_r5, STACK - 20, ppc_r1); + ppc_stw (buf, ppc_r6, STACK - 24, ppc_r1); + ppc_stw (buf, ppc_r7, STACK - 28, ppc_r1); + ppc_stw (buf, ppc_r8, STACK - 32, ppc_r1); + ppc_stw (buf, ppc_r9, STACK - 36, ppc_r1); + ppc_stw (buf, ppc_r10, STACK - 40, ppc_r1); + /* STACK - 44 contains r11, which is set in the method-specific + part of the trampoline (see bellow this 'if' block) */ + ppc_stw (buf, ppc_r12, STACK - 48, ppc_r1); + ppc_stw (buf, ppc_r13, STACK - 52, ppc_r1); + ppc_stw (buf, ppc_r14, STACK - 56, ppc_r1); + ppc_stw (buf, ppc_r15, STACK - 60, ppc_r1); + ppc_stw (buf, ppc_r16, STACK - 64, ppc_r1); + ppc_stw (buf, ppc_r17, STACK - 68, ppc_r1); + ppc_stw (buf, ppc_r18, STACK - 72, ppc_r1); + ppc_stw (buf, ppc_r19, STACK - 76, ppc_r1); + ppc_stw (buf, ppc_r20, STACK - 80, ppc_r1); + ppc_stw (buf, ppc_r21, STACK - 84, ppc_r1); + ppc_stw (buf, ppc_r22, STACK - 88, ppc_r1); + ppc_stw (buf, ppc_r23, STACK - 92, ppc_r1); + ppc_stw (buf, ppc_r24, STACK - 96, ppc_r1); + ppc_stw (buf, ppc_r25, STACK - 100, ppc_r1); + ppc_stw (buf, ppc_r26, STACK - 104, ppc_r1); + ppc_stw (buf, ppc_r27, STACK - 108, ppc_r1); + ppc_stw (buf, ppc_r28, STACK - 112, ppc_r1); + ppc_stw (buf, ppc_r29, STACK - 116, ppc_r1); + ppc_stw (buf, ppc_r30, STACK - 120, ppc_r1); + /* Save 'method' pseudo-parameter - the one passed in r11 */ + ppc_stw (buf, ppc_r11, STACK - 124, ppc_r1); + + /*---------------------------------------------------------- + STEP 1: call 'mono_get_lmf_addr()' to get the address of our + LMF. We'll need to restore it after the call to + 'ppc_magic_trampoline' and before the call to the native + method. + ----------------------------------------------------------*/ + + /* Calculate the address and make the call. Keep in mind that + we're using r0, so we'll have to restore it before calling + 'ppc_magic_trampoline' */ + ppc_lis (buf, ppc_r0, (guint32) mono_get_lmf_addr >> 16); + ppc_ori (buf, ppc_r0, ppc_r0, (guint32) mono_get_lmf_addr & 0xffff); + ppc_mtlr (buf, ppc_r0); + ppc_blrl (buf); + + /* XXX Update LMF !!! */ + + /*---------------------------------------------------------- + STEP 2: call 'ppc_magic_trampoline()', who will compile the + code and fix the method vtable entry for us + ----------------------------------------------------------*/ + + /* Set arguments */ + + /* Arg 1: MonoMethod *method. It was put in r11 by the + method-specific trampoline code, and then saved before the call + to mono_get_lmf_addr()'. Restore r11, by the way :-) */ + ppc_lwz (buf, ppc_r3, STACK - 124, ppc_r1); + ppc_lwz (buf, ppc_r11, STACK - 44, ppc_r1); + + /* Arg 2: code (next address to the instruction that called us) */ + ppc_lwz (buf, ppc_r4, STACK + 4, ppc_r1); + + /* Arg 3: stack pointer */ + ppc_mr (buf, ppc_r5, ppc_r1); + + /* Calculate call address, restore r0 and call + 'ppc_magic_trampoline'. Return value will be in r3 */ + ppc_lis (buf, ppc_r0, (guint32) ppc_magic_trampoline >> 16); + ppc_ori (buf, ppc_r0, ppc_r0, (guint32) ppc_magic_trampoline & 0xffff); + ppc_mtlr (buf, ppc_r0); + ppc_lwz (buf, ppc_r0, STACK - 8, ppc_r1); + ppc_blrl (buf); + + /* OK, code address is now on r3. Move it to r0, so that we + can restore r3 and use it from r0 later */ + ppc_mr (buf, ppc_r0, ppc_r3); + + + /*---------------------------------------------------------- + STEP 3: Restore the LMF + ----------------------------------------------------------*/ + + /* XXX Do it !!! */ + + /*---------------------------------------------------------- + STEP 4: call the compiled method + ----------------------------------------------------------*/ + + /* Restore registers */ + + ppc_lwz (buf, ppc_r3, STACK - 12, ppc_r1); + ppc_lwz (buf, ppc_r4, STACK - 16, ppc_r1); + ppc_lwz (buf, ppc_r5, STACK - 20, ppc_r1); + ppc_lwz (buf, ppc_r6, STACK - 24, ppc_r1); + ppc_lwz (buf, ppc_r7, STACK - 28, ppc_r1); + ppc_lwz (buf, ppc_r8, STACK - 32, ppc_r1); + ppc_lwz (buf, ppc_r9, STACK - 36, ppc_r1); + ppc_lwz (buf, ppc_r10, STACK - 40, ppc_r1); + + /* We haven't touched any of these, so there's no need to + restore them */ + /* + ppc_lwz (buf, ppc_r14, STACK - 56, ppc_r1); + ppc_lwz (buf, ppc_r15, STACK - 60, ppc_r1); + ppc_lwz (buf, ppc_r16, STACK - 64, ppc_r1); + ppc_lwz (buf, ppc_r17, STACK - 68, ppc_r1); + ppc_lwz (buf, ppc_r18, STACK - 72, ppc_r1); + ppc_lwz (buf, ppc_r19, STACK - 76, ppc_r1); + ppc_lwz (buf, ppc_r20, STACK - 80, ppc_r1); + ppc_lwz (buf, ppc_r21, STACK - 84, ppc_r1); + ppc_lwz (buf, ppc_r22, STACK - 88, ppc_r1); + ppc_lwz (buf, ppc_r23, STACK - 92, ppc_r1); + ppc_lwz (buf, ppc_r24, STACK - 96, ppc_r1); + ppc_lwz (buf, ppc_r25, STACK - 100, ppc_r1); + ppc_lwz (buf, ppc_r26, STACK - 104, ppc_r1); + ppc_lwz (buf, ppc_r27, STACK - 108, ppc_r1); + ppc_lwz (buf, ppc_r28, STACK - 112, ppc_r1); + ppc_lwz (buf, ppc_r29, STACK - 116, ppc_r1); + ppc_lwz (buf, ppc_r30, STACK - 120, ppc_r1); + */ + + /* Non-standard function epilogue. Instead of doing a proper + return, we just call the compiled code, so + that, when it finishes, the method returns here. */ + + ppc_mtlr (buf, ppc_r0); + ppc_blrl (buf); + + /* Restore stack pointer, r31, LR and return to caler */ + ppc_lwz (buf, ppc_r11, 0, ppc_r1); + ppc_lwz (buf, ppc_r31, -4, ppc_r11); + ppc_mr (buf, ppc_r1, ppc_r11); + ppc_lwz (buf, ppc_r0, 4, ppc_r1); + ppc_mtlr (buf, ppc_r0); + ppc_blr (buf); + + /* Flush instruction cache, sice we've generated code */ + mono_arch_flush_icache (vc, buf - vc); + + /* Sanity check */ + g_assert ((buf - vc) <= 512); + } + + /* This is the method-specific part of the trampoline. Its purpose is + to provide the generic part with the MonoMethod *method pointer. We'll + use r11 to keep that value, for instance. However, the generic part of + the trampoline relies on r11 having the same value it had before coming + here, so we must save it before. */ + code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE); + + /* Save r11. There's nothing magic in the '44', its just an arbitrary + position - see above */ + ppc_stw (buf, ppc_r11, -44, ppc_r1); + + /* Now save LR - we'll overwrite it now */ + ppc_mflr (buf, ppc_r11); + ppc_stw (buf, ppc_r11, 4, ppc_r1); + + /* Prepare the jump to the generic trampoline code.*/ + ppc_lis (buf, ppc_r11, (guint32) vc >> 16); + ppc_ori (buf, ppc_r11, ppc_r11, (guint32) vc & 0xffff); + ppc_mtlr (buf, ppc_r11); + + /* And finally put 'method' in r11 and fly! */ + ppc_lis (buf, ppc_r11, (guint32) method >> 16); + ppc_ori (buf, ppc_r11, ppc_r11, (guint32) method & 0xffff); + ppc_blr (buf); + + /* Flush instruction cache, sice we've generated code */ + mono_arch_flush_icache (code, buf - code); + + /* Sanity check */ + g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE); + + /* store trampoline address */ + method->info = code; + + mono_jit_stats.method_trampolines++; + + return code; +} + +#if 0 + +/** + * x86_magic_trampoline: + * @eax: saved x86 register + * @ecx: saved x86 register + * @edx: saved x86 register + * @esi: saved x86 register + * @edi: saved x86 register + * @ebx: saved x86 register + * @code: pointer into caller code + * @method: the method to translate + * + * This method is called by the trampoline functions for virtual + * methods. It inspects the caller code to find the address of the + * vtable slot, then calls the JIT compiler and writes the address + * of the compiled method back to the vtable. All virtual methods + * are called with: x86_call_membase (inst, basereg, disp). We always + * use 32 bit displacement to ensure that the length of the call + * instruction is 6 bytes. We need to get the value of the basereg + * and the constant displacement. + */ +static gpointer +x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, + int ebx, guint8 *code, MonoMethod *m) +{ + guint8 reg; + gint32 disp; + char *o; + guint32 breakpoint_id; + gpointer addr, trampoline; + + EnterCriticalSection (metadata_section); + addr = mono_compile_method (m); + LeaveCriticalSection (metadata_section); + g_assert (addr); + + /* go to the start of the call instruction + * + * address_byte = (m << 6) | (o << 3) | reg + * call opcode: 0xff address_byte displacement + * 0xff m=1,o=2 imm8 + * 0xff m=2,o=2 imm32 + */ + code -= 6; + if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) { + reg = code [4] & 0x07; + disp = (signed char)code [5]; + } else { + if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) { + reg = code [1] & 0x07; + disp = *((gint32*)(code + 2)); + } else if ((code [1] == 0xe8)) { + breakpoint_id = mono_method_has_breakpoint (m, TRUE); + if (breakpoint_id) { + mono_remove_breakpoint (breakpoint_id); + trampoline = get_breakpoint_trampoline (m, breakpoint_id, addr); + } else + trampoline = addr; + *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; + return trampoline; + } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) { + /* + * This is a interface call: should check the above code can't catch it earlier + * 8b 40 30 mov 0x30(%eax),%eax + * ff 10 call *(%eax) + */ + disp = 0; + reg = code [5] & 0x07; + } else { + printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3], + code [4], code [5], code [6]); + g_assert_not_reached (); + } + } + + switch (reg) { + case X86_EAX: + o = (gpointer)eax; + break; + case X86_EDX: + o = (gpointer)edx; + break; + case X86_ECX: + o = (gpointer)ecx; + break; + case X86_ESI: + o = (gpointer)esi; + break; + case X86_EDI: + o = (gpointer)edi; + break; + case X86_EBX: + o = (gpointer)ebx; + break; + default: + g_assert_not_reached (); + } + + o += disp; + + if (m->klass->valuetype) { + trampoline = *((gpointer *)o) = get_unbox_trampoline (m, addr); + } else { + trampoline = *((gpointer *)o) = addr; + } + + breakpoint_id = mono_method_has_breakpoint (m, TRUE); + if (breakpoint_id) { + mono_remove_breakpoint (breakpoint_id); + return get_breakpoint_trampoline (m, breakpoint_id, trampoline); + } else { + return trampoline; + } +} + +/** + * mono_arch_create_jit_trampoline: + * @method: pointer to the method info + * + * Creates a trampoline function for virtual methods. If the created + * code is called it first starts JIT compilation of method, + * and then calls the newly created method. I also replaces the + * corresponding vtable entry (see x86_magic_trampoline). + * + * Returns: a pointer to the newly created code + */ +gpointer +mono_arch_create_jit_trampoline (MonoMethod *method) +{ + guint8 *code, *buf; + + /* previously created trampoline code */ + if (method->info) + return method->info; + + /* we immediately compile runtime provided functions */ + if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) { + method->info = mono_compile_method (method); + return method->info; + } + + /* icalls use method->addr */ + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + MonoMethod *nm; + + nm = mono_marshal_get_native_wrapper (method); + method->info = mono_compile_method (nm); + return method->info; + } + + if (!mono_generic_trampoline_code) { + mono_generic_trampoline_code = buf = g_malloc (256); + /* save caller save regs because we need to do a call */ + x86_push_reg (buf, X86_EDX); + x86_push_reg (buf, X86_EAX); + x86_push_reg (buf, X86_ECX); + + /* save LMF begin */ + + /* save the IP (caller ip) */ + x86_push_membase (buf, X86_ESP, 16); + + x86_push_reg (buf, X86_EBX); + x86_push_reg (buf, X86_EDI); + x86_push_reg (buf, X86_ESI); + x86_push_reg (buf, X86_EBP); + + /* save method info */ + x86_push_membase (buf, X86_ESP, 32); + /* get the address of lmf for the current thread */ + x86_call_code (buf, mono_get_lmf_addr); + /* push lmf */ + x86_push_reg (buf, X86_EAX); + /* push *lfm (previous_lmf) */ + x86_push_membase (buf, X86_EAX, 0); + /* *(lmf) = ESP */ + x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4); + /* save LFM end */ + + /* push the method info */ + x86_push_membase (buf, X86_ESP, 44); + /* push the return address onto the stack */ + x86_push_membase (buf, X86_ESP, 52); + + /* save all register values */ + x86_push_reg (buf, X86_EBX); + x86_push_reg (buf, X86_EDI); + x86_push_reg (buf, X86_ESI); + x86_push_membase (buf, X86_ESP, 64); /* EDX */ + x86_push_membase (buf, X86_ESP, 64); /* ECX */ + x86_push_membase (buf, X86_ESP, 64); /* EAX */ + + x86_call_code (buf, x86_magic_trampoline); + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4); + + /* restore LMF start */ + /* ebx = previous_lmf */ + x86_pop_reg (buf, X86_EBX); + /* edi = lmf */ + x86_pop_reg (buf, X86_EDI); + /* *(lmf) = previous_lmf */ + x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4); + /* discard method info */ + x86_pop_reg (buf, X86_ESI); + /* restore caller saved regs */ + x86_pop_reg (buf, X86_EBP); + x86_pop_reg (buf, X86_ESI); + x86_pop_reg (buf, X86_EDI); + x86_pop_reg (buf, X86_EBX); + /* discard save IP */ + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4); + /* restore LMF end */ + + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16); + + /* call the compiled method */ + x86_jump_reg (buf, X86_EAX); + + g_assert ((buf - mono_generic_trampoline_code) <= 256); + } + + code = buf = g_malloc (16); + x86_push_imm (buf, method); + x86_jump_code (buf, mono_generic_trampoline_code); + g_assert ((buf - code) <= 16); + + /* store trampoline address */ + method->info = code; + + //mono_jit_stats.method_trampolines++; + + return code; +} + +#endif + diff --git a/mono/mini/tramp-x86.c b/mono/mini/tramp-x86.c new file mode 100644 index 00000000000..869b69c0750 --- /dev/null +++ b/mono/mini/tramp-x86.c @@ -0,0 +1,345 @@ +/* + * trampoline.c: JIT trampoline code + * + * Authors: + * Dietmar Maurer (dietmar@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#include +#include + +#include +#include +#include +#include + +#include "mini.h" +#include "mini-x86.h" + +/* + * define for the (broken) debugger breakpoint interface: + * The debugger should use the hw registers to set the breakpoints. + */ +#define mono_method_has_breakpoint(a,b) (0) +#define mono_remove_breakpoint(a) + +/* adapt to mini later... */ +#define mono_jit_share_code (1) + +/* + * Address of the x86 trampoline code. This is used by the debugger to check + * whether a method is a trampoline. + */ +guint8 *mono_generic_trampoline_code = NULL; + +/* + * Address of a special breakpoint trampoline code for the debugger. + */ +guint8 *mono_breakpoint_trampoline_code = NULL; + +/* + * get_unbox_trampoline: + * @m: method pointer + * @addr: pointer to native code for @m + * + * when value type methods are called through the vtable we need to unbox the + * this argument. This method returns a pointer to a trampoline which does + * unboxing before calling the method + */ +static gpointer +get_unbox_trampoline (MonoMethod *m, gpointer addr) +{ + guint8 *code, *start; + int this_pos = 4; + + if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret)) + this_pos = 8; + + start = code = g_malloc (16); + + x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject)); + x86_jump_code (code, addr); + g_assert ((code - start) < 16); + + return start; +} + +/* + * get_breakpoint_trampoline: + * @m: method pointer + * @addr: pointer to native code for @m + * + * creates a special trampoline for the debugger which is used to get + * a breakpoint after compiling a method. + */ +static gpointer +get_breakpoint_trampoline (MonoMethod *m, guint32 breakpoint_id, gpointer addr) +{ + guint8 *code, *start, *buf; + + if (!mono_breakpoint_trampoline_code) { + mono_breakpoint_trampoline_code = buf = g_malloc (8); + + x86_breakpoint (buf); + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8); + x86_ret (buf); + + g_assert ((buf - mono_breakpoint_trampoline_code) <= 8); + } + + start = code = g_malloc (22); + x86_push_imm (code, addr); + x86_push_imm (code, breakpoint_id); + x86_push_imm (code, m); + x86_jump_code (code, mono_breakpoint_trampoline_code); + g_assert ((code - start) <= 22); + + return start; +} + +/** + * x86_magic_trampoline: + * @eax: saved x86 register + * @ecx: saved x86 register + * @edx: saved x86 register + * @esi: saved x86 register + * @edi: saved x86 register + * @ebx: saved x86 register + * @code: pointer into caller code + * @method: the method to translate + * + * This method is called by the trampoline functions for virtual + * methods. It inspects the caller code to find the address of the + * vtable slot, then calls the JIT compiler and writes the address + * of the compiled method back to the vtable. All virtual methods + * are called with: x86_call_membase (inst, basereg, disp). We always + * use 32 bit displacement to ensure that the length of the call + * instruction is 6 bytes. We need to get the value of the basereg + * and the constant displacement. + */ +static gpointer +x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, + int ebx, guint8 *code, MonoMethod *m) +{ + guint8 reg; + gint32 disp; + char *o; + guint32 breakpoint_id; + gpointer addr, trampoline; + + EnterCriticalSection (metadata_section); + addr = mono_compile_method (m); + LeaveCriticalSection (metadata_section); + g_assert (addr); + + /* go to the start of the call instruction + * + * address_byte = (m << 6) | (o << 3) | reg + * call opcode: 0xff address_byte displacement + * 0xff m=1,o=2 imm8 + * 0xff m=2,o=2 imm32 + */ + code -= 6; + if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) { + reg = code [4] & 0x07; + disp = (signed char)code [5]; + } else { + if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) { + reg = code [1] & 0x07; + disp = *((gint32*)(code + 2)); + } else if ((code [1] == 0xe8)) { + breakpoint_id = mono_method_has_breakpoint (m, TRUE); + if (breakpoint_id) { + mono_remove_breakpoint (breakpoint_id); + trampoline = get_breakpoint_trampoline (m, breakpoint_id, addr); + } else + trampoline = addr; + *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; + return trampoline; + } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) { + /* + * This is a interface call: should check the above code can't catch it earlier + * 8b 40 30 mov 0x30(%eax),%eax + * ff 10 call *(%eax) + */ + disp = 0; + reg = code [5] & 0x07; + } else { + printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3], + code [4], code [5], code [6]); + g_assert_not_reached (); + } + } + + switch (reg) { + case X86_EAX: + o = (gpointer)eax; + break; + case X86_EDX: + o = (gpointer)edx; + break; + case X86_ECX: + o = (gpointer)ecx; + break; + case X86_ESI: + o = (gpointer)esi; + break; + case X86_EDI: + o = (gpointer)edi; + break; + case X86_EBX: + o = (gpointer)ebx; + break; + default: + g_assert_not_reached (); + } + + o += disp; + + if (m->klass->valuetype) { + trampoline = *((gpointer *)o) = get_unbox_trampoline (m, addr); + } else { + trampoline = *((gpointer *)o) = addr; + } + + breakpoint_id = mono_method_has_breakpoint (m, TRUE); + if (breakpoint_id) { + mono_remove_breakpoint (breakpoint_id); + return get_breakpoint_trampoline (m, breakpoint_id, trampoline); + } else { + return trampoline; + } +} + +/** + * mono_arch_create_jit_trampoline: + * @method: pointer to the method info + * + * Creates a trampoline function for virtual methods. If the created + * code is called it first starts JIT compilation of method, + * and then calls the newly created method. I also replaces the + * corresponding vtable entry (see x86_magic_trampoline). + * + * Returns: a pointer to the newly created code + */ +gpointer +mono_arch_create_jit_trampoline (MonoMethod *method) +{ + guint8 *code, *buf; + + /* previously created trampoline code */ + if (method->info) + return method->info; + + /* we immediately compile runtime provided functions */ + if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) { + method->info = mono_compile_method (method); + return method->info; + } + + /* icalls use method->addr */ + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { + MonoMethod *nm; + + if (!method->addr && (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + mono_lookup_pinvoke_call (method); + +#ifdef MONO_USE_EXC_TABLES + if (mono_method_blittable (method)) { + method->info = method->addr; + } else { +#endif + nm = mono_marshal_get_native_wrapper (method); + method->info = mono_compile_method (nm); +#ifdef MONO_USE_EXC_TABLES + } +#endif + return method->info; + } + + if (!mono_generic_trampoline_code) { + mono_generic_trampoline_code = buf = g_malloc (256); + /* save caller save regs because we need to do a call */ + x86_push_reg (buf, X86_EDX); + x86_push_reg (buf, X86_EAX); + x86_push_reg (buf, X86_ECX); + + /* save LMF begin */ + + /* save the IP (caller ip) */ + x86_push_membase (buf, X86_ESP, 16); + + x86_push_reg (buf, X86_EBX); + x86_push_reg (buf, X86_EDI); + x86_push_reg (buf, X86_ESI); + x86_push_reg (buf, X86_EBP); + + /* save method info */ + x86_push_membase (buf, X86_ESP, 32); + /* get the address of lmf for the current thread */ + x86_call_code (buf, mono_get_lmf_addr); + /* push lmf */ + x86_push_reg (buf, X86_EAX); + /* push *lfm (previous_lmf) */ + x86_push_membase (buf, X86_EAX, 0); + /* *(lmf) = ESP */ + x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4); + /* save LFM end */ + + /* push the method info */ + x86_push_membase (buf, X86_ESP, 44); + /* push the return address onto the stack */ + x86_push_membase (buf, X86_ESP, 52); + + /* save all register values */ + x86_push_reg (buf, X86_EBX); + x86_push_reg (buf, X86_EDI); + x86_push_reg (buf, X86_ESI); + x86_push_membase (buf, X86_ESP, 64); /* EDX */ + x86_push_membase (buf, X86_ESP, 64); /* ECX */ + x86_push_membase (buf, X86_ESP, 64); /* EAX */ + + x86_call_code (buf, x86_magic_trampoline); + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4); + + /* restore LMF start */ + /* ebx = previous_lmf */ + x86_pop_reg (buf, X86_EBX); + /* edi = lmf */ + x86_pop_reg (buf, X86_EDI); + /* *(lmf) = previous_lmf */ + x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4); + /* discard method info */ + x86_pop_reg (buf, X86_ESI); + /* restore caller saved regs */ + x86_pop_reg (buf, X86_EBP); + x86_pop_reg (buf, X86_ESI); + x86_pop_reg (buf, X86_EDI); + x86_pop_reg (buf, X86_EBX); + /* discard save IP */ + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4); + /* restore LMF end */ + + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16); + + /* call the compiled method */ + x86_jump_reg (buf, X86_EAX); + + g_assert ((buf - mono_generic_trampoline_code) <= 256); + } + + code = buf = g_malloc (16); + x86_push_imm (buf, method); + x86_jump_code (buf, mono_generic_trampoline_code); + g_assert ((buf - code) <= 16); + + /* store trampoline address */ + method->info = code; + + mono_jit_stats.method_trampolines++; + + return code; +} diff --git a/mono/mini/viewstat.pl b/mono/mini/viewstat.pl new file mode 100644 index 00000000000..50bd05b6701 --- /dev/null +++ b/mono/mini/viewstat.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl + +use GD::Graph::bars; +use GD::Graph::bars3d; +use Getopt::Std; + +$Usage = "usage: $0 [-e] [-o file] statfile"; + +# -e generate a 3D graph +# -o file write the graph to file, instead of starting the viewer + +getopts('eo:', \%Opts) + or die "$Usage"; +die "$Usage\n" + unless (@ARGV == 1); + +$statfile = shift; + +sub save_chart +{ + my $chart = shift or die "Need a chart!"; + my $name = shift or die "Need a name!"; + local(*OUT); + + open(OUT, ">$name") or + die "Cannot open $name.$ext for write: $!"; + binmode OUT; + print OUT $chart->gd->png(); + close OUT; +} + + +print STDERR "Processing file $statfile\n"; + +if ($Opts{'e'}) { + $graph = new GD::Graph::bars3d(800, 600); +} else { + $graph = new GD::Graph::bars(800, 600); +} +$graph->set( + y_label => 'Time', + y_long_ticks => 1, + + x_long_ticks => 1, + + y_tick_number => 8, + + x_labels_vertical => 1, + bar_spacing => 5, + + show_values => 1, + values_vertical => 1, + + l_margin => 10, + b_margin => 10, + r_margin => 10, + t_margin => 10, + + shadow_depth => 1, + + transparent => 0, +); + +if ($Opts{'e'}) { + $graph->set (overwrite => 1); + $graph->set (show_values => 0); +} + +require $statfile; + +if ($stattitle ne "") { + $graph->set (title=> $stattitle); +} + +$outfile = $Opts{'o'}; + +if ($outfile eq "") { + $tmp = $outfile = "/tmp/viewstat" . $$ . ".png"; +} + +$graph->plot(\@data); +save_chart($graph, $outfile); + +if ($tmp) { + `eog $outfile`; + `rm $tmp`; +} -- 2.25.1