* Removed all Id tags.
[cacao.git] / src / vm / jit / inline / inline.c
index 1f59f473b59202c6fd1d892c62d9b53bd5080838..773a11f48848d17493ebef33e5be6fbd95486e74 100644 (file)
@@ -1,11 +1,9 @@
-/* jit/inline.c - code inliner
+/* src/vm/jit/inline/inline.c - method inlining
 
-globals moved to structure and passed as parameter
-
-   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-   R. Grafl, A. Krall, C. Kruegel, C. Oates, R. Obermaisser,
-   M. Probst, S. Ring, E. Steiner, C. Thalinger, D. Thuernbeck,
-   P. Tomsich, J. Wenninger
+   Copyright (C) 1996-2005, 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
 
    This file is part of CACAO.
 
@@ -21,601 +19,3206 @@ globals moved to structure and passed as parameter
 
    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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.
-
-   Contact: cacao@complang.tuwien.ac.at
-
-   Authors: Dieter Thuernbeck
-
-   $Id: inline.c 1415 2004-10-11 20:12:08Z jowenn $
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
 
 */
 
 
+#include "config.h"
+
+#include <assert.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
-#include "global.h"
-#include "loader.h"
-#include "tables.h"
-#include "options.h"
-#include "jit/inline.h"
-#include "jit/jit.h"
-#include "jit/parse.h"
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "threads/lock-common.h"
+#include "threads/threads-common.h"
+
 #include "toolbox/logging.h"
-#include "toolbox/memory.h"
-
-#define METHINFO(m) \
-  utf_display(m->class->name); printf("."); fflush(stdout); \
-  method_display(m); fflush(stdout); \
-
-#define IMETHINFO(m) \
-  utf_display(m->class->name); printf("."); fflush(stdout); \
-  method_display(m); fflush(stdout); \
-  printf("\tm->jcodelength=%i; ",m->jcodelength); fflush(stdout); \
-  printf("m->jcode=%p;\n",m->jcode); fflush(stdout); \
-  printf("\tm->maxlocals=%i; ",m->maxlocals); fflush(stdout); \
-  printf("m->maxstack=%i;\n",m->maxstack); fflush(stdout);
-
-bool DEBUGi = false;
-// checked functions and macros: LOADCONST code_get OP1 BUILTIN block_insert bound_check ALIGN
-
-// replace jcodelength loops with correct number after main for loop in parse()!
-
-/*-----------------------------------------------------------*/
-/* just initialize global structure for non-inlining         */
-/*-----------------------------------------------------------*/
-
-t_inlining_globals *inlining_init0(methodinfo *m, 
-                                  t_inlining_globals *inline_env)
-{
- /* initialization for normal use in parse */
-        inlining_set_compiler_variables_fun(m, inline_env);
-        inline_env->isinlinedmethod = 0;
-       inline_env->cumjcodelength = m->jcodelength; /* for not inlining */
-        inline_env->cummaxstack = 0;
-        inline_env->cumextablelength = 0;
-        inline_env->cumlocals = m->maxlocals;
-        inline_env->cummethods = 0;//co not global or static-used only here?
-        inline_env->inlining_stack = NULL;
-        inline_env->inlining_rootinfo = NULL;
-return inline_env;
-}
-/*-----------------------------------------------------------*/
-
-t_inlining_globals *inlining_init(methodinfo *m)
-{
-       t_inlining_globals *inline_env = DNEW(t_inlining_globals);
-        inlining_init0(m,inline_env);
-if (useinlining)
-        {
-if (DEBUGi==true) {
-               printf("\n-------- Inlining init for: "); fflush(stdout);
-               IMETHINFO(m)
-                }
-        inline_env->cumjcodelength = 0;
-        inline_env->inlining_stack = NEW(list);
-        list_init(inline_env->inlining_stack, 
-                 OFFSET(t_inlining_stacknode, linkage));
-        /*------ analyze ------*/
-if (DEBUGi==true) {print_t_inlining_globals(inline_env);}
-        inline_env->inlining_rootinfo 
-               = inlining_analyse_method(m, 0, 0, 0, 0, inline_env);
-if (DEBUGi==true) {print_t_inlining_globals(inline_env);}
-        /*---------------------*/
-       //if (inline_env->cummethods == 0) {
-       //  inline_env = DNEW(t_inlining_globals);
-       //  inlining_init0(m,inline_env);
-       //  return inline_env;
-        //  }
-if (DEBUGi==true) {
-  printf("(l,s) (%i,%i) was (%i,%i)\n",
-    m->maxlocals, inline_env->cumlocals,
-    m->maxstack,  inline_env->cummaxstack); fflush(stdout);
-  }
-        m->maxlocals = inline_env->cumlocals;   //orig not used
-        m->maxstack = inline_env->cummaxstack;  //orig global maxstack var!!
 
-//panic("TEMP so can test just inline init\n");
-        }
-return inline_env;
+#include "vm/builtin.h"
+#include "vm/global.h"
+#include "vm/initialize.h"
+
+#include "vm/jit/jit.h"
+#include "vm/jit/parse.h"
+#include "vm/jit/reg.h"
+#include "vm/jit/show.h"
+#include "vm/jit/stack.h"
+
+#include "vm/jit/inline/inline.h"
+#include "vm/jit/loop/loop.h"
+
+#include "vm/jit/verify/typecheck.h"
+
+#include "vmcore/class.h"
+#include "vmcore/method.h"
+#include "vmcore/options.h"
+#include "vmcore/statistics.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_inline_debug_log) { 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
 
 
-void inlining_cleanup(t_inlining_globals *inline_env)
+/* compilation of callees *****************************************************/
+
+static bool inline_jit_compile_intern(jitdata *jd)
 {
-       FREE(inline_env->inlining_stack, t_inlining_stacknode);
+       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->class) );
+       if (!parse(jd)) {
+               return false;
+       }
+
+       /* call stack analysis pass */
+
+       if (!stack_analyse(jd)) {
+               return false;
+       }
+
+       return true;
 }
 
 
