/* src/vm/jit/parse.c - parser for JavaVM to intermediate code translation Copyright (C) 1996-2005, 2006, 2007, 2008 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include "vm/types.h" #include "mm/memory.h" #include "native/native.hpp" #include "threads/lock.hpp" #include "toolbox/logging.hpp" #include "vm/jit/builtin.hpp" #include "vm/exceptions.hpp" #include "vm/global.h" #include "vm/linker.hpp" #include "vm/loader.hpp" #include "vm/options.h" #include "vm/resolve.hpp" #if defined(ENABLE_STATISTICS) # include "vm/statistics.h" #endif #include "vm/string.hpp" #include "vm/suck.hpp" #include "vm/jit/asmpart.h" #include "vm/jit/jit.hpp" #include "vm/jit/parse.hpp" #include "vm/jit/loop/loop.h" #include "vm/jit/ir/bytecode.h" #define INSTRUCTIONS_INCREMENT 5 /* number of additional instructions to */ /* allocate if space runs out */ /* local macros ***************************************************************/ #define BYTECODEINDEX_TO_BASICBLOCK(dst) \ do { \ (dst).block = \ parse_bytecodeindex_to_basicblock(jd, &pd, (dst).insindex); \ } while (0) /* parserdata_t ***************************************************************/ typedef struct parsedata_t parsedata_t; struct parsedata_t { u1 *bytecodestart; /* start of bytecode instructions */ u1 *basicblockstart; /* start of bytecode basic-blocks */ s4 *bytecodemap; /* bytecode to IR mapping */ instruction *instructions; /* instruction array */ s4 instructionslength; /* length of the instruction array */ s4 *instructionmap; /* IR to basic-block mapping */ }; #if defined(__cplusplus) extern "C" { #endif /* parse_setup ***************************************************************** Fills the passed parsedata_t structure. *******************************************************************************/ static void parse_setup(jitdata *jd, parsedata_t *pd) { methodinfo *m; /* get required compiler data */ m = jd->m; /* bytecode start array */ pd->bytecodestart = (u1*) DumpMemory::allocate(sizeof(u1) * (m->jcodelength + 1)); MZERO(pd->bytecodestart, u1, m->jcodelength + 1); /* bytecode basic-block start array */ pd->basicblockstart = (u1*) DumpMemory::allocate(sizeof(u1) *(m->jcodelength + 1)); MZERO(pd->basicblockstart, u1, m->jcodelength + 1); /* bytecode instruction index to IR instruction mapping */ pd->bytecodemap = (s4*) DumpMemory::allocate(sizeof(s4) * (m->jcodelength + 1)); MSET(pd->bytecodemap, -1, s4, m->jcodelength + 1); /* allocate the instruction array */ pd->instructionslength = m->jcodelength + 1; pd->instructions = (instruction*) DumpMemory::allocate(sizeof(instruction) * pd->instructionslength); /* Zero the intermediate instructions array so we don't have any invalid pointers in it if we cannot finish stack_analyse(). */ MZERO(pd->instructions, instruction, pd->instructionslength); /* The instructionmap is allocated later when we know the count of instructions. */ pd->instructionmap = NULL; } /* parse_realloc_instructions ************************************************** Reallocate the instructions array so there is room for at least N additional instructions. RETURN VALUE: the new value for iptr *******************************************************************************/ static instruction *parse_realloc_instructions(parsedata_t *pd, s4 icount, s4 n) { /* increase the size of the instruction array */ pd->instructionslength += (n + INSTRUCTIONS_INCREMENT); /* reallocate the array */ pd->instructions = (instruction*) DumpMemory::reallocate(pd->instructions, sizeof(instruction) * icount, sizeof(instruction) * pd->instructionslength); MZERO(pd->instructions + icount, instruction, (pd->instructionslength - icount)); /* return the iptr */ return pd->instructions + icount; } /* parse_bytecodeindex_to_basicblock ******************************************* Resolves a bytecode index to the corresponding basic block. *******************************************************************************/ static basicblock *parse_bytecodeindex_to_basicblock(jitdata *jd, parsedata_t *pd, s4 bcindex) { s4 irindex; basicblock *bb; irindex = pd->bytecodemap[bcindex]; bb = jd->basicblocks + pd->instructionmap[irindex]; return bb; } /* parse_mark_exception_boundaries ********************************************* Mark exception handlers and the boundaries of the handled regions as basic block boundaries. IN: jd...............current jitdata RETURN VALUE: true.............everything ok false............an exception has been thrown *******************************************************************************/ static bool parse_mark_exception_boundaries(jitdata *jd, parsedata_t *pd) { s4 bcindex; s4 i; s4 len; raw_exception_entry *rex; methodinfo *m; m = jd->m; len = m->rawexceptiontablelength; if (len == 0) return true; rex = m->rawexceptiontable; for (i = 0; i < len; ++i, ++rex) { /* the start of the handled region becomes a basic block start */ bcindex = rex->startpc; CHECK_BYTECODE_INDEX(bcindex); MARK_BASICBLOCK(pd, bcindex); bcindex = rex->endpc; /* see JVM Spec 4.7.3 */ CHECK_BYTECODE_INDEX_EXCLUSIVE(bcindex); /* check that the range is valid */ #if defined(ENABLE_VERIFIER) if (bcindex <= rex->startpc) { exceptions_throw_verifyerror(m, "Invalid exception handler range"); return false; } #endif /* End of handled region becomes a basic block boundary (if it is the bytecode end, we'll use the special end block that is created anyway). */ if (bcindex < m->jcodelength) MARK_BASICBLOCK(pd, bcindex); else jd->branchtoend = true; /* the start of the handler becomes a basic block start */ bcindex = rex->handlerpc; CHECK_BYTECODE_INDEX(bcindex); MARK_BASICBLOCK(pd, bcindex); } /* everything ok */ return true; #if defined(ENABLE_VERIFIER) throw_invalid_bytecode_index: exceptions_throw_verifyerror(m, "Illegal bytecode index in exception table"); return false; #endif } /* parse_resolve_exception_table *********************************************** Enter the exception handlers and their ranges, resolved to basicblock *s, in the jitdata. IN: jd...............current jitdata RETURN VALUE: true.............everything ok false............an exception has been thrown *******************************************************************************/ static bool parse_resolve_exception_table(jitdata *jd, parsedata_t *pd) { methodinfo *m; raw_exception_entry *rex; exception_entry *ex; s4 i; s4 len; classinfo *exclass; m = jd->m; len = m->rawexceptiontablelength; /* common case: no handler entries */ if (len == 0) return true; /* allocate the exception table */ jd->exceptiontablelength = len; jd->exceptiontable = (exception_entry*) DumpMemory::allocate(sizeof(exception_entry) * (len + 1)); /* XXX why +1? */ /* copy and resolve the entries */ ex = jd->exceptiontable; rex = m->rawexceptiontable; for (i = 0; i < len; ++i, ++rex, ++ex) { /* resolve instruction indices to basic blocks */ ex->start = parse_bytecodeindex_to_basicblock(jd, pd, rex->startpc); ex->end = parse_bytecodeindex_to_basicblock(jd, pd, rex->endpc); ex->handler = parse_bytecodeindex_to_basicblock(jd, pd, rex->handlerpc); /* lazily resolve the catchtype */ if (rex->catchtype.any != NULL) { if (!resolve_classref_or_classinfo(m, rex->catchtype, resolveLazy, true, false, &exclass)) return false; /* if resolved, enter the result of resolution in the table */ if (exclass != NULL) rex->catchtype.cls = exclass; } ex->catchtype = rex->catchtype; ex->next = NULL; /* set by loop analysis */ ex->down = ex + 1; /* link to next exception entry */ } /* terminate the ->down linked list */ assert(ex != jd->exceptiontable); ex[-1].down = NULL; return true; } /******************************************************************************* function 'parse' scans the JavaVM code and generates intermediate code During parsing the block index table is used to store at bit pos 0 a flag which marks basic block starts and at position 1 to 31 the intermediate instruction index. After parsing the block index table is scanned, for marked positions a block is generated and the block number is stored in the block index table. *******************************************************************************/ /*** macro for checking the length of the bytecode ***/ #if defined(ENABLE_VERIFIER) #define CHECK_END_OF_BYTECODE(neededlength) \ do { \ if ((neededlength) > m->jcodelength) \ goto throw_unexpected_end_of_bytecode; \ } while (0) #else /* !ENABLE_VERIFIER */ #define CHECK_END_OF_BYTECODE(neededlength) #endif /* ENABLE_VERIFIER */ bool parse(jitdata *jd) { methodinfo *m; /* method being parsed */ codeinfo *code; parsedata_t pd; instruction *iptr; /* current ptr into instruction array */ s4 bcindex; /* bytecode instruction index */ s4 nextbc; /* start of next bytecode instruction */ s4 opcode; /* bytecode instruction opcode */ s4 irindex; /* IR instruction index */ s4 ircount; /* IR instruction count */ s4 bbcount; /* basic block count */ int s_count = 0; /* stack element counter */ bool blockend; /* true if basic block end has been reached */ bool iswide; /* true if last instruction was a wide */ constant_classref *cr; constant_classref *compr; classinfo *c; builtintable_entry *bte; constant_FMIref *fmi; methoddesc *md; unresolved_method *um; unresolved_field *uf; resolve_result_t result; u2 lineindex = 0; u2 currentline = 0; u2 linepcchange = 0; u4 flags; basicblock *bptr; int *local_map; /* local pointer to renaming map */ /* is assigned to rd->local_map at the end */ branch_target_t *table; lookup_target_t *lookup; s4 i; s4 j; /* get required compiler data */ m = jd->m; code = jd->code; /* allocate buffers for local variable renaming */ local_map = (int*) DumpMemory::allocate(sizeof(int) * m->maxlocals * 5); for (i = 0; i < m->maxlocals; i++) { local_map[i * 5 + 0] = 0; local_map[i * 5 + 1] = 0; local_map[i * 5 + 2] = 0; local_map[i * 5 + 3] = 0; local_map[i * 5 + 4] = 0; } /* initialize the parse data structures */ parse_setup(jd, &pd); /* initialize local variables */ iptr = pd.instructions; ircount = 0; bbcount = 0; blockend = false; iswide = false; /* mark basic block boundaries for exception table */ if (!parse_mark_exception_boundaries(jd, &pd)) return false; /* initialize stack element counter */ s_count = 1 + m->rawexceptiontablelength; /* setup line number info */ currentline = 0; linepcchange = 0; if (m->linenumbercount == 0) { lineindex = 0; } else { linepcchange = m->linenumbers[0].start_pc; } /*** LOOP OVER ALL BYTECODE INSTRUCTIONS **********************************/ for (bcindex = 0; bcindex < m->jcodelength; bcindex = nextbc) { /* mark this position as a valid bytecode instruction start */ pd.bytecodestart[bcindex] = 1; /* change the current line number, if necessary */ /* XXX rewrite this using pointer arithmetic */ if (linepcchange == bcindex) { if (m->linenumbercount > lineindex) { next_linenumber: currentline = m->linenumbers[lineindex].line_number; lineindex++; if (lineindex < m->linenumbercount) { linepcchange = m->linenumbers[lineindex].start_pc; if (linepcchange == bcindex) goto next_linenumber; } } } fetch_opcode: /* fetch next opcode */ opcode = SUCK_BE_U1(m->jcode + bcindex); /* If the previous instruction was a block-end instruction, mark the current bytecode instruction as basic-block starting instruction. */ /* NOTE: Some compilers put a BC_nop after a blockend instruction. */ if (blockend && (opcode != BC_nop)) { MARK_BASICBLOCK(&pd, bcindex); blockend = false; } /* If the current bytecode instruction was marked as basic-block starting instruction before (e.g. blockend, forward-branch target), mark the current IR instruction too. */ if (pd.basicblockstart[bcindex] != 0) { /* We need a NOP as last instruction in each basic block for basic block reordering (may be replaced with a GOTO later). */ INSTRUCTIONS_CHECK(1); OP(ICMD_NOP); } /* store intermediate instruction count (bit 0 mark block starts) */ pd.bytecodemap[bcindex] = ircount; /* compute next instruction start */ nextbc = bcindex + bytecode[opcode].length; CHECK_END_OF_BYTECODE(nextbc); /* add stack elements produced by this instruction */ s_count += bytecode[opcode].slots; /* We check here for the space of 1 instruction in the instruction array. If an opcode is converted to more than 1 instruction, this is checked in the corresponding case. */ INSTRUCTIONS_CHECK(1); /* translate this bytecode instruction */ switch (opcode) { case BC_nop: break; /* pushing constants onto the stack ***********************************/ case BC_bipush: OP_LOADCONST_I(SUCK_BE_S1(m->jcode + bcindex + 1)); break; case BC_sipush: OP_LOADCONST_I(SUCK_BE_S2(m->jcode + bcindex + 1)); break; case BC_ldc1: i = SUCK_BE_U1(m->jcode + bcindex + 1); goto pushconstantitem; case BC_ldc2: case BC_ldc2w: i = SUCK_BE_U2(m->jcode + bcindex + 1); pushconstantitem: #if defined(ENABLE_VERIFIER) if (i >= m->clazz->cpcount) { exceptions_throw_verifyerror(m, "Attempt to access constant outside range"); return false; } #endif switch (m->clazz->cptags[i]) { case CONSTANT_Integer: OP_LOADCONST_I(((constant_integer *) (m->clazz->cpinfos[i]))->value); break; case CONSTANT_Long: OP_LOADCONST_L(((constant_long *) (m->clazz->cpinfos[i]))->value); break; case CONSTANT_Float: OP_LOADCONST_F(((constant_float *) (m->clazz->cpinfos[i]))->value); break; case CONSTANT_Double: OP_LOADCONST_D(((constant_double *) (m->clazz->cpinfos[i]))->value); break; case CONSTANT_String: OP_LOADCONST_STRING(literalstring_new((utf *) (m->clazz->cpinfos[i]))); break; case CONSTANT_Class: cr = (constant_classref *) (m->clazz->cpinfos[i]); if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; /* if not resolved, c == NULL */ OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr); break; #if defined(ENABLE_VERIFIER) default: exceptions_throw_verifyerror(m, "Invalid constant type to push"); return false; #endif } break; case BC_aconst_null: OP_LOADCONST_NULL(); break; case BC_iconst_m1: case BC_iconst_0: case BC_iconst_1: case BC_iconst_2: case BC_iconst_3: case BC_iconst_4: case BC_iconst_5: OP_LOADCONST_I(opcode - BC_iconst_0); break; case BC_lconst_0: case BC_lconst_1: OP_LOADCONST_L(opcode - BC_lconst_0); break; case BC_fconst_0: case BC_fconst_1: case BC_fconst_2: OP_LOADCONST_F(opcode - BC_fconst_0); break; case BC_dconst_0: case BC_dconst_1: OP_LOADCONST_D(opcode - BC_dconst_0); break; /* stack operations ***************************************************/ /* We need space for additional instruction so we can translate these instructions to sequences of ICMD_COPY and ICMD_MOVE instructions. */ case BC_dup_x1: INSTRUCTIONS_CHECK(4); OP(opcode); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); break; case BC_dup_x2: INSTRUCTIONS_CHECK(6); OP(opcode); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); break; case BC_dup2: INSTRUCTIONS_CHECK(2); OP(opcode); OP(ICMD_NOP); break; case BC_dup2_x1: INSTRUCTIONS_CHECK(7); OP(opcode); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); break; case BC_dup2_x2: INSTRUCTIONS_CHECK(9); OP(opcode); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); OP(ICMD_NOP); break; case BC_swap: INSTRUCTIONS_CHECK(3); OP(opcode); OP(ICMD_NOP); OP(ICMD_NOP); break; /* local variable access instructions *********************************/ case BC_iload: case BC_fload: case BC_aload: if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); nextbc = bcindex + 3; iswide = false; } OP_LOAD_ONEWORD(opcode, i, opcode - BC_iload); break; case BC_lload: case BC_dload: if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); nextbc = bcindex + 3; iswide = false; } OP_LOAD_TWOWORD(opcode, i, opcode - BC_iload); break; case BC_iload_0: case BC_iload_1: case BC_iload_2: case BC_iload_3: OP_LOAD_ONEWORD(ICMD_ILOAD, opcode - BC_iload_0, TYPE_INT); break; case BC_lload_0: case BC_lload_1: case BC_lload_2: case BC_lload_3: OP_LOAD_TWOWORD(ICMD_LLOAD, opcode - BC_lload_0, TYPE_LNG); break; case BC_fload_0: case BC_fload_1: case BC_fload_2: case BC_fload_3: OP_LOAD_ONEWORD(ICMD_FLOAD, opcode - BC_fload_0, TYPE_FLT); break; case BC_dload_0: case BC_dload_1: case BC_dload_2: case BC_dload_3: OP_LOAD_TWOWORD(ICMD_DLOAD, opcode - BC_dload_0, TYPE_DBL); break; case BC_aload_0: case BC_aload_1: case BC_aload_2: case BC_aload_3: OP_LOAD_ONEWORD(ICMD_ALOAD, opcode - BC_aload_0, TYPE_ADR); break; case BC_istore: case BC_fstore: case BC_astore: if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); nextbc = bcindex + 3; iswide = false; } OP_STORE_ONEWORD(opcode, i, opcode - BC_istore); break; case BC_lstore: case BC_dstore: if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); nextbc = bcindex + 3; iswide = false; } OP_STORE_TWOWORD(opcode, i, opcode - BC_istore); break; case BC_istore_0: case BC_istore_1: case BC_istore_2: case BC_istore_3: OP_STORE_ONEWORD(ICMD_ISTORE, opcode - BC_istore_0, TYPE_INT); break; case BC_lstore_0: case BC_lstore_1: case BC_lstore_2: case BC_lstore_3: OP_STORE_TWOWORD(ICMD_LSTORE, opcode - BC_lstore_0, TYPE_LNG); break; case BC_fstore_0: case BC_fstore_1: case BC_fstore_2: case BC_fstore_3: OP_STORE_ONEWORD(ICMD_FSTORE, opcode - BC_fstore_0, TYPE_FLT); break; case BC_dstore_0: case BC_dstore_1: case BC_dstore_2: case BC_dstore_3: OP_STORE_TWOWORD(ICMD_DSTORE, opcode - BC_dstore_0, TYPE_DBL); break; case BC_astore_0: case BC_astore_1: case BC_astore_2: case BC_astore_3: OP_STORE_ONEWORD(ICMD_ASTORE, opcode - BC_astore_0, TYPE_ADR); break; case BC_iinc: { int v; if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); v = SUCK_BE_S1(m->jcode + bcindex + 2); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); v = SUCK_BE_S2(m->jcode + bcindex + 3); nextbc = bcindex + 5; iswide = false; } INDEX_ONEWORD(i); LOCALTYPE_USED(i, TYPE_INT); OP_LOCALINDEX_I(opcode, i, v); } break; /* wider index for loading, storing and incrementing ******************/ case BC_wide: bcindex++; iswide = true; goto fetch_opcode; /* managing arrays ****************************************************/ case BC_newarray: switch (SUCK_BE_S1(m->jcode + bcindex + 1)) { case 4: bte = builtintable_get_internal(BUILTIN_newarray_boolean); break; case 5: bte = builtintable_get_internal(BUILTIN_newarray_char); break; case 6: bte = builtintable_get_internal(BUILTIN_newarray_float); break; case 7: bte = builtintable_get_internal(BUILTIN_newarray_double); break; case 8: bte = builtintable_get_internal(BUILTIN_newarray_byte); break; case 9: bte = builtintable_get_internal(BUILTIN_newarray_short); break; case 10: bte = builtintable_get_internal(BUILTIN_newarray_int); break; case 11: bte = builtintable_get_internal(BUILTIN_newarray_long); break; #if defined(ENABLE_VERIFIER) default: exceptions_throw_verifyerror(m, "Invalid array-type to create"); return false; #endif } OP_BUILTIN_CHECK_EXCEPTION(bte); break; case BC_anewarray: i = SUCK_BE_U2(m->jcode + bcindex + 1); compr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); if (compr == NULL) return false; if (!(cr = class_get_classref_multiarray_of(1, compr))) return false; if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; INSTRUCTIONS_CHECK(2); OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); bte = builtintable_get_internal(BUILTIN_newarray); OP_BUILTIN_CHECK_EXCEPTION(bte); s_count++; break; case BC_multianewarray: i = SUCK_BE_U2(m->jcode + bcindex + 1); j = SUCK_BE_U1(m->jcode + bcindex + 3); cr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class); if (cr == NULL) return false; if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; /* if unresolved, c == NULL */ iptr->s1.argcount = j; OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, INS_FLAG_CHECK); code_unflag_leafmethod(code); break; /* control flow instructions ******************************************/ case BC_ifeq: case BC_iflt: case BC_ifle: case BC_ifne: case BC_ifgt: case BC_ifge: case BC_ifnull: case BC_ifnonnull: case BC_if_icmpeq: case BC_if_icmpne: case BC_if_icmplt: case BC_if_icmpgt: case BC_if_icmple: case BC_if_icmpge: case BC_if_acmpeq: case BC_if_acmpne: case BC_goto: i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); CHECK_BYTECODE_INDEX(i); MARK_BASICBLOCK(&pd, i); blockend = true; OP_INSINDEX(opcode, i); break; case BC_goto_w: i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); CHECK_BYTECODE_INDEX(i); MARK_BASICBLOCK(&pd, i); blockend = true; OP_INSINDEX(ICMD_GOTO, i); break; case BC_jsr: i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1); jsr_tail: CHECK_BYTECODE_INDEX(i); MARK_BASICBLOCK(&pd, i); blockend = true; OP_PREPARE_ZEROFLAGS(BC_jsr); iptr->sx.s23.s3.jsrtarget.insindex = i; PINC; break; case BC_jsr_w: i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1); goto jsr_tail; case BC_ret: if (iswide == false) { i = SUCK_BE_U1(m->jcode + bcindex + 1); } else { i = SUCK_BE_U2(m->jcode + bcindex + 1); nextbc = bcindex + 3; iswide = false; } blockend = true; OP_LOAD_ONEWORD(opcode, i, TYPE_ADR); break; case BC_ireturn: case BC_lreturn: case BC_freturn: case BC_dreturn: case BC_areturn: case BC_return: blockend = true; /* XXX ARETURN will need a flag in the typechecker */ OP(opcode); break; case BC_athrow: blockend = true; /* XXX ATHROW will need a flag in the typechecker */ OP(opcode); break; /* table jumps ********************************************************/ case BC_lookupswitch: { s4 num, j; lookup_target_t *lookup; #if defined(ENABLE_VERIFIER) s4 prevvalue = 0; #endif blockend = true; nextbc = MEMORY_ALIGN((bcindex + 1), 4); CHECK_END_OF_BYTECODE(nextbc + 8); OP_PREPARE_ZEROFLAGS(opcode); /* default target */ j = bcindex + SUCK_BE_S4(m->jcode + nextbc); iptr->sx.s23.s3.lookupdefault.insindex = j; nextbc += 4; CHECK_BYTECODE_INDEX(j); MARK_BASICBLOCK(&pd, j); /* number of pairs */ num = SUCK_BE_U4(m->jcode + nextbc); iptr->sx.s23.s2.lookupcount = num; nextbc += 4; /* allocate the intermediate code table */ lookup = (lookup_target_t*) DumpMemory::allocate(sizeof(lookup_target_t) * num); iptr->dst.lookup = lookup; /* iterate over the lookup table */ CHECK_END_OF_BYTECODE(nextbc + 8 * num); for (i = 0; i < num; i++) { /* value */ j = SUCK_BE_S4(m->jcode + nextbc); lookup->value = j; nextbc += 4; #if defined(ENABLE_VERIFIER) /* check if the lookup table is sorted correctly */ if (i && (j <= prevvalue)) { exceptions_throw_verifyerror(m, "Unsorted lookup switch"); return false; } prevvalue = j; #endif /* target */ j = bcindex + SUCK_BE_S4(m->jcode + nextbc); lookup->target.insindex = j; lookup++; nextbc += 4; CHECK_BYTECODE_INDEX(j); MARK_BASICBLOCK(&pd, j); } PINC; break; } case BC_tableswitch: { s4 num, j; s4 deftarget; branch_target_t *table; blockend = true; nextbc = MEMORY_ALIGN((bcindex + 1), 4); CHECK_END_OF_BYTECODE(nextbc + 12); OP_PREPARE_ZEROFLAGS(opcode); /* default target */ deftarget = bcindex + SUCK_BE_S4(m->jcode + nextbc); nextbc += 4; CHECK_BYTECODE_INDEX(deftarget); MARK_BASICBLOCK(&pd, deftarget); /* lower bound */ j = SUCK_BE_S4(m->jcode + nextbc); iptr->sx.s23.s2.tablelow = j; nextbc += 4; /* upper bound */ num = SUCK_BE_S4(m->jcode + nextbc); iptr->sx.s23.s3.tablehigh = num; nextbc += 4; /* calculate the number of table entries */ num = num - j + 1; #if defined(ENABLE_VERIFIER) if (num < 1) { exceptions_throw_verifyerror(m, "invalid TABLESWITCH: upper bound < lower bound"); return false; } #endif /* create the intermediate code table */ /* the first entry is the default target */ table = (branch_target_t*) DumpMemory::allocate(sizeof(branch_target_t) * (1 + num)); iptr->dst.table = table; (table++)->insindex = deftarget; /* iterate over the target table */ CHECK_END_OF_BYTECODE(nextbc + 4 * num); for (i = 0; i < num; i++) { j = bcindex + SUCK_BE_S4(m->jcode + nextbc); (table++)->insindex = j; nextbc += 4; CHECK_BYTECODE_INDEX(j); MARK_BASICBLOCK(&pd, j); } PINC; break; } /* load and store of object fields ************************************/ case BC_aastore: OP(opcode); code_unflag_leafmethod(code); break; case BC_getstatic: case BC_putstatic: case BC_getfield: case BC_putfield: i = SUCK_BE_U2(m->jcode + bcindex + 1); fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Fieldref); if (fmi == NULL) return false; OP_PREPARE_ZEROFLAGS(opcode); iptr->sx.s23.s3.fmiref = fmi; /* only with -noverify, otherwise the typechecker does this */ #if defined(ENABLE_VERIFIER) if (!JITDATA_HAS_FLAG_VERIFY(jd)) { #endif result = resolve_field_lazy(m, fmi); if (result == resolveFailed) return false; if (result != resolveSucceeded) { uf = resolve_create_unresolved_field(m->clazz, m, iptr); if (uf == NULL) return false; /* store the unresolved_field pointer */ iptr->sx.s23.s3.uf = uf; iptr->flags.bits |= INS_FLAG_UNRESOLVED; } #if defined(ENABLE_VERIFIER) } #endif PINC; break; /* method invocation **************************************************/ case BC_invokestatic: OP_PREPARE_ZEROFLAGS(opcode); i = SUCK_BE_U2(m->jcode + bcindex + 1); fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); if (fmi == NULL) return false; md = fmi->parseddesc.md; if (md->params == NULL) if (!descriptor_params_from_paramtypes(md, ACC_STATIC)) return false; goto invoke_method; case BC_invokespecial: OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); i = SUCK_BE_U2(m->jcode + bcindex + 1); fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); goto invoke_nonstatic_method; case BC_invokeinterface: OP_PREPARE_ZEROFLAGS(opcode); i = SUCK_BE_U2(m->jcode + bcindex + 1); fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_InterfaceMethodref); goto invoke_nonstatic_method; case BC_invokevirtual: OP_PREPARE_ZEROFLAGS(opcode); i = SUCK_BE_U2(m->jcode + bcindex + 1); fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref); invoke_nonstatic_method: if (fmi == NULL) return false; md = fmi->parseddesc.md; if (md->params == NULL) if (!descriptor_params_from_paramtypes(md, 0)) return false; invoke_method: code_unflag_leafmethod(code); iptr->sx.s23.s3.fmiref = fmi; /* only with -noverify, otherwise the typechecker does this */ #if defined(ENABLE_VERIFIER) if (!JITDATA_HAS_FLAG_VERIFY(jd)) { #endif result = resolve_method_lazy(m, fmi, (opcode == BC_invokespecial)); if (result == resolveFailed) return false; if (result == resolveSucceeded) { methodinfo *mi = iptr->sx.s23.s3.fmiref->p.method; /* if this call is monomorphic, turn it into an INVOKESPECIAL */ assert(IS_FMIREF_RESOLVED(iptr->sx.s23.s3.fmiref)); if ((iptr->opc == ICMD_INVOKEVIRTUAL) && (mi->flags & (ACC_FINAL | ACC_PRIVATE))) { iptr->opc = ICMD_INVOKESPECIAL; iptr->flags.bits |= INS_FLAG_CHECK; } } else { um = resolve_create_unresolved_method(m->clazz, m, fmi, (opcode == BC_invokestatic), (opcode == BC_invokespecial)); if (um == NULL) return false; /* store the unresolved_method pointer */ iptr->sx.s23.s3.um = um; iptr->flags.bits |= INS_FLAG_UNRESOLVED; } #if defined(ENABLE_VERIFIER) } #endif PINC; break; /* instructions taking class arguments ********************************/ case BC_new: i = SUCK_BE_U2(m->jcode + bcindex + 1); cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); if (cr == NULL) return false; if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; INSTRUCTIONS_CHECK(2); OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); bte = builtintable_get_internal(BUILTIN_new); OP_BUILTIN_CHECK_EXCEPTION(bte); s_count++; break; case BC_checkcast: i = SUCK_BE_U2(m->jcode + bcindex + 1); cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); if (cr == NULL) return false; if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; if (cr->name->text[0] == '[') { /* array type cast-check */ flags = INS_FLAG_CHECK | INS_FLAG_ARRAY; code_unflag_leafmethod(code); } else { /* object type cast-check */ flags = INS_FLAG_CHECK; } OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, flags); break; case BC_instanceof: i = SUCK_BE_U2(m->jcode + bcindex + 1); cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class); if (cr == NULL) return false; if (!resolve_classref(m, cr, resolveLazy, true, true, &c)) return false; if (cr->name->text[0] == '[') { /* array type cast-check */ INSTRUCTIONS_CHECK(2); OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr); bte = builtintable_get_internal(BUILTIN_arrayinstanceof); OP_BUILTIN_NO_EXCEPTION(bte); s_count++; } else { /* object type cast-check */ OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, 0 /* flags*/); } break; /* synchronization instructions ***************************************/ case BC_monitorenter: #if defined(ENABLE_THREADS) if (checksync) { bte = builtintable_get_internal(LOCK_monitor_enter); OP_BUILTIN_CHECK_EXCEPTION(bte); } else #endif { OP_CHECK_EXCEPTION(ICMD_CHECKNULL); OP(ICMD_POP); } break; case BC_monitorexit: #if defined(ENABLE_THREADS) if (checksync) { bte = builtintable_get_internal(LOCK_monitor_exit); OP_BUILTIN_CHECK_EXCEPTION(bte); } else #endif { OP_CHECK_EXCEPTION(ICMD_CHECKNULL); OP(ICMD_POP); } break; /* arithmetic instructions that may become builtin functions **********/ case BC_idiv: #if !SUPPORT_DIVISION bte = builtintable_get_internal(BUILTIN_idiv); OP_BUILTIN_ARITHMETIC(opcode, bte); #else # if SUPPORT_HARDWARE_DIVIDE_BY_ZERO OP(opcode); # else OP_CHECK_EXCEPTION(opcode); # endif #endif break; case BC_irem: #if !SUPPORT_DIVISION bte = builtintable_get_internal(BUILTIN_irem); OP_BUILTIN_ARITHMETIC(opcode, bte); #else # if SUPPORT_HARDWARE_DIVIDE_BY_ZERO OP(opcode); # else OP_CHECK_EXCEPTION(opcode); # endif #endif break; case BC_ldiv: #if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) bte = builtintable_get_internal(BUILTIN_ldiv); OP_BUILTIN_ARITHMETIC(opcode, bte); #else # if SUPPORT_HARDWARE_DIVIDE_BY_ZERO OP(opcode); # else OP_CHECK_EXCEPTION(opcode); # endif #endif break; case BC_lrem: #if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) bte = builtintable_get_internal(BUILTIN_lrem); OP_BUILTIN_ARITHMETIC(opcode, bte); #else # if SUPPORT_HARDWARE_DIVIDE_BY_ZERO OP(opcode); # else OP_CHECK_EXCEPTION(opcode); # endif #endif break; case BC_frem: #if defined(__I386__) OP(opcode); #else bte = builtintable_get_internal(BUILTIN_frem); OP_BUILTIN_NO_EXCEPTION(bte); #endif break; case BC_drem: #if defined(__I386__) OP(opcode); #else bte = builtintable_get_internal(BUILTIN_drem); OP_BUILTIN_NO_EXCEPTION(bte); #endif break; case BC_f2i: #if defined(__ALPHA__) bte = builtintable_get_internal(BUILTIN_f2i); OP_BUILTIN_NO_EXCEPTION(bte); #else OP(opcode); #endif break; case BC_f2l: #if defined(__ALPHA__) bte = builtintable_get_internal(BUILTIN_f2l); OP_BUILTIN_NO_EXCEPTION(bte); #else OP(opcode); #endif break; case BC_d2i: #if defined(__ALPHA__) bte = builtintable_get_internal(BUILTIN_d2i); OP_BUILTIN_NO_EXCEPTION(bte); #else OP(opcode); #endif break; case BC_d2l: #if defined(__ALPHA__) bte = builtintable_get_internal(BUILTIN_d2l); OP_BUILTIN_NO_EXCEPTION(bte); #else OP(opcode); #endif break; /* invalid opcodes ****************************************************/ /* check for invalid opcodes if the verifier is enabled */ #if defined(ENABLE_VERIFIER) case BC_breakpoint: exceptions_throw_verifyerror(m, "Quick instructions shouldn't appear, yet."); return false; /* Unused opcodes ************************************************** */ case 186: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: exceptions_throw_verifyerror(m, "Illegal opcode %d at instr %d\n", opcode, ircount); return false; break; #endif /* defined(ENABLE_VERIFIER) */ /* opcodes that don't require translation *****************************/ default: /* Straight-forward translation to HIR. */ OP(opcode); break; } /* end switch */ /* verifier checks ****************************************************/ #if defined(ENABLE_VERIFIER) /* If WIDE was used correctly, iswide should have been reset by now. */ if (iswide) { exceptions_throw_verifyerror(m, "Illegal instruction: WIDE before incompatible opcode"); return false; } #endif /* defined(ENABLE_VERIFIER) */ } /* end for */ if (JITDATA_HAS_FLAG_REORDER(jd)) { /* add a NOP to the last basic block */ INSTRUCTIONS_CHECK(1); OP(ICMD_NOP); } /*** END OF LOOP **********************************************************/ /* assert that we did not write more ICMDs than allocated */ assert(ircount <= pd.instructionslength); assert(ircount == (iptr - pd.instructions)); /*** verifier checks ******************************************************/ #if defined(ENABLE_VERIFIER) if (bcindex != m->jcodelength) { exceptions_throw_verifyerror(m, "Command-sequence crosses code-boundary"); return false; } if (!blockend) { exceptions_throw_verifyerror(m, "Falling off the end of the code"); return false; } #endif /* defined(ENABLE_VERIFIER) */ /*** setup the methodinfo, allocate stack and basic blocks ****************/ /* identify basic blocks */ /* check if first instruction is a branch target */ if (pd.basicblockstart[0] == 1) { jd->branchtoentry = true; } else { /* first instruction always starts a basic block */ iptr = pd.instructions; iptr->flags.bits |= INS_FLAG_BASICBLOCK; } /* Iterate over all bytecode instructions and set missing basic-block starts in IR instructions. */ for (bcindex = 0; bcindex < m->jcodelength; bcindex++) { /* Does the current bytecode instruction start a basic block? */ if (pd.basicblockstart[bcindex] == 1) { #if defined(ENABLE_VERIFIER) /* Check if this bytecode basic-block start at the beginning of a bytecode instruction. */ if (pd.bytecodestart[bcindex] == 0) { exceptions_throw_verifyerror(m, "Branch into middle of instruction"); return false; } #endif /* Get the IR instruction mapped to the bytecode instruction and set the basic block flag. */ irindex = pd.bytecodemap[bcindex]; iptr = pd.instructions + irindex; iptr->flags.bits |= INS_FLAG_BASICBLOCK; } } /* IR instruction index to basic-block index mapping */ pd.instructionmap = (s4*) DumpMemory::allocate(sizeof(s4) * ircount); MZERO(pd.instructionmap, s4, ircount); /* Iterate over all IR instructions and count the basic blocks. */ iptr = pd.instructions; bbcount = 0; for (i = 0; i < ircount; i++, iptr++) { if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { /* store the basic-block number in the IR instruction map */ pd.instructionmap[i] = bbcount; /* post-increment the basic-block count */ bbcount++; } } /* Allocate basic block array (one more for end ipc). */ jd->basicblocks = (basicblock*) DumpMemory::allocate(sizeof(basicblock) * (bbcount + 1)); MZERO(jd->basicblocks, basicblock, bbcount + 1); /* Now iterate again over all IR instructions and initialize the basic block structures and, in the same loop, resolve the branch-target instruction indices to basic blocks. */ iptr = pd.instructions; bptr = jd->basicblocks; bbcount = 0; for (i = 0; i < ircount; i++, iptr++) { /* check for basic block */ if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) { /* intialize the basic block */ BASICBLOCK_INIT(bptr, m); bptr->iinstr = iptr; if (bbcount > 0) { bptr[-1].icount = bptr->iinstr - bptr[-1].iinstr; } /* bptr->icount is set when the next block is allocated */ bptr->nr = bbcount++; bptr++; bptr[-1].next = bptr; } /* resolve instruction indices to basic blocks */ switch (iptr->opc) { case ICMD_IFEQ: case ICMD_IFLT: case ICMD_IFLE: case ICMD_IFNE: case ICMD_IFGT: case ICMD_IFGE: case ICMD_IFNULL: case ICMD_IFNONNULL: case ICMD_IF_ICMPEQ: case ICMD_IF_ICMPNE: case ICMD_IF_ICMPLT: case ICMD_IF_ICMPGT: case ICMD_IF_ICMPLE: case ICMD_IF_ICMPGE: case ICMD_IF_ACMPEQ: case ICMD_IF_ACMPNE: case ICMD_GOTO: BYTECODEINDEX_TO_BASICBLOCK(iptr->dst); break; case ICMD_JSR: BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.jsrtarget); break; case ICMD_TABLESWITCH: table = iptr->dst.table; BYTECODEINDEX_TO_BASICBLOCK(*table); table++; j = iptr->sx.s23.s3.tablehigh - iptr->sx.s23.s2.tablelow + 1; while (--j >= 0) { BYTECODEINDEX_TO_BASICBLOCK(*table); table++; } break; case ICMD_LOOKUPSWITCH: BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.lookupdefault); lookup = iptr->dst.lookup; j = iptr->sx.s23.s2.lookupcount; while (--j >= 0) { BYTECODEINDEX_TO_BASICBLOCK(lookup->target); lookup++; } break; } } /* set instruction count of last real block */ if (bbcount > 0) { bptr[-1].icount = (pd.instructions + ircount) - bptr[-1].iinstr; } /* allocate additional block at end */ BASICBLOCK_INIT(bptr, m); bptr->nr = bbcount; /* set basicblock pointers in exception table */ if (!parse_resolve_exception_table(jd, &pd)) return false; /* store the local map */ jd->local_map = local_map; /* calculate local variable renaming */ { s4 nlocals = 0; s4 i; s4 t; s4 varindex; s4 *mapptr; s4 *reversemap; mapptr = local_map; /* iterate over local_map[0..m->maxlocals*5-1] and allocate a unique */ /* variable index for each _used_ (javaindex,type) pair. */ /* (local_map[javaindex*5+type] = cacaoindex) */ /* Unused (javaindex,type) pairs are marked with UNUSED. */ for (i = 0; i < (m->maxlocals * 5); i++, mapptr++) { if (*mapptr) *mapptr = nlocals++; else *mapptr = UNUSED; } jd->localcount = nlocals; /* calculate the (maximum) number of variables needed */ jd->varcount = nlocals /* local variables */ + bbcount * m->maxstack /* invars */ + s_count; /* variables created within blocks (non-invar) */ /* reserve the first indices for local variables */ jd->vartop = nlocals; /* reserve extra variables needed by stack analyse */ jd->varcount += STACK_EXTRA_VARS; jd->vartop += STACK_EXTRA_VARS; /* The verifier needs space for saving invars in some cases and */ /* extra variables. */ #if defined(ENABLE_VERIFIER) jd->varcount += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack; #endif /* allocate and initialize the variable array */ jd->var = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * jd->varcount); MZERO(jd->var, varinfo, jd->varcount); /* set types of all locals in jd->var */ /* and fill the reverselocalmap */ reversemap = (s4*) DumpMemory::allocate(sizeof(s4) * nlocals); for (i = 0; i < m->maxlocals; i++) for (t=0; t<5; t++) { varindex = local_map[5*i + t]; if (varindex != UNUSED) { VAR(varindex)->type = t; reversemap[varindex] = i; } } jd->reverselocalmap = reversemap; } /* assign local variables to method variables */ jd->instructions = pd.instructions; jd->instructioncount = ircount; jd->basicblockcount = bbcount; jd->stackcount = s_count + bbcount * m->maxstack; /* in-stacks */ /* allocate stack table */ jd->stack = (stackelement_t*) DumpMemory::allocate(sizeof(stackelement_t) * jd->stackcount); /* everything's ok */ return true; /*** goto labels for throwing verifier exceptions *************************/ #if defined(ENABLE_VERIFIER) throw_unexpected_end_of_bytecode: exceptions_throw_verifyerror(m, "Unexpected end of bytecode"); return false; throw_invalid_bytecode_index: exceptions_throw_verifyerror(m, "Illegal target of branch instruction"); return false; throw_illegal_local_variable_number: exceptions_throw_verifyerror(m, "Illegal local variable number"); return false; #endif /* ENABLE_VERIFIER */ } #if defined(__cplusplus) } #endif /* * These are local overrides for various environment variables in Emacs. * Please do not remove this and leave it at the end of the file, where * Emacs will automagically detect them. * --------------------------------------------------------------------- * Local variables: * mode: c * indent-tabs-mode: t * c-basic-offset: 4 * tab-width: 4 * End: * vim:noexpandtab:sw=4:ts=4: */