\ \ JVM instruction specs \ \ This file contains primitive specifications in the following format: \ \ name ( stack effect ) [ number ] \ [""glossary entry""] \ C code \ [: \ Forth code] \ \ Note: Fields in brackets are optional. Word specifications have to \ be separated by at least one empty line \ \ Both name and stack items (in the stack effect) must \ conform to the C identifier syntax or the C compiler will complain. \ The branch field can be one of "branch", "cond", "indirect", or empty. \ \ These specifications are automatically translated into C-code for the \ interpreter and into some other files. I hope that your C compiler has \ decent optimization, otherwise the automatically generated code will \ be somewhat slow. The Forth version of the code is included for manual \ compilers, so they will need to compile only the important words. \ \ Note that stack pointer adjustment is performed according to stack \ effect by automatically generated code and NEXT is automatically \ appended to the C code. Also, you can use the names in the stack \ effect in the C code. Stack access is automatic. One exception: if \ your code does not fall through, the results are not stored into the \ stack. Use different names on both sides of the '--', if you change a \ value (some stores to the stack are optimized away). \ \E stack data-stack sp Cell \E \E s" u4" single data-stack type-prefix ui \E s" Cell" single data-stack type-prefix v \E s" s4" single data-stack type-prefix b \ byte \E s" s4" single data-stack type-prefix s \ short \E s" s4" single data-stack type-prefix i \E s" s8" double data-stack type-prefix l \E s" float" single data-stack type-prefix f \E s" double" double data-stack type-prefix d \ we use a lot of types for the former "a" (address) type; this gives \ better typechecking in the C code, and allows better output from the \ disassembler and the tracer. \E s" java_objectheader *" single data-stack type-prefix aRef \E s" java_arrayheader *" single data-stack type-prefix aArray \E s" Inst **" single data-stack type-prefix aaTarget \E s" classinfo *" single data-stack type-prefix aClass \E s" constant_classref *" single data-stack type-prefix acr \E s" u1 *" single data-stack type-prefix addr \E s" functionptr" single data-stack type-prefix af \E s" methodinfo *" single data-stack type-prefix am \E s" Cell *" single data-stack type-prefix acell \E s" Inst *" single data-stack type-prefix ainst \E s" unresolved_field *" single data-stack type-prefix auf \E s" unresolved_method *" single data-stack type-prefix aum \E s" vftbl_t *" single data-stack type-prefix avftbl \E inst-stream stack-prefix # \ The stack variables have the following types: \ \ name matches type \ i.* jint \ l.* jlong \ s.* Jshort \ b.* Jbyte \ c.* Jchar \ f.* jfloat \ d.* jdouble \ a.* jref \ r.* Jretaddr \ \ \ Starting a name with # indicates an inline argument instead of a stack \ argument; the rest of the name matches as usual. \ \ \ Questions: \ How does prims2x know the length of each instruction, \ where the instruction has immediate operands. We need \ some way to communicate this or compute it. \ \ Some instructions don't care about the type of the \ values that they work with. For example, POP doesn't \ care what type the value on the top of the stack is. \ We need to communicate this. \ I'll add a type v (for void, or variable, whichever you \ prefer) which just means there's one element of the normal \ size on the stack, and we don't care what its type is. \ \ \ /* \ Define the following macros before including this file. \ \ # define instr(name,opcode,label,len,syntax,body) \ # define undef(name,opcode,label,len,syntax,body) \ # define custm(name,opcode,label,len,syntax,body) \ \ \ To avoid race conditions, rewriting proceeds as follows: \ 1) the needed operands are fetched from the orignal byte code \ 2) the new operands are written \ 3) the new operator is written \ 4) the quick-version of the instruction is executed. \ \ Note: the byte code remains unchanged. \ \ */ \ This is stub code for methods that we have not yet translated. \ Initially, the code for each method is set to this stub. The \ first time the method is called, the code in the stub runs, which \ translates the bytecode, and replaces the stub with the threaded code. \ instr(NOP, 0, nop, 1, op, {}) ICONST ( #vConst -- vConst ) opt LCONST ( #l -- l ) opt ACONST ( #aRef -- aRef ) opt ILOAD ( #bLocal -- vResult ) 0x15 { vResult = access_local_int(bLocal); } LLOAD ( #bLocal -- lResult ) 0x16 { vm_twoCell2l(access_local_cell(bLocal), access_local_cell(bLocal+1), lResult); } ALOAD ( #bLocal -- aRef ) 0x19 { aRef = (java_objectheader *)access_local_cell(bLocal); } IALOAD ( aArray iIndex -- iResult ) 0x2e { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; iResult = access_array_int(aArray, iIndex); } LALOAD ( aArray iIndex -- lResult ) 0x2f { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; lResult = access_array_long(aArray, iIndex); } AALOAD ( aArray iIndex -- aRef ) 0x32 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; aRef = access_array_addr(aArray, iIndex); } BALOAD ( aArray iIndex -- iResult ) 0x33 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; iResult = access_array_byte(aArray, iIndex); } CALOAD ( aArray iIndex -- iResult ) 0x34 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; iResult = access_array_char(aArray, iIndex); } SALOAD ( aArray iIndex -- iResult ) 0x35 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; iResult = access_array_short(aArray, iIndex); } ISTORE ( #bLocal vValue -- ) 0x36 { access_local_int(bLocal) = vValue; } LSTORE ( #bLocal lValue -- ) 0x37 { vm_l2twoCell(lValue, access_local_cell(bLocal), access_local_cell(bLocal+1)); } ASTORE ( #bLocal aRef -- ) 0x3a { access_local_cell(bLocal) = (Cell)aRef; } IASTORE ( aArray iIndex iValue -- ) 0x4f { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; access_array_int(aArray, iIndex) = iValue; } LASTORE ( aArray iIndex lValue -- ) 0x50 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; access_array_long(aArray, iIndex) = lValue; } AASTORE ( aArray iIndex aRef -- ) 0x53 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); if (!builtin_canstore((java_objectarray *)aArray, aRef)) THROW(arraystoreexception); access_array_addr(aArray, iIndex) = aRef; } BASTORE ( aArray iIndex iValue -- ) 0x54 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; access_array_byte(aArray, iIndex) = iValue; } CASTORE ( aArray iIndex iValue -- ) 0x55 { CHECK_NULL_PTR(aArray); CHECK_OUT_OF_BOUNDS(aArray, iIndex); ; access_array_char(aArray, iIndex) = iValue; } POP ( vValue -- ) 0x57 POP2 ( vValue vValue -- ) 0x58 DUP ( vValue -- vValue vValue ) 0x59 DUP_X1 ( vValue2 vValue1 -- vValue1 vValue2 vValue1 ) 0x5a DUP_X2 ( vValue3 vValue2 vValue1 -- vValue1 vValue3 vValue2 vValue1 ) 0x5b DUP2 ( vValue2 vValue1 -- vValue2 vValue1 vValue2 vValue1 ) 0x5c DUP2_X1 (vValue3 vValue2 vValue1 -- vValue2 vValue1 vValue3 vValue2 vValue1 ) 0x5d DUP2_X2 ( vValue4 vValue3 vValue2 vValue1 -- vValue2 vValue1 vValue4 vValue3 vValue2 vValue1 ) 0x5e SWAP ( vValue2 vValue1 -- vValue1 vValue2 ) 0x5f IADD ( iValue1 iValue2 -- iResult ) 0x60 { iResult = iValue1 + iValue2; } LADD ( lValue1 lValue2 -- lResult ) 0x61 { lResult = lValue1 + lValue2; } FADD ( fValue1 fValue2 -- fResult ) 0x62 { fResult = fValue1 + fValue2; } DADD ( dValue1 dValue2 -- dResult ) 0x63 { dResult = dValue1 + dValue2; } ISUB ( iValue1 iValue2 -- iResult ) 0x64 { iResult = iValue1 - iValue2; } LSUB ( lValue1 lValue2 -- lResult ) 0x65 { lResult = lValue1 - lValue2; } FSUB ( fValue1 fValue2 -- fResult ) 0x66 { fResult = fValue1 - fValue2; } DSUB ( dValue1 dValue2 -- dResult ) 0x67 { dResult = dValue1 - dValue2; } IMUL ( iValue1 iValue2 -- iResult ) 0x68 { iResult = iValue1 * iValue2; } LMUL ( lValue1 lValue2 -- lResult ) 0x69 { lResult = lValue1 * lValue2; } FMUL ( fValue1 fValue2 -- fResult ) 0x6a { fResult = fValue1 * fValue2; } DMUL ( dValue1 dValue2 -- dResult ) 0x6b { dResult = dValue1 * dValue2; } IDIV ( iValue1 iValue2 -- iResult ) 0x6c { CHECK_ZERO_DIVISOR(iValue2); ; iResult = iValue1 / iValue2; } IDIVPOW2 ( i1 #ishift -- i2 ) opt { u4 sign = i1>>31; s4 offset = sign >> (32-ishift); i2=(i1+offset)>>ishift; } LDIV ( lValue1 lValue2 -- lResult ) 0x6d { CHECK_ZERO_DIVISOR(lValue2); ; lResult = lValue1 / lValue2; } LDIVPOW2 ( l1 #ishift -- l2 ) { u8 sign = l1>>63; s8 offset = sign >> (64-ishift); l2=(l1+offset)>>ishift; } FDIV ( fValue1 fValue2 -- fResult ) 0x6e { fResult = fValue1 / fValue2; } DDIV ( dValue1 dValue2 -- dResult ) 0x6f { dResult = dValue1 / dValue2; } IREM ( iValue1 iValue2 -- iResult ) 0x70 { CHECK_ZERO_DIVISOR(iValue2); iResult = iValue1 % iValue2; } IREMPOW2 ( i1 #imask -- i2 ) s4 x = i1; if (i1<0) x += imask; x &= ~imask; i2 = i1-x; LREM ( lValue1 lValue2 -- lResult ) 0x71 { CHECK_ZERO_DIVISOR(lValue2); lResult = lValue1 % lValue2; } LREMPOW2 ( l1 #lmask -- l2 ) s8 x = l1; if (l1<0) x += lmask; x &= ~lmask; l2 = l1-x; FREM ( fValue1 fValue2 -- fResult ) 0x72 { fResult = builtin_frem(fValue1, fValue2); } DREM ( dValue1 dValue2 -- dResult ) 0x73 { dResult = builtin_drem(dValue1, dValue2); } INEG ( iValue -- iResult ) 0x74 { iResult = -iValue; } LNEG ( lValue -- lResult ) 0x75 { lResult = -lValue; } FNEG ( fValue -- fResult ) 0x76 { fResult = -fValue; } DNEG ( dValue -- dResult ) 0x77 { dResult = -dValue; } ISHL ( iValue1 iValue2 -- iResult ) 0x78 { iResult = iValue1 << (iValue2 & 31); } LSHL ( lValue1 iValue2 -- lResult ) 0x79 { lResult = lValue1 << (iValue2 & 63); } ISHR ( iValue1 iValue2 -- iResult ) 0x7a { iResult = iValue1 >> (iValue2 & 31); } LSHR ( lValue1 iValue2 -- lResult ) 0x7b { lResult = lValue1 >> (iValue2 & 63); } IUSHR ( iValue1 iValue2 -- iResult ) 0x7c { iResult = ((unsigned) iValue1) >> (iValue2 & 31); } LUSHR ( lValue1 iValue2 -- lResult ) 0x7d { lResult = (unsigned long long) lValue1 >> (iValue2 & 63); } IAND ( iValue1 iValue2 -- iResult ) 0x7e { iResult = iValue1 & iValue2; } LAND ( lValue1 lValue2 -- lResult ) 0x7f { lResult = lValue1 & lValue2; } IOR ( iValue1 iValue2 -- iResult ) 0x80 { iResult = iValue1 | iValue2; } LOR ( lValue1 lValue2 -- lResult ) 0x81 { lResult = lValue1 | lValue2; } IXOR ( iValue1 iValue2 -- iResult ) 0x82 { iResult = iValue1 ^ iValue2; } LXOR ( lValue1 lValue2 -- lResult ) 0x83 { lResult = lValue1 ^ lValue2; } IINC ( #bIndex #iConst -- ) 0x84 { access_local_int((unsigned)bIndex) = access_local_int((unsigned)bIndex) + iConst; } I2L ( iValue -- lValue ) 0x85 { lValue = iValue; } I2F ( iValue -- fValue ) 0x86 { fValue = (float) iValue; } I2D ( iValue -- dValue ) 0x87 { dValue = (double) iValue; } L2I ( lValue -- iValue ) 0x88 { iValue = lValue; } L2F ( lValue -- fValue ) 0x89 { fValue = (float) lValue; } L2D ( lValue -- dValue ) 0x8a { dValue = (double) lValue; } F2I ( fValue -- iValue ) 0x8b { iValue = builtin_f2i(fValue); } F2L ( fValue -- lValue ) 0x8c { lValue = builtin_f2l(fValue); } F2D ( fValue -- dValue ) 0x8d { dValue = fValue; } D2I ( dValue -- iValue ) 0x8e { iValue = builtin_d2i(dValue); } D2L ( dValue -- lValue ) 0x8f { lValue = builtin_d2l(dValue); } D2F ( dValue -- fValue ) 0x90 { fValue = dValue; } INT2BYTE ( iValue -- iResult ) 0x91 { iResult = (iValue << 24) >> 24; /* XXX: try "(s1)iValue" */ } INT2CHAR ( iValue -- iResult ) 0x92 { iResult = iValue & 0xFFFF; } INT2SHORT ( iValue -- iResult ) 0x93 { iResult = (iValue << 16) >> 16; /* XXX: try "(s2)iValue" */ } LCMP ( lValue1 lValue2 -- iResult ) 0x94 { iResult = lValue1 < lValue2 ? -1 : lValue1 > lValue2 ? 1 : 0; } FCMPL ( fValue1 fValue2 -- iResult ) 0x95 { iResult = builtin_fcmpl(fValue1, fValue2); } FCMPG ( fValue1 fValue2 -- iResult ) 0x96 { iResult = builtin_fcmpg(fValue1, fValue2); } DCMPL ( dValue1 dValue2 -- iResult ) 0x97 { iResult = builtin_dcmpl(dValue1, dValue2); } DCMPG ( dValue1 dValue2 -- iResult ) 0x98 { iResult = builtin_dcmpg(dValue1, dValue2); } IFEQ ( #ainstTarget iValue -- ) 0x99 { if ( iValue == 0 ) { SET_IP(ainstTarget); INST_TAIL; } } IFNE ( #ainstTarget iValue -- ) 0x9a { if ( iValue != 0 ) { SET_IP(ainstTarget); INST_TAIL; } } IFLT ( #ainstTarget iValue -- ) 0x9b { if ( iValue < 0 ) SET_IP(ainstTarget); INST_TAIL; } IFGE ( #ainstTarget iValue -- ) 0x9c { if ( iValue >= 0 ) SET_IP(ainstTarget); INST_TAIL; } IFGT ( #ainstTarget iValue -- ) 0x9d { if ( iValue > 0 ) SET_IP(ainstTarget); INST_TAIL; } IFLE ( #ainstTarget iValue -- ) 0x9e { if ( iValue <= 0 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPEQ ( #ainstTarget iValue1 iValue2 -- ) 0x9f { if ( iValue1 == iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPNE ( #ainstTarget iValue1 iValue2 -- ) 0xa0 { if ( iValue1 != iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPLT ( #ainstTarget iValue1 iValue2 -- ) 0xa1 { if ( iValue1 < iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPGE ( #ainstTarget iValue1 iValue2 -- ) 0xa2 { if ( iValue1 >= iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPGT ( #ainstTarget iValue1 iValue2 -- ) 0xa3 { if ( iValue1 > iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ICMPLE ( #ainstTarget iValue1 iValue2 -- ) 0xa4 { if ( iValue1 <= iValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPEQ ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 == lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPNE ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 != lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPLT ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 < lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPGE ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 >= lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPGT ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 > lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_LCMPLE ( #ainstTarget lValue1 lValue2 -- ) opt { if ( lValue1 <= lValue2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ACMPEQ ( #ainstTarget aRef1 aRef2 -- ) 0xa5 { if ( aRef1 == aRef2 ) SET_IP(ainstTarget); INST_TAIL; } IF_ACMPNE ( #ainstTarget aRef1 aRef2 -- ) 0xa6 { if ( aRef1 != aRef2 ) SET_IP(ainstTarget); INST_TAIL; } GOTO ( #ainstTarget -- ) 0xa7 { SET_IP(ainstTarget); } JSR ( #ainstTarget -- ainstRA ) 0xa8 { /* Warning: The generator already adds 1 to the IP because there is an inline parameter. Thus, instead of writing IP + 1, we write...*/ ainstRA = IP; SET_IP(ainstTarget); } RET ( #bIndex -- ) 0xa9 { Inst * saved_ip; saved_ip = access_local_ref((unsigned) bIndex); SET_IP(saved_ip); } TABLESWITCH ( #iLow #iRange #addrSegment #iOffset #ainstDefault iIndex -- ) 0xaa { s4 idx = iIndex - iLow; if ( ((u4) idx) >= iRange ) { SET_IP(ainstDefault); INST_TAIL; } else { SET_IP(((Inst **)(addrSegment+iOffset))[idx]); INST_TAIL; } } LOOKUPSWITCH ( #iNpairs #addrSegment #iOffset #ainstDefault iKey -- ) 0xab { /* Note: This code should use a binary search, as the table is sorted. Note also, we reversed the order of the parms */ unsigned i; for ( i = 0; i < iNpairs; i++ ) { Cell *table = (Cell *)(addrSegment+iOffset); if ( iKey == (s4)(table[2 * i]) ) { SET_IP((Inst *)(table[2 * i + 1])); INST_TAIL; } } /* falls through if no match */ SET_IP(ainstDefault); } \ Our stack works like this: \ The stack holds locals, the operand stack and the return address stack. \ When we invoke a method, the paramaters are the top N elements \ on the stack. These become the first N local variables. \ Next we have space for the rest of the local variables. \ Next comes a single position on the stack which holds \ the value of the frame pointer for the calling function. \ Another position holds the instruction pointer of the caller. \ Finally, we have space for the elements of the operand stack. \ fp points to the bottom local; since the stack grows downwards, the \ upper end of the frame is fp+1, not fp. That's why the sp updates \ in the returns look like they are off by one. \ fp -> local0 \ local1 \ ... \ oldfp \ sp -> oldip ; at the start, empty stack \ stack0 ;once there is something on the stack \ ... IRETURN ( #iIndexFP vValue -- vResult ) 0xac { Inst *new_ip; new_ip = (Inst *)access_local_cell(iIndexFP + 1); sp = fp; fp = (Cell *)access_local_cell(iIndexFP); vResult = vValue; SET_IP(new_ip); } LRETURN ( #iIndexFP lValue -- lResult ) 0xad { Inst *new_ip = (Inst *)access_local_cell(iIndexFP + 1); sp = fp - 1; fp = (Cell *)access_local_cell(iIndexFP); lResult = lValue; SET_IP(new_ip); } RETURN ( #iIndexFP -- ) 0xb1 { Inst *new_ip; IF_spTOS(sp[0] = spTOS); new_ip = (Inst *)access_local_cell(iIndexFP + 1); sp = fp+1; fp = (Cell *)access_local_cell(iIndexFP); SET_IP(new_ip); IF_spTOS(spTOS = sp[0]); } GETSTATIC_CELL ( #addr #auf -- vResult ) opt { vResult = *(Cell *)addr; } GETSTATIC_INT ( #addr #auf -- iResult ) opt { iResult = *(s4 *)addr; } GETSTATIC_LONG ( #addr #auf -- lResult ) opt { lResult = *((s8 *) addr); } PUTSTATIC_CELL ( #addr #auf vValue -- ) opt { *((Cell *) addr) = vValue; } PUTSTATIC_INT ( #addr #auf iValue -- ) opt { *((s4 *) addr) = iValue; } PUTSTATIC_LONG ( #addr #auf lValue -- ) opt { *((s8 *) addr) = lValue; } GETFIELD_CELL ( #iOffset #auf aRef -- vResult ) opt { CHECK_NULL_PTR(aRef); vResult = *((Cell *) (((u1 *)aRef) + iOffset)); } GETFIELD_INT ( #iOffset #auf aRef -- iResult ) opt { CHECK_NULL_PTR(aRef); iResult = *((s4 *) (((u1 *)aRef) + iOffset)); } GETFIELD_LONG ( #iOffset #auf aRef -- lResult ) opt { CHECK_NULL_PTR(aRef); lResult = *((s8 *) (((u1 *)aRef) + iOffset)); } PUTFIELD_CELL ( #iOffset #auf aRef vValue -- ) opt { CHECK_NULL_PTR(aRef); *((Cell *) (((u1 *)aRef) + iOffset)) = vValue; } PUTFIELD_INT ( #iOffset #auf aRef iValue -- ) opt { CHECK_NULL_PTR(aRef); *((s4 *) (((u1 *)aRef) + iOffset)) = iValue; } PUTFIELD_LONG ( #iOffset #auf aRef lValue -- ) opt { CHECK_NULL_PTR(aRef); *((s8 *) (((u1 *)aRef) + iOffset)) = lValue; } \ !! called methods have the number of locals at offset -1. \ methods are always called indirectly through the codeptr in the stub \ (see createcompilerstub and TRANSLATE). INVOKEVIRTUAL ( #iOffset #iNargs #aum -- acelloldfp ainstoldip ) 0xd8 { java_objectheader *aRef = (java_objectheader *)(sp[iNargs + 1]); /* corrected for sp change by vmg */ char *v; Inst **stub; Inst *target; CHECK_NULL_PTR(aRef); v = (char *)(aRef->vftbl); stub = *(Inst ***)(v+iOffset); target = *stub; acelloldfp = fp; ainstoldip = IP; fp = sp + 1 + iNargs; sp = fp - MAXLOCALS(stub) - 1; SET_IP(target); } INVOKESTATIC ( #aaTarget #iNargs #aum -- acelloldfp ainstoldip ) 0xb8 /* an indirect pointer to target is passed to avoid references to uncompiled code */ { Inst *target = *aaTarget; acelloldfp = fp; ainstoldip = IP; /* sp is already updated by the generator, so we have to compensate for that */ fp = sp + 1 + iNargs; /* !! scale nargs at translation time */ sp = fp - MAXLOCALS(aaTarget) - 1; SET_IP(target); } INVOKESPECIAL ( #aaTarget #iNargs #aum -- acelloldfp ainstoldip ) 0xb7 /* an indirect pointer to target is passed to avoid references to uncompiled code */ { java_objectheader *aRef = (java_objectheader *)(sp[iNargs + 1]); /* corrected for sp change by vmg */ Inst *target = *aaTarget; CHECK_NULL_PTR(aRef); acelloldfp = fp; ainstoldip = IP; /* sp is already updated by the generator, so we have to compensate for that */ fp = sp + 1 + iNargs; /* !! scale nargs at translation time */ sp = fp - MAXLOCALS(aaTarget) - 1; SET_IP(target); } INVOKEINTERFACE ( #iInterfaceOffset #iOffset #iNargs #aum -- acelloldfp ainstoldip ) 0xd8 { java_objectheader *aRef; char *v, *t; Inst **stub; Inst *target; ; aRef = (java_objectheader *)sp[iNargs + 1]; CHECK_NULL_PTR(aRef); v = (char *)(aRef->vftbl); t = *(char **)(v + iInterfaceOffset); stub = *(Inst ***)(t+iOffset); target = *stub; acelloldfp = fp; ainstoldip = IP; fp = sp + 1 + iNargs; sp = fp - MAXLOCALS(stub) - 1; SET_IP(target); } \ the BUILTIN functions like NEW get their parameters on the stack \ instead of through immediate arguments. NEW ( aClass -- aRef ) 0xbb { /* fprintf(stderr,"new: class %lx, class-state=%d\n",(long)aClass,((Hjava_lang_Class*)aClass)->state); */ global_sp=sp; aRef = builtin_new((classinfo *)aClass); CLEAR_global_sp; } \ !! use a macro NEWARRAY_BOOLEAN ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_boolean(iSize); CLEAR_global_sp; NEWARRAY_CHAR ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_char(iSize); CLEAR_global_sp; NEWARRAY_FLOAT ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_float(iSize); CLEAR_global_sp; NEWARRAY_DOUBLE ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_double(iSize); CLEAR_global_sp; NEWARRAY_BYTE ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_byte(iSize); CLEAR_global_sp; NEWARRAY_SHORT ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_short(iSize); CLEAR_global_sp; NEWARRAY_INT ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_int(iSize); CLEAR_global_sp; NEWARRAY_LONG ( iSize -- aArray ) global_sp=sp; aArray = (java_arrayheader *)builtin_newarray_long(iSize); CLEAR_global_sp; NEWARRAY ( iSize avftbl -- aArray ) 0xbd { global_sp=sp; aArray = (java_arrayheader *)builtin_newarray(iSize, (vftbl_t *)avftbl); CLEAR_global_sp; } ARRAYLENGTH ( aArray -- iResult ) 0xbe { CHECK_NULL_PTR(aArray); iResult = length_array(aArray); } ATHROW ( aRef -- aRef1 ) 0xbf { Cell *new_sp, *new_fp; Inst *new_ip; CHECK_NULL_PTR(aRef); goto athrow; throw: CLEAR_global_sp; /* IF_spTOS(sp[0] = spTOS); */ aRef = *exceptionptr; *exceptionptr = NULL; athrow: new_ip = builtin_throw(IP,aRef,fp,&new_sp,&new_fp); if (new_ip==NULL) { /* !! sp = new_sp; ? */ IF_spTOS(sp[0] = spTOS); /* !! correct? even for superinstructions? */ global_sp = sp; SUPER_END; /* ATHROW may end a basic block */ return aRef; } SET_IP(new_ip); aRef1 = aRef; sp=new_sp-1; fp=new_fp; /* IF_spTOS(spTOS = sp[0]); */ } CHECKCAST ( #aClass #acr aRef -- aRef ) 0xc0 { if (!builtin_checkcast((java_objectheader *)aRef, (classinfo *)aClass)) THROW(classcastexception); } ARRAYCHECKCAST ( #avftbl #acr aRef -- aRef ) 0xc0 { if (!builtin_arraycheckcast(aRef, avftbl)) THROW(classcastexception); } INSTANCEOF ( #aClass #acr aRef -- iResult ) 0xc1 { iResult = builtin_instanceof(aRef, aClass); } ARRAYINSTANCEOF ( aRef avftbl -- iResult ) 0xc1 { iResult = builtin_arrayinstanceof(aRef, avftbl); } MONITORENTER ( aRef -- ) 0xc2 { #if defined(USE_THREADS) /* CHECK_NULL_PTR(aRef); is now done explicitly */ builtin_monitorenter(aRef); #endif } MONITOREXIT ( aRef -- ) 0xc3 { #if defined(USE_THREADS) /* CHECK_NULL_PTR(aRef); cannot happen */ builtin_monitorexit(aRef); #endif } CHECKNULL ( aRef -- aRef ) 0xc3 { CHECK_NULL_PTR(aRef); } MULTIANEWARRAY ( #avftbl #iSize #acr -- aRef ) 197 { long dims[iSize]; int i; IF_spTOS(sp[0] = spTOS); for (i=0; istate); */ global_sp=sp; aRef = builtin_new((classinfo *)aClass); CLEAR_global_sp; } PATCHER_NEW ( #aClass -- ) /* the unresolved class is in the cell right before this VM instruction; our code generator guarantees thus */ global_sp = sp; if (!patcher_builtin_new((u1 *)(IP-2))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-2] = INST_ADDR(NEW1); SET_IP(IP-2); NEWARRAY1 ( iSize #avftbl avftblUnused -- aRef ) 0xbd { global_sp=sp; aRef = (java_objectheader *)builtin_newarray(iSize, (vftbl_t *)avftbl); CLEAR_global_sp; } PATCHER_NEWARRAY ( #avftbl -- ) /* the unresolved class is in the cell right before this VM instruction; our code generator guarantees thus */ global_sp = sp; if (!patcher_builtin_newarray((u1 *)(IP-2))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-2] = INST_ADDR(NEWARRAY1); SET_IP(IP-2); PATCHER_MULTIANEWARRAY ( #avftbl #iSize #acr -- ) global_sp = sp; if (!patcher_builtin_multianewarray((u1 *)(IP-4))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-4] = INST_ADDR(MULTIANEWARRAY); SET_IP(IP-4); ARRAYINSTANCEOF1 ( aRef #avftbl avftblUnused -- iResult ) 0xc1 { iResult = builtin_arrayinstanceof(aRef, avftbl); } PATCHER_ARRAYINSTANCEOF ( #avftbl -- ) /* the unresolved class is in the cell right before this VM instruction; our code generator guarantees thus */ global_sp = sp; if (!patcher_builtin_newarray((u1 *)(IP-2))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-2] = INST_ADDR(ARRAYINSTANCEOF1); SET_IP(IP-2); PATCHER_INVOKESTATIC ( #aaTarget #iNargs #aum -- ) 0xd8 global_sp = sp; if (!patcher_invokestatic_special((u1 *)(IP-4))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-4] = INST_ADDR(INVOKESTATIC); SET_IP(IP-4); PATCHER_INVOKESPECIAL ( #aaTarget #iNargs #aum -- ) 0xd8 global_sp = sp; if (!patcher_invokestatic_special((u1 *)(IP-4))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-4] = INST_ADDR(INVOKESPECIAL); SET_IP(IP-4); PATCHER_INVOKEVIRTUAL ( #iOffset #iNargs #aum -- ) 0xd8 global_sp = sp; if (!patcher_invokevirtual((u1 *)(IP-4))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-4] = INST_ADDR(INVOKEVIRTUAL); SET_IP(IP-4); PATCHER_INVOKEINTERFACE ( #iInterfaceoffset #iOffset #iNargs #aum -- ) 0xd8 global_sp = sp; if (!patcher_invokeinterface((u1 *)(IP-5))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-5] = INST_ADDR(INVOKEINTERFACE); SET_IP(IP-5); PATCHER_CHECKCAST ( #aClass #acr -- ) 0xc global_sp = sp; if (!patcher_checkcast_instanceof((u1 *)(IP-3))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-3] = INST_ADDR(CHECKCAST); SET_IP(IP-3); PATCHER_ARRAYCHECKCAST ( #avftbl #acr -- ) 0xc0 global_sp = sp; if (!patcher_builtin_arraycheckcast((u1 *)(IP-3))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-3] = INST_ADDR(ARRAYCHECKCAST); SET_IP(IP-3); PATCHER_INSTANCEOF ( #aClass #acr -- ) 0xc1 global_sp = sp; if (!patcher_checkcast_instanceof((u1 *)(IP-3))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-3] = INST_ADDR(INSTANCEOF); SET_IP(IP-3); \ CHECKEXCEPTION could be integrated into NEW etc., but that's not as \ nice to maintain. CHECKEXCEPTION ( aRef -- aRef ) if (aRef==NULL) THROW0; \ This is stub code for methods that we have not yet translated. \ Initially, the code for each method is set to this stub. The \ first time the method is called, the code in the stub runs, which \ translates the bytecode, and replaces the stub with the threaded code. TRANSLATE ( #am -- ) { Inst *codeptr; ; IF_spTOS(sp[0] = spTOS); global_sp = sp; codeptr = (Inst *)jit_compile(am); CLEAR_global_sp; IP[-4] = codeptr; SET_IP(codeptr); } NATIVECALL ( #am #af acelloldfp ainstoldip -- ) sp = nativecall(af, am, sp); IF_spTOS(spTOS = sp[0]); fp = acelloldfp; SET_IP(ainstoldip); TRACENATIVECALL ( #am #af acelloldfp ainstoldip -- ) sp = nativecall(af, am, sp); IF_spTOS(spTOS = sp[0]); fp = acelloldfp; SET_IP(ainstoldip); { Cell v = spTOS; functionptr fp = codegen_findmethod((functionptr)(IP-1)); methodinfo *m = ((methodinfo **) fp)[-1]; float f; vm_Cell2f(v,f); builtin_displaymethodstop(m, (s8)v, f, f); } PATCHER_NATIVECALL ( #am #af -- ) #if !defined(ENABLE_STATICVM) global_sp = sp; if (!patcher_resolve_native((u1 *)(IP-3))) THROW0; CLEAR_global_sp; STORE_ORDER_BARRIER(); IP[-3] = runverbose ? INST_ADDR(TRACENATIVECALL) : INST_ADDR(NATIVECALL); SET_IP(IP-3); #else assert(false); #endif TRACECALL ( -- ) functionptr f = codegen_findmethod((functionptr)(IP-1)); methodinfo *m = ((methodinfo **) f)[-1]; builtin_trace_args( access_local_cell(0), access_local_cell(1), access_local_cell(2), access_local_cell(3), #if TRACE_ARGS_NUM > 4 access_local_cell(4), access_local_cell(5), #endif #if TRACE_ARGS_NUM == 8 access_local_cell(6), access_local_cell(7), #endif m); TRACERETURN ( v -- v ) functionptr fp = codegen_findmethod((functionptr)(IP-1)); methodinfo *m = ((methodinfo **) fp)[-1]; float f; vm_Cell2f(v,f); builtin_displaymethodstop(m, (s8)v, f, f); TRACELRETURN ( l -- l ) functionptr fp = codegen_findmethod((functionptr)(IP-1)); methodinfo *m = ((methodinfo **) fp)[-1]; Double_Store ds; ds.l = l; builtin_displaymethodstop(m, l, ds.d, ds.d); END ( -- ) IF_spTOS(sp[0] = spTOS); global_sp = sp; SUPER_END; vm_uncount_block(IP); /* undo the count part of SUPER_END, because there is no fallthrough */ return NULL; ICONST_ISTORE = ICONST ISTORE