-void inlining_push_compiler_variables(int i, int p, int nextp, int opcode,  u2 lineindex,u2 currentline,u2 linepcchange,inlining_methodinfo *inlinfo, t_inlining_globals *inline_env)
+static bool inline_jit_compile(inline_node *iln)
 {
-       t_inlining_stacknode *new = NEW(t_inlining_stacknode);
+       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 */
+
+       LOCK_MONITOR_ENTER(m);
+
+       /* allocate jitdata structure and fill it */
+
+       jd = jit_jitdata_new(m);
+       iln->jd = jd;
+
+       jd->flags = 0; /* XXX */
+
+       /* initialize the register allocator */
 
-       new->i = i;
-       new->p = p;
-       new->nextp = nextp;
-       new->opcode = opcode;
-       new->method = inline_env->method;
-       new->lineindex=lineindex;
-       new->currentline=currentline;
-       new->linepcchange=linepcchange;
-       new->inlinfo = inlinfo;
-       list_addfirst(inline_env->inlining_stack, new);
-       inline_env->isinlinedmethod++;
+       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 */
+
+       LOCK_MONITOR_EXIT(m);
+
+       return r;
 }
 
 
-void inlining_pop_compiler_variables(
-                                    int *i, int *p, int *nextp,
-                                   int *opcode, u2 *lineindex,
-                                   u2 *currentline,u2 *linepcchange,
-                                    inlining_methodinfo **inlinfo,
-                                    t_inlining_globals *inline_env)
+/* inlining tree handling *****************************************************/
+
+static void inline_insert_inline_node(inline_node *parent, inline_node *child)
 {
-       t_inlining_stacknode *tmp 
-         = (t_inlining_stacknode *) list_first(inline_env->inlining_stack);
+       inline_node *first;
+       inline_node *succ;
+
+       assert(parent && child);
+
+       child->parent = parent;
+
+       child->debugnr = parent->ctx->next_debugnr++; /* XXX debug */
 
-       if (!inline_env->isinlinedmethod) panic("Attempting to pop from inlining stack in toplevel method!\n");
+       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} */
 
-       *i = tmp->i;
-       *p = tmp->p;
-       *nextp = tmp->nextp;
-       *opcode = tmp->opcode;
+       /* 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;
+               }
+       }
 
-       *lineindex=tmp->lineindex;
-       *currentline=tmp->currentline;
-       *currentline=tmp->linepcchange;
+       assert(succ->callerpc > child->callerpc);
 
-       *inlinfo = tmp->inlinfo;
+       /* insert before succ */
 
