From: Stefan Ring Date: Mon, 13 Oct 2008 14:34:46 +0000 (+0200) Subject: Merged new changes from default (manually: src/vm/jit/emit-common.cpp, src/vm/ji X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=0809f4a711ccc2537204c735daa18017680ced86;hp=e954be2f4bc7a1747c109ea4929d189dd58e401e;p=cacao.git Merged new changes from default (manually: src/vm/jit/emit-common.cpp, src/vm/ji t/emit-common.hpp) --HG-- branch : subtype-trunk --- diff --git a/INSTALL b/INSTALL index f3869a941..d3c5b40a9 100644 --- a/INSTALL +++ b/INSTALL @@ -1,27 +1,237 @@ -Quick start instructions -======================== +Installation Instructions +************************* -You can check the configure options via `./configure --help'. But the -default settings should be ok. For building the package type: +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. - $ ./configure - $ make - $ make install +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. -The default installation prefix is `/usr/local/cacao'. You can change -this destination by providing the `--prefix=PATH' option to configure. +Basic Installation +================== -This version of cacao only supports the `--prefix' option, even if -configure processes the other options (`--bindir', `--libdir', etc.), -it will screw up your installation since CACAO tries to setup a -directory tree like the JVMs from Sun or IBM do. +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). -Requirements: -------------- + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. -In order to compile the Java classes from GNU classpath, you need a -Java compiler supported by GNU classpath. To get a working classpath -in an appropriate time, we recommend IBM jikes 1.18 or newer. For -further instructions concerning GNU classpath, please refer to -`src/classpath/INSTALL'. diff --git a/configure.ac b/configure.ac index a9572aad2..3f0c8a457 100644 --- a/configure.ac +++ b/configure.ac @@ -67,13 +67,18 @@ m68k ) JAVA_ARCH="m68k" ;; -mips | mipsel ) +mips ) ARCH_DIR="mips" ARCH_FLAGS="-D__MIPS__" - dnl Is this correct for mipsel? JAVA_ARCH="mips" ;; +mipsel ) + ARCH_DIR="mips" + ARCH_FLAGS="-D__MIPS__" + JAVA_ARCH="mipsel" + ;; + powerpc ) ARCH_DIR="powerpc" ARCH_FLAGS="-m32 -D__POWERPC__" diff --git a/src/mm/boehm-gc/include/private/gcconfig.h b/src/mm/boehm-gc/include/private/gcconfig.h index b828759d0..e1377ac08 100644 --- a/src/mm/boehm-gc/include/private/gcconfig.h +++ b/src/mm/boehm-gc/include/private/gcconfig.h @@ -1320,8 +1320,7 @@ # define DYNAMIC_LOADING extern int _end[]; # define DATAEND (_end) - extern int __data_start[]; -# define DATASTART ((ptr_t)(__data_start)) +# define SEARCH_FOR_DATA_START # define CPP_WORDSZ _MIPS_SZPTR # define ALIGNMENT (_MIPS_SZPTR/8) # if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2 @@ -1708,9 +1707,8 @@ # define OS_TYPE "LINUX" # define LINUX_STACKBOTTOM # define DYNAMIC_LOADING - extern int __data_start[]; -# define DATASTART ((ptr_t)(__data_start)) - extern int _end[]; +# define SEARCH_FOR_DATA_START + extern int _end[]; # define DATAEND (_end) # define CACHE_LINE_SIZE 256 # define GETPAGESIZE() 4096 diff --git a/src/vm/jit/Makefile.am b/src/vm/jit/Makefile.am index c8d27ba74..42bb84247 100644 --- a/src/vm/jit/Makefile.am +++ b/src/vm/jit/Makefile.am @@ -181,8 +181,8 @@ libjit_la_SOURCES = \ linenumbertable.hpp \ methodtree.c \ methodtree.h \ - parse.c \ - parse.h \ + parse.cpp \ + parse.hpp \ patcher-common.cpp \ patcher-common.hpp \ $(RECOMPILE_SOURCES) \ diff --git a/src/vm/jit/allocator/simplereg.h b/src/vm/jit/allocator/simplereg.h index 9ca4373a6..619a03951 100644 --- a/src/vm/jit/allocator/simplereg.h +++ b/src/vm/jit/allocator/simplereg.h @@ -33,7 +33,7 @@ #include "vm/jit/codegen-common.hpp" #include "vm/jit/jit.hpp" -#include "vm/jit/inline/inline.h" +#include "vm/jit/inline/inline.hpp" /* function prototypes ********************************************************/ diff --git a/src/vm/jit/alpha/codegen.c b/src/vm/jit/alpha/codegen.c index d44e2f8d1..b136fd4dc 100644 --- a/src/vm/jit/alpha/codegen.c +++ b/src/vm/jit/alpha/codegen.c @@ -57,7 +57,7 @@ #include "vm/jit/emit-common.hpp" #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/arm/codegen.c b/src/vm/jit/arm/codegen.c index efe6d3d74..0df334e9a 100644 --- a/src/vm/jit/arm/codegen.c +++ b/src/vm/jit/arm/codegen.c @@ -58,7 +58,7 @@ #include "vm/jit/jitcache.hpp" #include "vm/jit/linenumbertable.hpp" #include "vm/jit/methodheader.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" diff --git a/src/vm/jit/emit-common.cpp b/src/vm/jit/emit-common.cpp index 3bc48fa9c..370a64bd0 100644 --- a/src/vm/jit/emit-common.cpp +++ b/src/vm/jit/emit-common.cpp @@ -656,19 +656,48 @@ void emit_label_br(codegendata *cd, s4 label) #if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER -void emit_label_beqz(codegendata *cd, s4 label, s4 reg) +void emit_label_beqz(codegendata* cd, int label, int reg) { emit_label_bccz(cd, label, BRANCH_EQ, reg, BRANCH_OPT_NONE); } -void emit_label_bnez(codegendata *cd, s4 label, s4 reg) +void emit_label_bnez(codegendata* cd, int label, int reg) { emit_label_bccz(cd, label, BRANCH_NE, reg, BRANCH_OPT_NONE); } +void emit_label_bltz(codegendata* cd, int label, int reg) +{ + emit_label_bccz(cd, label, BRANCH_LT, reg, BRANCH_OPT_NONE); +} + +void emit_label_bgtz(codegendata* cd, int label, int reg) +{ + emit_label_bccz(cd, label, BRANCH_GT, reg, BRANCH_OPT_NONE); +} + #endif /* SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER */ +/* emit_label_bxx ************************************************************** + + Wrappers for label-branches on two integer registers. + + We use PACK_REGS here, so we don't have to change the branchref + data structure and the emit_bccz function. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS + +void emit_label_bne(codegendata* cd, int label, int s1, int s2) +{ + emit_label_bccz(cd, label, BRANCH_NE, PACK_REGS(s1, s2), BRANCH_OPT_NONE); +} + +#endif /* SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS */ + + /* emit_label_bxx ************************************************************** Wrappers for label-branches on condition codes. diff --git a/src/vm/jit/emit-common.hpp b/src/vm/jit/emit-common.hpp index b96bc08ea..611eca43d 100644 --- a/src/vm/jit/emit-common.hpp +++ b/src/vm/jit/emit-common.hpp @@ -1,9 +1,7 @@ /* src/vm/jit/emit-common.hpp - common code emitter functions - Copyright (C) 2006, 2007 R. Grafl, A. Krall, C. Kruegel, C. Oates, - R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner, - C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger, - Institut f. Computersprachen - TU Wien + Copyright (C) 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. @@ -163,8 +161,14 @@ void emit_label_bcc(codegendata *cd, s4 label, s4 condition, u4 options); void emit_label_br(codegendata *cd, s4 label); #if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER -void emit_label_beqz(codegendata *cd, s4 label, s4 reg); -void emit_label_bnez(codegendata *cd, s4 label, s4 reg); +void emit_label_beqz(codegendata* cd, int label, int reg); +void emit_label_bnez(codegendata* cd, int label, int reg); +void emit_label_bltz(codegendata* cd, int label, int reg); +void emit_label_bgtz(codegendata* cd, int label, int reg); +#endif + +#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS +void emit_label_bne(codegendata* cd, int label, int s1, int s2); #endif #if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER diff --git a/src/vm/jit/i386/codegen.c b/src/vm/jit/i386/codegen.c index bdedf5b64..c7adf83a5 100644 --- a/src/vm/jit/i386/codegen.c +++ b/src/vm/jit/i386/codegen.c @@ -60,7 +60,7 @@ #include "vm/jit/jit.hpp" #include "vm/jit/jitcache.hpp" #include "vm/jit/linenumbertable.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/inline/Makefile.am b/src/vm/jit/inline/Makefile.am index 8b46e1332..e3ca1468f 100644 --- a/src/vm/jit/inline/Makefile.am +++ b/src/vm/jit/inline/Makefile.am @@ -31,8 +31,8 @@ noinst_LTLIBRARIES = \ libinline.la libinline_la_SOURCES = \ - inline.c \ - inline.h \ + inline.cpp \ + inline.hpp \ inline_debug.inc diff --git a/src/vm/jit/inline/inline.c b/src/vm/jit/inline/inline.c deleted file mode 100644 index ca7fb42ad..000000000 --- a/src/vm/jit/inline/inline.c +++ /dev/null @@ -1,3237 +0,0 @@ -/* src/vm/jit/inline/inline.c - method inlining - - Copyright (C) 1996-2005, 2006, 2007, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#include "config.h" - -#include -#include -#include -#include - -#include "vm/types.h" - -#include "mm/memory.h" - -#include "threads/lock.hpp" -#include "threads/mutex.hpp" -#include "threads/thread.hpp" - -#include "toolbox/logging.h" - -#include "vm/jit/builtin.hpp" -#include "vm/class.hpp" -#include "vm/global.h" -#include "vm/initialize.hpp" -#include "vm/method.h" -#include "vm/options.h" -#include "vm/statistics.h" - -#include "vm/jit/jit.hpp" -#include "vm/jit/parse.h" -#include "vm/jit/reg.h" -#include "vm/jit/show.hpp" -#include "vm/jit/stack.h" - -#include "vm/jit/inline/inline.h" -#include "vm/jit/loop/loop.h" - -#include "vm/jit/verify/typecheck.h" - - -/* algorithm tuning constants *************************************************/ - -/* Algorithm Selection */ -/* Define exactly one of the following three to select the inlining */ -/* heuristics. */ - -/*#define INLINE_DEPTH_FIRST*/ -/*#define INLINE_BREADTH_FIRST*/ -#define INLINE_KNAPSACK - -/* Parameters for knapsack heuristics: */ - -#if defined(INLINE_KNAPSACK) - -#define INLINE_COUNTDOWN_INIT 1000 -#define INLINE_COST_OFFSET -16 -#define INLINE_COST_BUDGET 100 -/*#define INLINE_WEIGHT_BUDGET 5.0*/ -/*#define INLINE_ADD_NEGATIVE_TO_BUDGET*/ -/*#define INLINE_MAX_DEPTH 3*/ -/*#define INLINE_DIVIDE_COST_BY_FREQ */ - -#endif - -/* Parameters for depth-first heuristics: */ - -#if defined(INLINE_DEPTH_FIRST) - -#define INLINE_MAX_DEPTH 3 -#define INLINE_MAX_BLOCK_EXPANSION 10 -/*#define INLINE_MAX_ICMD_EXPANSION 10*/ -/*#define INLINE_CANCEL_ON_THRESHOLD*/ - -#endif - -/* Parameters for breadth-first heuristics: */ - -#if defined(INLINE_BREADTH_FIRST) - -/*#define INLINE_MAX_BLOCK_EXPANSION 10*/ -#define INLINE_MAX_ICMD_EXPANSION 5 - -#endif - - -/* debugging ******************************************************************/ - -#if !defined(NDEBUG) -#define INLINE_VERBOSE -#define DOLOG(code) do{ if (opt_TraceInlining >= 2) { code; } }while(0) -#define DOLOG_SHORT(code) do{ if (opt_TraceInlining >= 1) { code; } }while(0) -#else -#define DOLOG(code) -#endif - -#if defined(ENABLE_VERIFIER) && !defined(NDEBUG) -/* Define this to verify the resulting code after inlining. */ -/* Note: This is only useful for development and may require patches to the */ -/* verifier code. */ -/* #define INLINE_VERIFY_RESULT */ -#endif - - -/* types **********************************************************************/ - -typedef struct inline_node inline_node; -typedef struct inline_target_ref inline_target_ref; -typedef struct inline_context inline_context; -typedef struct inline_block_map inline_block_map; -typedef struct inline_site inline_site; -typedef struct inline_candidate inline_candidate; - -struct inline_node { - inline_context *ctx; - - jitdata *jd; - methodinfo *m; - inline_node *children; - inline_node *next; /* next node at this depth */ - inline_node *prev; /* prev node at this depth */ - int depth; /* inlining depth, 0 for root */ - - /* info about the call site (if depth > 0)*/ - inline_node *parent; /* node of the caller (NULL for root) */ - basicblock *callerblock; /* original block containing the INVOKE* */ - instruction *callerins; /* the original INVOKE* instruction */ - s4 callerpc; - s4 *n_passthroughvars; - int n_passthroughcount; - int n_selfpassthroughcount; /* # of pass-through vars of the call itself */ - exception_entry **o_handlers; - int n_handlercount; /* # of handlers protecting this call */ - int n_resultlocal; - int synclocal; /* variable used for synchr., or UNUSED */ - bool isstatic; /* this is a static call */ - - bool blockbefore; /* block boundary before inlined body? */ - bool blockafter; /* block boundary after inlined body? */ - - /* info about the callee */ - int localsoffset; - int prolog_instructioncount; /* # of ICMDs in the inlining prolog */ - int epilog_instructioncount; /* # of ICMDs in the inlining epilog */ - int extra_instructioncount; - int extra_exceptiontablelength; /* # of extra handlers to put in caller */ - bool synchronize; /* do we have to synchronize enter/exit? */ - basicblock *handler_monitorexit; /* handler for synchronized inlinees */ - s4 *varmap; - - /* cumulative values */ - int cumul_instructioncount; /* ICMDs in this node and its children */ - int cumul_basicblockcount; /* BBs started by this node and its children */ - int cumul_basicblockcount_root; /* BBs that have to be added to the root */ - /* node if this node is inlined */ - int cumul_blockmapcount; - int cumul_maxlocals; - int cumul_exceptiontablelength; - - /* output */ - instruction *inlined_iinstr; - instruction *inlined_iinstr_cursor; - basicblock *inlined_basicblocks; - basicblock *inlined_basicblocks_cursor; - - /* register data */ - registerdata *regdata; - - /* temporary */ - inline_target_ref *refs; - instruction *inline_start_instruction; - s4 *javalocals; - - /* XXX debug */ - char *indent; - int debugnr; -}; - -struct inline_target_ref { - inline_target_ref *next; - union { - basicblock **block; - s4 *nr; - } ref; - basicblock *target; - bool isnumber; -}; - -struct inline_block_map { - inline_node *iln; - basicblock *o_block; - basicblock *n_block; -}; - -struct inline_context { - inline_node *master; - - jitdata *resultjd; - - inline_candidate *candidates; - - int next_block_number; - inline_block_map *blockmap; - int blockmap_index; - - int maxinoutdepth; - - bool stopped; - - int next_debugnr; /* XXX debug */ -}; - -struct inline_site { - bool speculative; /* true, if inlining would be speculative */ - bool inlined; /* true, if this site has been inlined */ - - basicblock *bptr; /* basic block containing the call site */ - instruction *iptr; /* the invocation instruction */ - exception_entry **handlers; /* active handlers at the call site */ - s4 nhandlers; /* number of active handlers */ - s4 pc; /* PC of the invocation instruction */ -}; - -struct inline_candidate { - inline_candidate *next; - int freq; - int cost; - double weight; - inline_node *caller; - methodinfo *callee; - inline_site site; -}; - - -/* prototypes *****************************************************************/ - -static bool inline_analyse_code(inline_node *iln); -static void inline_post_process(jitdata *jd); - - -/* debug helpers **************************************************************/ - -#if !defined(NDEBUG) -#include "inline_debug.inc" -#endif - - -/* statistics *****************************************************************/ - -/*#define INLINE_STATISTICS*/ - -#if !defined(NDEBUG) -#define INLINE_STATISTICS -#endif - -#if defined(INLINE_STATISTICS) -int inline_stat_roots = 0; -int inline_stat_roots_transformed = 0; -int inline_stat_inlined_nodes = 0; -int inline_stat_max_depth = 0; - -void inline_print_stats() -{ - printf("inlining statistics:\n"); - printf(" roots analysed : %d\n", inline_stat_roots); - printf(" roots transformed: %d\n", inline_stat_roots_transformed); - printf(" inlined nodes : %d\n", inline_stat_inlined_nodes); - printf(" max depth : %d\n", inline_stat_max_depth); -} -#endif - - -/* compilation of callees *****************************************************/ - -static bool inline_jit_compile_intern(jitdata *jd) -{ - methodinfo *m; - - /* XXX should share code with jit.c */ - - assert(jd); - - /* XXX initialize the static function's class */ - - m = jd->m; - - /* call the compiler passes ***********************************************/ - - /* call parse pass */ - - DOLOG( log_message_class("Parsing ", m->clazz) ); - if (!parse(jd)) { - return false; - } - - /* call stack analysis pass */ - - if (!stack_analyse(jd)) { - return false; - } - - return true; -} - - -static bool inline_jit_compile(inline_node *iln) -{ - bool r; - methodinfo *m; - jitdata *jd; - - /* XXX should share code with jit.c */ - - assert(iln); - m = iln->m; - assert(m); - - /* enter a monitor on the method */ - - Mutex_lock(m->mutex); - - /* allocate jitdata structure and fill it */ - - jd = jit_jitdata_new(m); - iln->jd = jd; - - jd->flags = 0; /* XXX */ - - /* initialize the register allocator */ - - reg_setup(jd); - - /* setup the codegendata memory */ - - /* XXX do a pseudo setup */ - jd->cd = DNEW(codegendata); - MZERO(jd->cd, codegendata, 1); - jd->cd->method = m; - /* XXX uses too much dump memory codegen_setup(jd); */ - - /* now call internal compile function */ - - r = inline_jit_compile_intern(jd); - - if (r) { - iln->regdata = jd->rd; - } - - /* free some memory */ -#if 0 - -#if defined(ENABLE_JIT) -# if defined(ENABLE_INTRP) - if (!opt_intrp) -# endif - codegen_free(jd); -#endif - -#endif - - /* leave the monitor */ - - Mutex_unlock(m->mutex); - - return r; -} - - -/* inlining tree handling *****************************************************/ - -static void inline_insert_inline_node(inline_node *parent, inline_node *child) -{ - inline_node *first; - inline_node *succ; - - assert(parent && child); - - child->parent = parent; - - child->debugnr = parent->ctx->next_debugnr++; /* XXX debug */ - - first = parent->children; - if (!first) { - /* insert as only node */ - parent->children = child; - child->next = child; - child->prev = child; - return; - } - - /* {there is at least one child already there} */ - - /* XXX is this search necessary, or could we always add at the end? */ - - succ = first; - while (succ->callerpc < child->callerpc) { - succ = succ->next; - if (succ == first) { - /* insert as last node */ - child->prev = first->prev; - child->next = first; - child->prev->next = child; - child->next->prev = child; - return; - } - } - - assert(succ->callerpc > child->callerpc); - - /* insert before succ */ - - child->prev = succ->prev; - child->next = succ; - child->prev->next = child; - child->next->prev = child; - - if (parent->children == succ) - parent->children = child; -} - - -static void inline_remove_inline_node(inline_node *parent, inline_node *child) -{ - assert(parent); - assert(child); - assert(child->parent == parent); - - if (child->prev == child) { - /* remove the only child node */ - parent->children = NULL; - } - else { - child->prev->next = child->next; - child->next->prev = child->prev; - - if (parent->children == child) - parent->children = child->next; - } -} - - -/* inlining candidate handling ************************************************/ - -#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) -static void inline_add_candidate(inline_context *ctx, - inline_node *caller, - methodinfo *callee, - inline_site *site) -{ - inline_candidate **link; - inline_candidate *cand; - - cand = DNEW(inline_candidate); -#if defined(INLINE_DIVIDE_COST_BY_FREQ) - cand->freq = INLINE_COUNTDOWN_INIT - callee->hitcountdown; - if (cand->freq < 1) -#endif - cand->freq = 1; -#if defined(INLINE_KNAPSACK) - cand->cost = callee->jcodelength + INLINE_COST_OFFSET; -#endif -#if defined(INLINE_BREADTH_FIRST) - cand->cost = caller->depth; -#endif - cand->caller = caller; - cand->callee = callee; - cand->site = *site; - - cand->weight = (double)cand->cost / cand->freq; - - for (link = &(ctx->candidates); ; link = &((*link)->next)) { - if (!*link || (*link)->weight > cand->weight) { - cand->next = *link; - *link = cand; - break; - } - } -} -#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */ - -#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) -static inline_candidate * inline_pick_best_candidate(inline_context *ctx) -{ - inline_candidate *cand; - - cand = ctx->candidates; - - if (cand) - ctx->candidates = cand->next; - - return cand; -} -#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */ - -#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) -static void inline_candidate_println(inline_candidate *cand) -{ - printf("%10g (%5d / %5d) depth %2d ", - cand->weight, cand->cost, cand->freq, cand->caller->depth + 1); - method_println(cand->callee); -} -#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */ - - -#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) -static void inline_candidates_println(inline_context *ctx) -{ - inline_candidate *cand; - - for (cand = ctx->candidates; cand != NULL; cand = cand->next) { - printf(" "); - inline_candidate_println(cand); - } -} -#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */ - - -/* variable handling **********************************************************/ - -static s4 inline_new_variable(jitdata *jd, s4 type, s4 flags) -{ - s4 index; - s4 newcount; - - index = jd->vartop++; - if (index >= jd->varcount) { - newcount = jd->vartop * 2; /* XXX */ - jd->var = DMREALLOC(jd->var, varinfo, jd->varcount, newcount); - MZERO(jd->var + jd->varcount, varinfo, (newcount - jd->varcount)); - jd->varcount = newcount; - } - - jd->var[index].type = type; - jd->var[index].flags = flags; - - return index; -} - - -static s4 inline_new_variable_clone(jitdata *jd, jitdata *origjd, s4 origidx) -{ - varinfo *v; - s4 newidx; - - v = &(origjd->var[origidx]); - - newidx = inline_new_variable(jd, v->type, v->flags); - - jd->var[newidx].vv = v->vv; - - return newidx; -} - - -static s4 inline_new_temp_variable(jitdata *jd, s4 type) -{ - return inline_new_variable(jd, type, 0); -} - - -static s4 inline_translate_variable(jitdata *jd, jitdata *origjd, s4 *varmap, s4 index) -{ - s4 idx; - - idx = varmap[index]; - - if (idx < 0) { - idx = inline_new_variable_clone(jd, origjd, index); - varmap[index] = idx; - } - - return idx; -} - - -static s4 *create_variable_map(inline_node *callee) -{ - s4 *varmap; - s4 i, t; - s4 varindex; - s4 n_javaindex; - s4 avail; - varinfo *v; - - /* create the variable mapping */ - - varmap = DMNEW(s4, callee->jd->varcount); - for (i=0; ijd->varcount; ++i) - varmap[i] = -1; - - /* translate local variables */ - - for (i=0; im->maxlocals; ++i) { - for (t=0; t<5; ++t) { - varindex = callee->jd->local_map[5*i + t]; - if (varindex == UNUSED) - continue; - - v = &(callee->jd->var[varindex]); - assert(v->type == t || v->type == TYPE_VOID); /* XXX stack leaves VOID */ - v->type = t; /* XXX restore if it is TYPE_VOID */ - - avail = callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t]; - - if (avail == UNUSED) { - avail = inline_new_variable_clone(callee->ctx->resultjd, callee->jd, varindex); - callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t] = avail; - } - - varmap[varindex] = avail; - } - } - - /* for synchronized instance methods we need an extra local */ - - if (callee->synchronize && !(callee->m->flags & ACC_STATIC)) { - n_javaindex = callee->localsoffset - 1; - assert(n_javaindex >= 0); - assert(callee->parent); - assert(n_javaindex == callee->parent->localsoffset + callee->parent->m->maxlocals); - - avail = callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR]; - - if (avail == UNUSED) { - avail = inline_new_variable(callee->ctx->resultjd, TYPE_ADR, 0); - callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR] = avail; - } - - callee->synclocal = avail; - } - else { - callee->synclocal = UNUSED; - } - - return varmap; -} - - -/* basic block translation ****************************************************/ - -#define INLINE_RETURN_REFERENCE(callee) \ - ( (basicblock *) (ptrint) (0x333 + (callee)->depth) ) - - -static void inline_add_block_reference(inline_node *iln, basicblock **blockp) -{ - inline_target_ref *ref; - - ref = DNEW(inline_target_ref); - ref->ref.block = blockp; - ref->isnumber = false; - ref->next = iln->refs; - iln->refs = ref; -} - - -#if 0 -static void inline_add_blocknr_reference(inline_node *iln, s4 *nrp) -{ - inline_target_ref *ref; - - ref = DNEW(inline_target_ref); - ref->ref.nr = nrp; - ref->isnumber = true; - ref->next = iln->refs; - iln->refs = ref; -} -#endif - - -static void inline_block_translation(inline_node *iln, basicblock *o_bptr, basicblock *n_bptr) -{ - inline_context *ctx; - - ctx = iln->ctx; - assert(ctx->blockmap_index < ctx->master->cumul_blockmapcount); - - ctx->blockmap[ctx->blockmap_index].iln = iln; - ctx->blockmap[ctx->blockmap_index].o_block = o_bptr; - ctx->blockmap[ctx->blockmap_index].n_block = n_bptr; - - ctx->blockmap_index++; -} - - -static basicblock * inline_map_block(inline_node *iln, - basicblock *o_block, - inline_node *targetiln) -{ - inline_block_map *bm; - inline_block_map *bmend; - - assert(iln); - assert(targetiln); - - if (!o_block) - return NULL; - - bm = iln->ctx->blockmap; - bmend = bm + iln->ctx->blockmap_index; - - while (bm < bmend) { - assert(bm->iln && bm->o_block && bm->n_block); - if (bm->o_block == o_block && bm->iln == targetiln) - return bm->n_block; - bm++; - } - - assert(false); - return NULL; /* not reached */ -} - - -static void inline_resolve_block_refs(inline_target_ref **refs, - basicblock *o_bptr, - basicblock *n_bptr, - bool returnref) -{ - inline_target_ref *ref; - inline_target_ref *prev; - - prev = NULL; - for (ref = *refs; ref != NULL; ref = ref->next) { - if (ref->isnumber && !returnref) { - if (*(ref->ref.nr) == JAVALOCAL_FROM_RETADDR(o_bptr->nr)) { - *(ref->ref.nr) = JAVALOCAL_FROM_RETADDR(n_bptr->nr); - goto remove_ref; - } - } - else { - if (*(ref->ref.block) == o_bptr) { - *(ref->ref.block) = n_bptr; - goto remove_ref; - } - } - - /* skip this ref */ - - prev = ref; - continue; - -remove_ref: - /* remove this ref */ - - if (prev) { - prev->next = ref->next; - } - else { - *refs = ref->next; - } - } -} - - -/* basic block creation *******************************************************/ - -static basicblock * create_block(inline_node *container, - inline_node *iln, - inline_node *inner, - int indepth) -{ - basicblock *n_bptr; - inline_node *outer; - s4 i; - s4 depth; - s4 varidx; - s4 newvaridx; - - assert(container); - assert(iln); - assert(inner); - assert(indepth >= 0); - - n_bptr = container->inlined_basicblocks_cursor++; - assert(n_bptr); - assert((n_bptr - container->inlined_basicblocks) < container->cumul_basicblockcount); - - BASICBLOCK_INIT(n_bptr, iln->m); - - n_bptr->iinstr = container->inlined_iinstr_cursor; - n_bptr->next = n_bptr + 1; - n_bptr->nr = container->ctx->next_block_number++; - n_bptr->indepth = indepth; - n_bptr->flags = BBFINISHED; /* XXX */ - - /* set the inlineinfo of the new block */ - - if (iln->inline_start_instruction) - n_bptr->inlineinfo = iln->inline_start_instruction->sx.s23.s3.inlineinfo; - - if (indepth > container->ctx->maxinoutdepth) - container->ctx->maxinoutdepth = indepth; - - if (indepth) { - n_bptr->invars = DMNEW(s4, indepth); - - - for (i=0; iinvars[i] = -1; /* XXX debug */ - - /* pass-through variables enter the block */ - - outer = inner->parent; - while (outer != NULL) { - depth = outer->n_passthroughcount; - - assert(depth + inner->n_selfpassthroughcount <= indepth); - - for (i=0; in_selfpassthroughcount; ++i) { - varidx = inner->n_passthroughvars[i]; - newvaridx = - inline_new_variable_clone(container->ctx->resultjd, - outer->jd, - varidx); - n_bptr->invars[depth + i] = newvaridx; - outer->varmap[varidx] = newvaridx; - } - inner = outer; - outer = outer->parent; - } - } - else { - n_bptr->invars = NULL; - } - - /* XXX for the verifier. should not be here */ - - { - varinfo *dv; - - dv = DMNEW(varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS); - MZERO(dv, varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS); - n_bptr->inlocals = dv; - } - - return n_bptr; -} - - -static s4 *translate_javalocals(inline_node *iln, s4 *javalocals) -{ - s4 *jl; - s4 i, j; - - jl = DMNEW(s4, iln->jd->maxlocals); - - for (i=0; ijd->maxlocals; ++i) { - j = javalocals[i]; - if (j > UNUSED) - j = inline_translate_variable(iln->ctx->resultjd, iln->jd, iln->varmap, j); - jl[i] = j; - -#if 0 - if (j < UNUSED) { - /* an encoded returnAddress value - must be relocated */ - inline_add_blocknr_reference(iln, &(jl[i])); - } -#endif - } - - return jl; -} - - -static basicblock * create_body_block(inline_node *iln, - basicblock *o_bptr, s4 *varmap) -{ - basicblock *n_bptr; - s4 i; - - n_bptr = create_block(iln, iln, iln, - o_bptr->indepth + iln->n_passthroughcount); - - n_bptr->type = o_bptr->type; - n_bptr->flags = o_bptr->flags; - n_bptr->bitflags = o_bptr->bitflags; - - /* resolve references to this block */ - - inline_resolve_block_refs(&(iln->refs), o_bptr, n_bptr, false); - - /* translate the invars of the original block */ - - for (i=0; iindepth; ++i) { - n_bptr->invars[iln->n_passthroughcount + i] = - inline_translate_variable(iln->ctx->resultjd, iln->jd, - varmap, - o_bptr->invars[i]); - } - - /* translate javalocals info (not for dead code) */ - - if (n_bptr->flags >= BBREACHED) - n_bptr->javalocals = translate_javalocals(iln, o_bptr->javalocals); - - return n_bptr; -} - - -static basicblock * create_epilog_block(inline_node *caller, inline_node *callee, s4 *varmap) -{ - basicblock *n_bptr; - s4 retcount; - s4 idx; - - /* number of return variables */ - - retcount = (callee->n_resultlocal == -1 - && callee->m->parseddesc->returntype.type != TYPE_VOID) ? 1 : 0; - - /* start the epilog block */ - - n_bptr = create_block(caller, caller, callee, - callee->n_passthroughcount + retcount); - - /* resolve references to the return block */ - - inline_resolve_block_refs(&(callee->refs), - INLINE_RETURN_REFERENCE(callee), - n_bptr, - true); - - /* return variable */ - - if (retcount) { - idx = inline_new_variable(caller->ctx->resultjd, - callee->m->parseddesc->returntype.type, 0 /* XXX */); - n_bptr->invars[callee->n_passthroughcount] = idx; - varmap[callee->callerins->dst.varindex] = idx; - } - - /* set javalocals */ - - n_bptr->javalocals = DMNEW(s4, caller->jd->maxlocals); - MCOPY(n_bptr->javalocals, caller->javalocals, s4, caller->jd->maxlocals); - - /* set block flags & type */ - - n_bptr->flags = /* XXX original block flags */ BBFINISHED; - n_bptr->type = BBTYPE_STD; - - return n_bptr; -} - - -static void close_block(inline_node *iln, inline_node *inner, basicblock *n_bptr, s4 outdepth) -{ - inline_node *outer; - s4 i; - s4 depth; - s4 varidx; - - n_bptr->outdepth = outdepth; - n_bptr->outvars = DMNEW(s4, outdepth); - - for (i=0; ioutvars[i] = 0; /* XXX debug */ - - if (outdepth > iln->ctx->maxinoutdepth) - iln->ctx->maxinoutdepth = outdepth; - - /* pass-through variables leave the block */ - - outer = inner->parent; - while (outer != NULL) { - depth = outer->n_passthroughcount; - - assert(depth + inner->n_selfpassthroughcount <= outdepth); - - for (i=0; in_selfpassthroughcount; ++i) { - varidx = inner->n_passthroughvars[i]; - n_bptr->outvars[depth + i] = - inline_translate_variable(iln->ctx->resultjd, - outer->jd, - outer->varmap, - varidx); - } - inner = outer; - outer = outer->parent; - } -} - - -static void close_prolog_block(inline_node *iln, - basicblock *n_bptr, - inline_node *nextcall) -{ - /* XXX add original outvars! */ - close_block(iln, nextcall, n_bptr, nextcall->n_passthroughcount); - - /* pass-through variables */ - - DOLOG( printf("closed prolog block:\n"); - show_basicblock(iln->ctx->resultjd, n_bptr, SHOW_STACK); ); -} - - -static void close_body_block(inline_node *iln, - basicblock *n_bptr, - basicblock *o_bptr, - s4 *varmap, - s4 retcount, - s4 retidx) -{ - s4 i; - - close_block(iln, iln, n_bptr, iln->n_passthroughcount + o_bptr->outdepth + retcount); - - /* translate the outvars of the original block */ - - /* XXX reuse code */ - for (i=0; ioutdepth; ++i) { - n_bptr->outvars[iln->n_passthroughcount + i] = - inline_translate_variable(iln->ctx->resultjd, iln->jd, varmap, - o_bptr->outvars[i]); - } - - /* set the return variable, if any */ - - if (retcount) { - assert(retidx >= 0); - n_bptr->outvars[iln->n_passthroughcount + o_bptr->outdepth] = retidx; - } -} - - -/* inlined code generation ****************************************************/ - -static instruction * inline_instruction(inline_node *iln, - s4 opcode, - instruction *o_iptr) -{ - instruction *n_iptr; - - n_iptr = (iln->inlined_iinstr_cursor++); - assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); - - n_iptr->opc = opcode; - n_iptr->flags.bits = o_iptr->flags.bits & INS_FLAG_ID_MASK; - n_iptr->line = o_iptr->line; - - return n_iptr; -} - -static void inline_generate_sync_builtin(inline_node *iln, - inline_node *callee, - instruction *o_iptr, - s4 instancevar, - functionptr func) -{ - int syncvar; - instruction *n_ins; - - if (callee->m->flags & ACC_STATIC) { - /* ACONST */ - syncvar = inline_new_temp_variable(iln->ctx->resultjd, TYPE_ADR); - - n_ins = inline_instruction(iln, ICMD_ACONST, o_iptr); - n_ins->sx.val.c.cls = callee->m->clazz; - n_ins->dst.varindex = syncvar; - n_ins->flags.bits |= INS_FLAG_CLASS; - } - else { - syncvar = instancevar; - } - - assert(syncvar != UNUSED); - - /* MONITORENTER / MONITOREXIT */ - - n_ins = inline_instruction(iln, ICMD_BUILTIN, o_iptr); - n_ins->sx.s23.s3.bte = builtintable_get_internal(func); - n_ins->s1.argcount = 1; /* XXX add through-vars */ - n_ins->sx.s23.s2.args = DMNEW(s4, 1); - n_ins->sx.s23.s2.args[0] = syncvar; -} - -static s4 emit_inlining_prolog(inline_node *iln, - inline_node *callee, - instruction *o_iptr, - s4 *varmap) -{ - methodinfo *calleem; - methoddesc *md; - int i; - int localindex; - int type; - instruction *n_ins; - insinfo_inline *insinfo; - s4 varindex; - - assert(iln && callee && o_iptr); - - calleem = callee->m; - md = calleem->parseddesc; - - /* INLINE_START instruction */ - - n_ins = inline_instruction(iln, ICMD_INLINE_START, o_iptr); - - insinfo = DNEW(insinfo_inline); - insinfo->method = callee->m; - insinfo->outer = iln->m; - insinfo->synclocal = callee->synclocal; - insinfo->synchronize = callee->synchronize; - insinfo->javalocals_start = NULL; - insinfo->javalocals_end = NULL; - - /* info about stack vars live at the INLINE_START */ - - insinfo->throughcount = callee->n_passthroughcount; - insinfo->paramcount = md->paramcount; - insinfo->stackvarscount = o_iptr->s1.argcount; - insinfo->stackvars = DMNEW(s4, insinfo->stackvarscount); - for (i=0; istackvarscount; ++i) - insinfo->stackvars[i] = iln->varmap[o_iptr->sx.s23.s2.args[i]]; - - /* info about the surrounding inlining */ - - if (iln->inline_start_instruction) - insinfo->parent = iln->inline_start_instruction->sx.s23.s3.inlineinfo; - else - insinfo->parent = NULL; - - /* finish the INLINE_START instruction */ - - n_ins->sx.s23.s3.inlineinfo = insinfo; - callee->inline_start_instruction = n_ins; - - DOLOG( printf("%sprolog: ", iln->indent); - show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); - - /* handle parameters for the inlined callee */ - - localindex = callee->localsoffset + md->paramslots; - - for (i=md->paramcount-1; i>=0; --i) { - assert(iln); - - type = md->paramtypes[i].type; - - localindex -= IS_2_WORD_TYPE(type) ? 2 : 1; - assert(callee->regdata); - - /* translate the argument variable */ - - varindex = varmap[o_iptr->sx.s23.s2.args[i]]; - assert(varindex != UNUSED); - - /* remove preallocation from the argument variable */ - - iln->ctx->resultjd->var[varindex].flags &= ~(PREALLOC | INMEMORY); - - /* check the instance slot against NULL */ - /* we don't need that for methods, as the verifier */ - /* ensures that they are only called for an uninit. object */ - /* (which may not be NULL). */ - - if (!callee->isstatic && i == 0 && calleem->name != utf_init) { - assert(type == TYPE_ADR); - n_ins = inline_instruction(iln, ICMD_CHECKNULL, o_iptr); - n_ins->s1.varindex = varindex; - n_ins->dst.varindex = n_ins->s1.varindex; - } - - /* store argument into local variable of inlined callee */ - - if (callee->jd->local_map[5*(localindex - callee->localsoffset) + type] != UNUSED) - { - /* this value is used in the callee */ - - if (i == 0 && callee->synclocal != UNUSED) { - /* we also need it for synchronization, so copy it */ - assert(type == TYPE_ADR); - n_ins = inline_instruction(iln, ICMD_COPY, o_iptr); - } - else { - n_ins = inline_instruction(iln, ICMD_ISTORE + type, o_iptr); - n_ins->sx.s23.s3.javaindex = UNUSED; - } - n_ins->s1.varindex = varindex; - n_ins->dst.varindex = iln->ctx->resultjd->local_map[5*localindex + type]; - assert(n_ins->dst.varindex != UNUSED); - } - else if (i == 0 && callee->synclocal != UNUSED) { - /* the value is not used inside the callee, but we need it for */ - /* synchronization */ - /* XXX In this case it actually makes no sense to create a */ - /* separate synchronization variable. */ - - n_ins = inline_instruction(iln, ICMD_NOP, o_iptr); - } - else { - /* this value is not used, pop it */ - - n_ins = inline_instruction(iln, ICMD_POP, o_iptr); - n_ins->s1.varindex = varindex; - } - - DOLOG( printf("%sprolog: ", iln->indent); - show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); - } - - /* COPY for synchronized instance methods */ - - if (callee->synclocal != UNUSED) { - n_ins = inline_instruction(iln, ICMD_COPY, o_iptr); - n_ins->s1.varindex = varmap[o_iptr->sx.s23.s2.args[0]]; - n_ins->dst.varindex = callee->synclocal; - - assert(n_ins->s1.varindex != UNUSED); - } - - if (callee->synchronize) { - inline_generate_sync_builtin(iln, callee, o_iptr, - (callee->isstatic) ? UNUSED : varmap[o_iptr->sx.s23.s2.args[0]], - LOCK_monitor_enter); - } - - /* INLINE_BODY instruction */ - - n_ins = inline_instruction(iln, ICMD_INLINE_BODY, callee->jd->basicblocks[0].iinstr); - n_ins->sx.s23.s3.inlineinfo = insinfo; - - return 0; /* XXX */ -} - - -static void emit_inlining_epilog(inline_node *iln, inline_node *callee, instruction *o_iptr) -{ - instruction *n_ins; - s4 *jl; - - assert(iln && callee && o_iptr); - assert(callee->inline_start_instruction); - - if (callee->synchronize) { - inline_generate_sync_builtin(iln, callee, o_iptr, - callee->synclocal, - LOCK_monitor_exit); - } - - /* INLINE_END instruction */ - - n_ins = inline_instruction(iln, ICMD_INLINE_END, o_iptr); - n_ins->sx.s23.s3.inlineinfo = callee->inline_start_instruction->sx.s23.s3.inlineinfo; - - /* set the javalocals */ - - jl = DMNEW(s4, iln->jd->maxlocals); - MCOPY(jl, iln->javalocals, s4, iln->jd->maxlocals); - n_ins->sx.s23.s3.inlineinfo->javalocals_end = jl; - - DOLOG( printf("%sepilog: ", iln->indent); - show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); -} - - -#define TRANSLATE_VAROP(vo) \ - n_iptr->vo.varindex = inline_translate_variable(jd, origjd, varmap, n_iptr->vo.varindex) - - -static void inline_clone_instruction(inline_node *iln, - jitdata *jd, - jitdata *origjd, - s4 *varmap, - instruction *o_iptr, - instruction *n_iptr) -{ - icmdtable_entry_t *icmdt; - builtintable_entry *bte; - methoddesc *md; - s4 i, j; - branch_target_t *table; - lookup_target_t *lookup; - inline_node *scope; - - *n_iptr = *o_iptr; - - icmdt = &(icmd_table[o_iptr->opc]); - - switch (icmdt->dataflow) { - case DF_0_TO_0: - break; - - case DF_3_TO_0: - TRANSLATE_VAROP(sx.s23.s3); - case DF_2_TO_0: - TRANSLATE_VAROP(sx.s23.s2); - case DF_1_TO_0: - TRANSLATE_VAROP(s1); - break; - - case DF_2_TO_1: - TRANSLATE_VAROP(sx.s23.s2); - case DF_1_TO_1: - case DF_COPY: - case DF_MOVE: - TRANSLATE_VAROP(s1); - case DF_0_TO_1: - TRANSLATE_VAROP(dst); - break; - - case DF_N_TO_1: - n_iptr->sx.s23.s2.args = DMNEW(s4, n_iptr->s1.argcount); - for (i=0; is1.argcount; ++i) { - n_iptr->sx.s23.s2.args[i] = - inline_translate_variable(jd, origjd, varmap, - o_iptr->sx.s23.s2.args[i]); - } - TRANSLATE_VAROP(dst); - break; - - case DF_INVOKE: - INSTRUCTION_GET_METHODDESC(n_iptr, md); -clone_call: - n_iptr->s1.argcount += iln->n_passthroughcount; - n_iptr->sx.s23.s2.args = DMNEW(s4, n_iptr->s1.argcount); - for (i=0; is1.argcount; ++i) { - n_iptr->sx.s23.s2.args[i] = - inline_translate_variable(jd, origjd, varmap, - o_iptr->sx.s23.s2.args[i]); - } - for (scope = iln; scope != NULL; scope = scope->parent) { - for (j = 0; j < scope->n_selfpassthroughcount; ++j) { - n_iptr->sx.s23.s2.args[i++] = - scope->parent->varmap[scope->n_passthroughvars[j]]; - } - } - if (md->returntype.type != TYPE_VOID) - TRANSLATE_VAROP(dst); - break; - - case DF_BUILTIN: - bte = n_iptr->sx.s23.s3.bte; - md = bte->md; - goto clone_call; - - default: - assert(0); - } - - switch (icmdt->controlflow) { - case CF_RET: - TRANSLATE_VAROP(s1); /* XXX should be handled by data-flow */ - /* FALLTHROUGH */ - case CF_IF: - case CF_GOTO: - inline_add_block_reference(iln, &(n_iptr->dst.block)); - break; - - case CF_JSR: - inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.jsrtarget.block)); - break; - - case CF_TABLE: - i = n_iptr->sx.s23.s3.tablehigh - n_iptr->sx.s23.s2.tablelow + 1 + 1 /* default */; - - table = DMNEW(branch_target_t, i); - MCOPY(table, o_iptr->dst.table, branch_target_t, i); - n_iptr->dst.table = table; - - while (--i >= 0) { - inline_add_block_reference(iln, &(table->block)); - table++; - } - break; - - case CF_LOOKUP: - inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.lookupdefault.block)); - - i = n_iptr->sx.s23.s2.lookupcount; - lookup = DMNEW(lookup_target_t, i); - MCOPY(lookup, o_iptr->dst.lookup, lookup_target_t, i); - n_iptr->dst.lookup = lookup; - - while (--i >= 0) { - inline_add_block_reference(iln, &(lookup->target.block)); - lookup++; - } - break; - } - - /* XXX move this to dataflow section? */ - - switch (n_iptr->opc) { - case ICMD_ASTORE: -#if 0 - if (n_iptr->flags.bits & INS_FLAG_RETADDR) - inline_add_blocknr_reference(iln, &(n_iptr->sx.s23.s2.retaddrnr)); -#endif - /* FALLTHROUGH! */ - case ICMD_ISTORE: - case ICMD_LSTORE: - case ICMD_FSTORE: - case ICMD_DSTORE: - stack_javalocals_store(n_iptr, iln->javalocals); - break; - } -} - - -static void inline_rewrite_method(inline_node *iln) -{ - basicblock *o_bptr; - s4 len; - instruction *o_iptr; - instruction *n_iptr; - inline_node *nextcall; - basicblock *n_bptr; - inline_block_map *bm; - int i; - int icount; - jitdata *resultjd; - jitdata *origjd; - char indent[100]; /* XXX debug */ - s4 retcount; - s4 retidx; - - assert(iln); - - resultjd = iln->ctx->resultjd; - origjd = iln->jd; - - n_bptr = NULL; - nextcall = iln->children; - - /* XXX debug */ - for (i=0; idepth; ++i) - indent[i] = '\t'; - indent[i] = 0; - iln->indent = indent; - - DOLOG( printf("%srewriting: ", indent); method_println(iln->m); - printf("%s(passthrough: %d+%d)\n", - indent, iln->n_passthroughcount - iln->n_selfpassthroughcount, - iln->n_passthroughcount); ); - - /* set memory cursors */ - - iln->inlined_iinstr_cursor = iln->inlined_iinstr; - iln->inlined_basicblocks_cursor = iln->inlined_basicblocks; - - /* allocate temporary buffers */ - - iln->javalocals = DMNEW(s4, iln->jd->maxlocals); - - /* loop over basic blocks */ - - o_bptr = iln->jd->basicblocks; - for (; o_bptr; o_bptr = o_bptr->next) { - - if (o_bptr->flags < BBREACHED) { - - /* ignore the dummy end block */ - - if (o_bptr->icount == 0 && o_bptr->next == NULL) { - /* enter the following block as translation, for exception handler ranges */ - inline_block_translation(iln, o_bptr, iln->inlined_basicblocks_cursor); - continue; - } - - DOLOG( - printf("%s* skipping old L%03d (flags=%d, type=%d, oid=%d) of ", - indent, - o_bptr->nr, o_bptr->flags, o_bptr->type, - o_bptr->indepth); - method_println(iln->m); - ); - - n_bptr = create_body_block(iln, o_bptr, iln->varmap); - - /* enter it in the blockmap */ - - inline_block_translation(iln, o_bptr, n_bptr); - - close_body_block(iln, n_bptr, o_bptr, iln->varmap, 0, -1); - continue; - } - - len = o_bptr->icount; - o_iptr = o_bptr->iinstr; - - DOLOG( - printf("%s* rewriting old L%03d (flags=%d, type=%d, oid=%d) of ", - indent, - o_bptr->nr, o_bptr->flags, o_bptr->type, - o_bptr->indepth); - method_println(iln->m); - show_basicblock(iln->jd, o_bptr, SHOW_STACK); - ); - - if (iln->blockbefore || o_bptr != iln->jd->basicblocks) { - /* create an inlined clone of this block */ - - n_bptr = create_body_block(iln, o_bptr, iln->varmap); - icount = 0; - - /* enter it in the blockmap */ - - inline_block_translation(iln, o_bptr, n_bptr); - - /* initialize the javalocals */ - - MCOPY(iln->javalocals, n_bptr->javalocals, s4, iln->jd->maxlocals); - } - else { - s4 *jl; - - /* continue caller block */ - - n_bptr = iln->inlined_basicblocks_cursor - 1; - icount = n_bptr->icount; - - /* translate the javalocals */ - - jl = translate_javalocals(iln, o_bptr->javalocals); - iln->inline_start_instruction->sx.s23.s3.inlineinfo->javalocals_start = jl; - - MCOPY(iln->javalocals, jl, s4, iln->jd->maxlocals); - } - - /* iterate over the ICMDs of this block */ - - retcount = 0; - retidx = UNUSED; - - while (--len >= 0) { - - DOLOG( fputs(indent, stdout); show_icmd(iln->jd, o_iptr, false, SHOW_STACK); - printf("\n") ); - - /* handle calls that will be inlined */ - - if (nextcall && o_iptr == nextcall->callerins) { - - /* write the inlining prolog */ - - (void) emit_inlining_prolog(iln, nextcall, o_iptr, iln->varmap); - icount += nextcall->prolog_instructioncount; - - /* end current block, or glue blocks together */ - - n_bptr->icount = icount; - - if (nextcall->blockbefore) { - close_prolog_block(iln, n_bptr, nextcall); - } - else { - /* XXX */ - } - - /* check if the result is a local variable */ - - if (nextcall->m->parseddesc->returntype.type != TYPE_VOID - && o_iptr->dst.varindex < iln->jd->localcount) - { - nextcall->n_resultlocal = iln->varmap[o_iptr->dst.varindex]; - } - else - nextcall->n_resultlocal = -1; - - /* set memory pointers in the callee */ - - nextcall->inlined_iinstr = iln->inlined_iinstr_cursor; - nextcall->inlined_basicblocks = iln->inlined_basicblocks_cursor; - - /* recurse */ - - DOLOG( printf("%sentering inline ", indent); - show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") ); - - inline_rewrite_method(nextcall); - - DOLOG( printf("%sleaving inline ", indent); - show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") ); - - /* update memory cursors */ - - assert(nextcall->inlined_iinstr_cursor - <= iln->inlined_iinstr_cursor + nextcall->cumul_instructioncount); - assert(nextcall->inlined_basicblocks_cursor - == iln->inlined_basicblocks_cursor + nextcall->cumul_basicblockcount); - iln->inlined_iinstr_cursor = nextcall->inlined_iinstr_cursor; - iln->inlined_basicblocks_cursor = nextcall->inlined_basicblocks_cursor; - - /* start new block, or glue blocks together */ - - if (nextcall->blockafter) { - n_bptr = create_epilog_block(iln, nextcall, iln->varmap); - icount = 0; - } - else { - n_bptr = iln->inlined_basicblocks_cursor - 1; - icount = n_bptr->icount; - /* XXX */ - } - - /* emit inlining epilog */ - - emit_inlining_epilog(iln, nextcall, o_iptr); - icount += nextcall->epilog_instructioncount; - - /* proceed to next call */ - - nextcall = nextcall->next; - } - else { - n_iptr = (iln->inlined_iinstr_cursor++); - assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); - - switch (o_iptr->opc) { - case ICMD_RETURN: - if (iln->depth == 0) - goto default_clone; - goto return_tail; - - case ICMD_IRETURN: - case ICMD_ARETURN: - case ICMD_LRETURN: - case ICMD_FRETURN: - case ICMD_DRETURN: - if (iln->depth == 0) - goto default_clone; - retcount = 1; - retidx = iln->varmap[o_iptr->s1.varindex]; - if (iln->n_resultlocal != -1) { - /* store result in a local variable */ - - DOLOG( printf("USING RESULTLOCAL %d\n", iln->n_resultlocal); ); - /* This relies on the same sequence of types for */ - /* ?STORE and ?RETURN opcodes. */ - n_iptr->opc = ICMD_ISTORE + (o_iptr->opc - ICMD_IRETURN); - n_iptr->s1.varindex = retidx; - n_iptr->dst.varindex = iln->n_resultlocal; - n_iptr->sx.s23.s3.javaindex = UNUSED; /* XXX set real javaindex? */ - - retcount = 0; - retidx = UNUSED; - - n_iptr = (iln->inlined_iinstr_cursor++); - assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); - icount++; - } - else if ((retidx < resultjd->localcount && iln->blockafter) - || !iln->blockafter) /* XXX do we really always need the MOVE? */ - { - /* local must not become outvar, insert a MOVE */ - - n_iptr->opc = ICMD_MOVE; - n_iptr->s1.varindex = retidx; - retidx = inline_new_temp_variable(resultjd, - resultjd->var[retidx].type); - n_iptr->dst.varindex = retidx; - - n_iptr = (iln->inlined_iinstr_cursor++); - assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); - icount++; - } -return_tail: - if (iln->blockafter) { - n_iptr->opc = ICMD_GOTO; - n_iptr->dst.block = INLINE_RETURN_REFERENCE(iln); - inline_add_block_reference(iln, &(n_iptr->dst.block)); - } - else { - n_iptr->opc = ICMD_NOP; - } - break; -#if 0 - if (o_bptr->next == NULL || (o_bptr->next->icount==0 && o_bptr->next->next == NULL)) { - n_iptr->opc = ICMD_NOP; - break; - } - goto default_clone; - break; -#endif - - default: -default_clone: - inline_clone_instruction(iln, resultjd, iln->jd, iln->varmap, o_iptr, n_iptr); - } - - DOLOG( fputs(indent, stdout); show_icmd(resultjd, n_iptr, false, SHOW_STACK); - printf("\n");); - - icount++; - } - - o_iptr++; - } - - /* end of basic block */ - - if (iln->blockafter || (o_bptr->next && o_bptr->next->next)) { - close_body_block(iln, n_bptr, o_bptr, iln->varmap, retcount, retidx); - n_bptr->icount = icount; - - DOLOG( printf("closed body block:\n"); - show_basicblock(resultjd, n_bptr, SHOW_STACK); ); - } - else { - n_bptr->icount = icount; - assert(iln->parent); - if (retidx != UNUSED) - iln->parent->varmap[iln->callerins->dst.varindex] = retidx; - } - } - - bm = iln->ctx->blockmap; - for (i=0; ictx->blockmap_index; ++i, ++bm) { - assert(bm->iln && bm->o_block && bm->n_block); - if (bm->iln == iln) - inline_resolve_block_refs(&(iln->refs), bm->o_block, bm->n_block, false); - } - -#if !defined(NDEBUG) - if (iln->refs) { - inline_target_ref *ref; - ref = iln->refs; - while (ref) { - if (!iln->depth || ref->isnumber || *(ref->ref.block) != INLINE_RETURN_REFERENCE(iln)) { - DOLOG( printf("XXX REMAINING REF at depth %d: %p\n", iln->depth, - (void*)*(ref->ref.block)) ); - assert(false); - } - ref = ref->next; - } - } -#endif -} - - -static exception_entry * inline_exception_tables(inline_node *iln, - exception_entry *n_extable, - exception_entry **prevextable) -{ - inline_node *child; - inline_node *scope; - exception_entry *et; - - assert(iln); - assert(n_extable); - assert(prevextable); - - child = iln->children; - if (child) { - do { - n_extable = inline_exception_tables(child, n_extable, prevextable); - child = child->next; - } while (child != iln->children); - } - - et = iln->jd->exceptiontable; - for (; et != NULL; et = et->down) { - assert(et); - MZERO(n_extable, exception_entry, 1); - n_extable->start = inline_map_block(iln, et->start , iln); - n_extable->end = inline_map_block(iln, et->end , iln); - n_extable->handler = inline_map_block(iln, et->handler, iln); - n_extable->catchtype = et->catchtype; - - if (*prevextable) { - (*prevextable)->down = n_extable; - } - *prevextable = n_extable; - - n_extable++; - } - - if (iln->handler_monitorexit) { - exception_entry **activehandlers; - - MZERO(n_extable, exception_entry, 1); - n_extable->start = iln->inlined_basicblocks; - n_extable->end = iln->inlined_basicblocks_cursor; - n_extable->handler = iln->handler_monitorexit; - n_extable->catchtype.any = NULL; /* finally */ - - if (*prevextable) { - (*prevextable)->down = n_extable; - } - *prevextable = n_extable; - - n_extable++; - - /* We have to protect the created handler with the same handlers */ - /* that protect the method body itself. */ - - for (scope = iln; scope->parent != NULL; scope = scope->parent) { - - activehandlers = scope->o_handlers; - assert(activehandlers); - - while (*activehandlers) { - - assert(scope->parent); - - MZERO(n_extable, exception_entry, 1); - n_extable->start = iln->handler_monitorexit; - n_extable->end = iln->handler_monitorexit + 1; /* XXX ok in this case? */ - n_extable->handler = inline_map_block(scope->parent, - (*activehandlers)->handler, - scope->parent); - n_extable->catchtype = (*activehandlers)->catchtype; - - if (*prevextable) { - (*prevextable)->down = n_extable; - } - *prevextable = n_extable; - - n_extable++; - activehandlers++; - } - } - } - - return n_extable; -} - - -static void inline_locals(inline_node *iln) -{ - inline_node *child; - - assert(iln); - - iln->varmap = create_variable_map(iln); - - child = iln->children; - if (child) { - do { - inline_locals(child); - child = child->next; - } while (child != iln->children); - } - - if (iln->regdata->memuse > iln->ctx->resultjd->rd->memuse) - iln->ctx->resultjd->rd->memuse = iln->regdata->memuse; - if (iln->regdata->argintreguse > iln->ctx->resultjd->rd->argintreguse) - iln->ctx->resultjd->rd->argintreguse = iln->regdata->argintreguse; - if (iln->regdata->argfltreguse > iln->ctx->resultjd->rd->argfltreguse) - iln->ctx->resultjd->rd->argfltreguse = iln->regdata->argfltreguse; -} - - -static void inline_interface_variables(inline_node *iln) -{ - basicblock *bptr; - jitdata *resultjd; - s4 i; - varinfo *v; - - resultjd = iln->ctx->resultjd; - - resultjd->interface_map = DMNEW(interface_info, 5*iln->ctx->maxinoutdepth); - for (i=0; i<5*iln->ctx->maxinoutdepth; ++i) - resultjd->interface_map[i].flags = UNUSED; - - for (bptr = resultjd->basicblocks; bptr != NULL; bptr = bptr->next) { - assert(bptr->indepth <= iln->ctx->maxinoutdepth); - assert(bptr->outdepth <= iln->ctx->maxinoutdepth); - - for (i=0; iindepth; ++i) { - v = &(resultjd->var[bptr->invars[i]]); - v->flags |= INOUT; - if (v->type == TYPE_RET) - v->flags |= PREALLOC; - else - v->flags &= ~PREALLOC; - v->flags &= ~INMEMORY; - assert(bptr->invars[i] >= resultjd->localcount); - - if (resultjd->interface_map[5*i + v->type].flags == UNUSED) { - resultjd->interface_map[5*i + v->type].flags = v->flags; - } - else { - resultjd->interface_map[5*i + v->type].flags |= v->flags; - } - } - - for (i=0; ioutdepth; ++i) { - v = &(resultjd->var[bptr->outvars[i]]); - v->flags |= INOUT; - if (v->type == TYPE_RET) - v->flags |= PREALLOC; - else - v->flags &= ~PREALLOC; - v->flags &= ~INMEMORY; - assert(bptr->outvars[i] >= resultjd->localcount); - - if (resultjd->interface_map[5*i + v->type].flags == UNUSED) { - resultjd->interface_map[5*i + v->type].flags = v->flags; - } - else { - resultjd->interface_map[5*i + v->type].flags |= v->flags; - } - } - } -} - - -static void inline_write_exception_handlers(inline_node *master, inline_node *iln) -{ - basicblock *n_bptr; - instruction *n_ins; - inline_node *child; - builtintable_entry *bte; - s4 exvar; - s4 syncvar; - s4 i; - - child = iln->children; - if (child) { - do { - inline_write_exception_handlers(master, child); - child = child->next; - } while (child != iln->children); - } - - if (iln->synchronize) { - /* create the monitorexit handler */ - n_bptr = create_block(master, iln, iln, - iln->n_passthroughcount + 1); - n_bptr->type = BBTYPE_EXH; - n_bptr->flags = BBFINISHED; - - exvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0); - n_bptr->invars[iln->n_passthroughcount] = exvar; - - iln->handler_monitorexit = n_bptr; - - /* ACONST / ALOAD */ - - n_ins = master->inlined_iinstr_cursor++; - if (iln->m->flags & ACC_STATIC) { - n_ins->opc = ICMD_ACONST; - n_ins->sx.val.c.cls = iln->m->clazz; - n_ins->flags.bits = INS_FLAG_CLASS; - } - else { - n_ins->opc = ICMD_ALOAD; - n_ins->s1.varindex = iln->synclocal; - assert(n_ins->s1.varindex != UNUSED); - } - /* XXX could be PREALLOCed for builtin call */ - syncvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0); - n_ins->dst.varindex = syncvar; - n_ins->line = 0; - - /* MONITOREXIT */ - - bte = builtintable_get_internal(LOCK_monitor_exit); - - n_ins = master->inlined_iinstr_cursor++; - n_ins->opc = ICMD_BUILTIN; - n_ins->s1.argcount = 1 + iln->n_passthroughcount + 1; - n_ins->sx.s23.s2.args = DMNEW(s4, n_ins->s1.argcount); - n_ins->sx.s23.s2.args[0] = syncvar; - for (i=0; i < iln->n_passthroughcount + 1; ++i) { - n_ins->sx.s23.s2.args[1 + i] = n_bptr->invars[i]; - } - n_ins->sx.s23.s3.bte = bte; - n_ins->line = 0; - - /* ATHROW */ - - n_ins = master->inlined_iinstr_cursor++; - n_ins->opc = ICMD_ATHROW; - n_ins->flags.bits = 0; - n_ins->s1.varindex = exvar; - n_ins->line = 0; - - /* close basic block */ - - close_block(iln, iln, n_bptr, iln->n_passthroughcount); - n_bptr->icount = 3; - } -} - - -/* second pass driver *********************************************************/ - -static bool inline_transform(inline_node *iln, jitdata *jd) -{ - instruction *n_ins; - basicblock *n_bb; - basicblock *n_bptr; - exception_entry *n_ext; - exception_entry *prevext; - codegendata *n_cd; - jitdata *n_jd; - s4 i; -#if defined(INLINE_VERIFY_RESULT) - static int debug_verify_inlined_code = 1; -#endif -#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) - static int debug_counter = 0; -#endif - - DOLOG( dump_inline_tree(iln, 0); ); - - assert(iln && jd); - - n_ins = DMNEW(instruction, iln->cumul_instructioncount); - MZERO(n_ins, instruction, iln->cumul_instructioncount); - iln->inlined_iinstr = n_ins; - - n_bb = DMNEW(basicblock, iln->cumul_basicblockcount); - MZERO(n_bb, basicblock, iln->cumul_basicblockcount); - iln->inlined_basicblocks = n_bb; - - iln->ctx->blockmap = DMNEW(inline_block_map, iln->cumul_blockmapcount); - - n_jd = jit_jitdata_new(iln->m); - n_jd->flags = jd->flags; - n_jd->code->optlevel = jd->code->optlevel; - iln->ctx->resultjd = n_jd; - - reg_setup(n_jd); - - /* create the local_map */ - - n_jd->local_map = DMNEW(s4, 5*iln->cumul_maxlocals); - for (i=0; i<5*iln->cumul_maxlocals; ++i) - n_jd->local_map[i] = UNUSED; - - /* create / coalesce local variables */ - - n_jd->varcount = 0; - n_jd->vartop = 0; - n_jd->var = NULL; - - inline_locals(iln); - - n_jd->localcount = n_jd->vartop; - - /* extra variables for verification (debugging) */ - -#if defined(INLINE_VERIFY_RESULT) - if (debug_verify_inlined_code) { - n_jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + 100 /* XXX m->maxstack */; - if (n_jd->vartop > n_jd->varcount) { - /* XXX why? */ - n_jd->var = DMREALLOC(n_jd->var, varinfo, n_jd->varcount, n_jd->vartop); - n_jd->varcount = n_jd->vartop; - } - } -#endif /* defined(INLINE_VERIFY_RESULT) */ - - /* write inlined code */ - - inline_rewrite_method(iln); - - /* create exception handlers */ - - inline_write_exception_handlers(iln, iln); - - /* write the dummy end block */ - - n_bptr = create_block(iln, iln, iln, 0); - n_bptr->flags = BBUNDEF; - n_bptr->type = BBTYPE_STD; - - /* store created code in jitdata */ - - n_jd->basicblocks = iln->inlined_basicblocks; - n_jd->instructioncount = iln->cumul_instructioncount; - n_jd->instructions = iln->inlined_iinstr; - - /* link the basic blocks (dummy end block is not counted) */ - - n_jd->basicblockcount = (iln->inlined_basicblocks_cursor - iln->inlined_basicblocks) - 1; - for (i=0; ibasicblockcount + 1; ++i) - n_jd->basicblocks[i].next = &(n_jd->basicblocks[i+1]); - if (i) - n_jd->basicblocks[i-1].next = NULL; - - /* check basicblock numbers */ - -#if !defined(NDEBUG) - jit_check_basicblock_numbers(n_jd); -#endif - - /* create the exception table */ - - if (iln->cumul_exceptiontablelength) { - exception_entry *tableend; - - n_ext = DMNEW(exception_entry, iln->cumul_exceptiontablelength); - prevext = NULL; - tableend = inline_exception_tables(iln, n_ext, &prevext); - assert(tableend == n_ext + iln->cumul_exceptiontablelength); - if (prevext) - prevext->down = NULL; - - n_jd->exceptiontablelength = iln->cumul_exceptiontablelength; - n_jd->exceptiontable = n_ext; - } - else { - n_ext = NULL; - } - - /*******************************************************************************/ - - n_cd = n_jd->cd; - memcpy(n_cd, jd->cd, sizeof(codegendata)); - - n_cd->method = NULL; /* XXX */ - n_jd->maxlocals = iln->cumul_maxlocals; - n_jd->maxinterfaces = iln->ctx->maxinoutdepth; - - inline_post_process(n_jd); - - inline_interface_variables(iln); - - /* for debugging, verify the inlined result */ - -#if defined(INLINE_VERIFY_RESULT) - if (debug_verify_inlined_code) { - debug_verify_inlined_code = 0; - DOLOG( printf("VERIFYING INLINED RESULT...\n"); fflush(stdout); ); - if (!typecheck(n_jd)) { - exceptions_clear_exception(); - DOLOG( printf("XXX INLINED RESULT DID NOT PASS VERIFIER XXX\n") ); - return false; - } - else { - DOLOG( printf("VERIFICATION PASSED.\n") ); - } - debug_verify_inlined_code = 1; - } -#endif /* defined(INLINE_VERIFY_RESULT) */ - - /* we need bigger free memory stacks (XXX these should not be allocated in reg_setup) */ - - n_jd->rd->freemem = DMNEW(s4, iln->ctx->maxinoutdepth + 1000) /* XXX max vars/block */; - -#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) - if ( (n_jd->instructioncount >= opt_InlineMinSize) - && (n_jd->instructioncount <= opt_InlineMaxSize)) - { - if (debug_counter < opt_InlineCount) -#endif /* defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) */ - { - /* install the inlined result */ - - *jd->code = *n_jd->code; - n_jd->code = jd->code; - *jd = *n_jd; - - /* statistics and logging */ - -#if !defined(NDEBUG) - inline_stat_roots++; - - DOLOG_SHORT( - printf("==== %d.INLINE ==================================================================\n", - debug_counter); - printf("\ninline tree:\n"); - dump_inline_tree(iln, 0); - n_jd->flags |= JITDATA_FLAG_SHOWINTERMEDIATE | JITDATA_FLAG_SHOWDISASSEMBLE; - /* debug_dump_inlined_code(iln, n_method, n_cd, n_rd); */ - printf("-------- DONE -----------------------------------------------------------\n"); - fflush(stdout); - ); -#endif - } - -#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) - debug_counter++; - } -#endif - return true; -} - - -/******************************************************************************/ -/* FIRST PASS: build inlining tree */ -/******************************************************************************/ - - -/* inline_pre_parse_heuristics ************************************************* - - Perform heuristic checks whether a call site should be inlined. - These checks are evaluated before the callee has been parsed and analysed. - - IN: - caller...........inlining node of the caller - callee...........the called method - site.............information on the call site - - RETURN VALUE: - true........consider for inlining - false.......don't inline - -*******************************************************************************/ - -static bool inline_pre_parse_heuristics(const inline_node *caller, - const methodinfo *callee, - inline_site *site) -{ -#if defined(INLINE_MAX_DEPTH) - if (caller->depth >= INLINE_MAX_DEPTH) - return false; -#endif - - return true; -} - - -/* inline_post_parse_heuristics ************************************************ - - Perform heuristic checks whether a call site should be inlined. - These checks are evaluated after the callee has been parsed and analysed. - - IN: - caller...........inlining node of the caller (const) - callee...........the called method (const) - - RETURN VALUE: - true........consider for inlining - false.......don't inline - -*******************************************************************************/ - -static bool inline_post_parse_heuristics(const inline_node *caller, - const inline_node *callee) -{ - return true; -} - - -/* inline_afterwards_heuristics ************************************************ - - Perform heuristic checks whether a call site should be inlined. - These checks are evaluated after the inlining plan for the callee has - been made. - - IN: - caller...........inlining node of the caller (const) - callee...........the called method (const) - - RETURN VALUE: - true........consider for inlining - false.......don't inline - -*******************************************************************************/ - -static bool inline_afterwards_heuristics(const inline_node *caller, - const inline_node *callee) -{ - inline_node *cumulator; - -#if defined(INLINE_DEPTH_FIRST) - cumulator = caller; -#else - cumulator = caller->ctx->master; -#endif - - if (0 -#if defined(INLINE_MAX_BLOCK_EXPANSION) - || (cumulator->cumul_basicblockcount + callee->cumul_basicblockcount - > INLINE_MAX_BLOCK_EXPANSION*caller->ctx->master->jd->basicblockcount) -#endif -#if defined(INLINE_MAX_ICMD_EXPANSION) - || (cumulator->cumul_instructioncount + callee->cumul_instructioncount - > INLINE_MAX_ICMD_EXPANSION*caller->ctx->master->jd->instructioncount) -#endif - ) - { - return false; - } - - return true; -} - - -/* inline_is_monomorphic ******************************************************* - - Check if the given call site can be proven to be monomorphic. - - IN: - callee...........the called method - call.............the invocation instruction - - OUT: - site->speculative.....flags whether the inlining is speculative - (only defined if return value is true) - - RETURN VALUE: - true if the call site is (currently) monomorphic, - false if not or unknown - -*******************************************************************************/ - -static bool inline_is_monomorphic(const methodinfo *callee, - const instruction *call, - inline_site *site) -{ - if ((callee->flags & (ACC_STATIC | ACC_FINAL | ACC_PRIVATE) - || call->opc == ICMD_INVOKESPECIAL)) - { - site->speculative = false; - return true; - } - - /* XXX search single implementation for abstract monomorphics */ - - if ((callee->flags & (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED - | ACC_ABSTRACT)) - == (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED)) - { - if (1) { - DOLOG( printf("SPECULATIVE INLINE: "); method_println((methodinfo*)callee); ); - site->speculative = true; - - return true; - } - } - - /* possibly polymorphic call site */ - - return false; -} - - -/* inline_can_inline *********************************************************** - - Check if inlining of the given call site is possible. - - IN: - caller...........inlining node of the caller - callee...........the called method - call.............the invocation instruction - - OUT: - site->speculative.....flags whether the inlining is speculative - (only defined if return value is true) - - RETURN VALUE: - true if inlining is possible, false if not - -*******************************************************************************/ - -static bool inline_can_inline(const inline_node *caller, - const methodinfo *callee, - const instruction *call, - inline_site *site) -{ - const inline_node *active; - - /* cannot inline native methods */ - - if (callee->flags & ACC_NATIVE) - return false; - - /* cannot inline possibly polymorphic calls */ - - if (!inline_is_monomorphic(callee, call, site)) - return false; - - /* cannot inline recursive calls */ - - for (active = caller; active; active = active->parent) { - if (callee == active->m) { - DOLOG( printf("RECURSIVE!\n") ); - return false; - } - } - - /* inlining is possible */ - - return true; -} - - -/* inline_create_callee_node *************************************************** - - Create an inlining node for the given callee. - - IN: - caller...........inlining node of the caller (const) - callee...........the called method - - RETURN VALUE: - the new inlining node - -*******************************************************************************/ - -static inline_node * inline_create_callee_node(const inline_node *caller, - methodinfo *callee) -{ - inline_node *cn; /* the callee inline_node */ - - cn = DNEW(inline_node); - MZERO(cn, inline_node, 1); - - cn->depth = caller->depth + 1; - cn->ctx = caller->ctx; - cn->m = callee; - cn->synchronize = (callee->flags & ACC_SYNCHRONIZED); - cn->isstatic = (callee->flags & ACC_STATIC); - - return cn; -} - - -/* inline_set_callee_properties ************************************************ - - Set properties of the inlined call site. - - IN: - caller...........inlining node of the caller (const) - cn...............the called method - site.............info about the call site (const) - - OUT: - *cn..............has the properties set - -*******************************************************************************/ - -static void inline_set_callee_properties(const inline_node *caller, - inline_node *cn, - const inline_site *site) -{ - s4 argi; - s4 i, j; - basicblock *bptr; - - /* set info about the call site */ - - cn->callerblock = site->bptr; - cn->callerins = site->iptr; - cn->callerpc = site->pc; - cn->o_handlers = site->handlers; - cn->n_handlercount = caller->n_handlercount + site->nhandlers; - - /* determine if we need basic block boundaries before/after */ - - cn->blockbefore = false; - cn->blockafter = false; - - if (cn->jd->branchtoentry) - cn->blockbefore = true; - - if (cn->jd->branchtoend) - cn->blockafter = true; - - if (cn->jd->returncount > 1) - cn->blockafter = true; - - /* XXX make safer and reusable (maybe store last real block) */ - for (bptr = cn->jd->basicblocks; bptr && bptr->next && bptr->next->next; bptr = bptr->next) - ; - - if (cn->jd->returnblock != bptr) - cn->blockafter = true; - - /* info about the callee */ - - cn->localsoffset = caller->localsoffset + caller->m->maxlocals; - cn->prolog_instructioncount = cn->m->parseddesc->paramcount + 2; - cn->epilog_instructioncount = 1; /* INLINE_END */ - cn->extra_instructioncount = 0; - - /* we need a CHECKNULL for instance methods, except for */ - - if (!cn->isstatic && cn->m->name != utf_init) - cn->prolog_instructioncount += 1; - - /* deal with synchronized callees */ - - if (cn->synchronize) { - methoddesc *md; - builtintable_entry *bte; - - /* we need basic block boundaries because of the handler */ - - cn->blockbefore = true; - cn->blockafter = true; - - /* for synchronized static methods */ - /* we need an ACONST, MONITORENTER in the prolog */ - /* and ACONST, MONITOREXIT in the epilog */ - - /* for synchronized instance methods */ - /* we need an COPY, MONITORENTER in the prolog */ - /* and MONITOREXIT in the epilog */ - - if (cn->isstatic) { - cn->prolog_instructioncount += 2; - cn->epilog_instructioncount += 2; - } - else { - cn->prolog_instructioncount += 2; - cn->epilog_instructioncount += 1; - cn->localsoffset += 1; - } - - /* and exception handler */ - /* ALOAD, builtin_monitorexit, ATHROW */ - - cn->extra_instructioncount += 3; - - /* exception table entries */ - - cn->extra_exceptiontablelength = 1 + cn->n_handlercount; - - /* add exception handler block */ - - cn->cumul_basicblockcount_root++; - - /* we must call the builtins */ - - bte = builtintable_get_internal(LOCK_monitor_enter); - md = bte->md; - if (md->memuse > cn->regdata->memuse) - cn->regdata->memuse = md->memuse; - if (md->argintreguse > cn->regdata->argintreguse) - cn->regdata->argintreguse = md->argintreguse; - - bte = builtintable_get_internal(LOCK_monitor_exit); - md = bte->md; - if (md->memuse > cn->regdata->memuse) - cn->regdata->memuse = md->memuse; - if (md->argintreguse > cn->regdata->argintreguse) - cn->regdata->argintreguse = md->argintreguse; - } - - /* determine pass-through variables */ - - i = site->iptr->s1.argcount - cn->m->parseddesc->paramcount; /* max # of pass-though vars */ - - cn->n_passthroughvars = DMNEW(s4, i); - j = 0; - for (argi = site->iptr->s1.argcount - 1; argi >= cn->m->parseddesc->paramcount; --argi) { - s4 idx = site->iptr->sx.s23.s2.args[argi]; - if (idx >= caller->jd->localcount) { - cn->n_passthroughvars[j] = idx; - j++; - } - else { - DOLOG( printf("PASSING THROUGH LOCAL VARIABLE %d\n", idx); ); - } - } - assert(j <= i); - cn->n_selfpassthroughcount = j; - cn->n_passthroughcount = caller->n_passthroughcount + cn->n_selfpassthroughcount; -} - - -/* inline_cumulate_counters **************************************************** - - Cumulate counters after a node has been decided to become inlined. - - IN: - caller...........inlining node of the caller - callee...........inlining node of the callee (const) - - OUT: - *caller..........gets cumulated values added - -*******************************************************************************/ - -static void inline_cumulate_counters(inline_node *caller, - const inline_node *cn) -{ - caller->cumul_instructioncount += cn->prolog_instructioncount; - caller->cumul_instructioncount += cn->epilog_instructioncount; - caller->cumul_instructioncount += cn->extra_instructioncount; - caller->cumul_instructioncount += cn->cumul_instructioncount - 1 /*invoke*/; - - caller->cumul_basicblockcount += cn->cumul_basicblockcount; - caller->cumul_basicblockcount_root += cn->cumul_basicblockcount_root; - caller->cumul_blockmapcount += cn->cumul_blockmapcount; - caller->cumul_exceptiontablelength += cn->cumul_exceptiontablelength; - caller->cumul_exceptiontablelength += cn->extra_exceptiontablelength; - - if (cn->cumul_maxlocals > caller->cumul_maxlocals) - caller->cumul_maxlocals = cn->cumul_maxlocals; - - /* XXX extra block after inlined call */ - if (cn->blockafter) { - caller->cumul_basicblockcount += 1; - caller->cumul_blockmapcount += 1; - } -} - - -/* inline_analyse_callee ******************************************************* - - Analyse an inlining candidate callee. - - IN: - caller...........inlining node of the caller - callee...........the called method - site.............info about the call site - - OUT: - site->inlined....true if the callee has been selected for inlining - - RETURN VALUE: - the inline node of the callee, or - NULL if an error has occurred (don't use the inlining plan in this case) - -*******************************************************************************/ - -static inline_node * inline_analyse_callee(inline_node *caller, - methodinfo *callee, - inline_site *site) -{ - inline_node *cn; /* the callee inline_node */ - - /* create an inline tree node */ - - cn = inline_create_callee_node(caller, callee); - - /* get the intermediate representation of the callee */ - - if (!inline_jit_compile(cn)) - return NULL; - - /* evaluate heuristics after parsing the callee */ - - if (!inline_post_parse_heuristics(caller, cn)) - return cn; - - /* the call site will be inlined */ - - site->inlined = true; - - /* set info about the call site */ - - inline_set_callee_properties(caller, cn, site); - - /* insert the node into the inline tree */ - - inline_insert_inline_node(caller, cn); - - /* analyse recursively */ - - if (!inline_analyse_code(cn)) - return NULL; - - if (!inline_afterwards_heuristics(caller, cn)) { -#if defined(INLINE_CANCEL_ON_THRESHOLD) - return NULL; -#else - inline_remove_inline_node(caller, cn); - caller->ctx->stopped = true; - site->inlined = false; - return cn; -#endif - } - - /* cumulate counters */ - -#if defined(INLINE_DEPTH_FIRST) - inline_cumulate_counters(caller, cn); -#endif - -#if defined(INLINE_BREADTH_FIRST) - while (caller) { - inline_cumulate_counters(caller, cn); - caller = caller->parent; - } -#endif - - return cn; -} - - -/* inline_process_candidate **************************************************** - - Process a selected inlining candidate. - - IN: - cand.............the candidate - - RETURN VALUE: - true........everything ok - false.......an error has occurred, don't use the plan - -*******************************************************************************/ - -static bool inline_process_candidate(inline_candidate *cand) -{ - inline_node *cn; - - cn = inline_analyse_callee(cand->caller, - cand->callee, - &(cand->site)); - - if (!cn) - return false; - - if (!cand->site.inlined) - return true; - - /* store assumptions */ - - if (cand->site.speculative) - method_add_assumption_monomorphic(cand->callee, cand->caller->ctx->master->m); - - return true; -} - - -/* inline_analyse_code ********************************************************* - - Analyse the intermediate code of the given inlining node. - - IN: - iln..............the inlining node - - OUT: - *iln.............the inlining plan - - RETURN VALUE: - true........everything ok - false.......an error has occurred, don't use the plan - -*******************************************************************************/ - -static bool inline_analyse_code(inline_node *iln) -{ - methodinfo *m; - basicblock *bptr; - s4 len; - instruction *iptr; - methodinfo *callee; - exception_entry **handlers; - exception_entry *ex; - s4 nhandlers; - s4 blockendpc; - jitdata *mjd; - inline_site site; - - assert(iln); - - m = iln->m; - mjd = iln->jd; - - /* initialize cumulative counters */ - - iln->cumul_maxlocals = iln->localsoffset + m->maxlocals; - iln->cumul_exceptiontablelength += mjd->exceptiontablelength; - - /* iterate over basic blocks */ - - blockendpc = 0; - - for (bptr = mjd->basicblocks; bptr; bptr = bptr->next) { - - /* count the block */ - /* ignore dummy end blocks (but count them for the blockmap) */ - - iln->cumul_blockmapcount++; - if ((bptr != mjd->basicblocks || iln->blockbefore) - && - (bptr->icount > 0 || bptr->next != NULL)) - iln->cumul_basicblockcount++; - - /* skip dead code */ - - if (bptr->flags < BBREACHED) - continue; - - /* allocate the buffer of active exception handlers */ - /* XXX this wastes some memory, but probably it does not matter */ - - handlers = DMNEW(exception_entry*, mjd->exceptiontablelength + 1); - - /* determine the active exception handlers for this block */ - /* XXX maybe the handlers of a block should be part of our IR */ - /* XXX this should share code with the type checkers */ - nhandlers = 0; - for (ex = mjd->exceptiontable; ex ; ex = ex->down) { - if ((ex->start->nr <= bptr->nr) && (ex->end->nr > bptr->nr)) { - handlers[nhandlers++] = ex; - } - } - handlers[nhandlers] = NULL; - - len = bptr->icount; - iptr = bptr->iinstr; - - blockendpc += len; - iln->cumul_instructioncount += len; - - /* iterate over the instructions of the block */ - - for (; --len >= 0; ++iptr) { - - switch (iptr->opc) { - case ICMD_INVOKEVIRTUAL: - case ICMD_INVOKESPECIAL: - case ICMD_INVOKESTATIC: - case ICMD_INVOKEINTERFACE: - - if (!INSTRUCTION_IS_UNRESOLVED(iptr) && !iln->ctx->stopped) { - callee = iptr->sx.s23.s3.fmiref->p.method; - - if (inline_can_inline(iln, callee, iptr, &site)) { - site.inlined = false; - site.bptr = bptr; - site.iptr = iptr; - site.pc = blockendpc - len - 1; - site.handlers = handlers; - site.nhandlers = nhandlers; - - if (inline_pre_parse_heuristics(iln, callee, &site)) { -#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) - inline_add_candidate(iln->ctx, iln, callee, &site); -#else - inline_candidate cand; - cand.caller = iln; - cand.callee = callee; - cand.site = site; - - if (!inline_process_candidate(&cand)) - return false; -#endif - } - } - } - break; - - case ICMD_RETURN: - case ICMD_IRETURN: - case ICMD_ARETURN: - case ICMD_LRETURN: - case ICMD_FRETURN: - case ICMD_DRETURN: - /* extra ICMD_MOVE may be necessary */ - iln->cumul_instructioncount++; - break; - } - } - - /* end of basic block */ - } - - return true; -} - - -static void inline_cumulate_counters_recursive(inline_node *iln) -{ - inline_node *child; - - child = iln->children; - if (child) { - do { - inline_cumulate_counters_recursive(child); - inline_cumulate_counters(iln, child); - child = child->next; - } while (child != iln->children); - } -} - - -/* inline_make_inlining_plan *************************************************** - - Make an inlining plan for the given root node - - IN: - iln..............the root node - - OUT: - *iln.............the inlining plan - - RETURN VALUE: - true........everything ok - false.......an error has occurred, don't use the plan - -*******************************************************************************/ - -#if defined(INLINE_KNAPSACK) -static bool inline_make_inlining_plan(inline_node *iln) -{ - inline_candidate *cand; -#if defined(INLINE_COST_BUDGET) - s4 budget = INLINE_COST_BUDGET; -# define BUDGETMEMBER cost -#endif -#if defined(INLINE_WEIGHT_BUDGET) - double budget = INLINE_WEIGHT_BUDGET; -# define BUDGETMEMBER weight -#endif - - inline_analyse_code(iln); - - DOLOG( printf("candidates in "); method_println(iln->m); - inline_candidates_println(iln->ctx); ); - - while ((cand = inline_pick_best_candidate(iln->ctx)) != NULL) - { - if (cand->BUDGETMEMBER <= budget) { - DOLOG( printf(" picking: "); inline_candidate_println(cand); ); - - if (!inline_process_candidate(cand)) - return false; - -#if !defined(INLINE_ADD_NEGATIVE_TO_BUDGET) - if (cand->BUDGETMEMBER > 0) -#endif - budget -= cand->BUDGETMEMBER; - } - } - - inline_cumulate_counters_recursive(iln); - - return true; -} -#endif /* defined(INLINE_KNAPSACK) */ - - -#if defined(INLINE_DEPTH_FIRST) -static bool inline_make_inlining_plan(inline_node *iln) -{ - return inline_analyse_code(iln); -} -#endif /* defined(INLINE_DEPTH_FIRST) */ - - -#if defined(INLINE_BREADTH_FIRST) -static bool inline_make_inlining_plan(inline_node *iln) -{ - inline_candidate *cand; - - inline_analyse_code(iln); - - DOLOG( printf("candidates in "); method_println(iln->m); - inline_candidates_println(iln->ctx); ); - - while (!iln->ctx->stopped - && (cand = inline_pick_best_candidate(iln->ctx)) != NULL) - { - DOLOG( printf(" picking: "); inline_candidate_println(cand); ); - - if (!inline_process_candidate(cand)) - return false; - } - - return true; -} -#endif /* defined(INLINE_BREADTH_FIRST) */ - - -/* statistics *****************************************************************/ - -#if defined(INLINE_STATISTICS) -static void inline_gather_statistics_recursive(inline_node *iln) -{ - inline_node *child; - - inline_stat_inlined_nodes++; - - if (iln->depth > inline_stat_max_depth) - inline_stat_max_depth++; - - child = iln->children; - if (child) { - do { - inline_gather_statistics_recursive(child); - child = child->next; - } while (child != iln->children); - } -} -#endif /* defined(INLINE_STATISTICS) */ - - -#if defined(INLINE_STATISTICS) -static void inline_gather_statistics(inline_node *iln) -{ - inline_stat_roots_transformed++; - - inline_gather_statistics_recursive(iln); -} -#endif /* defined(INLINE_STATISTICS) */ - - -/* post processing ************************************************************/ - -#define POSTPROCESS_SRC(varindex) live[varindex]-- -#define POSTPROCESS_DST(varindex) live[varindex]++ - -#define POSTPROCESS_SRCOP(s) POSTPROCESS_SRC(iptr->s.varindex) -#define POSTPROCESS_DSTOP(d) POSTPROCESS_DST(iptr->d.varindex) - -#define MARKSAVED(varindex) jd->var[varindex].flags |= SAVEDVAR - -#define MARK_ALL_SAVED \ - do { \ - for (i=0; ivartop; ++i) \ - if (live[i]) \ - MARKSAVED(i); \ - } while (0) - -static void inline_post_process(jitdata *jd) -{ - codeinfo *code; - basicblock *bptr; - instruction *iptr; - instruction *iend; - s4 i; - icmdtable_entry_t *icmdt; - s4 *live; - methoddesc *md; - builtintable_entry *bte; - - /* Get required compiler data. */ - - code = jd->code; - - /* reset the SAVEDVAR flag of all variables */ - - for (i=0; ivartop; ++i) - jd->var[i].flags &= ~SAVEDVAR; - - /* allocate the life counters */ - - live = DMNEW(s4, jd->vartop); - MZERO(live, s4, jd->vartop); - - /* iterate over all basic blocks */ - - for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) { - if (bptr->flags < BBREACHED) - continue; - - /* make invars live */ - - for (i=0; iindepth; ++i) - POSTPROCESS_DST(bptr->invars[i]); - - iptr = bptr->iinstr; - iend = iptr + bptr->icount; - - for (; iptr < iend; ++iptr) { - - icmdt = &(icmd_table[iptr->opc]); - - switch (icmdt->dataflow) { - case DF_3_TO_0: - POSTPROCESS_SRCOP(sx.s23.s3); - case DF_2_TO_0: - POSTPROCESS_SRCOP(sx.s23.s2); - case DF_1_TO_0: - POSTPROCESS_SRCOP(s1); - case DF_0_TO_0: - if (icmdt->flags & ICMDTABLE_CALLS) { - code_unflag_leafmethod(code); - MARK_ALL_SAVED; - } - break; - - case DF_2_TO_1: - POSTPROCESS_SRCOP(sx.s23.s2); - case DF_1_TO_1: - case DF_MOVE: - POSTPROCESS_SRCOP(s1); - case DF_0_TO_1: - if (icmdt->flags & ICMDTABLE_CALLS) { - code_unflag_leafmethod(code); - MARK_ALL_SAVED; - } - case DF_COPY: - POSTPROCESS_DSTOP(dst); - break; - - case DF_N_TO_1: - for (i=0; is1.argcount; ++i) { - POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]); - } - if (icmdt->flags & ICMDTABLE_CALLS) { - code_unflag_leafmethod(code); - MARK_ALL_SAVED; - } - POSTPROCESS_DSTOP(dst); - break; - - case DF_INVOKE: - INSTRUCTION_GET_METHODDESC(iptr, md); - post_process_call: - code_unflag_leafmethod(code); - for (i=0; iparamcount; ++i) { - POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]); - } - for (; is1.argcount; ++i) { - MARKSAVED(iptr->sx.s23.s2.args[i]); - } - if (md->returntype.type != TYPE_VOID) - POSTPROCESS_DSTOP(dst); - break; - - case DF_BUILTIN: - bte = iptr->sx.s23.s3.bte; - md = bte->md; - goto post_process_call; - - default: - assert(0); - } - - } /* end instruction loop */ - - /* consume outvars */ - - for (i=0; ioutdepth; ++i) - POSTPROCESS_SRC(bptr->outvars[i]); - -#if !defined(NDEBUG) - for (i=jd->localcount; i < jd->vartop; ++i) - assert(live[i] == 0); -#endif - - } /* end basic block loop */ -} - - -/* inline_create_root_node ***************************************************** - - Create the root node of the inlining tree. - - IN: - jd...............the current jitdata of the root method - - RETURN VALUE: - the root node of the inlining tree - -*******************************************************************************/ - -static inline_node * inline_create_root_node(jitdata *jd) -{ - inline_node *iln; - - iln = DNEW(inline_node); - MZERO(iln, inline_node, 1); - - iln->m = jd->m; - iln->jd = jd; - iln->regdata = jd->rd; - - iln->blockbefore = true; - iln->blockafter = true; - - iln->cumul_instructioncount = 0; - iln->cumul_basicblockcount = 1 /* dummy end block */; - - /* create inlining context */ - - iln->ctx = DNEW(inline_context); - MZERO(iln->ctx, inline_context, 1); - iln->ctx->master = iln; - iln->ctx->next_debugnr = 1; /* XXX debug */ - - return iln; -} - - -/******************************************************************************/ -/* MAIN DRIVER FUNCTION */ -/******************************************************************************/ - -bool inline_inline(jitdata *jd) -{ - inline_node *iln; - - DOLOG( printf("==== INLINE ==================================================================\n"); - show_method(jd, SHOW_STACK); ); - -#if defined(INLINE_STATISTICS) - inline_stat_roots++; -#endif - - iln = inline_create_root_node(jd); - - if (inline_make_inlining_plan(iln)) { - - /* add blocks to the root node */ - - iln->cumul_basicblockcount += iln->cumul_basicblockcount_root; - iln->cumul_blockmapcount += iln->cumul_basicblockcount_root; - - DOLOG( printf("==== INLINE TRANSFORM ========================================================\n"); ); - - if (iln->children) - inline_transform(iln, jd); - -#if defined(INLINE_STATISTICS) - inline_gather_statistics(iln); -#endif - } - - DOLOG( printf("-------- DONE -----------------------------------------------------------\n"); - fflush(stdout); ); - - return true; -} - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ diff --git a/src/vm/jit/inline/inline.cpp b/src/vm/jit/inline/inline.cpp new file mode 100644 index 000000000..6ceffa01b --- /dev/null +++ b/src/vm/jit/inline/inline.cpp @@ -0,0 +1,3244 @@ +/* src/vm/jit/inline/inline.c - method inlining + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#include "config.h" + +#include +#include +#include +#include + +#include "vm/types.h" + +#include "mm/memory.h" + +#include "threads/lock.hpp" +#include "threads/mutex.hpp" +#include "threads/thread.hpp" + +#include "toolbox/logging.h" + +#include "vm/jit/builtin.hpp" +#include "vm/class.hpp" +#include "vm/global.h" +#include "vm/initialize.hpp" +#include "vm/method.h" +#include "vm/options.h" +#include "vm/statistics.h" + +#include "vm/jit/jit.hpp" +#include "vm/jit/parse.hpp" +#include "vm/jit/reg.h" +#include "vm/jit/show.hpp" +#include "vm/jit/stack.h" + +#include "vm/jit/inline/inline.hpp" +#include "vm/jit/loop/loop.h" + +#include "vm/jit/verify/typecheck.h" + + +/* algorithm tuning constants *************************************************/ + +/* Algorithm Selection */ +/* Define exactly one of the following three to select the inlining */ +/* heuristics. */ + +/*#define INLINE_DEPTH_FIRST*/ +/*#define INLINE_BREADTH_FIRST*/ +#define INLINE_KNAPSACK + +/* Parameters for knapsack heuristics: */ + +#if defined(INLINE_KNAPSACK) + +#define INLINE_COUNTDOWN_INIT 1000 +#define INLINE_COST_OFFSET -16 +#define INLINE_COST_BUDGET 100 +/*#define INLINE_WEIGHT_BUDGET 5.0*/ +/*#define INLINE_ADD_NEGATIVE_TO_BUDGET*/ +/*#define INLINE_MAX_DEPTH 3*/ +/*#define INLINE_DIVIDE_COST_BY_FREQ */ + +#endif + +/* Parameters for depth-first heuristics: */ + +#if defined(INLINE_DEPTH_FIRST) + +#define INLINE_MAX_DEPTH 3 +#define INLINE_MAX_BLOCK_EXPANSION 10 +/*#define INLINE_MAX_ICMD_EXPANSION 10*/ +/*#define INLINE_CANCEL_ON_THRESHOLD*/ + +#endif + +/* Parameters for breadth-first heuristics: */ + +#if defined(INLINE_BREADTH_FIRST) + +/*#define INLINE_MAX_BLOCK_EXPANSION 10*/ +#define INLINE_MAX_ICMD_EXPANSION 5 + +#endif + + +/* debugging ******************************************************************/ + +#if !defined(NDEBUG) +#define INLINE_VERBOSE +#define DOLOG(code) do{ if (opt_TraceInlining >= 2) { code; } }while(0) +#define DOLOG_SHORT(code) do{ if (opt_TraceInlining >= 1) { code; } }while(0) +#else +#define DOLOG(code) +#endif + +#if defined(ENABLE_VERIFIER) && !defined(NDEBUG) +/* Define this to verify the resulting code after inlining. */ +/* Note: This is only useful for development and may require patches to the */ +/* verifier code. */ +/* #define INLINE_VERIFY_RESULT */ +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* types **********************************************************************/ + +typedef struct inline_node inline_node; +typedef struct inline_target_ref inline_target_ref; +typedef struct inline_context inline_context; +typedef struct inline_block_map inline_block_map; +typedef struct inline_site inline_site; +typedef struct inline_candidate inline_candidate; + +struct inline_node { + inline_context *ctx; + + jitdata *jd; + methodinfo *m; + inline_node *children; + inline_node *next; /* next node at this depth */ + inline_node *prev; /* prev node at this depth */ + int depth; /* inlining depth, 0 for root */ + + /* info about the call site (if depth > 0)*/ + inline_node *parent; /* node of the caller (NULL for root) */ + basicblock *callerblock; /* original block containing the INVOKE* */ + instruction *callerins; /* the original INVOKE* instruction */ + s4 callerpc; + s4 *n_passthroughvars; + int n_passthroughcount; + int n_selfpassthroughcount; /* # of pass-through vars of the call itself */ + exception_entry **o_handlers; + int n_handlercount; /* # of handlers protecting this call */ + int n_resultlocal; + int synclocal; /* variable used for synchr., or UNUSED */ + bool isstatic; /* this is a static call */ + + bool blockbefore; /* block boundary before inlined body? */ + bool blockafter; /* block boundary after inlined body? */ + + /* info about the callee */ + int localsoffset; + int prolog_instructioncount; /* # of ICMDs in the inlining prolog */ + int epilog_instructioncount; /* # of ICMDs in the inlining epilog */ + int extra_instructioncount; + int extra_exceptiontablelength; /* # of extra handlers to put in caller */ + bool synchronize; /* do we have to synchronize enter/exit? */ + basicblock *handler_monitorexit; /* handler for synchronized inlinees */ + s4 *varmap; + + /* cumulative values */ + int cumul_instructioncount; /* ICMDs in this node and its children */ + int cumul_basicblockcount; /* BBs started by this node and its children */ + int cumul_basicblockcount_root; /* BBs that have to be added to the root */ + /* node if this node is inlined */ + int cumul_blockmapcount; + int cumul_maxlocals; + int cumul_exceptiontablelength; + + /* output */ + instruction *inlined_iinstr; + instruction *inlined_iinstr_cursor; + basicblock *inlined_basicblocks; + basicblock *inlined_basicblocks_cursor; + + /* register data */ + registerdata *regdata; + + /* temporary */ + inline_target_ref *refs; + instruction *inline_start_instruction; + s4 *javalocals; + + /* XXX debug */ + char *indent; + int debugnr; +}; + +struct inline_target_ref { + inline_target_ref *next; + union { + basicblock **block; + s4 *nr; + } ref; + basicblock *target; + bool isnumber; +}; + +struct inline_block_map { + inline_node *iln; + basicblock *o_block; + basicblock *n_block; +}; + +struct inline_context { + inline_node *master; + + jitdata *resultjd; + + inline_candidate *candidates; + + int next_block_number; + inline_block_map *blockmap; + int blockmap_index; + + int maxinoutdepth; + + bool stopped; + + int next_debugnr; /* XXX debug */ +}; + +struct inline_site { + bool speculative; /* true, if inlining would be speculative */ + bool inlined; /* true, if this site has been inlined */ + + basicblock *bptr; /* basic block containing the call site */ + instruction *iptr; /* the invocation instruction */ + exception_entry **handlers; /* active handlers at the call site */ + s4 nhandlers; /* number of active handlers */ + s4 pc; /* PC of the invocation instruction */ +}; + +struct inline_candidate { + inline_candidate *next; + int freq; + int cost; + double weight; + inline_node *caller; + methodinfo *callee; + inline_site site; +}; + + +/* prototypes *****************************************************************/ + +static bool inline_analyse_code(inline_node *iln); +static void inline_post_process(jitdata *jd); + + +/* debug helpers **************************************************************/ + +#if !defined(NDEBUG) +#include "inline_debug.inc" +#endif + + +/* statistics *****************************************************************/ + +/*#define INLINE_STATISTICS*/ + +#if !defined(NDEBUG) +#define INLINE_STATISTICS +#endif + +#if defined(INLINE_STATISTICS) +int inline_stat_roots = 0; +int inline_stat_roots_transformed = 0; +int inline_stat_inlined_nodes = 0; +int inline_stat_max_depth = 0; + +void inline_print_stats() +{ + printf("inlining statistics:\n"); + printf(" roots analysed : %d\n", inline_stat_roots); + printf(" roots transformed: %d\n", inline_stat_roots_transformed); + printf(" inlined nodes : %d\n", inline_stat_inlined_nodes); + printf(" max depth : %d\n", inline_stat_max_depth); +} +#endif + + +/* compilation of callees *****************************************************/ + +static bool inline_jit_compile_intern(jitdata *jd) +{ + methodinfo *m; + + /* XXX should share code with jit.c */ + + assert(jd); + + /* XXX initialize the static function's class */ + + m = jd->m; + + /* call the compiler passes ***********************************************/ + + /* call parse pass */ + + DOLOG( log_message_class("Parsing ", m->clazz) ); + if (!parse(jd)) { + return false; + } + + /* call stack analysis pass */ + + if (!stack_analyse(jd)) { + return false; + } + + return true; +} + + +static bool inline_jit_compile(inline_node *iln) +{ + bool r; + methodinfo *m; + jitdata *jd; + + /* XXX should share code with jit.c */ + + assert(iln); + m = iln->m; + assert(m); + + /* enter a monitor on the method */ + + m->mutex->lock(); + + /* allocate jitdata structure and fill it */ + + jd = jit_jitdata_new(m); + iln->jd = jd; + + jd->flags = 0; /* XXX */ + + /* initialize the register allocator */ + + reg_setup(jd); + + /* setup the codegendata memory */ + + /* XXX do a pseudo setup */ + jd->cd = (codegendata*) DumpMemory::allocate(sizeof(codegendata)); + MZERO(jd->cd, codegendata, 1); + jd->cd->method = m; + /* XXX uses too much dump memory codegen_setup(jd); */ + + /* now call internal compile function */ + + r = inline_jit_compile_intern(jd); + + if (r) { + iln->regdata = jd->rd; + } + + /* free some memory */ +#if 0 + +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (!opt_intrp) +# endif + codegen_free(jd); +#endif + +#endif + + /* leave the monitor */ + + m->mutex->unlock(); + + return r; +} + + +/* inlining tree handling *****************************************************/ + +static void inline_insert_inline_node(inline_node *parent, inline_node *child) +{ + inline_node *first; + inline_node *succ; + + assert(parent && child); + + child->parent = parent; + + child->debugnr = parent->ctx->next_debugnr++; /* XXX debug */ + + first = parent->children; + if (!first) { + /* insert as only node */ + parent->children = child; + child->next = child; + child->prev = child; + return; + } + + /* {there is at least one child already there} */ + + /* XXX is this search necessary, or could we always add at the end? */ + + succ = first; + while (succ->callerpc < child->callerpc) { + succ = succ->next; + if (succ == first) { + /* insert as last node */ + child->prev = first->prev; + child->next = first; + child->prev->next = child; + child->next->prev = child; + return; + } + } + + assert(succ->callerpc > child->callerpc); + + /* insert before succ */ + + child->prev = succ->prev; + child->next = succ; + child->prev->next = child; + child->next->prev = child; + + if (parent->children == succ) + parent->children = child; +} + + +static void inline_remove_inline_node(inline_node *parent, inline_node *child) +{ + assert(parent); + assert(child); + assert(child->parent == parent); + + if (child->prev == child) { + /* remove the only child node */ + parent->children = NULL; + } + else { + child->prev->next = child->next; + child->next->prev = child->prev; + + if (parent->children == child) + parent->children = child->next; + } +} + + +/* inlining candidate handling ************************************************/ + +#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) +static void inline_add_candidate(inline_context *ctx, + inline_node *caller, + methodinfo *callee, + inline_site *site) +{ + inline_candidate **link; + inline_candidate *cand; + + cand = (inline_candidate*) DumpMemory::allocate(sizeof(inline_candidate)); +#if defined(INLINE_DIVIDE_COST_BY_FREQ) + cand->freq = INLINE_COUNTDOWN_INIT - callee->hitcountdown; + if (cand->freq < 1) +#endif + cand->freq = 1; +#if defined(INLINE_KNAPSACK) + cand->cost = callee->jcodelength + INLINE_COST_OFFSET; +#endif +#if defined(INLINE_BREADTH_FIRST) + cand->cost = caller->depth; +#endif + cand->caller = caller; + cand->callee = callee; + cand->site = *site; + + cand->weight = (double)cand->cost / cand->freq; + + for (link = &(ctx->candidates); ; link = &((*link)->next)) { + if (!*link || (*link)->weight > cand->weight) { + cand->next = *link; + *link = cand; + break; + } + } +} +#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */ + +#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) +static inline_candidate * inline_pick_best_candidate(inline_context *ctx) +{ + inline_candidate *cand; + + cand = ctx->candidates; + + if (cand) + ctx->candidates = cand->next; + + return cand; +} +#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */ + +#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) +static void inline_candidate_println(inline_candidate *cand) +{ + printf("%10g (%5d / %5d) depth %2d ", + cand->weight, cand->cost, cand->freq, cand->caller->depth + 1); + method_println(cand->callee); +} +#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */ + + +#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) +static void inline_candidates_println(inline_context *ctx) +{ + inline_candidate *cand; + + for (cand = ctx->candidates; cand != NULL; cand = cand->next) { + printf(" "); + inline_candidate_println(cand); + } +} +#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */ + + +/* variable handling **********************************************************/ + +static s4 inline_new_variable(jitdata *jd, s4 type, s4 flags) +{ + s4 index; + s4 newcount; + + index = jd->vartop++; + if (index >= jd->varcount) { + newcount = jd->vartop * 2; /* XXX */ + jd->var = (varinfo*) DumpMemory::reallocate(jd->var, sizeof(varinfo) * jd->varcount, sizeof(varinfo) * newcount); + MZERO(jd->var + jd->varcount, varinfo, (newcount - jd->varcount)); + jd->varcount = newcount; + } + + jd->var[index].type = type; + jd->var[index].flags = flags; + + return index; +} + + +static s4 inline_new_variable_clone(jitdata *jd, jitdata *origjd, s4 origidx) +{ + varinfo *v; + s4 newidx; + + v = &(origjd->var[origidx]); + + newidx = inline_new_variable(jd, v->type, v->flags); + + jd->var[newidx].vv = v->vv; + + return newidx; +} + + +static s4 inline_new_temp_variable(jitdata *jd, s4 type) +{ + return inline_new_variable(jd, type, 0); +} + + +static s4 inline_translate_variable(jitdata *jd, jitdata *origjd, s4 *varmap, s4 index) +{ + s4 idx; + + idx = varmap[index]; + + if (idx < 0) { + idx = inline_new_variable_clone(jd, origjd, index); + varmap[index] = idx; + } + + return idx; +} + + +static s4 *create_variable_map(inline_node *callee) +{ + s4 *varmap; + s4 i, t; + s4 varindex; + s4 n_javaindex; + s4 avail; + varinfo *v; + + /* create the variable mapping */ + + varmap = (s4*) DumpMemory::allocate(sizeof(s4) * callee->jd->varcount); + for (i=0; ijd->varcount; ++i) + varmap[i] = -1; + + /* translate local variables */ + + for (i=0; im->maxlocals; ++i) { + for (t=0; t<5; ++t) { + varindex = callee->jd->local_map[5*i + t]; + if (varindex == UNUSED) + continue; + + v = &(callee->jd->var[varindex]); + assert(v->type == t || v->type == TYPE_VOID); /* XXX stack leaves VOID */ + v->type = t; /* XXX restore if it is TYPE_VOID */ + + avail = callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t]; + + if (avail == UNUSED) { + avail = inline_new_variable_clone(callee->ctx->resultjd, callee->jd, varindex); + callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t] = avail; + } + + varmap[varindex] = avail; + } + } + + /* for synchronized instance methods we need an extra local */ + + if (callee->synchronize && !(callee->m->flags & ACC_STATIC)) { + n_javaindex = callee->localsoffset - 1; + assert(n_javaindex >= 0); + assert(callee->parent); + assert(n_javaindex == callee->parent->localsoffset + callee->parent->m->maxlocals); + + avail = callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR]; + + if (avail == UNUSED) { + avail = inline_new_variable(callee->ctx->resultjd, TYPE_ADR, 0); + callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR] = avail; + } + + callee->synclocal = avail; + } + else { + callee->synclocal = UNUSED; + } + + return varmap; +} + + +/* basic block translation ****************************************************/ + +#define INLINE_RETURN_REFERENCE(callee) \ + ( (basicblock *) (ptrint) (0x333 + (callee)->depth) ) + + +static void inline_add_block_reference(inline_node *iln, basicblock **blockp) +{ + inline_target_ref *ref; + + ref = (inline_target_ref*) DumpMemory::allocate(sizeof(inline_target_ref)); + ref->ref.block = blockp; + ref->isnumber = false; + ref->next = iln->refs; + iln->refs = ref; +} + + +#if 0 +static void inline_add_blocknr_reference(inline_node *iln, s4 *nrp) +{ + inline_target_ref *ref; + + ref = (inline_target_ref*) DumpMemory::allocate(inline_target_ref); + ref->ref.nr = nrp; + ref->isnumber = true; + ref->next = iln->refs; + iln->refs = ref; +} +#endif + + +static void inline_block_translation(inline_node *iln, basicblock *o_bptr, basicblock *n_bptr) +{ + inline_context *ctx; + + ctx = iln->ctx; + assert(ctx->blockmap_index < ctx->master->cumul_blockmapcount); + + ctx->blockmap[ctx->blockmap_index].iln = iln; + ctx->blockmap[ctx->blockmap_index].o_block = o_bptr; + ctx->blockmap[ctx->blockmap_index].n_block = n_bptr; + + ctx->blockmap_index++; +} + + +static basicblock * inline_map_block(inline_node *iln, + basicblock *o_block, + inline_node *targetiln) +{ + inline_block_map *bm; + inline_block_map *bmend; + + assert(iln); + assert(targetiln); + + if (!o_block) + return NULL; + + bm = iln->ctx->blockmap; + bmend = bm + iln->ctx->blockmap_index; + + while (bm < bmend) { + assert(bm->iln && bm->o_block && bm->n_block); + if (bm->o_block == o_block && bm->iln == targetiln) + return bm->n_block; + bm++; + } + + assert(false); + return NULL; /* not reached */ +} + + +static void inline_resolve_block_refs(inline_target_ref **refs, + basicblock *o_bptr, + basicblock *n_bptr, + bool returnref) +{ + inline_target_ref *ref; + inline_target_ref *prev; + + prev = NULL; + for (ref = *refs; ref != NULL; ref = ref->next) { + if (ref->isnumber && !returnref) { + if (*(ref->ref.nr) == JAVALOCAL_FROM_RETADDR(o_bptr->nr)) { + *(ref->ref.nr) = JAVALOCAL_FROM_RETADDR(n_bptr->nr); + goto remove_ref; + } + } + else { + if (*(ref->ref.block) == o_bptr) { + *(ref->ref.block) = n_bptr; + goto remove_ref; + } + } + + /* skip this ref */ + + prev = ref; + continue; + +remove_ref: + /* remove this ref */ + + if (prev) { + prev->next = ref->next; + } + else { + *refs = ref->next; + } + } +} + + +/* basic block creation *******************************************************/ + +static basicblock * create_block(inline_node *container, + inline_node *iln, + inline_node *inner, + int indepth) +{ + basicblock *n_bptr; + inline_node *outer; + s4 i; + s4 depth; + s4 varidx; + s4 newvaridx; + + assert(container); + assert(iln); + assert(inner); + assert(indepth >= 0); + + n_bptr = container->inlined_basicblocks_cursor++; + assert(n_bptr); + assert((n_bptr - container->inlined_basicblocks) < container->cumul_basicblockcount); + + BASICBLOCK_INIT(n_bptr, iln->m); + + n_bptr->iinstr = container->inlined_iinstr_cursor; + n_bptr->next = n_bptr + 1; + n_bptr->nr = container->ctx->next_block_number++; + n_bptr->indepth = indepth; + n_bptr->flags = BBFINISHED; /* XXX */ + + /* set the inlineinfo of the new block */ + + if (iln->inline_start_instruction) + n_bptr->inlineinfo = iln->inline_start_instruction->sx.s23.s3.inlineinfo; + + if (indepth > container->ctx->maxinoutdepth) + container->ctx->maxinoutdepth = indepth; + + if (indepth) { + n_bptr->invars = (s4*) DumpMemory::allocate(sizeof(s4) * indepth); + + + for (i=0; iinvars[i] = -1; /* XXX debug */ + + /* pass-through variables enter the block */ + + outer = inner->parent; + while (outer != NULL) { + depth = outer->n_passthroughcount; + + assert(depth + inner->n_selfpassthroughcount <= indepth); + + for (i=0; in_selfpassthroughcount; ++i) { + varidx = inner->n_passthroughvars[i]; + newvaridx = + inline_new_variable_clone(container->ctx->resultjd, + outer->jd, + varidx); + n_bptr->invars[depth + i] = newvaridx; + outer->varmap[varidx] = newvaridx; + } + inner = outer; + outer = outer->parent; + } + } + else { + n_bptr->invars = NULL; + } + + /* XXX for the verifier. should not be here */ + + { + varinfo *dv; + + dv = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * (iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS)); + MZERO(dv, varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS); + n_bptr->inlocals = dv; + } + + return n_bptr; +} + + +static s4 *translate_javalocals(inline_node *iln, s4 *javalocals) +{ + s4 *jl; + s4 i, j; + + jl = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals); + + for (i=0; ijd->maxlocals; ++i) { + j = javalocals[i]; + if (j > UNUSED) + j = inline_translate_variable(iln->ctx->resultjd, iln->jd, iln->varmap, j); + jl[i] = j; + +#if 0 + if (j < UNUSED) { + /* an encoded returnAddress value - must be relocated */ + inline_add_blocknr_reference(iln, &(jl[i])); + } +#endif + } + + return jl; +} + + +static basicblock * create_body_block(inline_node *iln, + basicblock *o_bptr, s4 *varmap) +{ + basicblock *n_bptr; + s4 i; + + n_bptr = create_block(iln, iln, iln, + o_bptr->indepth + iln->n_passthroughcount); + + n_bptr->type = o_bptr->type; + n_bptr->flags = o_bptr->flags; + n_bptr->bitflags = o_bptr->bitflags; + + /* resolve references to this block */ + + inline_resolve_block_refs(&(iln->refs), o_bptr, n_bptr, false); + + /* translate the invars of the original block */ + + for (i=0; iindepth; ++i) { + n_bptr->invars[iln->n_passthroughcount + i] = + inline_translate_variable(iln->ctx->resultjd, iln->jd, + varmap, + o_bptr->invars[i]); + } + + /* translate javalocals info (not for dead code) */ + + if (n_bptr->flags >= BBREACHED) + n_bptr->javalocals = translate_javalocals(iln, o_bptr->javalocals); + + return n_bptr; +} + + +static basicblock * create_epilog_block(inline_node *caller, inline_node *callee, s4 *varmap) +{ + basicblock *n_bptr; + s4 retcount; + s4 idx; + + /* number of return variables */ + + retcount = (callee->n_resultlocal == -1 + && callee->m->parseddesc->returntype.type != TYPE_VOID) ? 1 : 0; + + /* start the epilog block */ + + n_bptr = create_block(caller, caller, callee, + callee->n_passthroughcount + retcount); + + /* resolve references to the return block */ + + inline_resolve_block_refs(&(callee->refs), + INLINE_RETURN_REFERENCE(callee), + n_bptr, + true); + + /* return variable */ + + if (retcount) { + idx = inline_new_variable(caller->ctx->resultjd, + callee->m->parseddesc->returntype.type, 0 /* XXX */); + n_bptr->invars[callee->n_passthroughcount] = idx; + varmap[callee->callerins->dst.varindex] = idx; + } + + /* set javalocals */ + + n_bptr->javalocals = (s4*) DumpMemory::allocate(sizeof(s4) * caller->jd->maxlocals); + MCOPY(n_bptr->javalocals, caller->javalocals, s4, caller->jd->maxlocals); + + /* set block flags & type */ + + n_bptr->flags = /* XXX original block flags */ BBFINISHED; + n_bptr->type = BBTYPE_STD; + + return n_bptr; +} + + +static void close_block(inline_node *iln, inline_node *inner, basicblock *n_bptr, s4 outdepth) +{ + inline_node *outer; + s4 i; + s4 depth; + s4 varidx; + + n_bptr->outdepth = outdepth; + n_bptr->outvars = (s4*) DumpMemory::allocate(sizeof(s4) * outdepth); + + for (i=0; ioutvars[i] = 0; /* XXX debug */ + + if (outdepth > iln->ctx->maxinoutdepth) + iln->ctx->maxinoutdepth = outdepth; + + /* pass-through variables leave the block */ + + outer = inner->parent; + while (outer != NULL) { + depth = outer->n_passthroughcount; + + assert(depth + inner->n_selfpassthroughcount <= outdepth); + + for (i=0; in_selfpassthroughcount; ++i) { + varidx = inner->n_passthroughvars[i]; + n_bptr->outvars[depth + i] = + inline_translate_variable(iln->ctx->resultjd, + outer->jd, + outer->varmap, + varidx); + } + inner = outer; + outer = outer->parent; + } +} + + +static void close_prolog_block(inline_node *iln, + basicblock *n_bptr, + inline_node *nextcall) +{ + /* XXX add original outvars! */ + close_block(iln, nextcall, n_bptr, nextcall->n_passthroughcount); + + /* pass-through variables */ + + DOLOG( printf("closed prolog block:\n"); + show_basicblock(iln->ctx->resultjd, n_bptr, SHOW_STACK); ); +} + + +static void close_body_block(inline_node *iln, + basicblock *n_bptr, + basicblock *o_bptr, + s4 *varmap, + s4 retcount, + s4 retidx) +{ + s4 i; + + close_block(iln, iln, n_bptr, iln->n_passthroughcount + o_bptr->outdepth + retcount); + + /* translate the outvars of the original block */ + + /* XXX reuse code */ + for (i=0; ioutdepth; ++i) { + n_bptr->outvars[iln->n_passthroughcount + i] = + inline_translate_variable(iln->ctx->resultjd, iln->jd, varmap, + o_bptr->outvars[i]); + } + + /* set the return variable, if any */ + + if (retcount) { + assert(retidx >= 0); + n_bptr->outvars[iln->n_passthroughcount + o_bptr->outdepth] = retidx; + } +} + + +/* inlined code generation ****************************************************/ + +static instruction * inline_instruction(inline_node *iln, + s4 opcode, + instruction *o_iptr) +{ + instruction *n_iptr; + + n_iptr = (iln->inlined_iinstr_cursor++); + assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); + + n_iptr->opc = opcode; + n_iptr->flags.bits = o_iptr->flags.bits & INS_FLAG_ID_MASK; + n_iptr->line = o_iptr->line; + + return n_iptr; +} + +static void inline_generate_sync_builtin(inline_node *iln, + inline_node *callee, + instruction *o_iptr, + s4 instancevar, + functionptr func) +{ + int syncvar; + instruction *n_ins; + + if (callee->m->flags & ACC_STATIC) { + /* ACONST */ + syncvar = inline_new_temp_variable(iln->ctx->resultjd, TYPE_ADR); + + n_ins = inline_instruction(iln, ICMD_ACONST, o_iptr); + n_ins->sx.val.c.cls = callee->m->clazz; + n_ins->dst.varindex = syncvar; + n_ins->flags.bits |= INS_FLAG_CLASS; + } + else { + syncvar = instancevar; + } + + assert(syncvar != UNUSED); + + /* MONITORENTER / MONITOREXIT */ + + n_ins = inline_instruction(iln, ICMD_BUILTIN, o_iptr); + n_ins->sx.s23.s3.bte = builtintable_get_internal(func); + n_ins->s1.argcount = 1; /* XXX add through-vars */ + n_ins->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4)); + n_ins->sx.s23.s2.args[0] = syncvar; +} + +static s4 emit_inlining_prolog(inline_node *iln, + inline_node *callee, + instruction *o_iptr, + s4 *varmap) +{ + methodinfo *calleem; + methoddesc *md; + int i; + int localindex; + int type; + instruction *n_ins; + insinfo_inline *insinfo; + s4 varindex; + + assert(iln && callee && o_iptr); + + calleem = callee->m; + md = calleem->parseddesc; + + /* INLINE_START instruction */ + + n_ins = inline_instruction(iln, ICMD_INLINE_START, o_iptr); + + insinfo = (insinfo_inline*) DumpMemory::allocate(sizeof(insinfo_inline)); + insinfo->method = callee->m; + insinfo->outer = iln->m; + insinfo->synclocal = callee->synclocal; + insinfo->synchronize = callee->synchronize; + insinfo->javalocals_start = NULL; + insinfo->javalocals_end = NULL; + + /* info about stack vars live at the INLINE_START */ + + insinfo->throughcount = callee->n_passthroughcount; + insinfo->paramcount = md->paramcount; + insinfo->stackvarscount = o_iptr->s1.argcount; + insinfo->stackvars = (s4*) DumpMemory::allocate(sizeof(s4) * insinfo->stackvarscount); + for (i=0; istackvarscount; ++i) + insinfo->stackvars[i] = iln->varmap[o_iptr->sx.s23.s2.args[i]]; + + /* info about the surrounding inlining */ + + if (iln->inline_start_instruction) + insinfo->parent = iln->inline_start_instruction->sx.s23.s3.inlineinfo; + else + insinfo->parent = NULL; + + /* finish the INLINE_START instruction */ + + n_ins->sx.s23.s3.inlineinfo = insinfo; + callee->inline_start_instruction = n_ins; + + DOLOG( printf("%sprolog: ", iln->indent); + show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); + + /* handle parameters for the inlined callee */ + + localindex = callee->localsoffset + md->paramslots; + + for (i=md->paramcount-1; i>=0; --i) { + assert(iln); + + type = md->paramtypes[i].type; + + localindex -= IS_2_WORD_TYPE(type) ? 2 : 1; + assert(callee->regdata); + + /* translate the argument variable */ + + varindex = varmap[o_iptr->sx.s23.s2.args[i]]; + assert(varindex != UNUSED); + + /* remove preallocation from the argument variable */ + + iln->ctx->resultjd->var[varindex].flags &= ~(PREALLOC | INMEMORY); + + /* check the instance slot against NULL */ + /* we don't need that for methods, as the verifier */ + /* ensures that they are only called for an uninit. object */ + /* (which may not be NULL). */ + + if (!callee->isstatic && i == 0 && calleem->name != utf_init) { + assert(type == TYPE_ADR); + n_ins = inline_instruction(iln, ICMD_CHECKNULL, o_iptr); + n_ins->s1.varindex = varindex; + n_ins->dst.varindex = n_ins->s1.varindex; + } + + /* store argument into local variable of inlined callee */ + + if (callee->jd->local_map[5*(localindex - callee->localsoffset) + type] != UNUSED) + { + /* this value is used in the callee */ + + if (i == 0 && callee->synclocal != UNUSED) { + /* we also need it for synchronization, so copy it */ + assert(type == TYPE_ADR); + n_ins = inline_instruction(iln, ICMD_COPY, o_iptr); + } + else { + n_ins = inline_instruction(iln, ICMD_ISTORE + type, o_iptr); + n_ins->sx.s23.s3.javaindex = UNUSED; + } + n_ins->s1.varindex = varindex; + n_ins->dst.varindex = iln->ctx->resultjd->local_map[5*localindex + type]; + assert(n_ins->dst.varindex != UNUSED); + } + else if (i == 0 && callee->synclocal != UNUSED) { + /* the value is not used inside the callee, but we need it for */ + /* synchronization */ + /* XXX In this case it actually makes no sense to create a */ + /* separate synchronization variable. */ + + n_ins = inline_instruction(iln, ICMD_NOP, o_iptr); + } + else { + /* this value is not used, pop it */ + + n_ins = inline_instruction(iln, ICMD_POP, o_iptr); + n_ins->s1.varindex = varindex; + } + + DOLOG( printf("%sprolog: ", iln->indent); + show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); + } + + /* COPY for synchronized instance methods */ + + if (callee->synclocal != UNUSED) { + n_ins = inline_instruction(iln, ICMD_COPY, o_iptr); + n_ins->s1.varindex = varmap[o_iptr->sx.s23.s2.args[0]]; + n_ins->dst.varindex = callee->synclocal; + + assert(n_ins->s1.varindex != UNUSED); + } + + if (callee->synchronize) { + inline_generate_sync_builtin(iln, callee, o_iptr, + (callee->isstatic) ? UNUSED : varmap[o_iptr->sx.s23.s2.args[0]], + LOCK_monitor_enter); + } + + /* INLINE_BODY instruction */ + + n_ins = inline_instruction(iln, ICMD_INLINE_BODY, callee->jd->basicblocks[0].iinstr); + n_ins->sx.s23.s3.inlineinfo = insinfo; + + return 0; /* XXX */ +} + + +static void emit_inlining_epilog(inline_node *iln, inline_node *callee, instruction *o_iptr) +{ + instruction *n_ins; + s4 *jl; + + assert(iln && callee && o_iptr); + assert(callee->inline_start_instruction); + + if (callee->synchronize) { + inline_generate_sync_builtin(iln, callee, o_iptr, + callee->synclocal, + LOCK_monitor_exit); + } + + /* INLINE_END instruction */ + + n_ins = inline_instruction(iln, ICMD_INLINE_END, o_iptr); + n_ins->sx.s23.s3.inlineinfo = callee->inline_start_instruction->sx.s23.s3.inlineinfo; + + /* set the javalocals */ + + jl = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals); + MCOPY(jl, iln->javalocals, s4, iln->jd->maxlocals); + n_ins->sx.s23.s3.inlineinfo->javalocals_end = jl; + + DOLOG( printf("%sepilog: ", iln->indent); + show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); ); +} + + +#define TRANSLATE_VAROP(vo) \ + n_iptr->vo.varindex = inline_translate_variable(jd, origjd, varmap, n_iptr->vo.varindex) + + +static void inline_clone_instruction(inline_node *iln, + jitdata *jd, + jitdata *origjd, + s4 *varmap, + instruction *o_iptr, + instruction *n_iptr) +{ + icmdtable_entry_t *icmdt; + builtintable_entry *bte; + methoddesc *md; + s4 i, j; + branch_target_t *table; + lookup_target_t *lookup; + inline_node *scope; + + *n_iptr = *o_iptr; + + icmdt = &(icmd_table[o_iptr->opc]); + + switch (icmdt->dataflow) { + case DF_0_TO_0: + break; + + case DF_3_TO_0: + TRANSLATE_VAROP(sx.s23.s3); + case DF_2_TO_0: + TRANSLATE_VAROP(sx.s23.s2); + case DF_1_TO_0: + TRANSLATE_VAROP(s1); + break; + + case DF_2_TO_1: + TRANSLATE_VAROP(sx.s23.s2); + case DF_1_TO_1: + case DF_COPY: + case DF_MOVE: + TRANSLATE_VAROP(s1); + case DF_0_TO_1: + TRANSLATE_VAROP(dst); + break; + + case DF_N_TO_1: + n_iptr->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_iptr->s1.argcount); + for (i=0; is1.argcount; ++i) { + n_iptr->sx.s23.s2.args[i] = + inline_translate_variable(jd, origjd, varmap, + o_iptr->sx.s23.s2.args[i]); + } + TRANSLATE_VAROP(dst); + break; + + case DF_INVOKE: + INSTRUCTION_GET_METHODDESC(n_iptr, md); +clone_call: + n_iptr->s1.argcount += iln->n_passthroughcount; + n_iptr->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_iptr->s1.argcount); + for (i=0; is1.argcount; ++i) { + n_iptr->sx.s23.s2.args[i] = + inline_translate_variable(jd, origjd, varmap, + o_iptr->sx.s23.s2.args[i]); + } + for (scope = iln; scope != NULL; scope = scope->parent) { + for (j = 0; j < scope->n_selfpassthroughcount; ++j) { + n_iptr->sx.s23.s2.args[i++] = + scope->parent->varmap[scope->n_passthroughvars[j]]; + } + } + if (md->returntype.type != TYPE_VOID) + TRANSLATE_VAROP(dst); + break; + + case DF_BUILTIN: + bte = n_iptr->sx.s23.s3.bte; + md = bte->md; + goto clone_call; + + default: + assert(0); + } + + switch (icmdt->controlflow) { + case CF_RET: + TRANSLATE_VAROP(s1); /* XXX should be handled by data-flow */ + /* FALLTHROUGH */ + case CF_IF: + case CF_GOTO: + inline_add_block_reference(iln, &(n_iptr->dst.block)); + break; + + case CF_JSR: + inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.jsrtarget.block)); + break; + + case CF_TABLE: + i = n_iptr->sx.s23.s3.tablehigh - n_iptr->sx.s23.s2.tablelow + 1 + 1 /* default */; + + table = (branch_target_t*) DumpMemory::allocate(sizeof(branch_target_t) * i); + MCOPY(table, o_iptr->dst.table, branch_target_t, i); + n_iptr->dst.table = table; + + while (--i >= 0) { + inline_add_block_reference(iln, &(table->block)); + table++; + } + break; + + case CF_LOOKUP: + inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.lookupdefault.block)); + + i = n_iptr->sx.s23.s2.lookupcount; + lookup = (lookup_target_t*) DumpMemory::allocate(sizeof(lookup_target_t) * i); + MCOPY(lookup, o_iptr->dst.lookup, lookup_target_t, i); + n_iptr->dst.lookup = lookup; + + while (--i >= 0) { + inline_add_block_reference(iln, &(lookup->target.block)); + lookup++; + } + break; + } + + /* XXX move this to dataflow section? */ + + switch (n_iptr->opc) { + case ICMD_ASTORE: +#if 0 + if (n_iptr->flags.bits & INS_FLAG_RETADDR) + inline_add_blocknr_reference(iln, &(n_iptr->sx.s23.s2.retaddrnr)); +#endif + /* FALLTHROUGH! */ + case ICMD_ISTORE: + case ICMD_LSTORE: + case ICMD_FSTORE: + case ICMD_DSTORE: + stack_javalocals_store(n_iptr, iln->javalocals); + break; + } +} + + +static void inline_rewrite_method(inline_node *iln) +{ + basicblock *o_bptr; + s4 len; + instruction *o_iptr; + instruction *n_iptr; + inline_node *nextcall; + basicblock *n_bptr; + inline_block_map *bm; + int i; + int icount; + jitdata *resultjd; + jitdata *origjd; + char indent[100]; /* XXX debug */ + s4 retcount; + s4 retidx; + + assert(iln); + + resultjd = iln->ctx->resultjd; + origjd = iln->jd; + + n_bptr = NULL; + nextcall = iln->children; + + /* XXX debug */ + for (i=0; idepth; ++i) + indent[i] = '\t'; + indent[i] = 0; + iln->indent = indent; + + DOLOG( printf("%srewriting: ", indent); method_println(iln->m); + printf("%s(passthrough: %d+%d)\n", + indent, iln->n_passthroughcount - iln->n_selfpassthroughcount, + iln->n_passthroughcount); ); + + /* set memory cursors */ + + iln->inlined_iinstr_cursor = iln->inlined_iinstr; + iln->inlined_basicblocks_cursor = iln->inlined_basicblocks; + + /* allocate temporary buffers */ + + iln->javalocals = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals); + + /* loop over basic blocks */ + + o_bptr = iln->jd->basicblocks; + for (; o_bptr; o_bptr = o_bptr->next) { + + if (o_bptr->flags < BBREACHED) { + + /* ignore the dummy end block */ + + if (o_bptr->icount == 0 && o_bptr->next == NULL) { + /* enter the following block as translation, for exception handler ranges */ + inline_block_translation(iln, o_bptr, iln->inlined_basicblocks_cursor); + continue; + } + + DOLOG( + printf("%s* skipping old L%03d (flags=%d, type=%d, oid=%d) of ", + indent, + o_bptr->nr, o_bptr->flags, o_bptr->type, + o_bptr->indepth); + method_println(iln->m); + ); + + n_bptr = create_body_block(iln, o_bptr, iln->varmap); + + /* enter it in the blockmap */ + + inline_block_translation(iln, o_bptr, n_bptr); + + close_body_block(iln, n_bptr, o_bptr, iln->varmap, 0, -1); + continue; + } + + len = o_bptr->icount; + o_iptr = o_bptr->iinstr; + + DOLOG( + printf("%s* rewriting old L%03d (flags=%d, type=%d, oid=%d) of ", + indent, + o_bptr->nr, o_bptr->flags, o_bptr->type, + o_bptr->indepth); + method_println(iln->m); + show_basicblock(iln->jd, o_bptr, SHOW_STACK); + ); + + if (iln->blockbefore || o_bptr != iln->jd->basicblocks) { + /* create an inlined clone of this block */ + + n_bptr = create_body_block(iln, o_bptr, iln->varmap); + icount = 0; + + /* enter it in the blockmap */ + + inline_block_translation(iln, o_bptr, n_bptr); + + /* initialize the javalocals */ + + MCOPY(iln->javalocals, n_bptr->javalocals, s4, iln->jd->maxlocals); + } + else { + s4 *jl; + + /* continue caller block */ + + n_bptr = iln->inlined_basicblocks_cursor - 1; + icount = n_bptr->icount; + + /* translate the javalocals */ + + jl = translate_javalocals(iln, o_bptr->javalocals); + iln->inline_start_instruction->sx.s23.s3.inlineinfo->javalocals_start = jl; + + MCOPY(iln->javalocals, jl, s4, iln->jd->maxlocals); + } + + /* iterate over the ICMDs of this block */ + + retcount = 0; + retidx = UNUSED; + + while (--len >= 0) { + + DOLOG( fputs(indent, stdout); show_icmd(iln->jd, o_iptr, false, SHOW_STACK); + printf("\n") ); + + /* handle calls that will be inlined */ + + if (nextcall && o_iptr == nextcall->callerins) { + + /* write the inlining prolog */ + + (void) emit_inlining_prolog(iln, nextcall, o_iptr, iln->varmap); + icount += nextcall->prolog_instructioncount; + + /* end current block, or glue blocks together */ + + n_bptr->icount = icount; + + if (nextcall->blockbefore) { + close_prolog_block(iln, n_bptr, nextcall); + } + else { + /* XXX */ + } + + /* check if the result is a local variable */ + + if (nextcall->m->parseddesc->returntype.type != TYPE_VOID + && o_iptr->dst.varindex < iln->jd->localcount) + { + nextcall->n_resultlocal = iln->varmap[o_iptr->dst.varindex]; + } + else + nextcall->n_resultlocal = -1; + + /* set memory pointers in the callee */ + + nextcall->inlined_iinstr = iln->inlined_iinstr_cursor; + nextcall->inlined_basicblocks = iln->inlined_basicblocks_cursor; + + /* recurse */ + + DOLOG( printf("%sentering inline ", indent); + show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") ); + + inline_rewrite_method(nextcall); + + DOLOG( printf("%sleaving inline ", indent); + show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") ); + + /* update memory cursors */ + + assert(nextcall->inlined_iinstr_cursor + <= iln->inlined_iinstr_cursor + nextcall->cumul_instructioncount); + assert(nextcall->inlined_basicblocks_cursor + == iln->inlined_basicblocks_cursor + nextcall->cumul_basicblockcount); + iln->inlined_iinstr_cursor = nextcall->inlined_iinstr_cursor; + iln->inlined_basicblocks_cursor = nextcall->inlined_basicblocks_cursor; + + /* start new block, or glue blocks together */ + + if (nextcall->blockafter) { + n_bptr = create_epilog_block(iln, nextcall, iln->varmap); + icount = 0; + } + else { + n_bptr = iln->inlined_basicblocks_cursor - 1; + icount = n_bptr->icount; + /* XXX */ + } + + /* emit inlining epilog */ + + emit_inlining_epilog(iln, nextcall, o_iptr); + icount += nextcall->epilog_instructioncount; + + /* proceed to next call */ + + nextcall = nextcall->next; + } + else { + n_iptr = (iln->inlined_iinstr_cursor++); + assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); + + switch (o_iptr->opc) { + case ICMD_RETURN: + if (iln->depth == 0) + goto default_clone; + goto return_tail; + + case ICMD_IRETURN: + case ICMD_ARETURN: + case ICMD_LRETURN: + case ICMD_FRETURN: + case ICMD_DRETURN: + if (iln->depth == 0) + goto default_clone; + retcount = 1; + retidx = iln->varmap[o_iptr->s1.varindex]; + if (iln->n_resultlocal != -1) { + /* store result in a local variable */ + + DOLOG( printf("USING RESULTLOCAL %d\n", iln->n_resultlocal); ); + /* This relies on the same sequence of types for */ + /* ?STORE and ?RETURN opcodes. */ + n_iptr->opc = ICMD_ISTORE + (o_iptr->opc - ICMD_IRETURN); + n_iptr->s1.varindex = retidx; + n_iptr->dst.varindex = iln->n_resultlocal; + n_iptr->sx.s23.s3.javaindex = UNUSED; /* XXX set real javaindex? */ + + retcount = 0; + retidx = UNUSED; + + n_iptr = (iln->inlined_iinstr_cursor++); + assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); + icount++; + } + else if ((retidx < resultjd->localcount && iln->blockafter) + || !iln->blockafter) /* XXX do we really always need the MOVE? */ + { + /* local must not become outvar, insert a MOVE */ + + n_iptr->opc = ICMD_MOVE; + n_iptr->s1.varindex = retidx; + retidx = inline_new_temp_variable(resultjd, + resultjd->var[retidx].type); + n_iptr->dst.varindex = retidx; + + n_iptr = (iln->inlined_iinstr_cursor++); + assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount); + icount++; + } +return_tail: + if (iln->blockafter) { + n_iptr->opc = ICMD_GOTO; + n_iptr->dst.block = INLINE_RETURN_REFERENCE(iln); + inline_add_block_reference(iln, &(n_iptr->dst.block)); + } + else { + n_iptr->opc = ICMD_NOP; + } + break; +#if 0 + if (o_bptr->next == NULL || (o_bptr->next->icount==0 && o_bptr->next->next == NULL)) { + n_iptr->opc = ICMD_NOP; + break; + } + goto default_clone; + break; +#endif + + default: +default_clone: + inline_clone_instruction(iln, resultjd, iln->jd, iln->varmap, o_iptr, n_iptr); + } + + DOLOG( fputs(indent, stdout); show_icmd(resultjd, n_iptr, false, SHOW_STACK); + printf("\n");); + + icount++; + } + + o_iptr++; + } + + /* end of basic block */ + + if (iln->blockafter || (o_bptr->next && o_bptr->next->next)) { + close_body_block(iln, n_bptr, o_bptr, iln->varmap, retcount, retidx); + n_bptr->icount = icount; + + DOLOG( printf("closed body block:\n"); + show_basicblock(resultjd, n_bptr, SHOW_STACK); ); + } + else { + n_bptr->icount = icount; + assert(iln->parent); + if (retidx != UNUSED) + iln->parent->varmap[iln->callerins->dst.varindex] = retidx; + } + } + + bm = iln->ctx->blockmap; + for (i=0; ictx->blockmap_index; ++i, ++bm) { + assert(bm->iln && bm->o_block && bm->n_block); + if (bm->iln == iln) + inline_resolve_block_refs(&(iln->refs), bm->o_block, bm->n_block, false); + } + +#if !defined(NDEBUG) + if (iln->refs) { + inline_target_ref *ref; + ref = iln->refs; + while (ref) { + if (!iln->depth || ref->isnumber || *(ref->ref.block) != INLINE_RETURN_REFERENCE(iln)) { + DOLOG( printf("XXX REMAINING REF at depth %d: %p\n", iln->depth, + (void*)*(ref->ref.block)) ); + assert(false); + } + ref = ref->next; + } + } +#endif +} + + +static exception_entry * inline_exception_tables(inline_node *iln, + exception_entry *n_extable, + exception_entry **prevextable) +{ + inline_node *child; + inline_node *scope; + exception_entry *et; + + assert(iln); + assert(n_extable); + assert(prevextable); + + child = iln->children; + if (child) { + do { + n_extable = inline_exception_tables(child, n_extable, prevextable); + child = child->next; + } while (child != iln->children); + } + + et = iln->jd->exceptiontable; + for (; et != NULL; et = et->down) { + assert(et); + MZERO(n_extable, exception_entry, 1); + n_extable->start = inline_map_block(iln, et->start , iln); + n_extable->end = inline_map_block(iln, et->end , iln); + n_extable->handler = inline_map_block(iln, et->handler, iln); + n_extable->catchtype = et->catchtype; + + if (*prevextable) { + (*prevextable)->down = n_extable; + } + *prevextable = n_extable; + + n_extable++; + } + + if (iln->handler_monitorexit) { + exception_entry **activehandlers; + + MZERO(n_extable, exception_entry, 1); + n_extable->start = iln->inlined_basicblocks; + n_extable->end = iln->inlined_basicblocks_cursor; + n_extable->handler = iln->handler_monitorexit; + n_extable->catchtype.any = NULL; /* finally */ + + if (*prevextable) { + (*prevextable)->down = n_extable; + } + *prevextable = n_extable; + + n_extable++; + + /* We have to protect the created handler with the same handlers */ + /* that protect the method body itself. */ + + for (scope = iln; scope->parent != NULL; scope = scope->parent) { + + activehandlers = scope->o_handlers; + assert(activehandlers); + + while (*activehandlers) { + + assert(scope->parent); + + MZERO(n_extable, exception_entry, 1); + n_extable->start = iln->handler_monitorexit; + n_extable->end = iln->handler_monitorexit + 1; /* XXX ok in this case? */ + n_extable->handler = inline_map_block(scope->parent, + (*activehandlers)->handler, + scope->parent); + n_extable->catchtype = (*activehandlers)->catchtype; + + if (*prevextable) { + (*prevextable)->down = n_extable; + } + *prevextable = n_extable; + + n_extable++; + activehandlers++; + } + } + } + + return n_extable; +} + + +static void inline_locals(inline_node *iln) +{ + inline_node *child; + + assert(iln); + + iln->varmap = create_variable_map(iln); + + child = iln->children; + if (child) { + do { + inline_locals(child); + child = child->next; + } while (child != iln->children); + } + + if (iln->regdata->memuse > iln->ctx->resultjd->rd->memuse) + iln->ctx->resultjd->rd->memuse = iln->regdata->memuse; + if (iln->regdata->argintreguse > iln->ctx->resultjd->rd->argintreguse) + iln->ctx->resultjd->rd->argintreguse = iln->regdata->argintreguse; + if (iln->regdata->argfltreguse > iln->ctx->resultjd->rd->argfltreguse) + iln->ctx->resultjd->rd->argfltreguse = iln->regdata->argfltreguse; +} + + +static void inline_interface_variables(inline_node *iln) +{ + basicblock *bptr; + jitdata *resultjd; + s4 i; + varinfo *v; + + resultjd = iln->ctx->resultjd; + + resultjd->interface_map = (interface_info*) DumpMemory::allocate(sizeof(interface_info) * 5 * iln->ctx->maxinoutdepth); + for (i=0; i<5*iln->ctx->maxinoutdepth; ++i) + resultjd->interface_map[i].flags = UNUSED; + + for (bptr = resultjd->basicblocks; bptr != NULL; bptr = bptr->next) { + assert(bptr->indepth <= iln->ctx->maxinoutdepth); + assert(bptr->outdepth <= iln->ctx->maxinoutdepth); + + for (i=0; iindepth; ++i) { + v = &(resultjd->var[bptr->invars[i]]); + v->flags |= INOUT; + if (v->type == TYPE_RET) + v->flags |= PREALLOC; + else + v->flags &= ~PREALLOC; + v->flags &= ~INMEMORY; + assert(bptr->invars[i] >= resultjd->localcount); + + if (resultjd->interface_map[5*i + v->type].flags == UNUSED) { + resultjd->interface_map[5*i + v->type].flags = v->flags; + } + else { + resultjd->interface_map[5*i + v->type].flags |= v->flags; + } + } + + for (i=0; ioutdepth; ++i) { + v = &(resultjd->var[bptr->outvars[i]]); + v->flags |= INOUT; + if (v->type == TYPE_RET) + v->flags |= PREALLOC; + else + v->flags &= ~PREALLOC; + v->flags &= ~INMEMORY; + assert(bptr->outvars[i] >= resultjd->localcount); + + if (resultjd->interface_map[5*i + v->type].flags == UNUSED) { + resultjd->interface_map[5*i + v->type].flags = v->flags; + } + else { + resultjd->interface_map[5*i + v->type].flags |= v->flags; + } + } + } +} + + +static void inline_write_exception_handlers(inline_node *master, inline_node *iln) +{ + basicblock *n_bptr; + instruction *n_ins; + inline_node *child; + builtintable_entry *bte; + s4 exvar; + s4 syncvar; + s4 i; + + child = iln->children; + if (child) { + do { + inline_write_exception_handlers(master, child); + child = child->next; + } while (child != iln->children); + } + + if (iln->synchronize) { + /* create the monitorexit handler */ + n_bptr = create_block(master, iln, iln, + iln->n_passthroughcount + 1); + n_bptr->type = BBTYPE_EXH; + n_bptr->flags = BBFINISHED; + + exvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0); + n_bptr->invars[iln->n_passthroughcount] = exvar; + + iln->handler_monitorexit = n_bptr; + + /* ACONST / ALOAD */ + + n_ins = master->inlined_iinstr_cursor++; + if (iln->m->flags & ACC_STATIC) { + n_ins->opc = ICMD_ACONST; + n_ins->sx.val.c.cls = iln->m->clazz; + n_ins->flags.bits = INS_FLAG_CLASS; + } + else { + n_ins->opc = ICMD_ALOAD; + n_ins->s1.varindex = iln->synclocal; + assert(n_ins->s1.varindex != UNUSED); + } + /* XXX could be PREALLOCed for builtin call */ + syncvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0); + n_ins->dst.varindex = syncvar; + n_ins->line = 0; + + /* MONITOREXIT */ + + bte = builtintable_get_internal(LOCK_monitor_exit); + + n_ins = master->inlined_iinstr_cursor++; + n_ins->opc = ICMD_BUILTIN; + n_ins->s1.argcount = 1 + iln->n_passthroughcount + 1; + n_ins->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_ins->s1.argcount); + n_ins->sx.s23.s2.args[0] = syncvar; + for (i=0; i < iln->n_passthroughcount + 1; ++i) { + n_ins->sx.s23.s2.args[1 + i] = n_bptr->invars[i]; + } + n_ins->sx.s23.s3.bte = bte; + n_ins->line = 0; + + /* ATHROW */ + + n_ins = master->inlined_iinstr_cursor++; + n_ins->opc = ICMD_ATHROW; + n_ins->flags.bits = 0; + n_ins->s1.varindex = exvar; + n_ins->line = 0; + + /* close basic block */ + + close_block(iln, iln, n_bptr, iln->n_passthroughcount); + n_bptr->icount = 3; + } +} + + +/* second pass driver *********************************************************/ + +static bool inline_transform(inline_node *iln, jitdata *jd) +{ + instruction *n_ins; + basicblock *n_bb; + basicblock *n_bptr; + exception_entry *n_ext; + exception_entry *prevext; + codegendata *n_cd; + jitdata *n_jd; + s4 i; +#if defined(INLINE_VERIFY_RESULT) + static int debug_verify_inlined_code = 1; +#endif +#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) + static int debug_counter = 0; +#endif + + DOLOG( dump_inline_tree(iln, 0); ); + + assert(iln && jd); + + n_ins = (instruction*) DumpMemory::allocate(sizeof(instruction) * iln->cumul_instructioncount); + MZERO(n_ins, instruction, iln->cumul_instructioncount); + iln->inlined_iinstr = n_ins; + + n_bb = (basicblock*) DumpMemory::allocate(sizeof(basicblock) * iln->cumul_basicblockcount); + MZERO(n_bb, basicblock, iln->cumul_basicblockcount); + iln->inlined_basicblocks = n_bb; + + iln->ctx->blockmap = (inline_block_map*) DumpMemory::allocate(sizeof(inline_block_map) * iln->cumul_blockmapcount); + + n_jd = jit_jitdata_new(iln->m); + n_jd->flags = jd->flags; + n_jd->code->optlevel = jd->code->optlevel; + iln->ctx->resultjd = n_jd; + + reg_setup(n_jd); + + /* create the local_map */ + + n_jd->local_map = (s4*) DumpMemory::allocate(sizeof(s4) * 5 * iln->cumul_maxlocals); + for (i=0; i<5*iln->cumul_maxlocals; ++i) + n_jd->local_map[i] = UNUSED; + + /* create / coalesce local variables */ + + n_jd->varcount = 0; + n_jd->vartop = 0; + n_jd->var = NULL; + + inline_locals(iln); + + n_jd->localcount = n_jd->vartop; + + /* extra variables for verification (debugging) */ + +#if defined(INLINE_VERIFY_RESULT) + if (debug_verify_inlined_code) { + n_jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + 100 /* XXX m->maxstack */; + if (n_jd->vartop > n_jd->varcount) { + /* XXX why? */ + n_jd->var = (varinfo*) DumpMemory::realloc(n_jd->var, sizeof(varinfo) * n_jd->varcount, sizeof(varinfo) * n_jd->vartop); + n_jd->varcount = n_jd->vartop; + } + } +#endif /* defined(INLINE_VERIFY_RESULT) */ + + /* write inlined code */ + + inline_rewrite_method(iln); + + /* create exception handlers */ + + inline_write_exception_handlers(iln, iln); + + /* write the dummy end block */ + + n_bptr = create_block(iln, iln, iln, 0); + n_bptr->flags = BBUNDEF; + n_bptr->type = BBTYPE_STD; + + /* store created code in jitdata */ + + n_jd->basicblocks = iln->inlined_basicblocks; + n_jd->instructioncount = iln->cumul_instructioncount; + n_jd->instructions = iln->inlined_iinstr; + + /* link the basic blocks (dummy end block is not counted) */ + + n_jd->basicblockcount = (iln->inlined_basicblocks_cursor - iln->inlined_basicblocks) - 1; + for (i=0; ibasicblockcount + 1; ++i) + n_jd->basicblocks[i].next = &(n_jd->basicblocks[i+1]); + if (i) + n_jd->basicblocks[i-1].next = NULL; + + /* check basicblock numbers */ + +#if !defined(NDEBUG) + jit_check_basicblock_numbers(n_jd); +#endif + + /* create the exception table */ + + if (iln->cumul_exceptiontablelength) { + exception_entry *tableend; + + n_ext = (exception_entry*) DumpMemory::allocate(sizeof(exception_entry) * iln->cumul_exceptiontablelength); + prevext = NULL; + tableend = inline_exception_tables(iln, n_ext, &prevext); + assert(tableend == n_ext + iln->cumul_exceptiontablelength); + if (prevext) + prevext->down = NULL; + + n_jd->exceptiontablelength = iln->cumul_exceptiontablelength; + n_jd->exceptiontable = n_ext; + } + else { + n_ext = NULL; + } + + /*******************************************************************************/ + + n_cd = n_jd->cd; + memcpy(n_cd, jd->cd, sizeof(codegendata)); + + n_cd->method = NULL; /* XXX */ + n_jd->maxlocals = iln->cumul_maxlocals; + n_jd->maxinterfaces = iln->ctx->maxinoutdepth; + + inline_post_process(n_jd); + + inline_interface_variables(iln); + + /* for debugging, verify the inlined result */ + +#if defined(INLINE_VERIFY_RESULT) + if (debug_verify_inlined_code) { + debug_verify_inlined_code = 0; + DOLOG( printf("VERIFYING INLINED RESULT...\n"); fflush(stdout); ); + if (!typecheck(n_jd)) { + exceptions_clear_exception(); + DOLOG( printf("XXX INLINED RESULT DID NOT PASS VERIFIER XXX\n") ); + return false; + } + else { + DOLOG( printf("VERIFICATION PASSED.\n") ); + } + debug_verify_inlined_code = 1; + } +#endif /* defined(INLINE_VERIFY_RESULT) */ + + /* we need bigger free memory stacks (XXX these should not be allocated in reg_setup) */ + + n_jd->rd->freemem = (s4*) DumpMemory::allocate(sizeof(s4) * (iln->ctx->maxinoutdepth + 1000)) /* XXX max vars/block */; + +#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) + if ( (n_jd->instructioncount >= opt_InlineMinSize) + && (n_jd->instructioncount <= opt_InlineMaxSize)) + { + if (debug_counter < opt_InlineCount) +#endif /* defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) */ + { + /* install the inlined result */ + + *jd->code = *n_jd->code; + n_jd->code = jd->code; + *jd = *n_jd; + + /* statistics and logging */ + +#if !defined(NDEBUG) + inline_stat_roots++; + + DOLOG_SHORT( + printf("==== %d.INLINE ==================================================================\n", + debug_counter); + printf("\ninline tree:\n"); + dump_inline_tree(iln, 0); + n_jd->flags |= JITDATA_FLAG_SHOWINTERMEDIATE | JITDATA_FLAG_SHOWDISASSEMBLE; + /* debug_dump_inlined_code(iln, n_method, n_cd, n_rd); */ + printf("-------- DONE -----------------------------------------------------------\n"); + fflush(stdout); + ); +#endif + } + +#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) + debug_counter++; + } +#endif + return true; +} + + +/******************************************************************************/ +/* FIRST PASS: build inlining tree */ +/******************************************************************************/ + + +/* inline_pre_parse_heuristics ************************************************* + + Perform heuristic checks whether a call site should be inlined. + These checks are evaluated before the callee has been parsed and analysed. + + IN: + caller...........inlining node of the caller + callee...........the called method + site.............information on the call site + + RETURN VALUE: + true........consider for inlining + false.......don't inline + +*******************************************************************************/ + +static bool inline_pre_parse_heuristics(const inline_node *caller, + const methodinfo *callee, + inline_site *site) +{ +#if defined(INLINE_MAX_DEPTH) + if (caller->depth >= INLINE_MAX_DEPTH) + return false; +#endif + + return true; +} + + +/* inline_post_parse_heuristics ************************************************ + + Perform heuristic checks whether a call site should be inlined. + These checks are evaluated after the callee has been parsed and analysed. + + IN: + caller...........inlining node of the caller (const) + callee...........the called method (const) + + RETURN VALUE: + true........consider for inlining + false.......don't inline + +*******************************************************************************/ + +static bool inline_post_parse_heuristics(const inline_node *caller, + const inline_node *callee) +{ + return true; +} + + +/* inline_afterwards_heuristics ************************************************ + + Perform heuristic checks whether a call site should be inlined. + These checks are evaluated after the inlining plan for the callee has + been made. + + IN: + caller...........inlining node of the caller (const) + callee...........the called method (const) + + RETURN VALUE: + true........consider for inlining + false.......don't inline + +*******************************************************************************/ + +static bool inline_afterwards_heuristics(const inline_node *caller, + const inline_node *callee) +{ + inline_node *cumulator; + +#if defined(INLINE_DEPTH_FIRST) + cumulator = caller; +#else + cumulator = caller->ctx->master; +#endif + + if (0 +#if defined(INLINE_MAX_BLOCK_EXPANSION) + || (cumulator->cumul_basicblockcount + callee->cumul_basicblockcount + > INLINE_MAX_BLOCK_EXPANSION*caller->ctx->master->jd->basicblockcount) +#endif +#if defined(INLINE_MAX_ICMD_EXPANSION) + || (cumulator->cumul_instructioncount + callee->cumul_instructioncount + > INLINE_MAX_ICMD_EXPANSION*caller->ctx->master->jd->instructioncount) +#endif + ) + { + return false; + } + + return true; +} + + +/* inline_is_monomorphic ******************************************************* + + Check if the given call site can be proven to be monomorphic. + + IN: + callee...........the called method + call.............the invocation instruction + + OUT: + site->speculative.....flags whether the inlining is speculative + (only defined if return value is true) + + RETURN VALUE: + true if the call site is (currently) monomorphic, + false if not or unknown + +*******************************************************************************/ + +static bool inline_is_monomorphic(const methodinfo *callee, + const instruction *call, + inline_site *site) +{ + if ((callee->flags & (ACC_STATIC | ACC_FINAL | ACC_PRIVATE) + || call->opc == ICMD_INVOKESPECIAL)) + { + site->speculative = false; + return true; + } + + /* XXX search single implementation for abstract monomorphics */ + + if ((callee->flags & (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED + | ACC_ABSTRACT)) + == (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED)) + { + if (1) { + DOLOG( printf("SPECULATIVE INLINE: "); method_println((methodinfo*)callee); ); + site->speculative = true; + + return true; + } + } + + /* possibly polymorphic call site */ + + return false; +} + + +/* inline_can_inline *********************************************************** + + Check if inlining of the given call site is possible. + + IN: + caller...........inlining node of the caller + callee...........the called method + call.............the invocation instruction + + OUT: + site->speculative.....flags whether the inlining is speculative + (only defined if return value is true) + + RETURN VALUE: + true if inlining is possible, false if not + +*******************************************************************************/ + +static bool inline_can_inline(const inline_node *caller, + const methodinfo *callee, + const instruction *call, + inline_site *site) +{ + const inline_node *active; + + /* cannot inline native methods */ + + if (callee->flags & ACC_NATIVE) + return false; + + /* cannot inline possibly polymorphic calls */ + + if (!inline_is_monomorphic(callee, call, site)) + return false; + + /* cannot inline recursive calls */ + + for (active = caller; active; active = active->parent) { + if (callee == active->m) { + DOLOG( printf("RECURSIVE!\n") ); + return false; + } + } + + /* inlining is possible */ + + return true; +} + + +/* inline_create_callee_node *************************************************** + + Create an inlining node for the given callee. + + IN: + caller...........inlining node of the caller (const) + callee...........the called method + + RETURN VALUE: + the new inlining node + +*******************************************************************************/ + +static inline_node * inline_create_callee_node(const inline_node *caller, + methodinfo *callee) +{ + inline_node *cn; /* the callee inline_node */ + + cn = (inline_node*) DumpMemory::allocate(sizeof(inline_node)); + MZERO(cn, inline_node, 1); + + cn->depth = caller->depth + 1; + cn->ctx = caller->ctx; + cn->m = callee; + cn->synchronize = (callee->flags & ACC_SYNCHRONIZED); + cn->isstatic = (callee->flags & ACC_STATIC); + + return cn; +} + + +/* inline_set_callee_properties ************************************************ + + Set properties of the inlined call site. + + IN: + caller...........inlining node of the caller (const) + cn...............the called method + site.............info about the call site (const) + + OUT: + *cn..............has the properties set + +*******************************************************************************/ + +static void inline_set_callee_properties(const inline_node *caller, + inline_node *cn, + const inline_site *site) +{ + s4 argi; + s4 i, j; + basicblock *bptr; + + /* set info about the call site */ + + cn->callerblock = site->bptr; + cn->callerins = site->iptr; + cn->callerpc = site->pc; + cn->o_handlers = site->handlers; + cn->n_handlercount = caller->n_handlercount + site->nhandlers; + + /* determine if we need basic block boundaries before/after */ + + cn->blockbefore = false; + cn->blockafter = false; + + if (cn->jd->branchtoentry) + cn->blockbefore = true; + + if (cn->jd->branchtoend) + cn->blockafter = true; + + if (cn->jd->returncount > 1) + cn->blockafter = true; + + /* XXX make safer and reusable (maybe store last real block) */ + for (bptr = cn->jd->basicblocks; bptr && bptr->next && bptr->next->next; bptr = bptr->next) + ; + + if (cn->jd->returnblock != bptr) + cn->blockafter = true; + + /* info about the callee */ + + cn->localsoffset = caller->localsoffset + caller->m->maxlocals; + cn->prolog_instructioncount = cn->m->parseddesc->paramcount + 2; + cn->epilog_instructioncount = 1; /* INLINE_END */ + cn->extra_instructioncount = 0; + + /* we need a CHECKNULL for instance methods, except for */ + + if (!cn->isstatic && cn->m->name != utf_init) + cn->prolog_instructioncount += 1; + + /* deal with synchronized callees */ + + if (cn->synchronize) { + methoddesc *md; + builtintable_entry *bte; + + /* we need basic block boundaries because of the handler */ + + cn->blockbefore = true; + cn->blockafter = true; + + /* for synchronized static methods */ + /* we need an ACONST, MONITORENTER in the prolog */ + /* and ACONST, MONITOREXIT in the epilog */ + + /* for synchronized instance methods */ + /* we need an COPY, MONITORENTER in the prolog */ + /* and MONITOREXIT in the epilog */ + + if (cn->isstatic) { + cn->prolog_instructioncount += 2; + cn->epilog_instructioncount += 2; + } + else { + cn->prolog_instructioncount += 2; + cn->epilog_instructioncount += 1; + cn->localsoffset += 1; + } + + /* and exception handler */ + /* ALOAD, builtin_monitorexit, ATHROW */ + + cn->extra_instructioncount += 3; + + /* exception table entries */ + + cn->extra_exceptiontablelength = 1 + cn->n_handlercount; + + /* add exception handler block */ + + cn->cumul_basicblockcount_root++; + + /* we must call the builtins */ + + bte = builtintable_get_internal(LOCK_monitor_enter); + md = bte->md; + if (md->memuse > cn->regdata->memuse) + cn->regdata->memuse = md->memuse; + if (md->argintreguse > cn->regdata->argintreguse) + cn->regdata->argintreguse = md->argintreguse; + + bte = builtintable_get_internal(LOCK_monitor_exit); + md = bte->md; + if (md->memuse > cn->regdata->memuse) + cn->regdata->memuse = md->memuse; + if (md->argintreguse > cn->regdata->argintreguse) + cn->regdata->argintreguse = md->argintreguse; + } + + /* determine pass-through variables */ + + i = site->iptr->s1.argcount - cn->m->parseddesc->paramcount; /* max # of pass-though vars */ + + cn->n_passthroughvars = (s4*) DumpMemory::allocate(sizeof(s4) * i); + j = 0; + for (argi = site->iptr->s1.argcount - 1; argi >= cn->m->parseddesc->paramcount; --argi) { + s4 idx = site->iptr->sx.s23.s2.args[argi]; + if (idx >= caller->jd->localcount) { + cn->n_passthroughvars[j] = idx; + j++; + } + else { + DOLOG( printf("PASSING THROUGH LOCAL VARIABLE %d\n", idx); ); + } + } + assert(j <= i); + cn->n_selfpassthroughcount = j; + cn->n_passthroughcount = caller->n_passthroughcount + cn->n_selfpassthroughcount; +} + + +/* inline_cumulate_counters **************************************************** + + Cumulate counters after a node has been decided to become inlined. + + IN: + caller...........inlining node of the caller + callee...........inlining node of the callee (const) + + OUT: + *caller..........gets cumulated values added + +*******************************************************************************/ + +static void inline_cumulate_counters(inline_node *caller, + const inline_node *cn) +{ + caller->cumul_instructioncount += cn->prolog_instructioncount; + caller->cumul_instructioncount += cn->epilog_instructioncount; + caller->cumul_instructioncount += cn->extra_instructioncount; + caller->cumul_instructioncount += cn->cumul_instructioncount - 1 /*invoke*/; + + caller->cumul_basicblockcount += cn->cumul_basicblockcount; + caller->cumul_basicblockcount_root += cn->cumul_basicblockcount_root; + caller->cumul_blockmapcount += cn->cumul_blockmapcount; + caller->cumul_exceptiontablelength += cn->cumul_exceptiontablelength; + caller->cumul_exceptiontablelength += cn->extra_exceptiontablelength; + + if (cn->cumul_maxlocals > caller->cumul_maxlocals) + caller->cumul_maxlocals = cn->cumul_maxlocals; + + /* XXX extra block after inlined call */ + if (cn->blockafter) { + caller->cumul_basicblockcount += 1; + caller->cumul_blockmapcount += 1; + } +} + + +/* inline_analyse_callee ******************************************************* + + Analyse an inlining candidate callee. + + IN: + caller...........inlining node of the caller + callee...........the called method + site.............info about the call site + + OUT: + site->inlined....true if the callee has been selected for inlining + + RETURN VALUE: + the inline node of the callee, or + NULL if an error has occurred (don't use the inlining plan in this case) + +*******************************************************************************/ + +static inline_node * inline_analyse_callee(inline_node *caller, + methodinfo *callee, + inline_site *site) +{ + inline_node *cn; /* the callee inline_node */ + + /* create an inline tree node */ + + cn = inline_create_callee_node(caller, callee); + + /* get the intermediate representation of the callee */ + + if (!inline_jit_compile(cn)) + return NULL; + + /* evaluate heuristics after parsing the callee */ + + if (!inline_post_parse_heuristics(caller, cn)) + return cn; + + /* the call site will be inlined */ + + site->inlined = true; + + /* set info about the call site */ + + inline_set_callee_properties(caller, cn, site); + + /* insert the node into the inline tree */ + + inline_insert_inline_node(caller, cn); + + /* analyse recursively */ + + if (!inline_analyse_code(cn)) + return NULL; + + if (!inline_afterwards_heuristics(caller, cn)) { +#if defined(INLINE_CANCEL_ON_THRESHOLD) + return NULL; +#else + inline_remove_inline_node(caller, cn); + caller->ctx->stopped = true; + site->inlined = false; + return cn; +#endif + } + + /* cumulate counters */ + +#if defined(INLINE_DEPTH_FIRST) + inline_cumulate_counters(caller, cn); +#endif + +#if defined(INLINE_BREADTH_FIRST) + while (caller) { + inline_cumulate_counters(caller, cn); + caller = caller->parent; + } +#endif + + return cn; +} + + +/* inline_process_candidate **************************************************** + + Process a selected inlining candidate. + + IN: + cand.............the candidate + + RETURN VALUE: + true........everything ok + false.......an error has occurred, don't use the plan + +*******************************************************************************/ + +static bool inline_process_candidate(inline_candidate *cand) +{ + inline_node *cn; + + cn = inline_analyse_callee(cand->caller, + cand->callee, + &(cand->site)); + + if (!cn) + return false; + + if (!cand->site.inlined) + return true; + + /* store assumptions */ + + if (cand->site.speculative) + method_add_assumption_monomorphic(cand->callee, cand->caller->ctx->master->m); + + return true; +} + + +/* inline_analyse_code ********************************************************* + + Analyse the intermediate code of the given inlining node. + + IN: + iln..............the inlining node + + OUT: + *iln.............the inlining plan + + RETURN VALUE: + true........everything ok + false.......an error has occurred, don't use the plan + +*******************************************************************************/ + +static bool inline_analyse_code(inline_node *iln) +{ + methodinfo *m; + basicblock *bptr; + s4 len; + instruction *iptr; + methodinfo *callee; + exception_entry **handlers; + exception_entry *ex; + s4 nhandlers; + s4 blockendpc; + jitdata *mjd; + inline_site site; + + assert(iln); + + m = iln->m; + mjd = iln->jd; + + /* initialize cumulative counters */ + + iln->cumul_maxlocals = iln->localsoffset + m->maxlocals; + iln->cumul_exceptiontablelength += mjd->exceptiontablelength; + + /* iterate over basic blocks */ + + blockendpc = 0; + + for (bptr = mjd->basicblocks; bptr; bptr = bptr->next) { + + /* count the block */ + /* ignore dummy end blocks (but count them for the blockmap) */ + + iln->cumul_blockmapcount++; + if ((bptr != mjd->basicblocks || iln->blockbefore) + && + (bptr->icount > 0 || bptr->next != NULL)) + iln->cumul_basicblockcount++; + + /* skip dead code */ + + if (bptr->flags < BBREACHED) + continue; + + /* allocate the buffer of active exception handlers */ + /* XXX this wastes some memory, but probably it does not matter */ + + handlers = (exception_entry**) DumpMemory::allocate(sizeof(exception_entry*) * (mjd->exceptiontablelength + 1)); + + /* determine the active exception handlers for this block */ + /* XXX maybe the handlers of a block should be part of our IR */ + /* XXX this should share code with the type checkers */ + nhandlers = 0; + for (ex = mjd->exceptiontable; ex ; ex = ex->down) { + if ((ex->start->nr <= bptr->nr) && (ex->end->nr > bptr->nr)) { + handlers[nhandlers++] = ex; + } + } + handlers[nhandlers] = NULL; + + len = bptr->icount; + iptr = bptr->iinstr; + + blockendpc += len; + iln->cumul_instructioncount += len; + + /* iterate over the instructions of the block */ + + for (; --len >= 0; ++iptr) { + + switch (iptr->opc) { + case ICMD_INVOKEVIRTUAL: + case ICMD_INVOKESPECIAL: + case ICMD_INVOKESTATIC: + case ICMD_INVOKEINTERFACE: + + if (!INSTRUCTION_IS_UNRESOLVED(iptr) && !iln->ctx->stopped) { + callee = iptr->sx.s23.s3.fmiref->p.method; + + if (inline_can_inline(iln, callee, iptr, &site)) { + site.inlined = false; + site.bptr = bptr; + site.iptr = iptr; + site.pc = blockendpc - len - 1; + site.handlers = handlers; + site.nhandlers = nhandlers; + + if (inline_pre_parse_heuristics(iln, callee, &site)) { +#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) + inline_add_candidate(iln->ctx, iln, callee, &site); +#else + inline_candidate cand; + cand.caller = iln; + cand.callee = callee; + cand.site = site; + + if (!inline_process_candidate(&cand)) + return false; +#endif + } + } + } + break; + + case ICMD_RETURN: + case ICMD_IRETURN: + case ICMD_ARETURN: + case ICMD_LRETURN: + case ICMD_FRETURN: + case ICMD_DRETURN: + /* extra ICMD_MOVE may be necessary */ + iln->cumul_instructioncount++; + break; + } + } + + /* end of basic block */ + } + + return true; +} + + +static void inline_cumulate_counters_recursive(inline_node *iln) +{ + inline_node *child; + + child = iln->children; + if (child) { + do { + inline_cumulate_counters_recursive(child); + inline_cumulate_counters(iln, child); + child = child->next; + } while (child != iln->children); + } +} + + +/* inline_make_inlining_plan *************************************************** + + Make an inlining plan for the given root node + + IN: + iln..............the root node + + OUT: + *iln.............the inlining plan + + RETURN VALUE: + true........everything ok + false.......an error has occurred, don't use the plan + +*******************************************************************************/ + +#if defined(INLINE_KNAPSACK) +static bool inline_make_inlining_plan(inline_node *iln) +{ + inline_candidate *cand; +#if defined(INLINE_COST_BUDGET) + s4 budget = INLINE_COST_BUDGET; +# define BUDGETMEMBER cost +#endif +#if defined(INLINE_WEIGHT_BUDGET) + double budget = INLINE_WEIGHT_BUDGET; +# define BUDGETMEMBER weight +#endif + + inline_analyse_code(iln); + + DOLOG( printf("candidates in "); method_println(iln->m); + inline_candidates_println(iln->ctx); ); + + while ((cand = inline_pick_best_candidate(iln->ctx)) != NULL) + { + if (cand->BUDGETMEMBER <= budget) { + DOLOG( printf(" picking: "); inline_candidate_println(cand); ); + + if (!inline_process_candidate(cand)) + return false; + +#if !defined(INLINE_ADD_NEGATIVE_TO_BUDGET) + if (cand->BUDGETMEMBER > 0) +#endif + budget -= cand->BUDGETMEMBER; + } + } + + inline_cumulate_counters_recursive(iln); + + return true; +} +#endif /* defined(INLINE_KNAPSACK) */ + + +#if defined(INLINE_DEPTH_FIRST) +static bool inline_make_inlining_plan(inline_node *iln) +{ + return inline_analyse_code(iln); +} +#endif /* defined(INLINE_DEPTH_FIRST) */ + + +#if defined(INLINE_BREADTH_FIRST) +static bool inline_make_inlining_plan(inline_node *iln) +{ + inline_candidate *cand; + + inline_analyse_code(iln); + + DOLOG( printf("candidates in "); method_println(iln->m); + inline_candidates_println(iln->ctx); ); + + while (!iln->ctx->stopped + && (cand = inline_pick_best_candidate(iln->ctx)) != NULL) + { + DOLOG( printf(" picking: "); inline_candidate_println(cand); ); + + if (!inline_process_candidate(cand)) + return false; + } + + return true; +} +#endif /* defined(INLINE_BREADTH_FIRST) */ + + +/* statistics *****************************************************************/ + +#if defined(INLINE_STATISTICS) +static void inline_gather_statistics_recursive(inline_node *iln) +{ + inline_node *child; + + inline_stat_inlined_nodes++; + + if (iln->depth > inline_stat_max_depth) + inline_stat_max_depth++; + + child = iln->children; + if (child) { + do { + inline_gather_statistics_recursive(child); + child = child->next; + } while (child != iln->children); + } +} +#endif /* defined(INLINE_STATISTICS) */ + + +#if defined(INLINE_STATISTICS) +static void inline_gather_statistics(inline_node *iln) +{ + inline_stat_roots_transformed++; + + inline_gather_statistics_recursive(iln); +} +#endif /* defined(INLINE_STATISTICS) */ + + +/* post processing ************************************************************/ + +#define POSTPROCESS_SRC(varindex) live[varindex]-- +#define POSTPROCESS_DST(varindex) live[varindex]++ + +#define POSTPROCESS_SRCOP(s) POSTPROCESS_SRC(iptr->s.varindex) +#define POSTPROCESS_DSTOP(d) POSTPROCESS_DST(iptr->d.varindex) + +#define MARKSAVED(varindex) jd->var[varindex].flags |= SAVEDVAR + +#define MARK_ALL_SAVED \ + do { \ + for (i=0; ivartop; ++i) \ + if (live[i]) \ + MARKSAVED(i); \ + } while (0) + +static void inline_post_process(jitdata *jd) +{ + codeinfo *code; + basicblock *bptr; + instruction *iptr; + instruction *iend; + s4 i; + icmdtable_entry_t *icmdt; + s4 *live; + methoddesc *md; + builtintable_entry *bte; + + /* Get required compiler data. */ + + code = jd->code; + + /* reset the SAVEDVAR flag of all variables */ + + for (i=0; ivartop; ++i) + jd->var[i].flags &= ~SAVEDVAR; + + /* allocate the life counters */ + + live = (s4*) DumpMemory::allocate(sizeof(s4) * jd->vartop); + MZERO(live, s4, jd->vartop); + + /* iterate over all basic blocks */ + + for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) { + if (bptr->flags < BBREACHED) + continue; + + /* make invars live */ + + for (i=0; iindepth; ++i) + POSTPROCESS_DST(bptr->invars[i]); + + iptr = bptr->iinstr; + iend = iptr + bptr->icount; + + for (; iptr < iend; ++iptr) { + + icmdt = &(icmd_table[iptr->opc]); + + switch (icmdt->dataflow) { + case DF_3_TO_0: + POSTPROCESS_SRCOP(sx.s23.s3); + case DF_2_TO_0: + POSTPROCESS_SRCOP(sx.s23.s2); + case DF_1_TO_0: + POSTPROCESS_SRCOP(s1); + case DF_0_TO_0: + if (icmdt->flags & ICMDTABLE_CALLS) { + code_unflag_leafmethod(code); + MARK_ALL_SAVED; + } + break; + + case DF_2_TO_1: + POSTPROCESS_SRCOP(sx.s23.s2); + case DF_1_TO_1: + case DF_MOVE: + POSTPROCESS_SRCOP(s1); + case DF_0_TO_1: + if (icmdt->flags & ICMDTABLE_CALLS) { + code_unflag_leafmethod(code); + MARK_ALL_SAVED; + } + case DF_COPY: + POSTPROCESS_DSTOP(dst); + break; + + case DF_N_TO_1: + for (i=0; is1.argcount; ++i) { + POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]); + } + if (icmdt->flags & ICMDTABLE_CALLS) { + code_unflag_leafmethod(code); + MARK_ALL_SAVED; + } + POSTPROCESS_DSTOP(dst); + break; + + case DF_INVOKE: + INSTRUCTION_GET_METHODDESC(iptr, md); + post_process_call: + code_unflag_leafmethod(code); + for (i=0; iparamcount; ++i) { + POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]); + } + for (; is1.argcount; ++i) { + MARKSAVED(iptr->sx.s23.s2.args[i]); + } + if (md->returntype.type != TYPE_VOID) + POSTPROCESS_DSTOP(dst); + break; + + case DF_BUILTIN: + bte = iptr->sx.s23.s3.bte; + md = bte->md; + goto post_process_call; + + default: + assert(0); + } + + } /* end instruction loop */ + + /* consume outvars */ + + for (i=0; ioutdepth; ++i) + POSTPROCESS_SRC(bptr->outvars[i]); + +#if !defined(NDEBUG) + for (i=jd->localcount; i < jd->vartop; ++i) + assert(live[i] == 0); +#endif + + } /* end basic block loop */ +} + + +/* inline_create_root_node ***************************************************** + + Create the root node of the inlining tree. + + IN: + jd...............the current jitdata of the root method + + RETURN VALUE: + the root node of the inlining tree + +*******************************************************************************/ + +static inline_node * inline_create_root_node(jitdata *jd) +{ + inline_node *iln; + + iln = (inline_node*) DumpMemory::allocate(sizeof(inline_node)); + MZERO(iln, inline_node, 1); + + iln->m = jd->m; + iln->jd = jd; + iln->regdata = jd->rd; + + iln->blockbefore = true; + iln->blockafter = true; + + iln->cumul_instructioncount = 0; + iln->cumul_basicblockcount = 1 /* dummy end block */; + + /* create inlining context */ + + iln->ctx = (inline_context*) DumpMemory::allocate(sizeof(inline_context)); + MZERO(iln->ctx, inline_context, 1); + iln->ctx->master = iln; + iln->ctx->next_debugnr = 1; /* XXX debug */ + + return iln; +} + + +/******************************************************************************/ +/* MAIN DRIVER FUNCTION */ +/******************************************************************************/ + +bool inline_inline(jitdata *jd) +{ + inline_node *iln; + + DOLOG( printf("==== INLINE ==================================================================\n"); + show_method(jd, SHOW_STACK); ); + +#if defined(INLINE_STATISTICS) + inline_stat_roots++; +#endif + + iln = inline_create_root_node(jd); + + if (inline_make_inlining_plan(iln)) { + + /* add blocks to the root node */ + + iln->cumul_basicblockcount += iln->cumul_basicblockcount_root; + iln->cumul_blockmapcount += iln->cumul_basicblockcount_root; + + DOLOG( printf("==== INLINE TRANSFORM ========================================================\n"); ); + + if (iln->children) + inline_transform(iln, jd); + +#if defined(INLINE_STATISTICS) + inline_gather_statistics(iln); +#endif + } + + DOLOG( printf("-------- DONE -----------------------------------------------------------\n"); + fflush(stdout); ); + + return true; +} + +#if defined(__cplusplus) +} +#endif + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/inline/inline.h b/src/vm/jit/inline/inline.h deleted file mode 100644 index 629f649a9..000000000 --- a/src/vm/jit/inline/inline.h +++ /dev/null @@ -1,62 +0,0 @@ -/* src/vm/jit/inline/inline.h - code inliner - - Copyright (C) 1996-2005, 2006, 2007, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#ifndef _INLINE_H -#define _INLINE_H - -#include "config.h" - -#include - -#include "vm/jit/jit.hpp" - - -/* function prototypes ********************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -bool inline_inline(jitdata *jd); - -#ifdef __cplusplus -} -#endif - -#endif /* _INLINE_H */ - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ diff --git a/src/vm/jit/inline/inline.hpp b/src/vm/jit/inline/inline.hpp new file mode 100644 index 000000000..629f649a9 --- /dev/null +++ b/src/vm/jit/inline/inline.hpp @@ -0,0 +1,62 @@ +/* src/vm/jit/inline/inline.h - code inliner + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#ifndef _INLINE_H +#define _INLINE_H + +#include "config.h" + +#include + +#include "vm/jit/jit.hpp" + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +bool inline_inline(jitdata *jd); + +#ifdef __cplusplus +} +#endif + +#endif /* _INLINE_H */ + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/intrp/codegen.c b/src/vm/jit/intrp/codegen.c index ddb68c730..4fbca4a70 100644 --- a/src/vm/jit/intrp/codegen.c +++ b/src/vm/jit/intrp/codegen.c @@ -58,7 +58,7 @@ #include "vm/jit/codegen-common.hpp" #include "vm/jit/dseg.h" #include "vm/jit/jit.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher.h" #include "vm/jit/stack.h" #include "vm/jit/stacktrace.hpp" diff --git a/src/vm/jit/jit.cpp b/src/vm/jit/jit.cpp index 09c9e862e..c218f05e8 100644 --- a/src/vm/jit/jit.cpp +++ b/src/vm/jit/jit.cpp @@ -58,7 +58,7 @@ #include "vm/jit/disass.h" #include "vm/jit/dseg.h" #include "vm/jit/jit.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/reg.h" #include "vm/jit/show.hpp" @@ -84,7 +84,7 @@ #endif #if defined(ENABLE_INLINING) -# include "vm/jit/inline/inline.h" +# include "vm/jit/inline/inline.hpp" #endif #include "vm/jit/ir/bytecode.h" diff --git a/src/vm/jit/jit.hpp b/src/vm/jit/jit.hpp index 4a235519f..838e33028 100644 --- a/src/vm/jit/jit.hpp +++ b/src/vm/jit/jit.hpp @@ -52,7 +52,7 @@ typedef struct exception_entry exception_entry; #include "vm/jit/stacktrace.hpp" #if defined(ENABLE_INLINING) -# include "vm/jit/inline/inline.h" +# include "vm/jit/inline/inline.hpp" #endif #include "vm/jit/ir/bytecode.h" diff --git a/src/vm/jit/m68k/codegen.c b/src/vm/jit/m68k/codegen.c index 6a0254b8c..a6ad45abe 100644 --- a/src/vm/jit/m68k/codegen.c +++ b/src/vm/jit/m68k/codegen.c @@ -57,7 +57,7 @@ #include "vm/jit/emit-common.hpp" #include "vm/jit/jit.hpp" #include "vm/jit/abi.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" #include "vm/jit/stacktrace.hpp" diff --git a/src/vm/jit/mips/codegen.c b/src/vm/jit/mips/codegen.c index ee88ea3b5..e81137a70 100644 --- a/src/vm/jit/mips/codegen.c +++ b/src/vm/jit/mips/codegen.c @@ -1403,13 +1403,13 @@ bool codegen_emit(jitdata *jd) M_CMPLT(s1, s2, REG_ITMP3); M_CMPLT(s2, s1, REG_ITMP1); M_ISUB(REG_ITMP1, REG_ITMP3, d); - M_BNEZ(d, 4); - M_NOP; + emit_label_bnez(cd, BRANCH_LABEL_1, d); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); M_CMPULT(s1, s2, REG_ITMP3); M_CMPULT(s2, s1, REG_ITMP1); M_ISUB(REG_ITMP1, REG_ITMP3, d); + emit_label(cd, BRANCH_LABEL_1); #endif emit_store_dst(jd, iptr, d); break; @@ -2430,12 +2430,12 @@ bool codegen_emit(jitdata *jd) ICONST(REG_ITMP2, iptr->sx.val.l >> 32); M_CMPLT(s1, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2); s2 = emit_load_s1_low(jd, iptr, REG_ITMP3); - M_BNE(s1, REG_ITMP2, 5); /* XXX */ - M_NOP; ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff); M_CMPULT(s2, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); } #endif break; @@ -2460,22 +2460,22 @@ bool codegen_emit(jitdata *jd) #else if (iptr->sx.val.l == 0) { s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED); - M_BGTZ(GET_HIGH_REG(s1), 5); /* XXX */ - M_NOP; + emit_label_bgtz(cd, BRANCH_LABEL_1, GET_HIGH_REG(s1)); emit_bltz(cd, iptr->dst.block, GET_HIGH_REG(s1)); - emit_beqz(cd, iptr->dst.block, GET_LOW_REG(s1)); + emit_beqz(cd, iptr->dst.block, GET_LOW_REG(s1)); + emit_label(cd, BRANCH_LABEL_1); } else { s1 = emit_load_s1_high(jd, iptr, REG_ITMP1); ICONST(REG_ITMP2, iptr->sx.val.l >> 32); M_CMPLT(s1, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2); s2 = emit_load_s1_low(jd, iptr, REG_ITMP3); - M_BNE(s1, REG_ITMP2, 5); /* XXX */ - M_NOP; ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff); M_CMPUGT(s2, REG_ITMP2, REG_ITMP3); emit_beqz(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); } #endif break; @@ -2530,21 +2530,21 @@ bool codegen_emit(jitdata *jd) if (iptr->sx.val.l == 0) { s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED); emit_bgtz(cd, iptr->dst.block, GET_HIGH_REG(s1)); - M_BLTZ(GET_HIGH_REG(s1), 3); /* XXX */ - M_NOP; + emit_label_bltz(cd, BRANCH_LABEL_1, GET_HIGH_REG(s1)); emit_bnez(cd, iptr->dst.block, GET_LOW_REG(s1)); + emit_label(cd, BRANCH_LABEL_1); } else { s1 = emit_load_s1_high(jd, iptr, REG_ITMP1); ICONST(REG_ITMP2, iptr->sx.val.l >> 32); M_CMPGT(s1, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2); s2 = emit_load_s1_low(jd, iptr, REG_ITMP3); - M_BNE(s1, REG_ITMP2, 5); /* XXX */ - M_NOP; ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff); M_CMPUGT(s2, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); } #endif break; @@ -2576,12 +2576,12 @@ bool codegen_emit(jitdata *jd) ICONST(REG_ITMP2, iptr->sx.val.l >> 32); M_CMPGT(s1, REG_ITMP2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2); s2 = emit_load_s1_low(jd, iptr, REG_ITMP3); - M_BNE(s1, REG_ITMP2, 5); /* XXX */ - M_NOP; ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff); M_CMPULT(s2, REG_ITMP2, REG_ITMP3); emit_beqz(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); } #endif break; @@ -2603,11 +2603,11 @@ bool codegen_emit(jitdata *jd) s1 = emit_load_s1_high(jd, iptr, REG_ITMP1); s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); - M_BNE(s1, s2, 3); /* XXX TWISTI: uff, that is a problem */ - M_NOP; + emit_label_bne(cd, BRANCH_LABEL_1, s1, s2); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); emit_beq(cd, iptr->dst.block, s1, s2); + emit_label(cd, BRANCH_LABEL_1); break; #endif @@ -2654,13 +2654,12 @@ bool codegen_emit(jitdata *jd) M_CMPLT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); M_CMPGT(s1, s2, REG_ITMP3); - /* load low-bits before the branch, so we know the distance */ + emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); - M_BNEZ(REG_ITMP3, 4); /* XXX */ - M_NOP; M_CMPULT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); break; #endif @@ -2683,13 +2682,12 @@ bool codegen_emit(jitdata *jd) M_CMPGT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); M_CMPLT(s1, s2, REG_ITMP3); - /* load low-bits before the branch, so we know the distance */ + emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); - M_BNEZ(REG_ITMP3, 4); /* XXX */ - M_NOP; M_CMPUGT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); break; #endif @@ -2712,13 +2710,12 @@ bool codegen_emit(jitdata *jd) M_CMPLT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); M_CMPGT(s1, s2, REG_ITMP3); - /* load low-bits before the branch, so we know the distance */ + emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); - M_BNEZ(REG_ITMP3, 4); /* XXX */ - M_NOP; M_CMPUGT(s1, s2, REG_ITMP3); emit_beqz(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); break; #endif @@ -2741,13 +2738,12 @@ bool codegen_emit(jitdata *jd) M_CMPGT(s1, s2, REG_ITMP3); emit_bnez(cd, iptr->dst.block, REG_ITMP3); M_CMPLT(s1, s2, REG_ITMP3); - /* load low-bits before the branch, so we know the distance */ + emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3); s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); - M_BNEZ(REG_ITMP3, 4); /* XXX */ - M_NOP; M_CMPULT(s1, s2, REG_ITMP3); emit_beqz(cd, iptr->dst.block, REG_ITMP3); + emit_label(cd, BRANCH_LABEL_1); break; #endif diff --git a/src/vm/jit/parse.c b/src/vm/jit/parse.c deleted file mode 100644 index 7b1c1ace3..000000000 --- a/src/vm/jit/parse.c +++ /dev/null @@ -1,1922 +0,0 @@ -/* src/vm/jit/parse.c - parser for JavaVM to intermediate code translation - - Copyright (C) 1996-2005, 2006, 2007, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#include "config.h" - -#include -#include - -#include "vm/types.h" - -#include "mm/memory.h" - -#include "native/native.hpp" - -#include "threads/lock.hpp" - -#include "toolbox/logging.h" - -#include "vm/jit/builtin.hpp" -#include "vm/exceptions.hpp" -#include "vm/global.h" -#include "vm/linker.h" -#include "vm/loader.hpp" -#include "vm/options.h" -#include "vm/resolve.hpp" - -#if defined(ENABLE_STATISTICS) -# include "vm/statistics.h" -#endif - -#include "vm/string.hpp" -#include "vm/suck.hpp" - -#include "vm/jit/asmpart.h" -#include "vm/jit/jit.hpp" -#include "vm/jit/parse.h" -#include "vm/jit/loop/loop.h" - -#include "vm/jit/ir/bytecode.h" - - -#define INSTRUCTIONS_INCREMENT 5 /* number of additional instructions to */ - /* allocate if space runs out */ - - -/* local macros ***************************************************************/ - -#define BYTECODEINDEX_TO_BASICBLOCK(dst) \ - do { \ - (dst).block = \ - parse_bytecodeindex_to_basicblock(jd, &pd, (dst).insindex); \ - } while (0) - - -/* parserdata_t ***************************************************************/ - -typedef struct parsedata_t parsedata_t; - -struct parsedata_t { - u1 *bytecodestart; /* start of bytecode instructions */ - u1 *basicblockstart; /* start of bytecode basic-blocks */ - - s4 *bytecodemap; /* bytecode to IR mapping */ - - instruction *instructions; /* instruction array */ - s4 instructionslength; /* length of the instruction array */ - - s4 *instructionmap; /* IR to basic-block mapping */ -}; - - -/* parse_setup ***************************************************************** - - Fills the passed parsedata_t structure. - -*******************************************************************************/ - -static void parse_setup(jitdata *jd, parsedata_t *pd) -{ - methodinfo *m; - - /* get required compiler data */ - - m = jd->m; - - /* bytecode start array */ - - pd->bytecodestart = DMNEW(u1, m->jcodelength + 1); - MZERO(pd->bytecodestart, u1, m->jcodelength + 1); - - /* bytecode basic-block start array */ - - pd->basicblockstart = DMNEW(u1, m->jcodelength + 1); - MZERO(pd->basicblockstart, u1, m->jcodelength + 1); - - /* bytecode instruction index to IR instruction mapping */ - - pd->bytecodemap = DMNEW(s4, m->jcodelength + 1); - MSET(pd->bytecodemap, -1, s4, m->jcodelength + 1); - - /* allocate the instruction array */ - - pd->instructionslength = m->jcodelength + 1; - pd->instructions = DMNEW(instruction, pd->instructionslength); - - /* Zero the intermediate instructions array so we don't have any - invalid pointers in it if we cannot finish stack_analyse(). */ - - MZERO(pd->instructions, instruction, pd->instructionslength); - - /* The instructionmap is allocated later when we know the count of - instructions. */ - - pd->instructionmap = NULL; -} - - -/* parse_realloc_instructions ************************************************** - - Reallocate the instructions array so there is room for at least N - additional instructions. - - RETURN VALUE: - the new value for iptr - -*******************************************************************************/ - -static instruction *parse_realloc_instructions(parsedata_t *pd, s4 icount, s4 n) -{ - /* increase the size of the instruction array */ - - pd->instructionslength += (n + INSTRUCTIONS_INCREMENT); - - /* reallocate the array */ - - pd->instructions = DMREALLOC(pd->instructions, instruction, icount, - pd->instructionslength); - MZERO(pd->instructions + icount, instruction, - (pd->instructionslength - icount)); - - /* return the iptr */ - - return pd->instructions + icount; -} - - -/* parse_bytecodeindex_to_basicblock ******************************************* - - Resolves a bytecode index to the corresponding basic block. - -*******************************************************************************/ - -static basicblock *parse_bytecodeindex_to_basicblock(jitdata *jd, - parsedata_t *pd, - s4 bcindex) -{ - s4 irindex; - basicblock *bb; - - irindex = pd->bytecodemap[bcindex]; - bb = jd->basicblocks + pd->instructionmap[irindex]; - - return bb; -} - - -/* parse_mark_exception_boundaries ********************************************* - - Mark exception handlers and the boundaries of the handled regions as - basic block boundaries. - - IN: - jd...............current jitdata - - RETURN VALUE: - true.............everything ok - false............an exception has been thrown - -*******************************************************************************/ - -static bool parse_mark_exception_boundaries(jitdata *jd, parsedata_t *pd) -{ - s4 bcindex; - s4 i; - s4 len; - raw_exception_entry *rex; - methodinfo *m; - - m = jd->m; - - len = m->rawexceptiontablelength; - - if (len == 0) - return true; - - rex = m->rawexceptiontable; - - for (i = 0; i < len; ++i, ++rex) { - - /* the start of the handled region becomes a basic block start */ - - bcindex = rex->startpc; - CHECK_BYTECODE_INDEX(bcindex); - MARK_BASICBLOCK(pd, bcindex); - - bcindex = rex->endpc; /* see JVM Spec 4.7.3 */ - CHECK_BYTECODE_INDEX_EXCLUSIVE(bcindex); - - /* check that the range is valid */ - -#if defined(ENABLE_VERIFIER) - if (bcindex <= rex->startpc) { - exceptions_throw_verifyerror(m, "Invalid exception handler range"); - return false; - } -#endif - - /* End of handled region becomes a basic block boundary (if it - is the bytecode end, we'll use the special end block that - is created anyway). */ - - if (bcindex < m->jcodelength) - MARK_BASICBLOCK(pd, bcindex); - else - jd->branchtoend = true; - - /* the start of the handler becomes a basic block start */ - - bcindex = rex->handlerpc; - CHECK_BYTECODE_INDEX(bcindex); - MARK_BASICBLOCK(pd, bcindex); - } - - /* everything ok */ - - return true; - -#if defined(ENABLE_VERIFIER) -throw_invalid_bytecode_index: - exceptions_throw_verifyerror(m, - "Illegal bytecode index in exception table"); - return false; -#endif -} - - -/* parse_resolve_exception_table *********************************************** - - Enter the exception handlers and their ranges, resolved to basicblock *s, - in the jitdata. - - IN: - jd...............current jitdata - - RETURN VALUE: - true.............everything ok - false............an exception has been thrown - -*******************************************************************************/ - -static bool parse_resolve_exception_table(jitdata *jd, parsedata_t *pd) -{ - methodinfo *m; - raw_exception_entry *rex; - exception_entry *ex; - s4 i; - s4 len; - classinfo *exclass; - - m = jd->m; - - len = m->rawexceptiontablelength; - - /* common case: no handler entries */ - - if (len == 0) - return true; - - /* allocate the exception table */ - - jd->exceptiontablelength = len; - jd->exceptiontable = DMNEW(exception_entry, len + 1); /* XXX why +1? */ - - /* copy and resolve the entries */ - - ex = jd->exceptiontable; - rex = m->rawexceptiontable; - - for (i = 0; i < len; ++i, ++rex, ++ex) { - /* resolve instruction indices to basic blocks */ - - ex->start = parse_bytecodeindex_to_basicblock(jd, pd, rex->startpc); - ex->end = parse_bytecodeindex_to_basicblock(jd, pd, rex->endpc); - ex->handler = parse_bytecodeindex_to_basicblock(jd, pd, rex->handlerpc); - - /* lazily resolve the catchtype */ - - if (rex->catchtype.any != NULL) { - if (!resolve_classref_or_classinfo(m, - rex->catchtype, - resolveLazy, true, false, - &exclass)) - return false; - - /* if resolved, enter the result of resolution in the table */ - - if (exclass != NULL) - rex->catchtype.cls = exclass; - } - - ex->catchtype = rex->catchtype; - ex->next = NULL; /* set by loop analysis */ - ex->down = ex + 1; /* link to next exception entry */ - } - - /* terminate the ->down linked list */ - - assert(ex != jd->exceptiontable); - ex[-1].down = NULL; - - return true; -} - - -/******************************************************************************* - - function 'parse' scans the JavaVM code and generates intermediate code - - During parsing the block index table is used to store at bit pos 0 - a flag which marks basic block starts and at position 1 to 31 the - intermediate instruction index. After parsing the block index table - is scanned, for marked positions a block is generated and the block - number is stored in the block index table. - -*******************************************************************************/ - -/*** macro for checking the length of the bytecode ***/ - -#if defined(ENABLE_VERIFIER) -#define CHECK_END_OF_BYTECODE(neededlength) \ - do { \ - if ((neededlength) > m->jcodelength) \ - goto throw_unexpected_end_of_bytecode; \ - } while (0) -#else /* !ENABLE_VERIFIER */ -#define CHECK_END_OF_BYTECODE(neededlength) -#endif /* ENABLE_VERIFIER */ - -bool parse(jitdata *jd) -{ - methodinfo *m; /* method being parsed */ - codeinfo *code; - parsedata_t pd; - instruction *iptr; /* current ptr into instruction array */ - - s4 bcindex; /* bytecode instruction index */ - s4 nextbc; /* start of next bytecode instruction */ - s4 opcode; /* bytecode instruction opcode */ - - s4 irindex; /* IR instruction index */ - s4 ircount; /* IR instruction count */ - - s4 bbcount; /* basic block count */ - - int s_count = 0; /* stack element counter */ - bool blockend; /* true if basic block end has been reached */ - bool iswide; /* true if last instruction was a wide */ - - constant_classref *cr; - constant_classref *compr; - classinfo *c; - builtintable_entry *bte; - constant_FMIref *fmi; - methoddesc *md; - unresolved_method *um; - unresolved_field *uf; - - resolve_result_t result; - u2 lineindex = 0; - u2 currentline = 0; - u2 linepcchange = 0; - u4 flags; - basicblock *bptr; - - int *local_map; /* local pointer to renaming map */ - /* is assigned to rd->local_map at the end */ - branch_target_t *table; - lookup_target_t *lookup; - s4 i; - s4 j; - - /* get required compiler data */ - - m = jd->m; - code = jd->code; - - /* allocate buffers for local variable renaming */ - - local_map = DMNEW(int, m->maxlocals * 5); - - for (i = 0; i < m->maxlocals; i++) { - local_map[i * 5 + 0] = 0; - local_map[i * 5 + 1] = 0; - local_map[i * 5 + 2] = 0; - local_map[i * 5 + 3] = 0; - local_map[i * 5 + 4] = 0; - } - - /* initialize the parse data structures */ - - parse_setup(jd, &pd); - - /* initialize local variables */ - - iptr = pd.instructions; - ircount = 0; - bbcount = 0; - blockend = false; - iswide = false; - - /* mark basic block boundaries for exception table */ - - if (!parse_mark_exception_boundaries(jd, &pd)) - return false; - - /* initialize stack element counter */ - - s_count = 1 + m->rawexceptiontablelength; - - /* setup line number info */ - - currentline = 0; - linepcchange = 0; - - if (m->linenumbercount == 0) { - lineindex = 0; - } - else { - linepcchange = m->linenumbers[0].start_pc; - } - - /*** LOOP OVER ALL BYTECODE INSTRUCTIONS **********************************/ - - for (bcindex = 0; bcindex < m->jcodelength; bcindex = nextbc) { - - /* mark this position as a valid bytecode instruction start */ - - pd.bytecodestart[bcindex] = 1; - - /* change the current line number, if necessary */ - - /* XXX rewrite this using pointer arithmetic */ - - if (linepcchange == bcindex) { - if (m->linenumbercount > lineindex) { -next_linenumber: - currentline = m->linenumbers[lineindex].line_number; - lineindex++; - if (lineindex < m->linenumbercount) { - linepcchange = m->linenumbers[lineindex].start_pc; - if (linepcchange == bcindex) - goto next_linenumber; - } - } - } - -fetch_opcode: - /* fetch next opcode */ - - opcode = SUCK_BE_U1(m->jcode + bcindex); - - /* If the previous instruction was a block-end instruction, - mark the current bytecode instruction as basic-block - starting instruction. */ - - /* NOTE: Some compilers put a BC_nop after a blockend - instruction. */ - - if (blockend && (opcode != BC_nop)) { - MARK_BASICBLOCK(&pd, bcindex); - blockend = false; - } - - /* If the current bytecode instruction was marked as - basic-block starting instruction before (e.g. blockend, - forward-branch target), mark the current IR instruction - too. */ - - if (pd.basicblockstart[bcindex] != 0) { - /* We need a NOP as last instruction in each basic block - for basic block reordering (may be replaced with a GOTO - later). */ - - INSTRUCTIONS_CHECK(1); - OP(ICMD_NOP); - } - - /* store intermediate instruction count (bit 0 mark block starts) */ - - pd.bytecodemap[bcindex] = ircount; - - /* compute next instruction start */ - - nextbc = bcindex + bytecode[opcode].length; - - CHECK_END_OF_BYTECODE(nextbc); - - /* add stack elements produced by this instruction */ - - s_count += bytecode[opcode].slots; - - /* We check here for the space of 1 instruction in the - instruction array. If an opcode is converted to more than - 1 instruction, this is checked in the corresponding - case. */ - - INSTRUCTIONS_CHECK(1); - - /* translate this bytecode instruction */ - switch (opcode) { - - case BC_nop: - break; - - /* pushing constants onto the stack ***********************************/ - - case BC_bipush: - OP_LOADCONST_I(SUCK_BE_S1(m->jcode + bcindex + 1)); - break; - - case BC_sipush: - OP_LOADCONST_I(SUCK_BE_S2(m->jcode + bcindex + 1)); - break; - - case BC_ldc1: - i = SUCK_BE_U1(m->jcode + bcindex + 1); - goto pushconstantitem; - - case BC_ldc2: - case BC_ldc2w: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - - pushconstantitem: - -#if defined(ENABLE_VERIFIER) - if (i >= m->clazz->cpcount) { - exceptions_throw_verifyerror(m, - "Attempt to access constant outside range"); - return false; - } -#endif - - switch (m->clazz->cptags[i]) { - case CONSTANT_Integer: - OP_LOADCONST_I(((constant_integer *) (m->clazz->cpinfos[i]))->value); - break; - case CONSTANT_Long: - OP_LOADCONST_L(((constant_long *) (m->clazz->cpinfos[i]))->value); - break; - case CONSTANT_Float: - OP_LOADCONST_F(((constant_float *) (m->clazz->cpinfos[i]))->value); - break; - case CONSTANT_Double: - OP_LOADCONST_D(((constant_double *) (m->clazz->cpinfos[i]))->value); - break; - case CONSTANT_String: - OP_LOADCONST_STRING(literalstring_new((utf *) (m->clazz->cpinfos[i]))); - break; - case CONSTANT_Class: - cr = (constant_classref *) (m->clazz->cpinfos[i]); - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - /* if not resolved, c == NULL */ - - OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr); - - break; - -#if defined(ENABLE_VERIFIER) - default: - exceptions_throw_verifyerror(m, - "Invalid constant type to push"); - return false; -#endif - } - break; - - case BC_aconst_null: - OP_LOADCONST_NULL(); - break; - - case BC_iconst_m1: - case BC_iconst_0: - case BC_iconst_1: - case BC_iconst_2: - case BC_iconst_3: - case BC_iconst_4: - case BC_iconst_5: - OP_LOADCONST_I(opcode - BC_iconst_0); - break; - - case BC_lconst_0: - case BC_lconst_1: - OP_LOADCONST_L(opcode - BC_lconst_0); - break; - - case BC_fconst_0: - case BC_fconst_1: - case BC_fconst_2: - OP_LOADCONST_F(opcode - BC_fconst_0); - break; - - case BC_dconst_0: - case BC_dconst_1: - OP_LOADCONST_D(opcode - BC_dconst_0); - break; - - /* stack operations ***************************************************/ - - /* We need space for additional instruction so we can - translate these instructions to sequences of ICMD_COPY and - ICMD_MOVE instructions. */ - - case BC_dup_x1: - INSTRUCTIONS_CHECK(4); - OP(opcode); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - break; - - case BC_dup_x2: - INSTRUCTIONS_CHECK(6); - OP(opcode); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - break; - - case BC_dup2: - INSTRUCTIONS_CHECK(2); - OP(opcode); - OP(ICMD_NOP); - break; - - case BC_dup2_x1: - INSTRUCTIONS_CHECK(7); - OP(opcode); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - break; - - case BC_dup2_x2: - INSTRUCTIONS_CHECK(9); - OP(opcode); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - OP(ICMD_NOP); - break; - - case BC_swap: - INSTRUCTIONS_CHECK(3); - OP(opcode); - OP(ICMD_NOP); - OP(ICMD_NOP); - break; - - /* local variable access instructions *********************************/ - - case BC_iload: - case BC_fload: - case BC_aload: - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - nextbc = bcindex + 3; - iswide = false; - } - OP_LOAD_ONEWORD(opcode, i, opcode - BC_iload); - break; - - case BC_lload: - case BC_dload: - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - nextbc = bcindex + 3; - iswide = false; - } - OP_LOAD_TWOWORD(opcode, i, opcode - BC_iload); - break; - - case BC_iload_0: - case BC_iload_1: - case BC_iload_2: - case BC_iload_3: - OP_LOAD_ONEWORD(ICMD_ILOAD, opcode - BC_iload_0, TYPE_INT); - break; - - case BC_lload_0: - case BC_lload_1: - case BC_lload_2: - case BC_lload_3: - OP_LOAD_TWOWORD(ICMD_LLOAD, opcode - BC_lload_0, TYPE_LNG); - break; - - case BC_fload_0: - case BC_fload_1: - case BC_fload_2: - case BC_fload_3: - OP_LOAD_ONEWORD(ICMD_FLOAD, opcode - BC_fload_0, TYPE_FLT); - break; - - case BC_dload_0: - case BC_dload_1: - case BC_dload_2: - case BC_dload_3: - OP_LOAD_TWOWORD(ICMD_DLOAD, opcode - BC_dload_0, TYPE_DBL); - break; - - case BC_aload_0: - case BC_aload_1: - case BC_aload_2: - case BC_aload_3: - OP_LOAD_ONEWORD(ICMD_ALOAD, opcode - BC_aload_0, TYPE_ADR); - break; - - case BC_istore: - case BC_fstore: - case BC_astore: - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - nextbc = bcindex + 3; - iswide = false; - } - OP_STORE_ONEWORD(opcode, i, opcode - BC_istore); - break; - - case BC_lstore: - case BC_dstore: - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - nextbc = bcindex + 3; - iswide = false; - } - OP_STORE_TWOWORD(opcode, i, opcode - BC_istore); - break; - - case BC_istore_0: - case BC_istore_1: - case BC_istore_2: - case BC_istore_3: - OP_STORE_ONEWORD(ICMD_ISTORE, opcode - BC_istore_0, TYPE_INT); - break; - - case BC_lstore_0: - case BC_lstore_1: - case BC_lstore_2: - case BC_lstore_3: - OP_STORE_TWOWORD(ICMD_LSTORE, opcode - BC_lstore_0, TYPE_LNG); - break; - - case BC_fstore_0: - case BC_fstore_1: - case BC_fstore_2: - case BC_fstore_3: - OP_STORE_ONEWORD(ICMD_FSTORE, opcode - BC_fstore_0, TYPE_FLT); - break; - - case BC_dstore_0: - case BC_dstore_1: - case BC_dstore_2: - case BC_dstore_3: - OP_STORE_TWOWORD(ICMD_DSTORE, opcode - BC_dstore_0, TYPE_DBL); - break; - - case BC_astore_0: - case BC_astore_1: - case BC_astore_2: - case BC_astore_3: - OP_STORE_ONEWORD(ICMD_ASTORE, opcode - BC_astore_0, TYPE_ADR); - break; - - case BC_iinc: - { - int v; - - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - v = SUCK_BE_S1(m->jcode + bcindex + 2); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - v = SUCK_BE_S2(m->jcode + bcindex + 3); - nextbc = bcindex + 5; - iswide = false; - } - INDEX_ONEWORD(i); - LOCALTYPE_USED(i, TYPE_INT); - OP_LOCALINDEX_I(opcode, i, v); - } - break; - - /* wider index for loading, storing and incrementing ******************/ - - case BC_wide: - bcindex++; - iswide = true; - goto fetch_opcode; - - /* managing arrays ****************************************************/ - - case BC_newarray: - switch (SUCK_BE_S1(m->jcode + bcindex + 1)) { - case 4: - bte = builtintable_get_internal(BUILTIN_newarray_boolean); - break; - case 5: - bte = builtintable_get_internal(BUILTIN_newarray_char); - break; - case 6: - bte = builtintable_get_internal(BUILTIN_newarray_float); - break; - case 7: - bte = builtintable_get_internal(BUILTIN_newarray_double); - break; - case 8: - bte = builtintable_get_internal(BUILTIN_newarray_byte); - break; - case 9: - bte = builtintable_get_internal(BUILTIN_newarray_short); - break; - case 10: - bte = builtintable_get_internal(BUILTIN_newarray_int); - break; - case 11: - bte = builtintable_get_internal(BUILTIN_newarray_long); - break; -#if defined(ENABLE_VERIFIER) - default: - exceptions_throw_verifyerror(m, "Invalid array-type to create"); - return false; -#endif - } - OP_BUILTIN_CHECK_EXCEPTION(bte); - break; - - case BC_anewarray: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - compr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); - if (compr == NULL) - return false; - - if (!(cr = class_get_classref_multiarray_of(1, compr))) - return false; - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - INSTRUCTIONS_CHECK(2); - OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); - bte = builtintable_get_internal(BUILTIN_newarray); - OP_BUILTIN_CHECK_EXCEPTION(bte); - s_count++; - break; - - case BC_multianewarray: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - j = SUCK_BE_U1(m->jcode + bcindex + 3); - - cr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); - if (cr == NULL) - return false; - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - /* if unresolved, c == NULL */ - - iptr->s1.argcount = j; - OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, INS_FLAG_CHECK); - code_unflag_leafmethod(code); - break; - - /* control flow instructions ******************************************/ - - case BC_ifeq: - case BC_iflt: - case BC_ifle: - case BC_ifne: - case BC_ifgt: - case BC_ifge: - case BC_ifnull: - case BC_ifnonnull: - case BC_if_icmpeq: - case BC_if_icmpne: - case BC_if_icmplt: - case BC_if_icmpgt: - case BC_if_icmple: - case BC_if_icmpge: - case BC_if_acmpeq: - case BC_if_acmpne: - case BC_goto: - i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); - CHECK_BYTECODE_INDEX(i); - MARK_BASICBLOCK(&pd, i); - blockend = true; - OP_INSINDEX(opcode, i); - break; - - case BC_goto_w: - i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); - CHECK_BYTECODE_INDEX(i); - MARK_BASICBLOCK(&pd, i); - blockend = true; - OP_INSINDEX(ICMD_GOTO, i); - break; - - case BC_jsr: - i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); -jsr_tail: - CHECK_BYTECODE_INDEX(i); - MARK_BASICBLOCK(&pd, i); - blockend = true; - OP_PREPARE_ZEROFLAGS(BC_jsr); - iptr->sx.s23.s3.jsrtarget.insindex = i; - PINC; - break; - - case BC_jsr_w: - i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); - goto jsr_tail; - - case BC_ret: - if (iswide == false) { - i = SUCK_BE_U1(m->jcode + bcindex + 1); - } - else { - i = SUCK_BE_U2(m->jcode + bcindex + 1); - nextbc = bcindex + 3; - iswide = false; - } - blockend = true; - - OP_LOAD_ONEWORD(opcode, i, TYPE_ADR); - break; - - case BC_ireturn: - case BC_lreturn: - case BC_freturn: - case BC_dreturn: - case BC_areturn: - case BC_return: - blockend = true; - /* XXX ARETURN will need a flag in the typechecker */ - OP(opcode); - break; - - case BC_athrow: - blockend = true; - /* XXX ATHROW will need a flag in the typechecker */ - OP(opcode); - break; - - - /* table jumps ********************************************************/ - - case BC_lookupswitch: - { - s4 num, j; - lookup_target_t *lookup; -#if defined(ENABLE_VERIFIER) - s4 prevvalue = 0; -#endif - blockend = true; - nextbc = MEMORY_ALIGN((bcindex + 1), 4); - - CHECK_END_OF_BYTECODE(nextbc + 8); - - OP_PREPARE_ZEROFLAGS(opcode); - - /* default target */ - - j = bcindex + SUCK_BE_S4(m->jcode + nextbc); - iptr->sx.s23.s3.lookupdefault.insindex = j; - nextbc += 4; - CHECK_BYTECODE_INDEX(j); - MARK_BASICBLOCK(&pd, j); - - /* number of pairs */ - - num = SUCK_BE_U4(m->jcode + nextbc); - iptr->sx.s23.s2.lookupcount = num; - nextbc += 4; - - /* allocate the intermediate code table */ - - lookup = DMNEW(lookup_target_t, num); - iptr->dst.lookup = lookup; - - /* iterate over the lookup table */ - - CHECK_END_OF_BYTECODE(nextbc + 8 * num); - - for (i = 0; i < num; i++) { - /* value */ - - j = SUCK_BE_S4(m->jcode + nextbc); - lookup->value = j; - - nextbc += 4; - -#if defined(ENABLE_VERIFIER) - /* check if the lookup table is sorted correctly */ - - if (i && (j <= prevvalue)) { - exceptions_throw_verifyerror(m, "Unsorted lookup switch"); - return false; - } - prevvalue = j; -#endif - /* target */ - - j = bcindex + SUCK_BE_S4(m->jcode + nextbc); - lookup->target.insindex = j; - lookup++; - nextbc += 4; - CHECK_BYTECODE_INDEX(j); - MARK_BASICBLOCK(&pd, j); - } - - PINC; - break; - } - - case BC_tableswitch: - { - s4 num, j; - s4 deftarget; - branch_target_t *table; - - blockend = true; - nextbc = MEMORY_ALIGN((bcindex + 1), 4); - - CHECK_END_OF_BYTECODE(nextbc + 12); - - OP_PREPARE_ZEROFLAGS(opcode); - - /* default target */ - - deftarget = bcindex + SUCK_BE_S4(m->jcode + nextbc); - nextbc += 4; - CHECK_BYTECODE_INDEX(deftarget); - MARK_BASICBLOCK(&pd, deftarget); - - /* lower bound */ - - j = SUCK_BE_S4(m->jcode + nextbc); - iptr->sx.s23.s2.tablelow = j; - nextbc += 4; - - /* upper bound */ - - num = SUCK_BE_S4(m->jcode + nextbc); - iptr->sx.s23.s3.tablehigh = num; - nextbc += 4; - - /* calculate the number of table entries */ - - num = num - j + 1; - -#if defined(ENABLE_VERIFIER) - if (num < 1) { - exceptions_throw_verifyerror(m, - "invalid TABLESWITCH: upper bound < lower bound"); - return false; - } -#endif - /* create the intermediate code table */ - /* the first entry is the default target */ - - table = DMNEW(branch_target_t, 1 + num); - iptr->dst.table = table; - (table++)->insindex = deftarget; - - /* iterate over the target table */ - - CHECK_END_OF_BYTECODE(nextbc + 4 * num); - - for (i = 0; i < num; i++) { - j = bcindex + SUCK_BE_S4(m->jcode + nextbc); - (table++)->insindex = j; - nextbc += 4; - CHECK_BYTECODE_INDEX(j); - MARK_BASICBLOCK(&pd, j); - } - - PINC; - break; - } - - - /* load and store of object fields ************************************/ - - case BC_aastore: - OP(opcode); - code_unflag_leafmethod(code); - break; - - case BC_getstatic: - case BC_putstatic: - case BC_getfield: - case BC_putfield: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - fmi = class_getconstant(m->clazz, i, CONSTANT_Fieldref); - - if (fmi == NULL) - return false; - - OP_PREPARE_ZEROFLAGS(opcode); - iptr->sx.s23.s3.fmiref = fmi; - - /* only with -noverify, otherwise the typechecker does this */ - -#if defined(ENABLE_VERIFIER) - if (!JITDATA_HAS_FLAG_VERIFY(jd)) { -#endif - result = resolve_field_lazy(m, fmi); - - if (result == resolveFailed) - return false; - - if (result != resolveSucceeded) { - uf = resolve_create_unresolved_field(m->clazz, m, iptr); - - if (uf == NULL) - return false; - - /* store the unresolved_field pointer */ - - iptr->sx.s23.s3.uf = uf; - iptr->flags.bits |= INS_FLAG_UNRESOLVED; - } -#if defined(ENABLE_VERIFIER) - } -#endif - PINC; - break; - - - /* method invocation **************************************************/ - - case BC_invokestatic: - OP_PREPARE_ZEROFLAGS(opcode); - - i = SUCK_BE_U2(m->jcode + bcindex + 1); - fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref); - - if (fmi == NULL) - return false; - - md = fmi->parseddesc.md; - - if (md->params == NULL) - if (!descriptor_params_from_paramtypes(md, ACC_STATIC)) - return false; - - goto invoke_method; - - case BC_invokespecial: - OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); - - i = SUCK_BE_U2(m->jcode + bcindex + 1); - fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref); - - goto invoke_nonstatic_method; - - case BC_invokeinterface: - OP_PREPARE_ZEROFLAGS(opcode); - - i = SUCK_BE_U2(m->jcode + bcindex + 1); - fmi = class_getconstant(m->clazz, i, CONSTANT_InterfaceMethodref); - - goto invoke_nonstatic_method; - - case BC_invokevirtual: - OP_PREPARE_ZEROFLAGS(opcode); - - i = SUCK_BE_U2(m->jcode + bcindex + 1); - fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref); - -invoke_nonstatic_method: - if (fmi == NULL) - return false; - - md = fmi->parseddesc.md; - - if (md->params == NULL) - if (!descriptor_params_from_paramtypes(md, 0)) - return false; - -invoke_method: - code_unflag_leafmethod(code); - - iptr->sx.s23.s3.fmiref = fmi; - - /* only with -noverify, otherwise the typechecker does this */ - -#if defined(ENABLE_VERIFIER) - if (!JITDATA_HAS_FLAG_VERIFY(jd)) { -#endif - result = resolve_method_lazy(m, fmi, - (opcode == BC_invokespecial)); - - if (result == resolveFailed) - return false; - - if (result == resolveSucceeded) { - methodinfo *mi = iptr->sx.s23.s3.fmiref->p.method; - - /* if this call is monomorphic, turn it into an - INVOKESPECIAL */ - - assert(IS_FMIREF_RESOLVED(iptr->sx.s23.s3.fmiref)); - - if ((iptr->opc == ICMD_INVOKEVIRTUAL) - && (mi->flags & (ACC_FINAL | ACC_PRIVATE))) - { - iptr->opc = ICMD_INVOKESPECIAL; - iptr->flags.bits |= INS_FLAG_CHECK; - } - } - else { - um = resolve_create_unresolved_method(m->clazz, m, fmi, - (opcode == BC_invokestatic), - (opcode == BC_invokespecial)); - - if (um == NULL) - return false; - - /* store the unresolved_method pointer */ - - iptr->sx.s23.s3.um = um; - iptr->flags.bits |= INS_FLAG_UNRESOLVED; - } -#if defined(ENABLE_VERIFIER) - } -#endif - PINC; - break; - - /* instructions taking class arguments ********************************/ - - case BC_new: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - cr = class_getconstant(m->clazz, i, CONSTANT_Class); - - if (cr == NULL) - return false; - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - INSTRUCTIONS_CHECK(2); - OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); - bte = builtintable_get_internal(BUILTIN_new); - OP_BUILTIN_CHECK_EXCEPTION(bte); - s_count++; - break; - - case BC_checkcast: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - cr = class_getconstant(m->clazz, i, CONSTANT_Class); - - if (cr == NULL) - return false; - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - if (cr->name->text[0] == '[') { - /* array type cast-check */ - flags = INS_FLAG_CHECK | INS_FLAG_ARRAY; - code_unflag_leafmethod(code); - } - else { - /* object type cast-check */ - flags = INS_FLAG_CHECK; - } - OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, flags); - break; - - case BC_instanceof: - i = SUCK_BE_U2(m->jcode + bcindex + 1); - cr = class_getconstant(m->clazz, i, CONSTANT_Class); - - if (cr == NULL) - return false; - - if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) - return false; - - if (cr->name->text[0] == '[') { - /* array type cast-check */ - INSTRUCTIONS_CHECK(2); - OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); - bte = builtintable_get_internal(BUILTIN_arrayinstanceof); - OP_BUILTIN_NO_EXCEPTION(bte); - s_count++; - } - else { - /* object type cast-check */ - OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, 0 /* flags*/); - } - break; - - /* synchronization instructions ***************************************/ - - case BC_monitorenter: -#if defined(ENABLE_THREADS) - if (checksync) { - bte = builtintable_get_internal(LOCK_monitor_enter); - OP_BUILTIN_CHECK_EXCEPTION(bte); - } - else -#endif - { - OP_CHECK_EXCEPTION(ICMD_CHECKNULL); - OP(ICMD_POP); - } - break; - - case BC_monitorexit: -#if defined(ENABLE_THREADS) - if (checksync) { - bte = builtintable_get_internal(LOCK_monitor_exit); - OP_BUILTIN_CHECK_EXCEPTION(bte); - } - else -#endif - { - OP_CHECK_EXCEPTION(ICMD_CHECKNULL); - OP(ICMD_POP); - } - break; - - /* arithmetic instructions that may become builtin functions **********/ - - case BC_idiv: -#if !SUPPORT_DIVISION - bte = builtintable_get_internal(BUILTIN_idiv); - OP_BUILTIN_ARITHMETIC(opcode, bte); -#else -# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO - OP(opcode); -# else - OP_CHECK_EXCEPTION(opcode); -# endif -#endif - break; - - case BC_irem: -#if !SUPPORT_DIVISION - bte = builtintable_get_internal(BUILTIN_irem); - OP_BUILTIN_ARITHMETIC(opcode, bte); -#else -# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO - OP(opcode); -# else - OP_CHECK_EXCEPTION(opcode); -# endif -#endif - break; - - case BC_ldiv: -#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) - bte = builtintable_get_internal(BUILTIN_ldiv); - OP_BUILTIN_ARITHMETIC(opcode, bte); -#else -# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO - OP(opcode); -# else - OP_CHECK_EXCEPTION(opcode); -# endif -#endif - break; - - case BC_lrem: -#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) - bte = builtintable_get_internal(BUILTIN_lrem); - OP_BUILTIN_ARITHMETIC(opcode, bte); -#else -# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO - OP(opcode); -# else - OP_CHECK_EXCEPTION(opcode); -# endif -#endif - break; - - case BC_frem: -#if defined(__I386__) - OP(opcode); -#else - bte = builtintable_get_internal(BUILTIN_frem); - OP_BUILTIN_NO_EXCEPTION(bte); -#endif - break; - - case BC_drem: -#if defined(__I386__) - OP(opcode); -#else - bte = builtintable_get_internal(BUILTIN_drem); - OP_BUILTIN_NO_EXCEPTION(bte); -#endif - break; - - case BC_f2i: -#if defined(__ALPHA__) - bte = builtintable_get_internal(BUILTIN_f2i); - OP_BUILTIN_NO_EXCEPTION(bte); -#else - OP(opcode); -#endif - break; - - case BC_f2l: -#if defined(__ALPHA__) - bte = builtintable_get_internal(BUILTIN_f2l); - OP_BUILTIN_NO_EXCEPTION(bte); -#else - OP(opcode); -#endif - break; - - case BC_d2i: -#if defined(__ALPHA__) - bte = builtintable_get_internal(BUILTIN_d2i); - OP_BUILTIN_NO_EXCEPTION(bte); -#else - OP(opcode); -#endif - break; - - case BC_d2l: -#if defined(__ALPHA__) - bte = builtintable_get_internal(BUILTIN_d2l); - OP_BUILTIN_NO_EXCEPTION(bte); -#else - OP(opcode); -#endif - break; - - - /* invalid opcodes ****************************************************/ - - /* check for invalid opcodes if the verifier is enabled */ -#if defined(ENABLE_VERIFIER) - case BC_breakpoint: - exceptions_throw_verifyerror(m, "Quick instructions shouldn't appear, yet."); - return false; - - - /* Unused opcodes ************************************************** */ - - case 186: - case 203: - case 204: - case 205: - case 206: - case 207: - case 208: - case 209: - case 210: - case 211: - case 212: - case 213: - case 214: - case 215: - case 216: - case 217: - case 218: - case 219: - case 220: - case 221: - case 222: - case 223: - case 224: - case 225: - case 226: - case 227: - case 228: - case 229: - case 230: - case 231: - case 232: - case 233: - case 234: - case 235: - case 236: - case 237: - case 238: - case 239: - case 240: - case 241: - case 242: - case 243: - case 244: - case 245: - case 246: - case 247: - case 248: - case 249: - case 250: - case 251: - case 252: - case 253: - case 254: - case 255: - exceptions_throw_verifyerror(m, "Illegal opcode %d at instr %d\n", - opcode, ircount); - return false; - break; -#endif /* defined(ENABLE_VERIFIER) */ - - /* opcodes that don't require translation *****************************/ - - default: - /* Straight-forward translation to HIR. */ - OP(opcode); - break; - - } /* end switch */ - - /* verifier checks ****************************************************/ - -#if defined(ENABLE_VERIFIER) - /* If WIDE was used correctly, iswide should have been reset by now. */ - if (iswide) { - exceptions_throw_verifyerror(m, - "Illegal instruction: WIDE before incompatible opcode"); - return false; - } -#endif /* defined(ENABLE_VERIFIER) */ - - } /* end for */ - - if (JITDATA_HAS_FLAG_REORDER(jd)) { - /* add a NOP to the last basic block */ - - INSTRUCTIONS_CHECK(1); - OP(ICMD_NOP); - } - - /*** END OF LOOP **********************************************************/ - - /* assert that we did not write more ICMDs than allocated */ - - assert(ircount <= pd.instructionslength); - assert(ircount == (iptr - pd.instructions)); - - /*** verifier checks ******************************************************/ - -#if defined(ENABLE_VERIFIER) - if (bcindex != m->jcodelength) { - exceptions_throw_verifyerror(m, - "Command-sequence crosses code-boundary"); - return false; - } - - if (!blockend) { - exceptions_throw_verifyerror(m, "Falling off the end of the code"); - return false; - } -#endif /* defined(ENABLE_VERIFIER) */ - - /*** setup the methodinfo, allocate stack and basic blocks ****************/ - - /* identify basic blocks */ - - /* check if first instruction is a branch target */ - - if (pd.basicblockstart[0] == 1) { - jd->branchtoentry = true; - } - else { - /* first instruction always starts a basic block */ - - iptr = pd.instructions; - - iptr->flags.bits |= INS_FLAG_BASICBLOCK; - } - - /* Iterate over all bytecode instructions and set missing - basic-block starts in IR instructions. */ - - for (bcindex = 0; bcindex < m->jcodelength; bcindex++) { - /* Does the current bytecode instruction start a basic - block? */ - - if (pd.basicblockstart[bcindex] == 1) { -#if defined(ENABLE_VERIFIER) - /* Check if this bytecode basic-block start at the - beginning of a bytecode instruction. */ - - if (pd.bytecodestart[bcindex] == 0) { - exceptions_throw_verifyerror(m, - "Branch into middle of instruction"); - return false; - } -#endif - - /* Get the IR instruction mapped to the bytecode - instruction and set the basic block flag. */ - - irindex = pd.bytecodemap[bcindex]; - iptr = pd.instructions + irindex; - - iptr->flags.bits |= INS_FLAG_BASICBLOCK; - } - } - - /* IR instruction index to basic-block index mapping */ - - pd.instructionmap = DMNEW(s4, ircount); - MZERO(pd.instructionmap, s4, ircount); - - /* Iterate over all IR instructions and count the basic blocks. */ - - iptr = pd.instructions; - - bbcount = 0; - - for (i = 0; i < ircount; i++, iptr++) { - if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { - /* store the basic-block number in the IR instruction - map */ - - pd.instructionmap[i] = bbcount; - - /* post-increment the basic-block count */ - - bbcount++; - } - } - - /* Allocate basic block array (one more for end ipc). */ - - jd->basicblocks = DMNEW(basicblock, bbcount + 1); - MZERO(jd->basicblocks, basicblock, bbcount + 1); - - /* Now iterate again over all IR instructions and initialize the - basic block structures and, in the same loop, resolve the - branch-target instruction indices to basic blocks. */ - - iptr = pd.instructions; - bptr = jd->basicblocks; - - bbcount = 0; - - for (i = 0; i < ircount; i++, iptr++) { - /* check for basic block */ - - if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { - /* intialize the basic block */ - - BASICBLOCK_INIT(bptr, m); - - bptr->iinstr = iptr; - - if (bbcount > 0) { - bptr[-1].icount = bptr->iinstr - bptr[-1].iinstr; - } - - /* bptr->icount is set when the next block is allocated */ - - bptr->nr = bbcount++; - bptr++; - bptr[-1].next = bptr; - } - - /* resolve instruction indices to basic blocks */ - - switch (iptr->opc) { - case ICMD_IFEQ: - case ICMD_IFLT: - case ICMD_IFLE: - case ICMD_IFNE: - case ICMD_IFGT: - case ICMD_IFGE: - case ICMD_IFNULL: - case ICMD_IFNONNULL: - case ICMD_IF_ICMPEQ: - case ICMD_IF_ICMPNE: - case ICMD_IF_ICMPLT: - case ICMD_IF_ICMPGT: - case ICMD_IF_ICMPLE: - case ICMD_IF_ICMPGE: - case ICMD_IF_ACMPEQ: - case ICMD_IF_ACMPNE: - case ICMD_GOTO: - BYTECODEINDEX_TO_BASICBLOCK(iptr->dst); - break; - - case ICMD_JSR: - BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.jsrtarget); - break; - - case ICMD_TABLESWITCH: - table = iptr->dst.table; - - BYTECODEINDEX_TO_BASICBLOCK(*table); - table++; - - j = iptr->sx.s23.s3.tablehigh - iptr->sx.s23.s2.tablelow + 1; - - while (--j >= 0) { - BYTECODEINDEX_TO_BASICBLOCK(*table); - table++; - } - break; - - case ICMD_LOOKUPSWITCH: - BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.lookupdefault); - - lookup = iptr->dst.lookup; - - j = iptr->sx.s23.s2.lookupcount; - - while (--j >= 0) { - BYTECODEINDEX_TO_BASICBLOCK(lookup->target); - lookup++; - } - break; - } - } - - /* set instruction count of last real block */ - - if (bbcount > 0) { - bptr[-1].icount = (pd.instructions + ircount) - bptr[-1].iinstr; - } - - /* allocate additional block at end */ - - BASICBLOCK_INIT(bptr, m); - bptr->nr = bbcount; - - /* set basicblock pointers in exception table */ - - if (!parse_resolve_exception_table(jd, &pd)) - return false; - - /* store the local map */ - - jd->local_map = local_map; - - /* calculate local variable renaming */ - - { - s4 nlocals = 0; - s4 i; - s4 t; - s4 varindex; - s4 *mapptr; - s4 *reversemap; - - mapptr = local_map; - - /* iterate over local_map[0..m->maxlocals*5-1] and allocate a unique */ - /* variable index for each _used_ (javaindex,type) pair. */ - /* (local_map[javaindex*5+type] = cacaoindex) */ - /* Unused (javaindex,type) pairs are marked with UNUSED. */ - - for (i = 0; i < (m->maxlocals * 5); i++, mapptr++) { - if (*mapptr) - *mapptr = nlocals++; - else - *mapptr = UNUSED; - } - - jd->localcount = nlocals; - - /* calculate the (maximum) number of variables needed */ - - jd->varcount = - nlocals /* local variables */ - + bbcount * m->maxstack /* invars */ - + s_count; /* variables created within blocks (non-invar) */ - - /* reserve the first indices for local variables */ - - jd->vartop = nlocals; - - /* reserve extra variables needed by stack analyse */ - - jd->varcount += STACK_EXTRA_VARS; - jd->vartop += STACK_EXTRA_VARS; - - /* The verifier needs space for saving invars in some cases and */ - /* extra variables. */ - -#if defined(ENABLE_VERIFIER) - jd->varcount += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; - jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; -#endif - /* allocate and initialize the variable array */ - - jd->var = DMNEW(varinfo, jd->varcount); - MZERO(jd->var, varinfo, jd->varcount); - - /* set types of all locals in jd->var */ - /* and fill the reverselocalmap */ - - reversemap = DMNEW(s4, nlocals); - - for (i = 0; i < m->maxlocals; i++) - for (t=0; t<5; t++) { - varindex = local_map[5*i + t]; - if (varindex != UNUSED) { - VAR(varindex)->type = t; - reversemap[varindex] = i; - } - } - - jd->reverselocalmap = reversemap; - } - - /* assign local variables to method variables */ - - jd->instructions = pd.instructions; - jd->instructioncount = ircount; - jd->basicblockcount = bbcount; - jd->stackcount = s_count + bbcount * m->maxstack; /* in-stacks */ - - /* allocate stack table */ - - jd->stack = DMNEW(stackelement_t, jd->stackcount); - - /* everything's ok */ - - return true; - - /*** goto labels for throwing verifier exceptions *************************/ - -#if defined(ENABLE_VERIFIER) - -throw_unexpected_end_of_bytecode: - exceptions_throw_verifyerror(m, "Unexpected end of bytecode"); - return false; - -throw_invalid_bytecode_index: - exceptions_throw_verifyerror(m, "Illegal target of branch instruction"); - return false; - -throw_illegal_local_variable_number: - exceptions_throw_verifyerror(m, "Illegal local variable number"); - return false; - -#endif /* ENABLE_VERIFIER */ -} - - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ diff --git a/src/vm/jit/parse.cpp b/src/vm/jit/parse.cpp new file mode 100644 index 000000000..ddc5d9b80 --- /dev/null +++ b/src/vm/jit/parse.cpp @@ -0,0 +1,1928 @@ +/* src/vm/jit/parse.c - parser for JavaVM to intermediate code translation + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#include "config.h" + +#include +#include + +#include "vm/types.h" + +#include "mm/memory.h" + +#include "native/native.hpp" + +#include "threads/lock.hpp" + +#include "toolbox/logging.h" + +#include "vm/jit/builtin.hpp" +#include "vm/exceptions.hpp" +#include "vm/global.h" +#include "vm/linker.h" +#include "vm/loader.hpp" +#include "vm/options.h" +#include "vm/resolve.hpp" + +#if defined(ENABLE_STATISTICS) +# include "vm/statistics.h" +#endif + +#include "vm/string.hpp" +#include "vm/suck.hpp" + +#include "vm/jit/asmpart.h" +#include "vm/jit/jit.hpp" +#include "vm/jit/parse.hpp" +#include "vm/jit/loop/loop.h" + +#include "vm/jit/ir/bytecode.h" + + +#define INSTRUCTIONS_INCREMENT 5 /* number of additional instructions to */ + /* allocate if space runs out */ + + +/* local macros ***************************************************************/ + +#define BYTECODEINDEX_TO_BASICBLOCK(dst) \ + do { \ + (dst).block = \ + parse_bytecodeindex_to_basicblock(jd, &pd, (dst).insindex); \ + } while (0) + + +/* parserdata_t ***************************************************************/ + +typedef struct parsedata_t parsedata_t; + +struct parsedata_t { + u1 *bytecodestart; /* start of bytecode instructions */ + u1 *basicblockstart; /* start of bytecode basic-blocks */ + + s4 *bytecodemap; /* bytecode to IR mapping */ + + instruction *instructions; /* instruction array */ + s4 instructionslength; /* length of the instruction array */ + + s4 *instructionmap; /* IR to basic-block mapping */ +}; + +#if defined(__cplusplus) +extern "C" { +#endif + +/* parse_setup ***************************************************************** + + Fills the passed parsedata_t structure. + +*******************************************************************************/ + +static void parse_setup(jitdata *jd, parsedata_t *pd) +{ + methodinfo *m; + + /* get required compiler data */ + + m = jd->m; + + /* bytecode start array */ + + pd->bytecodestart = (u1*) DumpMemory::allocate(sizeof(u1) * (m->jcodelength + 1)); + MZERO(pd->bytecodestart, u1, m->jcodelength + 1); + + /* bytecode basic-block start array */ + + pd->basicblockstart = (u1*) DumpMemory::allocate(sizeof(u1) *(m->jcodelength + 1)); + MZERO(pd->basicblockstart, u1, m->jcodelength + 1); + + /* bytecode instruction index to IR instruction mapping */ + + pd->bytecodemap = (s4*) DumpMemory::allocate(sizeof(s4) * (m->jcodelength + 1)); + MSET(pd->bytecodemap, -1, s4, m->jcodelength + 1); + + /* allocate the instruction array */ + + pd->instructionslength = m->jcodelength + 1; + pd->instructions = (instruction*) DumpMemory::allocate(sizeof(instruction) * pd->instructionslength); + + /* Zero the intermediate instructions array so we don't have any + invalid pointers in it if we cannot finish stack_analyse(). */ + + MZERO(pd->instructions, instruction, pd->instructionslength); + + /* The instructionmap is allocated later when we know the count of + instructions. */ + + pd->instructionmap = NULL; +} + + +/* parse_realloc_instructions ************************************************** + + Reallocate the instructions array so there is room for at least N + additional instructions. + + RETURN VALUE: + the new value for iptr + +*******************************************************************************/ + +static instruction *parse_realloc_instructions(parsedata_t *pd, s4 icount, s4 n) +{ + /* increase the size of the instruction array */ + + pd->instructionslength += (n + INSTRUCTIONS_INCREMENT); + + /* reallocate the array */ + + pd->instructions = (instruction*) DumpMemory::reallocate(pd->instructions, sizeof(instruction) * icount, + sizeof(instruction) * pd->instructionslength); + MZERO(pd->instructions + icount, instruction, + (pd->instructionslength - icount)); + + /* return the iptr */ + + return pd->instructions + icount; +} + + +/* parse_bytecodeindex_to_basicblock ******************************************* + + Resolves a bytecode index to the corresponding basic block. + +*******************************************************************************/ + +static basicblock *parse_bytecodeindex_to_basicblock(jitdata *jd, + parsedata_t *pd, + s4 bcindex) +{ + s4 irindex; + basicblock *bb; + + irindex = pd->bytecodemap[bcindex]; + bb = jd->basicblocks + pd->instructionmap[irindex]; + + return bb; +} + + +/* parse_mark_exception_boundaries ********************************************* + + Mark exception handlers and the boundaries of the handled regions as + basic block boundaries. + + IN: + jd...............current jitdata + + RETURN VALUE: + true.............everything ok + false............an exception has been thrown + +*******************************************************************************/ + +static bool parse_mark_exception_boundaries(jitdata *jd, parsedata_t *pd) +{ + s4 bcindex; + s4 i; + s4 len; + raw_exception_entry *rex; + methodinfo *m; + + m = jd->m; + + len = m->rawexceptiontablelength; + + if (len == 0) + return true; + + rex = m->rawexceptiontable; + + for (i = 0; i < len; ++i, ++rex) { + + /* the start of the handled region becomes a basic block start */ + + bcindex = rex->startpc; + CHECK_BYTECODE_INDEX(bcindex); + MARK_BASICBLOCK(pd, bcindex); + + bcindex = rex->endpc; /* see JVM Spec 4.7.3 */ + CHECK_BYTECODE_INDEX_EXCLUSIVE(bcindex); + + /* check that the range is valid */ + +#if defined(ENABLE_VERIFIER) + if (bcindex <= rex->startpc) { + exceptions_throw_verifyerror(m, "Invalid exception handler range"); + return false; + } +#endif + + /* End of handled region becomes a basic block boundary (if it + is the bytecode end, we'll use the special end block that + is created anyway). */ + + if (bcindex < m->jcodelength) + MARK_BASICBLOCK(pd, bcindex); + else + jd->branchtoend = true; + + /* the start of the handler becomes a basic block start */ + + bcindex = rex->handlerpc; + CHECK_BYTECODE_INDEX(bcindex); + MARK_BASICBLOCK(pd, bcindex); + } + + /* everything ok */ + + return true; + +#if defined(ENABLE_VERIFIER) +throw_invalid_bytecode_index: + exceptions_throw_verifyerror(m, + "Illegal bytecode index in exception table"); + return false; +#endif +} + + +/* parse_resolve_exception_table *********************************************** + + Enter the exception handlers and their ranges, resolved to basicblock *s, + in the jitdata. + + IN: + jd...............current jitdata + + RETURN VALUE: + true.............everything ok + false............an exception has been thrown + +*******************************************************************************/ + +static bool parse_resolve_exception_table(jitdata *jd, parsedata_t *pd) +{ + methodinfo *m; + raw_exception_entry *rex; + exception_entry *ex; + s4 i; + s4 len; + classinfo *exclass; + + m = jd->m; + + len = m->rawexceptiontablelength; + + /* common case: no handler entries */ + + if (len == 0) + return true; + + /* allocate the exception table */ + + jd->exceptiontablelength = len; + jd->exceptiontable = (exception_entry*) DumpMemory::allocate(sizeof(exception_entry) * (len + 1)); /* XXX why +1? */ + + /* copy and resolve the entries */ + + ex = jd->exceptiontable; + rex = m->rawexceptiontable; + + for (i = 0; i < len; ++i, ++rex, ++ex) { + /* resolve instruction indices to basic blocks */ + + ex->start = parse_bytecodeindex_to_basicblock(jd, pd, rex->startpc); + ex->end = parse_bytecodeindex_to_basicblock(jd, pd, rex->endpc); + ex->handler = parse_bytecodeindex_to_basicblock(jd, pd, rex->handlerpc); + + /* lazily resolve the catchtype */ + + if (rex->catchtype.any != NULL) { + if (!resolve_classref_or_classinfo(m, + rex->catchtype, + resolveLazy, true, false, + &exclass)) + return false; + + /* if resolved, enter the result of resolution in the table */ + + if (exclass != NULL) + rex->catchtype.cls = exclass; + } + + ex->catchtype = rex->catchtype; + ex->next = NULL; /* set by loop analysis */ + ex->down = ex + 1; /* link to next exception entry */ + } + + /* terminate the ->down linked list */ + + assert(ex != jd->exceptiontable); + ex[-1].down = NULL; + + return true; +} + + +/******************************************************************************* + + function 'parse' scans the JavaVM code and generates intermediate code + + During parsing the block index table is used to store at bit pos 0 + a flag which marks basic block starts and at position 1 to 31 the + intermediate instruction index. After parsing the block index table + is scanned, for marked positions a block is generated and the block + number is stored in the block index table. + +*******************************************************************************/ + +/*** macro for checking the length of the bytecode ***/ + +#if defined(ENABLE_VERIFIER) +#define CHECK_END_OF_BYTECODE(neededlength) \ + do { \ + if ((neededlength) > m->jcodelength) \ + goto throw_unexpected_end_of_bytecode; \ + } while (0) +#else /* !ENABLE_VERIFIER */ +#define CHECK_END_OF_BYTECODE(neededlength) +#endif /* ENABLE_VERIFIER */ + +bool parse(jitdata *jd) +{ + methodinfo *m; /* method being parsed */ + codeinfo *code; + parsedata_t pd; + instruction *iptr; /* current ptr into instruction array */ + + s4 bcindex; /* bytecode instruction index */ + s4 nextbc; /* start of next bytecode instruction */ + s4 opcode; /* bytecode instruction opcode */ + + s4 irindex; /* IR instruction index */ + s4 ircount; /* IR instruction count */ + + s4 bbcount; /* basic block count */ + + int s_count = 0; /* stack element counter */ + bool blockend; /* true if basic block end has been reached */ + bool iswide; /* true if last instruction was a wide */ + + constant_classref *cr; + constant_classref *compr; + classinfo *c; + builtintable_entry *bte; + constant_FMIref *fmi; + methoddesc *md; + unresolved_method *um; + unresolved_field *uf; + + resolve_result_t result; + u2 lineindex = 0; + u2 currentline = 0; + u2 linepcchange = 0; + u4 flags; + basicblock *bptr; + + int *local_map; /* local pointer to renaming map */ + /* is assigned to rd->local_map at the end */ + branch_target_t *table; + lookup_target_t *lookup; + s4 i; + s4 j; + + /* get required compiler data */ + + m = jd->m; + code = jd->code; + + /* allocate buffers for local variable renaming */ + + local_map = (int*) DumpMemory::allocate(sizeof(int) * m->maxlocals * 5); + + for (i = 0; i < m->maxlocals; i++) { + local_map[i * 5 + 0] = 0; + local_map[i * 5 + 1] = 0; + local_map[i * 5 + 2] = 0; + local_map[i * 5 + 3] = 0; + local_map[i * 5 + 4] = 0; + } + + /* initialize the parse data structures */ + + parse_setup(jd, &pd); + + /* initialize local variables */ + + iptr = pd.instructions; + ircount = 0; + bbcount = 0; + blockend = false; + iswide = false; + + /* mark basic block boundaries for exception table */ + + if (!parse_mark_exception_boundaries(jd, &pd)) + return false; + + /* initialize stack element counter */ + + s_count = 1 + m->rawexceptiontablelength; + + /* setup line number info */ + + currentline = 0; + linepcchange = 0; + + if (m->linenumbercount == 0) { + lineindex = 0; + } + else { + linepcchange = m->linenumbers[0].start_pc; + } + + /*** LOOP OVER ALL BYTECODE INSTRUCTIONS **********************************/ + + for (bcindex = 0; bcindex < m->jcodelength; bcindex = nextbc) { + + /* mark this position as a valid bytecode instruction start */ + + pd.bytecodestart[bcindex] = 1; + + /* change the current line number, if necessary */ + + /* XXX rewrite this using pointer arithmetic */ + + if (linepcchange == bcindex) { + if (m->linenumbercount > lineindex) { +next_linenumber: + currentline = m->linenumbers[lineindex].line_number; + lineindex++; + if (lineindex < m->linenumbercount) { + linepcchange = m->linenumbers[lineindex].start_pc; + if (linepcchange == bcindex) + goto next_linenumber; + } + } + } + +fetch_opcode: + /* fetch next opcode */ + + opcode = SUCK_BE_U1(m->jcode + bcindex); + + /* If the previous instruction was a block-end instruction, + mark the current bytecode instruction as basic-block + starting instruction. */ + + /* NOTE: Some compilers put a BC_nop after a blockend + instruction. */ + + if (blockend && (opcode != BC_nop)) { + MARK_BASICBLOCK(&pd, bcindex); + blockend = false; + } + + /* If the current bytecode instruction was marked as + basic-block starting instruction before (e.g. blockend, + forward-branch target), mark the current IR instruction + too. */ + + if (pd.basicblockstart[bcindex] != 0) { + /* We need a NOP as last instruction in each basic block + for basic block reordering (may be replaced with a GOTO + later). */ + + INSTRUCTIONS_CHECK(1); + OP(ICMD_NOP); + } + + /* store intermediate instruction count (bit 0 mark block starts) */ + + pd.bytecodemap[bcindex] = ircount; + + /* compute next instruction start */ + + nextbc = bcindex + bytecode[opcode].length; + + CHECK_END_OF_BYTECODE(nextbc); + + /* add stack elements produced by this instruction */ + + s_count += bytecode[opcode].slots; + + /* We check here for the space of 1 instruction in the + instruction array. If an opcode is converted to more than + 1 instruction, this is checked in the corresponding + case. */ + + INSTRUCTIONS_CHECK(1); + + /* translate this bytecode instruction */ + switch (opcode) { + + case BC_nop: + break; + + /* pushing constants onto the stack ***********************************/ + + case BC_bipush: + OP_LOADCONST_I(SUCK_BE_S1(m->jcode + bcindex + 1)); + break; + + case BC_sipush: + OP_LOADCONST_I(SUCK_BE_S2(m->jcode + bcindex + 1)); + break; + + case BC_ldc1: + i = SUCK_BE_U1(m->jcode + bcindex + 1); + goto pushconstantitem; + + case BC_ldc2: + case BC_ldc2w: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + + pushconstantitem: + +#if defined(ENABLE_VERIFIER) + if (i >= m->clazz->cpcount) { + exceptions_throw_verifyerror(m, + "Attempt to access constant outside range"); + return false; + } +#endif + + switch (m->clazz->cptags[i]) { + case CONSTANT_Integer: + OP_LOADCONST_I(((constant_integer *) (m->clazz->cpinfos[i]))->value); + break; + case CONSTANT_Long: + OP_LOADCONST_L(((constant_long *) (m->clazz->cpinfos[i]))->value); + break; + case CONSTANT_Float: + OP_LOADCONST_F(((constant_float *) (m->clazz->cpinfos[i]))->value); + break; + case CONSTANT_Double: + OP_LOADCONST_D(((constant_double *) (m->clazz->cpinfos[i]))->value); + break; + case CONSTANT_String: + OP_LOADCONST_STRING(literalstring_new((utf *) (m->clazz->cpinfos[i]))); + break; + case CONSTANT_Class: + cr = (constant_classref *) (m->clazz->cpinfos[i]); + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + /* if not resolved, c == NULL */ + + OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr); + + break; + +#if defined(ENABLE_VERIFIER) + default: + exceptions_throw_verifyerror(m, + "Invalid constant type to push"); + return false; +#endif + } + break; + + case BC_aconst_null: + OP_LOADCONST_NULL(); + break; + + case BC_iconst_m1: + case BC_iconst_0: + case BC_iconst_1: + case BC_iconst_2: + case BC_iconst_3: + case BC_iconst_4: + case BC_iconst_5: + OP_LOADCONST_I(opcode - BC_iconst_0); + break; + + case BC_lconst_0: + case BC_lconst_1: + OP_LOADCONST_L(opcode - BC_lconst_0); + break; + + case BC_fconst_0: + case BC_fconst_1: + case BC_fconst_2: + OP_LOADCONST_F(opcode - BC_fconst_0); + break; + + case BC_dconst_0: + case BC_dconst_1: + OP_LOADCONST_D(opcode - BC_dconst_0); + break; + + /* stack operations ***************************************************/ + + /* We need space for additional instruction so we can + translate these instructions to sequences of ICMD_COPY and + ICMD_MOVE instructions. */ + + case BC_dup_x1: + INSTRUCTIONS_CHECK(4); + OP(opcode); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + break; + + case BC_dup_x2: + INSTRUCTIONS_CHECK(6); + OP(opcode); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + break; + + case BC_dup2: + INSTRUCTIONS_CHECK(2); + OP(opcode); + OP(ICMD_NOP); + break; + + case BC_dup2_x1: + INSTRUCTIONS_CHECK(7); + OP(opcode); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + break; + + case BC_dup2_x2: + INSTRUCTIONS_CHECK(9); + OP(opcode); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + OP(ICMD_NOP); + break; + + case BC_swap: + INSTRUCTIONS_CHECK(3); + OP(opcode); + OP(ICMD_NOP); + OP(ICMD_NOP); + break; + + /* local variable access instructions *********************************/ + + case BC_iload: + case BC_fload: + case BC_aload: + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + nextbc = bcindex + 3; + iswide = false; + } + OP_LOAD_ONEWORD(opcode, i, opcode - BC_iload); + break; + + case BC_lload: + case BC_dload: + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + nextbc = bcindex + 3; + iswide = false; + } + OP_LOAD_TWOWORD(opcode, i, opcode - BC_iload); + break; + + case BC_iload_0: + case BC_iload_1: + case BC_iload_2: + case BC_iload_3: + OP_LOAD_ONEWORD(ICMD_ILOAD, opcode - BC_iload_0, TYPE_INT); + break; + + case BC_lload_0: + case BC_lload_1: + case BC_lload_2: + case BC_lload_3: + OP_LOAD_TWOWORD(ICMD_LLOAD, opcode - BC_lload_0, TYPE_LNG); + break; + + case BC_fload_0: + case BC_fload_1: + case BC_fload_2: + case BC_fload_3: + OP_LOAD_ONEWORD(ICMD_FLOAD, opcode - BC_fload_0, TYPE_FLT); + break; + + case BC_dload_0: + case BC_dload_1: + case BC_dload_2: + case BC_dload_3: + OP_LOAD_TWOWORD(ICMD_DLOAD, opcode - BC_dload_0, TYPE_DBL); + break; + + case BC_aload_0: + case BC_aload_1: + case BC_aload_2: + case BC_aload_3: + OP_LOAD_ONEWORD(ICMD_ALOAD, opcode - BC_aload_0, TYPE_ADR); + break; + + case BC_istore: + case BC_fstore: + case BC_astore: + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + nextbc = bcindex + 3; + iswide = false; + } + OP_STORE_ONEWORD(opcode, i, opcode - BC_istore); + break; + + case BC_lstore: + case BC_dstore: + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + nextbc = bcindex + 3; + iswide = false; + } + OP_STORE_TWOWORD(opcode, i, opcode - BC_istore); + break; + + case BC_istore_0: + case BC_istore_1: + case BC_istore_2: + case BC_istore_3: + OP_STORE_ONEWORD(ICMD_ISTORE, opcode - BC_istore_0, TYPE_INT); + break; + + case BC_lstore_0: + case BC_lstore_1: + case BC_lstore_2: + case BC_lstore_3: + OP_STORE_TWOWORD(ICMD_LSTORE, opcode - BC_lstore_0, TYPE_LNG); + break; + + case BC_fstore_0: + case BC_fstore_1: + case BC_fstore_2: + case BC_fstore_3: + OP_STORE_ONEWORD(ICMD_FSTORE, opcode - BC_fstore_0, TYPE_FLT); + break; + + case BC_dstore_0: + case BC_dstore_1: + case BC_dstore_2: + case BC_dstore_3: + OP_STORE_TWOWORD(ICMD_DSTORE, opcode - BC_dstore_0, TYPE_DBL); + break; + + case BC_astore_0: + case BC_astore_1: + case BC_astore_2: + case BC_astore_3: + OP_STORE_ONEWORD(ICMD_ASTORE, opcode - BC_astore_0, TYPE_ADR); + break; + + case BC_iinc: + { + int v; + + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + v = SUCK_BE_S1(m->jcode + bcindex + 2); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + v = SUCK_BE_S2(m->jcode + bcindex + 3); + nextbc = bcindex + 5; + iswide = false; + } + INDEX_ONEWORD(i); + LOCALTYPE_USED(i, TYPE_INT); + OP_LOCALINDEX_I(opcode, i, v); + } + break; + + /* wider index for loading, storing and incrementing ******************/ + + case BC_wide: + bcindex++; + iswide = true; + goto fetch_opcode; + + /* managing arrays ****************************************************/ + + case BC_newarray: + switch (SUCK_BE_S1(m->jcode + bcindex + 1)) { + case 4: + bte = builtintable_get_internal(BUILTIN_newarray_boolean); + break; + case 5: + bte = builtintable_get_internal(BUILTIN_newarray_char); + break; + case 6: + bte = builtintable_get_internal(BUILTIN_newarray_float); + break; + case 7: + bte = builtintable_get_internal(BUILTIN_newarray_double); + break; + case 8: + bte = builtintable_get_internal(BUILTIN_newarray_byte); + break; + case 9: + bte = builtintable_get_internal(BUILTIN_newarray_short); + break; + case 10: + bte = builtintable_get_internal(BUILTIN_newarray_int); + break; + case 11: + bte = builtintable_get_internal(BUILTIN_newarray_long); + break; +#if defined(ENABLE_VERIFIER) + default: + exceptions_throw_verifyerror(m, "Invalid array-type to create"); + return false; +#endif + } + OP_BUILTIN_CHECK_EXCEPTION(bte); + break; + + case BC_anewarray: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + compr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); + if (compr == NULL) + return false; + + if (!(cr = class_get_classref_multiarray_of(1, compr))) + return false; + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + INSTRUCTIONS_CHECK(2); + OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); + bte = builtintable_get_internal(BUILTIN_newarray); + OP_BUILTIN_CHECK_EXCEPTION(bte); + s_count++; + break; + + case BC_multianewarray: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + j = SUCK_BE_U1(m->jcode + bcindex + 3); + + cr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); + if (cr == NULL) + return false; + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + /* if unresolved, c == NULL */ + + iptr->s1.argcount = j; + OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, INS_FLAG_CHECK); + code_unflag_leafmethod(code); + break; + + /* control flow instructions ******************************************/ + + case BC_ifeq: + case BC_iflt: + case BC_ifle: + case BC_ifne: + case BC_ifgt: + case BC_ifge: + case BC_ifnull: + case BC_ifnonnull: + case BC_if_icmpeq: + case BC_if_icmpne: + case BC_if_icmplt: + case BC_if_icmpgt: + case BC_if_icmple: + case BC_if_icmpge: + case BC_if_acmpeq: + case BC_if_acmpne: + case BC_goto: + i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); + CHECK_BYTECODE_INDEX(i); + MARK_BASICBLOCK(&pd, i); + blockend = true; + OP_INSINDEX(opcode, i); + break; + + case BC_goto_w: + i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); + CHECK_BYTECODE_INDEX(i); + MARK_BASICBLOCK(&pd, i); + blockend = true; + OP_INSINDEX(ICMD_GOTO, i); + break; + + case BC_jsr: + i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); +jsr_tail: + CHECK_BYTECODE_INDEX(i); + MARK_BASICBLOCK(&pd, i); + blockend = true; + OP_PREPARE_ZEROFLAGS(BC_jsr); + iptr->sx.s23.s3.jsrtarget.insindex = i; + PINC; + break; + + case BC_jsr_w: + i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); + goto jsr_tail; + + case BC_ret: + if (iswide == false) { + i = SUCK_BE_U1(m->jcode + bcindex + 1); + } + else { + i = SUCK_BE_U2(m->jcode + bcindex + 1); + nextbc = bcindex + 3; + iswide = false; + } + blockend = true; + + OP_LOAD_ONEWORD(opcode, i, TYPE_ADR); + break; + + case BC_ireturn: + case BC_lreturn: + case BC_freturn: + case BC_dreturn: + case BC_areturn: + case BC_return: + blockend = true; + /* XXX ARETURN will need a flag in the typechecker */ + OP(opcode); + break; + + case BC_athrow: + blockend = true; + /* XXX ATHROW will need a flag in the typechecker */ + OP(opcode); + break; + + + /* table jumps ********************************************************/ + + case BC_lookupswitch: + { + s4 num, j; + lookup_target_t *lookup; +#if defined(ENABLE_VERIFIER) + s4 prevvalue = 0; +#endif + blockend = true; + nextbc = MEMORY_ALIGN((bcindex + 1), 4); + + CHECK_END_OF_BYTECODE(nextbc + 8); + + OP_PREPARE_ZEROFLAGS(opcode); + + /* default target */ + + j = bcindex + SUCK_BE_S4(m->jcode + nextbc); + iptr->sx.s23.s3.lookupdefault.insindex = j; + nextbc += 4; + CHECK_BYTECODE_INDEX(j); + MARK_BASICBLOCK(&pd, j); + + /* number of pairs */ + + num = SUCK_BE_U4(m->jcode + nextbc); + iptr->sx.s23.s2.lookupcount = num; + nextbc += 4; + + /* allocate the intermediate code table */ + + lookup = (lookup_target_t*) DumpMemory::allocate(sizeof(lookup_target_t) * num); + iptr->dst.lookup = lookup; + + /* iterate over the lookup table */ + + CHECK_END_OF_BYTECODE(nextbc + 8 * num); + + for (i = 0; i < num; i++) { + /* value */ + + j = SUCK_BE_S4(m->jcode + nextbc); + lookup->value = j; + + nextbc += 4; + +#if defined(ENABLE_VERIFIER) + /* check if the lookup table is sorted correctly */ + + if (i && (j <= prevvalue)) { + exceptions_throw_verifyerror(m, "Unsorted lookup switch"); + return false; + } + prevvalue = j; +#endif + /* target */ + + j = bcindex + SUCK_BE_S4(m->jcode + nextbc); + lookup->target.insindex = j; + lookup++; + nextbc += 4; + CHECK_BYTECODE_INDEX(j); + MARK_BASICBLOCK(&pd, j); + } + + PINC; + break; + } + + case BC_tableswitch: + { + s4 num, j; + s4 deftarget; + branch_target_t *table; + + blockend = true; + nextbc = MEMORY_ALIGN((bcindex + 1), 4); + + CHECK_END_OF_BYTECODE(nextbc + 12); + + OP_PREPARE_ZEROFLAGS(opcode); + + /* default target */ + + deftarget = bcindex + SUCK_BE_S4(m->jcode + nextbc); + nextbc += 4; + CHECK_BYTECODE_INDEX(deftarget); + MARK_BASICBLOCK(&pd, deftarget); + + /* lower bound */ + + j = SUCK_BE_S4(m->jcode + nextbc); + iptr->sx.s23.s2.tablelow = j; + nextbc += 4; + + /* upper bound */ + + num = SUCK_BE_S4(m->jcode + nextbc); + iptr->sx.s23.s3.tablehigh = num; + nextbc += 4; + + /* calculate the number of table entries */ + + num = num - j + 1; + +#if defined(ENABLE_VERIFIER) + if (num < 1) { + exceptions_throw_verifyerror(m, + "invalid TABLESWITCH: upper bound < lower bound"); + return false; + } +#endif + /* create the intermediate code table */ + /* the first entry is the default target */ + + table = (branch_target_t*) DumpMemory::allocate(sizeof(branch_target_t) * (1 + num)); + iptr->dst.table = table; + (table++)->insindex = deftarget; + + /* iterate over the target table */ + + CHECK_END_OF_BYTECODE(nextbc + 4 * num); + + for (i = 0; i < num; i++) { + j = bcindex + SUCK_BE_S4(m->jcode + nextbc); + (table++)->insindex = j; + nextbc += 4; + CHECK_BYTECODE_INDEX(j); + MARK_BASICBLOCK(&pd, j); + } + + PINC; + break; + } + + + /* load and store of object fields ************************************/ + + case BC_aastore: + OP(opcode); + code_unflag_leafmethod(code); + break; + + case BC_getstatic: + case BC_putstatic: + case BC_getfield: + case BC_putfield: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Fieldref); + + if (fmi == NULL) + return false; + + OP_PREPARE_ZEROFLAGS(opcode); + iptr->sx.s23.s3.fmiref = fmi; + + /* only with -noverify, otherwise the typechecker does this */ + +#if defined(ENABLE_VERIFIER) + if (!JITDATA_HAS_FLAG_VERIFY(jd)) { +#endif + result = resolve_field_lazy(m, fmi); + + if (result == resolveFailed) + return false; + + if (result != resolveSucceeded) { + uf = resolve_create_unresolved_field(m->clazz, m, iptr); + + if (uf == NULL) + return false; + + /* store the unresolved_field pointer */ + + iptr->sx.s23.s3.uf = uf; + iptr->flags.bits |= INS_FLAG_UNRESOLVED; + } +#if defined(ENABLE_VERIFIER) + } +#endif + PINC; + break; + + + /* method invocation **************************************************/ + + case BC_invokestatic: + OP_PREPARE_ZEROFLAGS(opcode); + + i = SUCK_BE_U2(m->jcode + bcindex + 1); + fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); + + if (fmi == NULL) + return false; + + md = fmi->parseddesc.md; + + if (md->params == NULL) + if (!descriptor_params_from_paramtypes(md, ACC_STATIC)) + return false; + + goto invoke_method; + + case BC_invokespecial: + OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); + + i = SUCK_BE_U2(m->jcode + bcindex + 1); + fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); + + goto invoke_nonstatic_method; + + case BC_invokeinterface: + OP_PREPARE_ZEROFLAGS(opcode); + + i = SUCK_BE_U2(m->jcode + bcindex + 1); + fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_InterfaceMethodref); + + goto invoke_nonstatic_method; + + case BC_invokevirtual: + OP_PREPARE_ZEROFLAGS(opcode); + + i = SUCK_BE_U2(m->jcode + bcindex + 1); + fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); + +invoke_nonstatic_method: + if (fmi == NULL) + return false; + + md = fmi->parseddesc.md; + + if (md->params == NULL) + if (!descriptor_params_from_paramtypes(md, 0)) + return false; + +invoke_method: + code_unflag_leafmethod(code); + + iptr->sx.s23.s3.fmiref = fmi; + + /* only with -noverify, otherwise the typechecker does this */ + +#if defined(ENABLE_VERIFIER) + if (!JITDATA_HAS_FLAG_VERIFY(jd)) { +#endif + result = resolve_method_lazy(m, fmi, + (opcode == BC_invokespecial)); + + if (result == resolveFailed) + return false; + + if (result == resolveSucceeded) { + methodinfo *mi = iptr->sx.s23.s3.fmiref->p.method; + + /* if this call is monomorphic, turn it into an + INVOKESPECIAL */ + + assert(IS_FMIREF_RESOLVED(iptr->sx.s23.s3.fmiref)); + + if ((iptr->opc == ICMD_INVOKEVIRTUAL) + && (mi->flags & (ACC_FINAL | ACC_PRIVATE))) + { + iptr->opc = ICMD_INVOKESPECIAL; + iptr->flags.bits |= INS_FLAG_CHECK; + } + } + else { + um = resolve_create_unresolved_method(m->clazz, m, fmi, + (opcode == BC_invokestatic), + (opcode == BC_invokespecial)); + + if (um == NULL) + return false; + + /* store the unresolved_method pointer */ + + iptr->sx.s23.s3.um = um; + iptr->flags.bits |= INS_FLAG_UNRESOLVED; + } +#if defined(ENABLE_VERIFIER) + } +#endif + PINC; + break; + + /* instructions taking class arguments ********************************/ + + case BC_new: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); + + if (cr == NULL) + return false; + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + INSTRUCTIONS_CHECK(2); + OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); + bte = builtintable_get_internal(BUILTIN_new); + OP_BUILTIN_CHECK_EXCEPTION(bte); + s_count++; + break; + + case BC_checkcast: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); + + if (cr == NULL) + return false; + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + if (cr->name->text[0] == '[') { + /* array type cast-check */ + flags = INS_FLAG_CHECK | INS_FLAG_ARRAY; + code_unflag_leafmethod(code); + } + else { + /* object type cast-check */ + flags = INS_FLAG_CHECK; + } + OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, flags); + break; + + case BC_instanceof: + i = SUCK_BE_U2(m->jcode + bcindex + 1); + cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); + + if (cr == NULL) + return false; + + if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) + return false; + + if (cr->name->text[0] == '[') { + /* array type cast-check */ + INSTRUCTIONS_CHECK(2); + OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); + bte = builtintable_get_internal(BUILTIN_arrayinstanceof); + OP_BUILTIN_NO_EXCEPTION(bte); + s_count++; + } + else { + /* object type cast-check */ + OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, 0 /* flags*/); + } + break; + + /* synchronization instructions ***************************************/ + + case BC_monitorenter: +#if defined(ENABLE_THREADS) + if (checksync) { + bte = builtintable_get_internal(LOCK_monitor_enter); + OP_BUILTIN_CHECK_EXCEPTION(bte); + } + else +#endif + { + OP_CHECK_EXCEPTION(ICMD_CHECKNULL); + OP(ICMD_POP); + } + break; + + case BC_monitorexit: +#if defined(ENABLE_THREADS) + if (checksync) { + bte = builtintable_get_internal(LOCK_monitor_exit); + OP_BUILTIN_CHECK_EXCEPTION(bte); + } + else +#endif + { + OP_CHECK_EXCEPTION(ICMD_CHECKNULL); + OP(ICMD_POP); + } + break; + + /* arithmetic instructions that may become builtin functions **********/ + + case BC_idiv: +#if !SUPPORT_DIVISION + bte = builtintable_get_internal(BUILTIN_idiv); + OP_BUILTIN_ARITHMETIC(opcode, bte); +#else +# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO + OP(opcode); +# else + OP_CHECK_EXCEPTION(opcode); +# endif +#endif + break; + + case BC_irem: +#if !SUPPORT_DIVISION + bte = builtintable_get_internal(BUILTIN_irem); + OP_BUILTIN_ARITHMETIC(opcode, bte); +#else +# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO + OP(opcode); +# else + OP_CHECK_EXCEPTION(opcode); +# endif +#endif + break; + + case BC_ldiv: +#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) + bte = builtintable_get_internal(BUILTIN_ldiv); + OP_BUILTIN_ARITHMETIC(opcode, bte); +#else +# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO + OP(opcode); +# else + OP_CHECK_EXCEPTION(opcode); +# endif +#endif + break; + + case BC_lrem: +#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) + bte = builtintable_get_internal(BUILTIN_lrem); + OP_BUILTIN_ARITHMETIC(opcode, bte); +#else +# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO + OP(opcode); +# else + OP_CHECK_EXCEPTION(opcode); +# endif +#endif + break; + + case BC_frem: +#if defined(__I386__) + OP(opcode); +#else + bte = builtintable_get_internal(BUILTIN_frem); + OP_BUILTIN_NO_EXCEPTION(bte); +#endif + break; + + case BC_drem: +#if defined(__I386__) + OP(opcode); +#else + bte = builtintable_get_internal(BUILTIN_drem); + OP_BUILTIN_NO_EXCEPTION(bte); +#endif + break; + + case BC_f2i: +#if defined(__ALPHA__) + bte = builtintable_get_internal(BUILTIN_f2i); + OP_BUILTIN_NO_EXCEPTION(bte); +#else + OP(opcode); +#endif + break; + + case BC_f2l: +#if defined(__ALPHA__) + bte = builtintable_get_internal(BUILTIN_f2l); + OP_BUILTIN_NO_EXCEPTION(bte); +#else + OP(opcode); +#endif + break; + + case BC_d2i: +#if defined(__ALPHA__) + bte = builtintable_get_internal(BUILTIN_d2i); + OP_BUILTIN_NO_EXCEPTION(bte); +#else + OP(opcode); +#endif + break; + + case BC_d2l: +#if defined(__ALPHA__) + bte = builtintable_get_internal(BUILTIN_d2l); + OP_BUILTIN_NO_EXCEPTION(bte); +#else + OP(opcode); +#endif + break; + + + /* invalid opcodes ****************************************************/ + + /* check for invalid opcodes if the verifier is enabled */ +#if defined(ENABLE_VERIFIER) + case BC_breakpoint: + exceptions_throw_verifyerror(m, "Quick instructions shouldn't appear, yet."); + return false; + + + /* Unused opcodes ************************************************** */ + + case 186: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 230: + case 231: + case 232: + case 233: + case 234: + case 235: + case 236: + case 237: + case 238: + case 239: + case 240: + case 241: + case 242: + case 243: + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + exceptions_throw_verifyerror(m, "Illegal opcode %d at instr %d\n", + opcode, ircount); + return false; + break; +#endif /* defined(ENABLE_VERIFIER) */ + + /* opcodes that don't require translation *****************************/ + + default: + /* Straight-forward translation to HIR. */ + OP(opcode); + break; + + } /* end switch */ + + /* verifier checks ****************************************************/ + +#if defined(ENABLE_VERIFIER) + /* If WIDE was used correctly, iswide should have been reset by now. */ + if (iswide) { + exceptions_throw_verifyerror(m, + "Illegal instruction: WIDE before incompatible opcode"); + return false; + } +#endif /* defined(ENABLE_VERIFIER) */ + + } /* end for */ + + if (JITDATA_HAS_FLAG_REORDER(jd)) { + /* add a NOP to the last basic block */ + + INSTRUCTIONS_CHECK(1); + OP(ICMD_NOP); + } + + /*** END OF LOOP **********************************************************/ + + /* assert that we did not write more ICMDs than allocated */ + + assert(ircount <= pd.instructionslength); + assert(ircount == (iptr - pd.instructions)); + + /*** verifier checks ******************************************************/ + +#if defined(ENABLE_VERIFIER) + if (bcindex != m->jcodelength) { + exceptions_throw_verifyerror(m, + "Command-sequence crosses code-boundary"); + return false; + } + + if (!blockend) { + exceptions_throw_verifyerror(m, "Falling off the end of the code"); + return false; + } +#endif /* defined(ENABLE_VERIFIER) */ + + /*** setup the methodinfo, allocate stack and basic blocks ****************/ + + /* identify basic blocks */ + + /* check if first instruction is a branch target */ + + if (pd.basicblockstart[0] == 1) { + jd->branchtoentry = true; + } + else { + /* first instruction always starts a basic block */ + + iptr = pd.instructions; + + iptr->flags.bits |= INS_FLAG_BASICBLOCK; + } + + /* Iterate over all bytecode instructions and set missing + basic-block starts in IR instructions. */ + + for (bcindex = 0; bcindex < m->jcodelength; bcindex++) { + /* Does the current bytecode instruction start a basic + block? */ + + if (pd.basicblockstart[bcindex] == 1) { +#if defined(ENABLE_VERIFIER) + /* Check if this bytecode basic-block start at the + beginning of a bytecode instruction. */ + + if (pd.bytecodestart[bcindex] == 0) { + exceptions_throw_verifyerror(m, + "Branch into middle of instruction"); + return false; + } +#endif + + /* Get the IR instruction mapped to the bytecode + instruction and set the basic block flag. */ + + irindex = pd.bytecodemap[bcindex]; + iptr = pd.instructions + irindex; + + iptr->flags.bits |= INS_FLAG_BASICBLOCK; + } + } + + /* IR instruction index to basic-block index mapping */ + + pd.instructionmap = (s4*) DumpMemory::allocate(sizeof(s4) * ircount); + MZERO(pd.instructionmap, s4, ircount); + + /* Iterate over all IR instructions and count the basic blocks. */ + + iptr = pd.instructions; + + bbcount = 0; + + for (i = 0; i < ircount; i++, iptr++) { + if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { + /* store the basic-block number in the IR instruction + map */ + + pd.instructionmap[i] = bbcount; + + /* post-increment the basic-block count */ + + bbcount++; + } + } + + /* Allocate basic block array (one more for end ipc). */ + + jd->basicblocks = (basicblock*) DumpMemory::allocate(sizeof(basicblock) * (bbcount + 1)); + MZERO(jd->basicblocks, basicblock, bbcount + 1); + + /* Now iterate again over all IR instructions and initialize the + basic block structures and, in the same loop, resolve the + branch-target instruction indices to basic blocks. */ + + iptr = pd.instructions; + bptr = jd->basicblocks; + + bbcount = 0; + + for (i = 0; i < ircount; i++, iptr++) { + /* check for basic block */ + + if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { + /* intialize the basic block */ + + BASICBLOCK_INIT(bptr, m); + + bptr->iinstr = iptr; + + if (bbcount > 0) { + bptr[-1].icount = bptr->iinstr - bptr[-1].iinstr; + } + + /* bptr->icount is set when the next block is allocated */ + + bptr->nr = bbcount++; + bptr++; + bptr[-1].next = bptr; + } + + /* resolve instruction indices to basic blocks */ + + switch (iptr->opc) { + case ICMD_IFEQ: + case ICMD_IFLT: + case ICMD_IFLE: + case ICMD_IFNE: + case ICMD_IFGT: + case ICMD_IFGE: + case ICMD_IFNULL: + case ICMD_IFNONNULL: + case ICMD_IF_ICMPEQ: + case ICMD_IF_ICMPNE: + case ICMD_IF_ICMPLT: + case ICMD_IF_ICMPGT: + case ICMD_IF_ICMPLE: + case ICMD_IF_ICMPGE: + case ICMD_IF_ACMPEQ: + case ICMD_IF_ACMPNE: + case ICMD_GOTO: + BYTECODEINDEX_TO_BASICBLOCK(iptr->dst); + break; + + case ICMD_JSR: + BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.jsrtarget); + break; + + case ICMD_TABLESWITCH: + table = iptr->dst.table; + + BYTECODEINDEX_TO_BASICBLOCK(*table); + table++; + + j = iptr->sx.s23.s3.tablehigh - iptr->sx.s23.s2.tablelow + 1; + + while (--j >= 0) { + BYTECODEINDEX_TO_BASICBLOCK(*table); + table++; + } + break; + + case ICMD_LOOKUPSWITCH: + BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.lookupdefault); + + lookup = iptr->dst.lookup; + + j = iptr->sx.s23.s2.lookupcount; + + while (--j >= 0) { + BYTECODEINDEX_TO_BASICBLOCK(lookup->target); + lookup++; + } + break; + } + } + + /* set instruction count of last real block */ + + if (bbcount > 0) { + bptr[-1].icount = (pd.instructions + ircount) - bptr[-1].iinstr; + } + + /* allocate additional block at end */ + + BASICBLOCK_INIT(bptr, m); + bptr->nr = bbcount; + + /* set basicblock pointers in exception table */ + + if (!parse_resolve_exception_table(jd, &pd)) + return false; + + /* store the local map */ + + jd->local_map = local_map; + + /* calculate local variable renaming */ + + { + s4 nlocals = 0; + s4 i; + s4 t; + s4 varindex; + s4 *mapptr; + s4 *reversemap; + + mapptr = local_map; + + /* iterate over local_map[0..m->maxlocals*5-1] and allocate a unique */ + /* variable index for each _used_ (javaindex,type) pair. */ + /* (local_map[javaindex*5+type] = cacaoindex) */ + /* Unused (javaindex,type) pairs are marked with UNUSED. */ + + for (i = 0; i < (m->maxlocals * 5); i++, mapptr++) { + if (*mapptr) + *mapptr = nlocals++; + else + *mapptr = UNUSED; + } + + jd->localcount = nlocals; + + /* calculate the (maximum) number of variables needed */ + + jd->varcount = + nlocals /* local variables */ + + bbcount * m->maxstack /* invars */ + + s_count; /* variables created within blocks (non-invar) */ + + /* reserve the first indices for local variables */ + + jd->vartop = nlocals; + + /* reserve extra variables needed by stack analyse */ + + jd->varcount += STACK_EXTRA_VARS; + jd->vartop += STACK_EXTRA_VARS; + + /* The verifier needs space for saving invars in some cases and */ + /* extra variables. */ + +#if defined(ENABLE_VERIFIER) + jd->varcount += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; + jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; +#endif + /* allocate and initialize the variable array */ + + jd->var = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * jd->varcount); + MZERO(jd->var, varinfo, jd->varcount); + + /* set types of all locals in jd->var */ + /* and fill the reverselocalmap */ + + reversemap = (s4*) DumpMemory::allocate(sizeof(s4) * nlocals); + + for (i = 0; i < m->maxlocals; i++) + for (t=0; t<5; t++) { + varindex = local_map[5*i + t]; + if (varindex != UNUSED) { + VAR(varindex)->type = t; + reversemap[varindex] = i; + } + } + + jd->reverselocalmap = reversemap; + } + + /* assign local variables to method variables */ + + jd->instructions = pd.instructions; + jd->instructioncount = ircount; + jd->basicblockcount = bbcount; + jd->stackcount = s_count + bbcount * m->maxstack; /* in-stacks */ + + /* allocate stack table */ + + jd->stack = (stackelement_t*) DumpMemory::allocate(sizeof(stackelement_t) * jd->stackcount); + + /* everything's ok */ + + return true; + + /*** goto labels for throwing verifier exceptions *************************/ + +#if defined(ENABLE_VERIFIER) + +throw_unexpected_end_of_bytecode: + exceptions_throw_verifyerror(m, "Unexpected end of bytecode"); + return false; + +throw_invalid_bytecode_index: + exceptions_throw_verifyerror(m, "Illegal target of branch instruction"); + return false; + +throw_illegal_local_variable_number: + exceptions_throw_verifyerror(m, "Illegal local variable number"); + return false; + +#endif /* ENABLE_VERIFIER */ +} + +#if defined(__cplusplus) +} +#endif + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/parse.h b/src/vm/jit/parse.h deleted file mode 100644 index b10d7dad5..000000000 --- a/src/vm/jit/parse.h +++ /dev/null @@ -1,298 +0,0 @@ -/* src/vm/jit/parse.h - parser header - - Copyright (C) 1996-2005, 2006, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#ifndef _PARSE_H -#define _PARSE_H - -#include "config.h" -#include "vm/types.h" - -#include "vm/global.h" -#include "vm/jit/codegen-common.hpp" - - -/* macros for verifier checks during parsing **********************************/ - -#if defined(ENABLE_VERIFIER) - -/* We have to check local variables indices here because they are */ -/* used in stack.c to index the locals array. */ - -#define INDEX_ONEWORD(num) \ - do { \ - if (((num) < 0) || ((num) >= m->maxlocals)) \ - goto throw_illegal_local_variable_number; \ - } while (0) - -#define INDEX_TWOWORD(num) \ - do { \ - if (((num) < 0) || (((num) + 1) >= m->maxlocals)) \ - goto throw_illegal_local_variable_number; \ - } while (0) - -/* CHECK_BYTECODE_INDEX(i) checks whether i is a valid bytecode index. */ -/* The end of the bytecode (i == m->jcodelength) is considered valid. */ - -#define CHECK_BYTECODE_INDEX(i) \ - do { \ - if (((i) < 0) || ((i) >= m->jcodelength)) \ - goto throw_invalid_bytecode_index; \ - } while (0) - -/* CHECK_BYTECODE_INDEX_EXCLUSIVE is used for the exclusive ends */ -/* of exception handler ranges. */ -#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) \ - do { \ - if ((i) < 0 || (i) > m->jcodelength) \ - goto throw_invalid_bytecode_index; \ - } while (0) - -#else /* !defined(ENABLE_VERIFIER) */ - -#define INDEX_ONEWORD(num) -#define INDEX_TWOWORD(num) -#define CHECK_BYTECODE_INDEX(i) -#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) - -#endif /* defined(ENABLE_VERIFIER) */ - - -/* basic block generating macro ***********************************************/ - -#define MARK_BASICBLOCK(pd, i) \ - do { \ - (pd)->basicblockstart[(i)] = 1; \ - } while (0) - -#define INSTRUCTIONS_CHECK(i) \ - if ((ircount + (i)) > pd.instructionslength) \ - iptr = parse_realloc_instructions(&pd, ircount, (i)) - - -/* intermediate code generating macros ****************************************/ - -/* These macros ALWAYS set the following fields of *iptr to valid values: */ -/* iptr->opc */ -/* iptr->flags */ -/* iptr->line */ - -/* These macros do NOT touch the following fields of *iptr, unless a value is */ -/* given for them: */ -/* iptr->s1 */ -/* iptr->sx */ -/* iptr->dst */ - -/* The _PREPARE macros omit the PINC, so you can set additional fields */ -/* afterwards. */ - -#define PINC \ - iptr++; ircount++ - -#define OP_PREPARE_FLAGS(o, f) \ - iptr->opc = (o); \ - iptr->line = currentline; \ - iptr->flags.bits |= (f) | (ircount << INS_FLAG_ID_SHIFT); - -#define OP_PREPARE_ZEROFLAGS(o) \ - OP_PREPARE_FLAGS(o, 0) - -#define OP_PREPARE(o) \ - OP_PREPARE_ZEROFLAGS(o) - -#define OP(o) \ - OP_PREPARE_ZEROFLAGS(o); \ - PINC - -#define OP_CHECK_EXCEPTION(o) \ - OP_PREPARE_FLAGS(o, INS_FLAG_CHECK); \ - PINC - -#define OP_LOADCONST_I(v) \ - OP_PREPARE_ZEROFLAGS(ICMD_ICONST); \ - iptr->sx.val.i = (v); \ - PINC - -#define OP_LOADCONST_L(v) \ - OP_PREPARE_ZEROFLAGS(ICMD_LCONST); \ - iptr->sx.val.l = (v); \ - PINC - -#define OP_LOADCONST_F(v) \ - OP_PREPARE_ZEROFLAGS(ICMD_FCONST); \ - iptr->sx.val.f = (v); \ - PINC - -#define OP_LOADCONST_D(v) \ - OP_PREPARE_ZEROFLAGS(ICMD_DCONST); \ - iptr->sx.val.d = (v); \ - PINC - -#define OP_LOADCONST_NULL() \ - OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \ - iptr->sx.val.anyptr = NULL; \ - PINC - -#define OP_LOADCONST_STRING(v) \ - OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \ - iptr->sx.val.stringconst = (v); \ - PINC - -#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS(cl, cr, extraflags) \ - OP_PREPARE(ICMD_ACONST); \ - if (cl) { \ - iptr->sx.val.c.cls = (cl); \ - iptr->flags.bits |= INS_FLAG_CLASS | (extraflags); \ - } \ - else { \ - iptr->sx.val.c.ref = (cr); \ - iptr->flags.bits |= INS_FLAG_CLASS | INS_FLAG_UNRESOLVED \ - | (extraflags); \ - } \ - PINC - -#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr) \ - OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), INS_FLAG_CHECK) - -#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr) \ - OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), 0) - -#define OP_S3_CLASSINFO_OR_CLASSREF(o, c, cr, extraflags) \ - OP_PREPARE(o); \ - if (c) { \ - iptr->sx.s23.s3.c.cls= (c); \ - iptr->flags.bits |= (extraflags); \ - } \ - else { \ - iptr->sx.s23.s3.c.ref= (cr); \ - iptr->flags.bits |= INS_FLAG_UNRESOLVED | (extraflags); \ - } \ - PINC - -#define OP_INSINDEX(o, iindex) \ - OP_PREPARE_ZEROFLAGS(o); \ - iptr->dst.insindex = (iindex); \ - PINC - -# define OP_LOCALINDEX(o,index) \ - OP_PREPARE_ZEROFLAGS(o); \ - iptr->s1.varindex = (index); \ - PINC - -# define OP_LOCALINDEX_I(o,index,v) \ - OP_PREPARE_ZEROFLAGS(o); \ - iptr->s1.varindex = (index); \ - iptr->sx.val.i = (v); \ - PINC - -# define LOCALTYPE_USED(index,type) \ - do { \ - local_map[(index) * 5 + (type)] = 1; \ - } while (0) - -#define OP_LOAD_ONEWORD(o,index,type) \ - do { \ - INDEX_ONEWORD(index); \ - OP_LOCALINDEX(o,index); \ - LOCALTYPE_USED(index,type); \ - } while (0) - -#define OP_LOAD_TWOWORD(o,index,type) \ - do { \ - INDEX_TWOWORD(index); \ - OP_LOCALINDEX(o,index); \ - LOCALTYPE_USED(index,type); \ - } while (0) - -# define OP_STORE_ONEWORD(o,index,type) \ - do { \ - INDEX_ONEWORD(index); \ - OP_PREPARE_ZEROFLAGS(o); \ - iptr->dst.varindex = (index); \ - LOCALTYPE_USED(index,type); \ - PINC; \ - } while (0) - -# define OP_STORE_TWOWORD(o,index,type) \ - do { \ - INDEX_TWOWORD(index); \ - OP_PREPARE_ZEROFLAGS(o); \ - iptr->dst.varindex = (index); \ - LOCALTYPE_USED(index,type); \ - PINC; \ - } while (0) - -#define OP_BUILTIN_CHECK_EXCEPTION(bte) \ - code_unflag_leafmethod(code); \ - OP_PREPARE_FLAGS(ICMD_BUILTIN, INS_FLAG_CHECK); \ - iptr->sx.s23.s3.bte = (bte); \ - PINC - -#define OP_BUILTIN_NO_EXCEPTION(bte) \ - code_unflag_leafmethod(code); \ - OP_PREPARE_ZEROFLAGS(ICMD_BUILTIN); \ - iptr->sx.s23.s3.bte = (bte); \ - PINC - -#define OP_BUILTIN_ARITHMETIC(opcode, bte) \ - code_unflag_leafmethod(code); \ - OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); \ - iptr->sx.s23.s3.bte = (bte); \ - PINC - -/* CAUTION: You must set iptr->flags yourself when using this! */ -#define OP_FMIREF_PREPARE(o, fmiref) \ - OP_PREPARE(o); \ - iptr->sx.s23.s3.fmiref = (fmiref); - - -/* function prototypes ********************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -bool parse(jitdata *jd); - -#ifdef __cplusplus -} -#endif - -#endif /* _PARSE_H */ - - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ - diff --git a/src/vm/jit/parse.hpp b/src/vm/jit/parse.hpp new file mode 100644 index 000000000..b10d7dad5 --- /dev/null +++ b/src/vm/jit/parse.hpp @@ -0,0 +1,298 @@ +/* src/vm/jit/parse.h - parser header + + Copyright (C) 1996-2005, 2006, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#ifndef _PARSE_H +#define _PARSE_H + +#include "config.h" +#include "vm/types.h" + +#include "vm/global.h" +#include "vm/jit/codegen-common.hpp" + + +/* macros for verifier checks during parsing **********************************/ + +#if defined(ENABLE_VERIFIER) + +/* We have to check local variables indices here because they are */ +/* used in stack.c to index the locals array. */ + +#define INDEX_ONEWORD(num) \ + do { \ + if (((num) < 0) || ((num) >= m->maxlocals)) \ + goto throw_illegal_local_variable_number; \ + } while (0) + +#define INDEX_TWOWORD(num) \ + do { \ + if (((num) < 0) || (((num) + 1) >= m->maxlocals)) \ + goto throw_illegal_local_variable_number; \ + } while (0) + +/* CHECK_BYTECODE_INDEX(i) checks whether i is a valid bytecode index. */ +/* The end of the bytecode (i == m->jcodelength) is considered valid. */ + +#define CHECK_BYTECODE_INDEX(i) \ + do { \ + if (((i) < 0) || ((i) >= m->jcodelength)) \ + goto throw_invalid_bytecode_index; \ + } while (0) + +/* CHECK_BYTECODE_INDEX_EXCLUSIVE is used for the exclusive ends */ +/* of exception handler ranges. */ +#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) \ + do { \ + if ((i) < 0 || (i) > m->jcodelength) \ + goto throw_invalid_bytecode_index; \ + } while (0) + +#else /* !defined(ENABLE_VERIFIER) */ + +#define INDEX_ONEWORD(num) +#define INDEX_TWOWORD(num) +#define CHECK_BYTECODE_INDEX(i) +#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) + +#endif /* defined(ENABLE_VERIFIER) */ + + +/* basic block generating macro ***********************************************/ + +#define MARK_BASICBLOCK(pd, i) \ + do { \ + (pd)->basicblockstart[(i)] = 1; \ + } while (0) + +#define INSTRUCTIONS_CHECK(i) \ + if ((ircount + (i)) > pd.instructionslength) \ + iptr = parse_realloc_instructions(&pd, ircount, (i)) + + +/* intermediate code generating macros ****************************************/ + +/* These macros ALWAYS set the following fields of *iptr to valid values: */ +/* iptr->opc */ +/* iptr->flags */ +/* iptr->line */ + +/* These macros do NOT touch the following fields of *iptr, unless a value is */ +/* given for them: */ +/* iptr->s1 */ +/* iptr->sx */ +/* iptr->dst */ + +/* The _PREPARE macros omit the PINC, so you can set additional fields */ +/* afterwards. */ + +#define PINC \ + iptr++; ircount++ + +#define OP_PREPARE_FLAGS(o, f) \ + iptr->opc = (o); \ + iptr->line = currentline; \ + iptr->flags.bits |= (f) | (ircount << INS_FLAG_ID_SHIFT); + +#define OP_PREPARE_ZEROFLAGS(o) \ + OP_PREPARE_FLAGS(o, 0) + +#define OP_PREPARE(o) \ + OP_PREPARE_ZEROFLAGS(o) + +#define OP(o) \ + OP_PREPARE_ZEROFLAGS(o); \ + PINC + +#define OP_CHECK_EXCEPTION(o) \ + OP_PREPARE_FLAGS(o, INS_FLAG_CHECK); \ + PINC + +#define OP_LOADCONST_I(v) \ + OP_PREPARE_ZEROFLAGS(ICMD_ICONST); \ + iptr->sx.val.i = (v); \ + PINC + +#define OP_LOADCONST_L(v) \ + OP_PREPARE_ZEROFLAGS(ICMD_LCONST); \ + iptr->sx.val.l = (v); \ + PINC + +#define OP_LOADCONST_F(v) \ + OP_PREPARE_ZEROFLAGS(ICMD_FCONST); \ + iptr->sx.val.f = (v); \ + PINC + +#define OP_LOADCONST_D(v) \ + OP_PREPARE_ZEROFLAGS(ICMD_DCONST); \ + iptr->sx.val.d = (v); \ + PINC + +#define OP_LOADCONST_NULL() \ + OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \ + iptr->sx.val.anyptr = NULL; \ + PINC + +#define OP_LOADCONST_STRING(v) \ + OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \ + iptr->sx.val.stringconst = (v); \ + PINC + +#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS(cl, cr, extraflags) \ + OP_PREPARE(ICMD_ACONST); \ + if (cl) { \ + iptr->sx.val.c.cls = (cl); \ + iptr->flags.bits |= INS_FLAG_CLASS | (extraflags); \ + } \ + else { \ + iptr->sx.val.c.ref = (cr); \ + iptr->flags.bits |= INS_FLAG_CLASS | INS_FLAG_UNRESOLVED \ + | (extraflags); \ + } \ + PINC + +#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr) \ + OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), INS_FLAG_CHECK) + +#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr) \ + OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), 0) + +#define OP_S3_CLASSINFO_OR_CLASSREF(o, c, cr, extraflags) \ + OP_PREPARE(o); \ + if (c) { \ + iptr->sx.s23.s3.c.cls= (c); \ + iptr->flags.bits |= (extraflags); \ + } \ + else { \ + iptr->sx.s23.s3.c.ref= (cr); \ + iptr->flags.bits |= INS_FLAG_UNRESOLVED | (extraflags); \ + } \ + PINC + +#define OP_INSINDEX(o, iindex) \ + OP_PREPARE_ZEROFLAGS(o); \ + iptr->dst.insindex = (iindex); \ + PINC + +# define OP_LOCALINDEX(o,index) \ + OP_PREPARE_ZEROFLAGS(o); \ + iptr->s1.varindex = (index); \ + PINC + +# define OP_LOCALINDEX_I(o,index,v) \ + OP_PREPARE_ZEROFLAGS(o); \ + iptr->s1.varindex = (index); \ + iptr->sx.val.i = (v); \ + PINC + +# define LOCALTYPE_USED(index,type) \ + do { \ + local_map[(index) * 5 + (type)] = 1; \ + } while (0) + +#define OP_LOAD_ONEWORD(o,index,type) \ + do { \ + INDEX_ONEWORD(index); \ + OP_LOCALINDEX(o,index); \ + LOCALTYPE_USED(index,type); \ + } while (0) + +#define OP_LOAD_TWOWORD(o,index,type) \ + do { \ + INDEX_TWOWORD(index); \ + OP_LOCALINDEX(o,index); \ + LOCALTYPE_USED(index,type); \ + } while (0) + +# define OP_STORE_ONEWORD(o,index,type) \ + do { \ + INDEX_ONEWORD(index); \ + OP_PREPARE_ZEROFLAGS(o); \ + iptr->dst.varindex = (index); \ + LOCALTYPE_USED(index,type); \ + PINC; \ + } while (0) + +# define OP_STORE_TWOWORD(o,index,type) \ + do { \ + INDEX_TWOWORD(index); \ + OP_PREPARE_ZEROFLAGS(o); \ + iptr->dst.varindex = (index); \ + LOCALTYPE_USED(index,type); \ + PINC; \ + } while (0) + +#define OP_BUILTIN_CHECK_EXCEPTION(bte) \ + code_unflag_leafmethod(code); \ + OP_PREPARE_FLAGS(ICMD_BUILTIN, INS_FLAG_CHECK); \ + iptr->sx.s23.s3.bte = (bte); \ + PINC + +#define OP_BUILTIN_NO_EXCEPTION(bte) \ + code_unflag_leafmethod(code); \ + OP_PREPARE_ZEROFLAGS(ICMD_BUILTIN); \ + iptr->sx.s23.s3.bte = (bte); \ + PINC + +#define OP_BUILTIN_ARITHMETIC(opcode, bte) \ + code_unflag_leafmethod(code); \ + OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); \ + iptr->sx.s23.s3.bte = (bte); \ + PINC + +/* CAUTION: You must set iptr->flags yourself when using this! */ +#define OP_FMIREF_PREPARE(o, fmiref) \ + OP_PREPARE(o); \ + iptr->sx.s23.s3.fmiref = (fmiref); + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +bool parse(jitdata *jd); + +#ifdef __cplusplus +} +#endif + +#endif /* _PARSE_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ + diff --git a/src/vm/jit/powerpc/codegen.c b/src/vm/jit/powerpc/codegen.c index c7431f809..dcfc0482e 100644 --- a/src/vm/jit/powerpc/codegen.c +++ b/src/vm/jit/powerpc/codegen.c @@ -59,7 +59,7 @@ #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" #include "vm/jit/methodheader.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/powerpc64/codegen.c b/src/vm/jit/powerpc64/codegen.c index 64855d6dd..6de89aaf0 100644 --- a/src/vm/jit/powerpc64/codegen.c +++ b/src/vm/jit/powerpc64/codegen.c @@ -59,7 +59,7 @@ #include "vm/jit/emit-common.hpp" #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/s390/codegen.c b/src/vm/jit/s390/codegen.c index 7db818cad..f69e661fb 100644 --- a/src/vm/jit/s390/codegen.c +++ b/src/vm/jit/s390/codegen.c @@ -61,7 +61,7 @@ #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" #include "vm/jit/methodheader.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/show.cpp b/src/vm/jit/show.cpp index d4dad98c7..e0ba2a3cf 100644 --- a/src/vm/jit/show.cpp +++ b/src/vm/jit/show.cpp @@ -45,7 +45,7 @@ #include "vm/jit/show.hpp" #include "vm/jit/disass.h" #include "vm/jit/stack.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #if defined(ENABLE_DEBUG_FILTER) # include diff --git a/src/vm/jit/sparc64/codegen.c b/src/vm/jit/sparc64/codegen.c index dc0533c2f..b048fbe32 100644 --- a/src/vm/jit/sparc64/codegen.c +++ b/src/vm/jit/sparc64/codegen.c @@ -55,7 +55,7 @@ #include "vm/jit/emit-common.hpp" #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher.h" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp" diff --git a/src/vm/jit/stack.c b/src/vm/jit/stack.c index 2420c649f..8c7ad97c3 100644 --- a/src/vm/jit/stack.c +++ b/src/vm/jit/stack.c @@ -54,7 +54,7 @@ #include "vm/jit/abi.h" #include "vm/jit/cfg.h" #include "vm/jit/codegen-common.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/show.hpp" #if defined(ENABLE_DISASSEMBLER) diff --git a/src/vm/jit/verify/typecheck-stackbased.c b/src/vm/jit/verify/typecheck-stackbased.c index b978516b5..7056142f0 100644 --- a/src/vm/jit/verify/typecheck-stackbased.c +++ b/src/vm/jit/verify/typecheck-stackbased.c @@ -38,7 +38,7 @@ #include "vm/globals.hpp" #include "vm/primitive.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/show.hpp" #include "vm/jit/stack.h" #include "vm/jit/verify/typecheck-common.h" diff --git a/src/vm/jit/verify/typecheck-typeinferer.c b/src/vm/jit/verify/typecheck-typeinferer.c index 8dfbd4f74..60af00882 100644 --- a/src/vm/jit/verify/typecheck-typeinferer.c +++ b/src/vm/jit/verify/typecheck-typeinferer.c @@ -48,7 +48,7 @@ #include "vm/jit/jit.hpp" #include "vm/jit/show.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/verify/typecheck-typeinferer.h" diff --git a/src/vm/jit/verify/typecheck.c b/src/vm/jit/verify/typecheck.c index 842f89f16..00e3cfdea 100644 --- a/src/vm/jit/verify/typecheck.c +++ b/src/vm/jit/verify/typecheck.c @@ -159,7 +159,7 @@ error reporting. #include "vm/resolve.hpp" #include "vm/jit/jit.hpp" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/show.hpp" #include diff --git a/src/vm/jit/x86_64/codegen.c b/src/vm/jit/x86_64/codegen.c index ff7bf884a..96f4967bc 100644 --- a/src/vm/jit/x86_64/codegen.c +++ b/src/vm/jit/x86_64/codegen.c @@ -63,7 +63,7 @@ #include "vm/jit/jit.hpp" #include "vm/jit/linenumbertable.hpp" #include "vm/jit/methodheader.h" -#include "vm/jit/parse.h" +#include "vm/jit/parse.hpp" #include "vm/jit/patcher-common.hpp" #include "vm/jit/reg.h" #include "vm/jit/replace.hpp"