-        inline_env->method = tmp->method; /*co*/
-        inline_env->class = inline_env->method->class; /*co*/
-        inline_env->jcodelength = inline_env->method->jcodelength; /*co*/
-        inline_env->jcode = inline_env->method->jcode; /*co*/
+       child->prev = succ->prev;
+       child->next = succ;
+       child->prev->next = child;
+       child->next->prev = child;
 
-        list_remove(inline_env->inlining_stack, tmp);
-        FREE(tmp, t_inlining_stacknode);
-        inline_env->isinlinedmethod--;
+       if (parent->children == succ)
+               parent->children = child;
 }
 
 
-void inlining_set_compiler_variables_fun(methodinfo *m,
-                                        t_inlining_globals *inline_env)
+static void inline_remove_inline_node(inline_node *parent, inline_node *child)
 {
-       /* XXX TWISTI */
-        inline_env->method = m; /*co*/
-        inline_env->class  = m->class; /*co*/
-        inline_env->jcode  = m->jcode; /*co*/
-        inline_env->jcodelength = m->jcodelength; /*co*/
+       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;
+       }
 }
 
 
-classinfo *first_occurence(classinfo* class, utf* name, utf* desc)
+/* 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)
 {
-       classinfo *first = class;
-       
-       for (; class->super != NULL ; class = class->super) {
-               if (class_findmethod(class->super, name, desc) != NULL) {
-                       first = class->super;
-               }                       
+       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;
 
-       return first;
+       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)) */
 
 
-bool is_unique_rec(classinfo *class, methodinfo *m, utf* name, utf* desc)
+#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST))
+static void inline_candidates_println(inline_context *ctx)
 {
-       methodinfo *tmp = class_findmethod(class, name, desc);
-       if ((tmp != NULL) && (tmp != m))
-               return false;
+       inline_candidate *cand;
 
-       for (; class != NULL; class = class->nextsub) {
-               if ((class->sub != NULL) && !is_unique_rec(class->sub, m, name, desc)) {
-                       return false; 
-               }
+       for (cand = ctx->candidates; cand != NULL; cand = cand->next) {
+               printf("    ");
+               inline_candidate_println(cand);
        }
-       return true;
 }
+#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */
+
 
+/* variable handling **********************************************************/
 
-bool is_unique_method(classinfo *class, methodinfo *m, utf* name, utf* desc)
+static s4 inline_new_variable(jitdata *jd, s4 type, s4 flags)
 {
-       classinfo *firstclass;
-       
-       /*      sprintf (logtext, "First occurence of: ");
-       utf_sprint (logtext+strlen(logtext), m->class->name);
-       strcpy (logtext+strlen(logtext), ".");
-       utf_sprint (logtext+strlen(logtext), m->name);
-       utf_sprint (logtext+strlen(logtext), m->descriptor);
-       dolog (); */
-       
-       firstclass = first_occurence(class, name, desc);
-       
-       /*      sprintf (logtext, "\nis in class:");
-       utf_sprint (logtext+strlen(logtext), firstclass->name);
-       dolog (); */
+       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;
+       }
 
-       if (firstclass != class) return false;
+       jd->var[index].type = type;
+       jd->var[index].flags = flags;
 
-       return is_unique_rec(class, m, name, desc);
+       return index;
 }
 
-inlining_methodinfo *inlining_analyse_method(methodinfo *m, 
-                                         int level, int gp, 
-                                         int firstlocal, int maxstackdepth,
-                                         t_inlining_globals *inline_env)
+
+static s4 inline_new_variable_clone(jitdata *jd, jitdata *origjd, s4 origidx)
 {
-       inlining_methodinfo *newnode = DNEW(inlining_methodinfo);
-       /*u1 *jcode = m->jcode;*/
-       int jcodelength = m->jcodelength;
-       int p;
-       int nextp;
-       int opcode;
-       int i;
-       bool iswide = false, oldiswide;
-       bool *readonly = NULL;
-       int  *label_index = NULL;
-       bool isnotrootlevel = (level > 0);
-       bool isnotleaflevel = (level < INLINING_MAXDEPTH);
-
-       //      if (level == 0) gp = 0;
-       /*
-       sprintf (logtext, "Performing inlining analysis of: ");
-       utf_sprint (logtext+strlen(logtext), m->class->name);
-       strcpy (logtext+strlen(logtext), ".");
-       utf_sprint (logtext+strlen(logtext), m->name);
-       utf_sprint (logtext+strlen(logtext), m->descriptor);
-       dolog (); */
-
-       if (isnotrootlevel) {
-               newnode->readonly = readonly = DMNEW(bool, m->maxlocals); //FIXME only paramcount entrys necessary
-               for (i = 0; i < m->maxlocals; readonly[i++] = true);
-               isnotrootlevel = true;
-
-       } else {
-               readonly = NULL;
-       }
-       
-       label_index = DMNEW(int, jcodelength+200);
-
-       newnode->inlinedmethods = DNEW(list);
-       list_init(newnode->inlinedmethods, OFFSET(inlining_methodinfo, linkage));
-
-       newnode->method = m;
-       newnode->startgp = gp;
-       newnode->readonly = readonly;
-       newnode->label_index = label_index;
-       newnode->firstlocal = firstlocal;
-       inline_env->cumjcodelength += jcodelength + m->paramcount + 1 + 5;
-
-       if ((firstlocal + m->maxlocals) > inline_env->cumlocals) {
-               inline_env->cumlocals = firstlocal + m->maxlocals;
-       }
-
-       if ((maxstackdepth + m->maxstack) > inline_env->cummaxstack) {
-               inline_env->cummaxstack = maxstackdepth + m->maxstack;
-       }
-
-       inline_env->cumextablelength += m->exceptiontablelength;
-   
-
-       for (p = 0; p < jcodelength; gp += (nextp - p), p = nextp) {
-               opcode = code_get_u1 (p,m);
-               nextp = p + jcommandsize[opcode];
-               oldiswide = iswide;
-
-               /* figure out nextp */
-
-               switch (opcode) {
-               case JAVA_ILOAD:
-               case JAVA_LLOAD:
-               case JAVA_FLOAD:
-               case JAVA_DLOAD:
-               case JAVA_ALOAD: 
-
-               case JAVA_ISTORE:
-               case JAVA_LSTORE:
-               case JAVA_FSTORE:
-               case JAVA_DSTORE:
-               case JAVA_ASTORE: 
-
-               case JAVA_RET:
-                       if (iswide) {
-                               nextp = p + 3;
-                               iswide = false;
-                       }
-                       break;
+       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; i<callee->jd->varcount; ++i)
+               varmap[i] = -1;
+
+       /* translate local variables */
 
-               case JAVA_IINC:
-                       if (iswide) {
-                               nextp = p + 5;
-                               iswide = false;
+       for (i=0; i<callee->m->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;
                        }
-                       break;
 
-               case JAVA_WIDE:
-                       iswide = true;
-                       nextp = p + 1;
-                       break;
+                       varmap[varindex] = avail;
+               }
+       }
 
-               case JAVA_LOOKUPSWITCH:
-                       nextp = ALIGN((p + 1), 4) + 4;
-                       nextp += code_get_u4(nextp,m) * 8 + 4;
-                       break;
+       /* for synchronized instance methods we need an extra local */
 
-               case JAVA_TABLESWITCH:
-                       nextp = ALIGN((p + 1), 4) + 4;
-                       nextp += (code_get_u4(nextp+4,m) - code_get_u4(nextp,m) + 1) * 4 + 4;
-                       break;
+       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;
                }
 
-               /* detect readonly variables in inlined methods */
-               
-               if (isnotrootlevel) { 
-                       bool iswide = oldiswide;
-                       
-                       switch (opcode) {
-                       case JAVA_ISTORE:
-                       case JAVA_LSTORE:
-                       case JAVA_FSTORE:
-                       case JAVA_DSTORE:
-                       case JAVA_ASTORE: 
-                               if (!iswide) {
-                                       i = code_get_u1(p + 1,m);
-
-                               } else {
-                                       i = code_get_u2(p + 1,m);
-                               }
-                               readonly[i] = false;
-                               break;
-
-                       case JAVA_ISTORE_0:
-                       case JAVA_LSTORE_0:
-                       case JAVA_FSTORE_0:
-                       case JAVA_ASTORE_0:
-                               readonly[0] = false;
-                               break;
-
-                       case JAVA_ISTORE_1:
-                       case JAVA_LSTORE_1:
-                       case JAVA_FSTORE_1:
-                       case JAVA_ASTORE_1:
-                               readonly[1] = false;
-                               break;
-
-                       case JAVA_ISTORE_2:
-                       case JAVA_LSTORE_2:
-                       case JAVA_FSTORE_2:
-                       case JAVA_ASTORE_2:
-                               readonly[2] = false;
-                               break;
-
-                       case JAVA_ISTORE_3:
-                       case JAVA_LSTORE_3:
-                       case JAVA_FSTORE_3:
-                       case JAVA_ASTORE_3:
-                               readonly[3] = false;
-                               break;
-
-                       case JAVA_IINC:
-                               if (!iswide) {
-                                       i = code_get_u1(p + 1,m);
-
-                               } else {
-                                       i = code_get_u2(p + 1,m);
-                               }
-                               readonly[i] = false;
-                               break;
+               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;
+}
+
+
+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;
+}
+
+
+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;
                        }
                }
 
-               /*              for (i=lastlabel; i<=p; i++) label_index[i] = gp; 
-               //              printf("lastlabel=%d p=%d gp=%d\n",lastlabel, p, gp);
-               lastlabel = p+1; */
-               for (i = p; i < nextp; i++) label_index[i] = gp;
+               /* skip this ref */
 
-               if (isnotleaflevel) { 
+               prev = ref;
+               continue;
 
-                       switch (opcode) {
-                       case JAVA_INVOKEVIRTUAL:
-                               if (!inlinevirtuals)
-                                       break;
-                       case JAVA_INVOKESTATIC:
-                               i = code_get_u2(p + 1,m);
-                               {
-                                       constant_FMIref *imr;
-                                       methodinfo *imi;
+remove_ref:
+               /* remove this ref */
 
-                                       imr = class_getconstant(m->class, i, CONSTANT_Methodref);
+               if (prev) {
+                       prev->next = ref->next;
+               }
+               else {
+                       *refs = ref->next;
+               }
+       }
+}
 
-                                       if (!class_load(imr->class))
-                                               return NULL;
 
-                                       if (!class_link(imr->class))
-                                               return NULL;
+/* basic block creation *******************************************************/
 
-                                       imi = class_resolveclassmethod(imr->class,
-                                                                                                  imr->name,
-                                                                                                  imr->descriptor,
-                                                                                                  m->class,
-                                                                                                  true);
+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;
 
-                                       if (!imi)
-                                               panic("Exception thrown while parsing bytecode"); /* XXX should be passed on */
+       assert(container);
+       assert(iln);
+       assert(inner);
+       assert(indepth >= 0);
 
-                                       if (opcode == JAVA_INVOKEVIRTUAL) {
-                                               if (!is_unique_method(imi->class, imi, imr->name, imr->descriptor))
-                                                       break;
-                                       }
+       n_bptr = container->inlined_basicblocks_cursor++;
+       assert(n_bptr);
+       assert((n_bptr - container->inlined_basicblocks) < container->cumul_basicblockcount);
 
-                                       if (imi->flags & ACC_NATIVE) log_text("Native method,no inlining"); //DEBUG
-                                       if ((inline_env->cummethods < INLINING_MAXMETHODS) &&
-                                               (!(imi->flags & ACC_NATIVE)) &&  
-                                               (!inlineoutsiders || (m->class == imr->class)) && 
-                                               (imi->jcodelength < INLINING_MAXCODESIZE) && 
-                                               (imi->jcodelength > 0) && 
-                                               (!inlineexceptions || (imi->exceptiontablelength == 0))) { //FIXME: eliminate empty methods?
-                                               inlining_methodinfo *tmp;
-                                               descriptor2types(imi);
-
-                                               inline_env->cummethods++;
-
-                                               if (verbose) {
-                                                       char logtext[MAXLOGTEXT];
-                                                       sprintf(logtext, "Going to inline: ");
-                                                       utf_sprint(logtext  +strlen(logtext), imi->class->name);
-                                                       strcpy(logtext + strlen(logtext), ".");
-                                                       utf_sprint(logtext + strlen(logtext), imi->name);
-                                                       utf_sprint(logtext + strlen(logtext), imi->descriptor);
-                                                       log_text(logtext);
-                                               }
-                                               
-                                               tmp =inlining_analyse_method(imi, level + 1, gp, firstlocal + m->maxlocals, maxstackdepth + m->maxstack, inline_env);
-                                               list_addlast(newnode->inlinedmethods, tmp);
-                                               gp = tmp->stopgp;
-                                               p = nextp;
-                                       }
-                               }
-                               break;
+       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; i<indepth; ++i)
+                       n_bptr->invars[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; i<inner->n_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;
                        }
-               }  
-       } /* for */
-       
-       newnode->stopgp = gp;
-
-        if (DEBUGi==true) {
-         printf ("\nResult of inlining analysis of: ");
-         IMETHINFO(m);
-         printf("was called by:\n"); fflush(stdout);
-         IMETHINFO(inline_env->method);
-         printf ("label_index[0..%d]->", jcodelength);
-         for (i=0;i<jcodelength; i++) printf ("%d:%d ", i, label_index[i]);
-         printf("stopgp : %d\n",newnode->stopgp); 
-          }
-
-    return newnode;
-}
-
-/* --------------------------------------------------------------------*/
-/*  print_ functions: check inline structures contain what is expected */
-/* --------------------------------------------------------------------*/
-void print_t_inlining_globals (t_inlining_globals *g) 
-{
-printf("\n------------\nt_inlining_globals struct for: \n\t");fflush(stdout); 
-METHINFO(g->method);
-printf("\tclass=");fflush(stdout);
-  utf_display(g->class->name);printf("\n");fflush(stdout);
-
-printf("\tjcodelength=%i; jcode=%p;\n",g->jcodelength, g->jcode);
-
-if (g->isinlinedmethod==true) {
-  printf("\tisinlinedmethod=true ");fflush(stdout);  
-  }
-else {
-  printf("\tisinlinedmethod=false");fflush(stdout);  
-  }
-
-printf("\tcumjcodelength=%i ,cummaxstack=%i ,cumextablelength=%i ",
- g->cumjcodelength,    g->cummaxstack,  g->cumextablelength);fflush(stdout);
-printf("\tcumlocals=%i ,cummethods=%i \n",
- g->cumlocals,    g->cummethods);fflush(stdout);  
-
-printf("s>s>s> ");fflush(stdout);
-print_inlining_stack     (g->inlining_stack);
-printf("i>i>i> "); fflush(stdout);
-print_inlining_methodinfo(g->inlining_rootinfo);
-printf("-------------------\n");fflush(stdout);
-}
-
-/* --------------------------------------------------------------------*/
-void print_inlining_stack     ( list                *s)
-{
-if (s==NULL) { 
-  printf("\n\tinlining_stack: NULL\n");
-  return;
-  }
-  
-  {/* print first  element to see if get into stack */
-  t_inlining_stacknode *is;
-  printf("\n\tinlining_stack: NOT NULL\n");
-
-  is=list_first(s); 
-  if (is==NULL) { 
-    printf("\n\tinlining_stack = init'd but EMPTY\n");
-    fflush(stdout);
-    return;
-    }
-  }
-
-  {
-  t_inlining_stacknode *is;
-  printf("\n\tinlining_stack: NOT NULL\n");
-
-
-  for (is=list_first(s); 
-       is!=NULL;
-       is=list_next(s,is)) {
-        printf("\n\ti>--->inlining_stack entry: \n"); fflush(stdout);
-        METHINFO(is->method);
-        printf("i=%i, p=%i, nextp=%i, opcode=%i;\n",
-               is->i,is->p,is->nextp,is->opcode);fflush(stdout);
-        print_inlining_methodinfo(is->inlinfo);
-    } /*end for */
-  } 
-
-}
-
-/* --------------------------------------------------------------------*/
-void print_inlining_methodinfo( inlining_methodinfo *r) {
-if (r==NULL) { 
-  printf("\n\tinlining_methodinfo: NULL\n");
-  return;
-  }
-
-if (r->method != NULL) {
-  utf_display(r->method->class->name); printf("."); fflush(stdout); \
-  method_display(r->method); fflush(stdout); \
-  }
-else {
-  printf("method is NULL!!!!!\n");fflush(stdout);
-  }
-
-printf("\n\tinlining_methodinfo for:"); fflush(stdout);
-if (r->readonly==NULL) {
-  printf("\treadonly==NULL ");fflush(stdout);  
-  }
-else {
-  int i;
-  printf("\treadonly=");fflush(stdout);  
-  for (i = 0; i < r->method->maxlocals; i++)  {
-    if (r->readonly[i] == true)
-      printf("[i]=T;");
-    else
-      printf("[i]=F;");
-    fflush(stdout);
-    } 
-  }
-
-
-printf("\tstartgp=%i; stopgp=%i; firstlocal=%i; label_index=%p;\n",
-          r->startgp, r->stopgp, r->firstlocal, r->label_index);
-{int i;
-printf ("label_index[0..%d]->", r->method->jcodelength);
-for (i=0; i<r->method->jcodelength; i++) printf ("%d:%d ", i, r->label_index[i]);
-}
-
-{
-inlining_methodinfo *im;
-for (im=list_first(r->inlinedmethods); 
-     im!=NULL;
-     im=list_next(r->inlinedmethods,im)) {
-  } 
+                       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; i<iln->jd->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; i<o_bptr->indepth; ++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; i<outdepth; ++i)
+               n_bptr->outvars[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; i<inner->n_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; i<o_bptr->outdepth; ++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->class;
+               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; i<insinfo->stackvarscount; ++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 <init> 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; i<n_iptr->s1.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; i<o_iptr->s1.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; i<iln->depth; ++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; i<iln->ctx->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; i<bptr->indepth; ++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; i<bptr->outdepth; ++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->class;
+                       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; i<n_jd->basicblockcount + 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)) {
+                       *exceptionptr = NULL;
+                       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(HAS_4BYTE_STACKSLOT)
+       n_jd->rd->freemem_2 = DMNEW(s4, iln->ctx->maxinoutdepth + 1000) /* XXX max vars/block */;
+#endif
+
+#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
+       if (   (n_jd->instructioncount >= opt_inline_debug_min_size)
+               && (n_jd->instructioncount <= opt_inline_debug_max_size))
+       {
+          if (debug_counter <= opt_inline_debug_end_counter)
+#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(
+                       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 <init> */
+
+       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; i<jd->vartop; ++i)                                 \
+            if (live[i])                                             \
+                MARKSAVED(i);                                        \
+    } while (0)
+
+static void inline_post_process(jitdata *jd)
+{
+       basicblock *bptr;
+       instruction *iptr;
+       instruction *iend;
+       s4 i;
+       icmdtable_entry_t *icmdt;
+       s4 *live;
+       methoddesc *md;
+       builtintable_entry *bte;
+
+       /* reset the SAVEDVAR flag of all variables */
+
+       for (i=0; i<jd->vartop; ++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; i<bptr->indepth; ++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) {
+                                               jd->isleafmethod = false;
+                                               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) {
+                                               jd->isleafmethod = false;
+                                               MARK_ALL_SAVED;
+                                       }
+                               case DF_COPY:
+                                       POSTPROCESS_DSTOP(dst);
+                                       break;
+
+                               case DF_N_TO_1:
+                                       for (i=0; i<iptr->s1.argcount; ++i) {
+                                               POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
+                                       }
+                                       if (icmdt->flags & ICMDTABLE_CALLS) {
+                                               jd->isleafmethod = false;
+                                               MARK_ALL_SAVED;
+                                       }
+                                       POSTPROCESS_DSTOP(dst);
+                                       break;
+
+                               case DF_INVOKE:
+                                       INSTRUCTION_GET_METHODDESC(iptr, md);
+               post_process_call:
+                                       jd->isleafmethod = false;
+                                       for (i=0; i<md->paramcount; ++i) {
+                                               POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
+                                       }
+                                       for (; i<iptr->s1.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; i<bptr->outdepth; ++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
@@ -627,4 +3230,5 @@ for (im=list_first(r->inlinedmethods);
  * c-basic-offset: 4
  * tab-width: 4
  * End:
+ * vim:noexpandtab:sw=4:ts=4:
  */