\
\ 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" fieldinfo *"         single data-stack type-prefix afi
\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.
\ 
\ */

\ stack cache setup

\E register IPTOS Cell
\E register spTOS Cell

\E create IPregs IPTOS ,
\E create regs spTOS ,

\E IPregs 1 0 stack-state IPss1
\E regs 1 0 stack-state ss0

\ the first of these is the default state
\E state S0

\E ss0 data-stack S0 set-ss

\E IPss1 inst-stream S0 set-ss

\E data-stack to cache-stack
\E here 1 cache-states 2! s0 ,

\ !! the following should be automatic
\E S0 to state-default
\E state-default to state-in
\E state-default to state-out

\E s" codegendata *" threaded-code-pointer-type 2!

\ 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

ACONST1 ( #aRef #acrUnused -- aRef ) opt

PATCHER_ACONST ( #aRef #acr ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_aconst((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(ACONST1);
SET_IP(IP - 3);
patchersuper_rewrite(IP);

ILOAD ( #vLocal -- vResult ) 0x15
{
  vResult = access_local_int(vLocal);
}

LLOAD ( #vLocal -- lResult ) 0x16
{
  vm_twoCell2l(access_local_cell(vLocal), access_local_cell(vLocal-SIZEOF_VOID_P), lResult);
}

ALOAD ( #vLocal -- aRef ) 0x19
{
  aRef = (java_objectheader *)access_local_cell(vLocal);
}

IALOAD ( aArray iIndex -- vResult ) 0x2e
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  vResult = access_array_int(aArray, iIndex);
}

FALOAD ( aArray iIndex -- fResult ) 0x2e
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  fResult = access_array_float(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 -- vResult ) 0x33
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  vResult = access_array_byte(aArray, iIndex);
}

CALOAD ( aArray iIndex -- vResult ) 0x34
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  vResult = access_array_char(aArray, iIndex);
}

SALOAD ( aArray iIndex -- vResult ) 0x35
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  vResult = access_array_short(aArray, iIndex);
}

ISTORE ( #vLocal vValue -- ) 0x36
{
  access_local_int(vLocal) = vValue;
}

LSTORE ( #vLocal lValue -- ) 0x37
{
  vm_l2twoCell(lValue, access_local_cell(vLocal), access_local_cell(vLocal-SIZEOF_VOID_P));
}

ASTORE ( #vLocal aRef -- ) 0x3a
{
  access_local_cell(vLocal) = (Cell)aRef;
}

IASTORE ( aArray iIndex iValue -- ) 0x4f
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  access_array_int(aArray, iIndex) = iValue;
}

FASTORE ( aArray iIndex fValue -- ) 0x4f
{
  CHECK_NULL_PTR(aArray);
  CHECK_OUT_OF_BOUNDS(aArray, iIndex);
  ;
  access_array_float(aArray, iIndex) = fValue;
}

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 ( vValue1 vValue2 -- vResult ) 0x60
{
  vResult = vValue2 + vValue1;
}

LADD ( lValue1 lValue2 -- lResult ) 0x61
{
  lResult = lValue2 + lValue1;
}

FADD ( fValue1 fValue2 -- fResult ) 0x62
{
  fResult = fValue1 + fValue2;
}

DADD ( dValue1 dValue2 -- dResult ) 0x63
{
  dResult = dValue1 + dValue2;
}

ISUB ( vValue1 vValue2 -- vResult ) 0x64
{
  vResult = vValue1 - vValue2;
}

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 ( vValue1 vValue2 -- vResult ) 0x68
{
  vResult = vValue1 * vValue2;
}

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);
  ;
  if (iValue1 == 0x80000000 && iValue2 == -1)
    iResult=iValue1;
  else
    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);
  if (lValue1 == 0x8000000000000000LL && lValue2 == -1)
    lResult=lValue1;
  else
    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);
  if (iValue1 == 0x80000000 && iValue2 == -1)
    iResult=0;
  else
    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);
  if (lValue1 == 0x8000000000000000LL && lValue2 == -1)
    lResult=0;
  else
    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 ( vValue -- vResult ) 0x74
{
  vResult = -vValue;
}

LNEG ( lValue -- lResult ) 0x75
{
  lResult = -lValue;
}

FNEG ( fValue -- fResult ) 0x76
{
  fResult = -fValue;
}

DNEG ( dValue -- dResult ) 0x77
{
  dResult = -dValue;
}

ISHL ( vValue1 vValue2 -- vResult ) 0x78
{
  vResult = vValue1 << (vValue2 & 31);
}

LSHL ( lValue1 vValue2 -- lResult ) 0x79
{
  lResult = lValue1 << (vValue2 & 63);
}

ISHR ( iValue1 iValue2 -- iResult ) 0x7a
{
  iResult = iValue1 >> (iValue2 & 31);
}

LSHR ( lValue1 iValue2 -- lResult ) 0x7b
{
  lResult = lValue1 >> (iValue2 & 63);
}

IUSHR ( iValue1 vValue2 -- iResult ) 0x7c
{
  iResult = ((unsigned) iValue1) >> (vValue2 & 31);
}

LUSHR ( lValue1 vValue2 -- lResult ) 0x7d
{
  lResult = (unsigned long long) lValue1 >> (vValue2 & 63);
}

IAND ( vValue1 vValue2 -- vResult ) 0x7e
{
  vResult = vValue1 & vValue2;
}

LAND ( lValue1 lValue2 -- lResult ) 0x7f
{
  lResult = lValue1 & lValue2;
}

IOR ( vValue1 vValue2 -- vResult ) 0x80
{
  vResult = vValue1 | vValue2;
}

LOR ( lValue1 lValue2 -- lResult ) 0x81
{
  lResult = lValue1 | lValue2;
}

IXOR ( vValue1 vValue2 -- vResult ) 0x82
{
  vResult = vValue1 ^ vValue2;
}

LXOR ( lValue1 lValue2 -- lResult ) 0x83
{
  lResult = lValue1 ^ lValue2;
}

IINC ( #vOffset #iConst  -- ) 0x84
{
  access_local_int(vOffset) += 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 -- vResult ) 0x94
{
  vResult = lValue1 < lValue2 ? -1 : lValue1 > lValue2 ? 1 : 0;
}

FCMPL ( fValue1 fValue2 -- vResult ) 0x95
{
  vResult = builtin_fcmpl(fValue1, fValue2);
}

FCMPG ( fValue1 fValue2 -- vResult ) 0x96
{
  vResult = 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);
  }
}

IFNE ( #ainstTarget iValue -- ) 0x9a
{
  if ( iValue != 0 ) {
    SET_IP(ainstTarget);
  }
}

IFLT ( #ainstTarget iValue -- ) 0x9b
{
  if ( iValue < 0 )
    SET_IP(ainstTarget);
}

IFGE ( #ainstTarget iValue -- ) 0x9c
{
  if ( iValue >= 0 )
    SET_IP(ainstTarget);
}

IFGT ( #ainstTarget iValue -- ) 0x9d
{
  if ( iValue > 0 )
    SET_IP(ainstTarget);
}

IFLE ( #ainstTarget iValue -- ) 0x9e
{
  if ( iValue <= 0 )
    SET_IP(ainstTarget);
}

IF_ICMPEQ ( #ainstTarget iValue1 iValue2 -- ) 0x9f
{
  if ( iValue1 == iValue2 )
    SET_IP(ainstTarget);
}

IF_ICMPNE  ( #ainstTarget iValue1 iValue2 -- ) 0xa0
{
  if ( iValue1 != iValue2 )
    SET_IP(ainstTarget);
}

IF_ICMPLT  ( #ainstTarget iValue1 iValue2 -- ) 0xa1
{
  if ( iValue1 < iValue2 )
    SET_IP(ainstTarget);
}

IF_ICMPGE  ( #ainstTarget iValue1 iValue2 -- ) 0xa2
{
  if ( iValue1 >= iValue2 )
    SET_IP(ainstTarget);
}

IF_ICMPGT  ( #ainstTarget iValue1 iValue2 -- ) 0xa3
{
  if ( iValue1 > iValue2 )
    SET_IP(ainstTarget);
}

IF_ICMPLE  ( #ainstTarget iValue1 iValue2 -- ) 0xa4
{
  if ( iValue1 <= iValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPEQ ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 == lValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPNE  ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 != lValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPLT  ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 < lValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPGE  ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 >= lValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPGT  ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 > lValue2 )
    SET_IP(ainstTarget);
}

IF_LCMPLE  ( #ainstTarget lValue1 lValue2 -- ) opt
{
  if ( lValue1 <= lValue2 )
    SET_IP(ainstTarget);
}

IF_ACMPEQ  ( #ainstTarget aRef1 aRef2 -- ) 0xa5
{
  if ( aRef1 == aRef2 )
    SET_IP(ainstTarget);
}

IF_ACMPNE  ( #ainstTarget aRef1 aRef2 -- ) 0xa6
{
  if ( aRef1 != aRef2 )
    SET_IP(ainstTarget);
}

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 ( #vOffset -- ) 0xa9
{
  Inst * saved_ip;
  saved_ip = access_local_ref(vOffset);
  SET_IP(saved_ip);
}

TABLESWITCH ( #iLow #iRange #addrSegment #iOffset #ainstDefault iIndex -- ) 0xaa
{
  s4 idx = iIndex - iLow;
  if ( ((u4) idx) >= iRange ) {
    SET_IP(ainstDefault);
  }
  else {
    SET_IP(((Inst **)(addrSegment+iOffset))[idx]);
  }
}

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 */
  int i;

  i = iNpairs;

  while (--i >= 0) {
    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 ( #vOffsetFP ... vValue -- vResult ) 0xac
{
  Inst *new_ip;
  MAYBE_UNUSED Cell *currentsp = sp;
  new_ip = (Inst *) access_local_cell(vOffsetFP - SIZEOF_VOID_P);
  sp = fp + 1;
  fp = (Cell *) access_local_cell(vOffsetFP);
  CLEARSTACK(currentsp - 6, sp - 1);
  vResult = vValue;
  SET_IP(new_ip);
}

LRETURN ( #vOffsetFP ... lValue -- lResult ) 0xad
{
  Inst *new_ip;
  MAYBE_UNUSED Cell *currentsp = sp;
  new_ip = (Inst *) access_local_cell(vOffsetFP - SIZEOF_VOID_P);
  sp = fp + 1;
  fp = (Cell *) access_local_cell(vOffsetFP);
  CLEARSTACK(currentsp - 7, sp - 2);
  lResult = lValue;
  SET_IP(new_ip);
}

RETURN ( #vOffsetFP ... -- ) 0xb1
{
  Inst *new_ip;
  MAYBE_UNUSED Cell *currentsp = sp;
  new_ip = (Inst *) access_local_cell(vOffsetFP - SIZEOF_VOID_P);
  sp = fp + 1;
  fp = (Cell *) access_local_cell(vOffsetFP);
  CLEARSTACK(currentsp - 5, sp - 1);
  SET_IP(new_ip);
}

GETSTATIC_CELL ( #addr #afi -- vResult ) opt
{
  vResult = *(Cell *)addr;
}

GETSTATIC_INT ( #addr #afi -- iResult ) opt
{
  iResult = *(s4 *)addr;
}

GETSTATIC_FLOAT ( #addr #afi -- fResult ) opt
{
  fResult = *(float *)addr;
}

GETSTATIC_LONG ( #addr #afi -- lResult ) opt
{
  lResult = *((s8 *) addr);
}

PUTSTATIC_CELL ( #addr #afi vValue -- ) opt
{
  *((Cell *) addr) = vValue;
}

PUTSTATIC_INT ( #addr #afi iValue -- ) opt
{
  *((s4 *) addr) = iValue;
}

PUTSTATIC_FLOAT ( #addr #afi fValue -- ) opt
{
  *((float *) addr) = fValue;
}

PUTSTATIC_LONG ( #addr #afi lValue -- ) opt
{
  *((s8 *) addr) = lValue;
}

GETFIELD_CELL ( #vOffset #afi aRef -- vResult ) opt
{
  CHECK_NULL_PTR(aRef);
  vResult = *((Cell *) (((u1 *)aRef) + vOffset));
}

GETFIELD_INT ( #vOffset #afi aRef -- iResult ) opt
{
  CHECK_NULL_PTR(aRef);
  iResult = *((s4 *) (((u1 *)aRef) + vOffset));
}

GETFIELD_FLOAT ( #vOffset #afi aRef -- fResult ) opt
{
  CHECK_NULL_PTR(aRef);
  fResult = *((float *) (((u1 *) aRef) + vOffset));
}

GETFIELD_LONG ( #vOffset #afi aRef -- lResult ) opt
{
  CHECK_NULL_PTR(aRef);
  lResult = *((s8 *) (((u1 *)aRef) + vOffset));
}


PUTFIELD_CELL ( #vOffset #afi aRef vValue -- ) opt
{
  CHECK_NULL_PTR(aRef);
  *((Cell *) (((u1 *)aRef) + vOffset)) = vValue;
}

PUTFIELD_INT ( #vOffset #afi aRef iValue -- ) opt
{
  CHECK_NULL_PTR(aRef);
  *((s4 *) (((u1 *)aRef) + vOffset)) = iValue;
}

PUTFIELD_FLOAT ( #vOffset #afi aRef fValue -- ) opt
{
  CHECK_NULL_PTR(aRef);
  *((float *) (((u1 *)aRef) + vOffset)) = fValue;
}

PUTFIELD_LONG ( #vOffset #afi aRef lValue -- ) opt
{
  CHECK_NULL_PTR(aRef);
  *((s8 *) (((u1 *)aRef) + vOffset)) = 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 ( #vOffset #iNargs #am ... -- ... acelloldfp ainstoldip ) 0xd8
{
  java_objectheader *aRef = (java_objectheader *)(sp[iNargs - 1]);
  char *v;
  Inst **stub;
  Inst *target;
  CHECK_NULL_PTR(aRef);
  v = (char *)(aRef->vftbl);
  stub = *(Inst ***)(v+vOffset);
  target = *stub;
  acelloldfp = fp;
  ainstoldip = IP;
  fp = sp - 1 + iNargs;
  sp = fp - FRAMESIZE(stub) + 1;
  SET_IP(target);
}

INVOKESTATIC ( #aaTarget #iNargs #am ... -- ... acelloldfp ainstoldip ) 0xb8
/* an indirect pointer to target is passed to avoid references to uncompiled code */
{
  Inst *target = *aaTarget;
  acelloldfp = fp;
  ainstoldip = IP;
  fp = sp - 1 + iNargs; /* !! scale nargs at translation time */
  sp = fp - FRAMESIZE(aaTarget) + 1;
  SET_IP(target);
}

INVOKESPECIAL ( #aaTarget #iNargs #am ... -- ... acelloldfp ainstoldip ) 0xb7
/* an indirect pointer to target is passed to avoid references to uncompiled code */
{
  java_objectheader *aRef = (java_objectheader *)(sp[iNargs - 1]);
  Inst *target = *aaTarget;
  CHECK_NULL_PTR(aRef);
  acelloldfp = fp;
  ainstoldip = IP;
  fp = sp - 1 + iNargs; /* !! scale nargs at translation time */
  sp = fp - FRAMESIZE(aaTarget) + 1;
  SET_IP(target);
}

INVOKEINTERFACE ( #iInterfaceOffset #vOffset #iNargs #am ... -- ... 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+vOffset);
  target = *stub;
  acelloldfp = fp;
  ainstoldip = IP;
  fp = sp - 1 + iNargs;
  sp = fp - FRAMESIZE(stub) + 1;
  SET_IP(target);
}

\ the BUILTIN functions like NEW get their parameters on the stack
\ instead of through immediate arguments.

\ !! do we need to synchronize the stack here?
\ !! if so, is global_sp right?

NEW ( ... aClass -- ... aRef ) 0xbb
{
  global_sp = sp;
  aRef = builtin_new((classinfo *) aClass);
  if (aRef == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

\ !! use a macro

NEWARRAY_BOOLEAN ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_boolean(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_BYTE ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_byte(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_CHAR ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_char(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_SHORT ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_short(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_INT ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_int(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_LONG ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_long(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_FLOAT ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_float(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY_DOUBLE ( ... iSize -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray_double(iSize);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

NEWARRAY ( ... iSize aClass -- ... aArray )
{
  global_sp = sp;
  aArray = (java_arrayheader *) builtin_newarray(iSize, aClass);
  if (aArray == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
}

ARRAYLENGTH ( aArray -- iResult )
{
  CHECK_NULL_PTR(aArray);
  iResult = length_array(aArray);
}

ATHROW ( ... aRef -- ... aRef1 )
{
  Cell *new_sp, *new_fp;
  Inst *new_ip;
  CHECK_NULL_PTR(aRef);
  goto athrow;
throw:
  CLEAR_global_sp;
  aRef = *exceptionptr;
  *exceptionptr = NULL;
athrow:
  new_ip = intrp_asm_handle_exception(IP, aRef, fp, &new_sp, &new_fp);
  if (new_ip == NULL) {
    /* !! sp = new_sp; ? */
    global_sp = sp - 1;
    SUPER_END; /* ATHROW may end a basic block */
    return aRef;
  }
  SET_IP(new_ip);
  aRef1 = aRef;
  sp = new_sp;
  fp = new_fp;
}

CHECKCAST ( #aClass #acr aRef -- aRef ) 0xc0
{
  if (!builtin_checkcast(aRef, (classinfo *) aClass))
    THROW_CLASSCASTEXCEPTION(aRef);
}

ARRAYCHECKCAST ( #aClass #acr aRef -- aRef ) 0xc0
{
  if (!builtin_arraycheckcast(aRef, aClass))
    THROW_CLASSCASTEXCEPTION(aRef);
}

PATCHER_ARRAYCHECKCAST ( #avftbl #acr ... -- ) 0xc0
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_builtin_arraycheckcast((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(ARRAYCHECKCAST);
SET_IP(IP - 3);
patchersuper_rewrite(IP);

INSTANCEOF ( #aClass #acr aRef  -- iResult )
{
  iResult = builtin_instanceof(aRef, aClass);
}

ARRAYINSTANCEOF ( aRef aClass  -- iResult )
{
  iResult = builtin_arrayinstanceof(aRef, aClass);
}

MONITORENTER ( aRef -- )
{
#if defined(ENABLE_THREADS)
  /* CHECK_NULL_PTR(aRef); is now done explicitly inside lock_monitor_enter */
  global_sp = sp;
  if (!lock_monitor_enter(aRef))
	THROW0;
  CLEAR_global_sp;
#endif
}

MONITOREXIT ( aRef -- )
{
#if defined(ENABLE_THREADS)
  global_sp = sp;
  if (!lock_monitor_exit(aRef))
	THROW0;
  CLEAR_global_sp;
#endif
}

CHECKNULL ( aRef -- aRef )
{
  CHECK_NULL_PTR(aRef);
}

MULTIANEWARRAY ( #aClass #iSize #acr ... -- aRef )
{
  long *dims;
  int i;

  dims = MNEW(long, iSize);

  for (i = 0; i < iSize; i++) {
    dims[i] = sp[iSize - i - 1];
  }
  global_sp = sp;
  aRef = (java_objectheader *) builtin_multianewarray(iSize, aClass, dims);

  MFREE(dims, long, iSize);

  if (aRef == NULL) {
    global_sp = sp;
    *exceptionptr = stacktrace_inline_fillInStackTrace(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP);
    CLEAR_global_sp;
    THROW0;
  }
  CLEAR_global_sp;
  sp += iSize;
}

IFNULL ( #ainstTarget aRef -- )  0xc6
{
  if ( aRef == NULL ) {
    SET_IP(ainstTarget);
  }
}

IFNONNULL ( #ainstTarget aRef -- ) 0xc7
{
  if ( aRef != NULL ) {
    SET_IP(ainstTarget);
  }
}

\ patchers for resolving fields

PATCHER_GETSTATIC_INT ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_FLOAT ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_LONG ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_CELL ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ patchers for statically initializing classes

PATCHER_GETSTATIC_CLINIT_INT ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_CLINIT_FLOAT ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_CLINIT_LONG ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETSTATIC_CLINIT_CELL ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETSTATIC_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ patchers for resolving fields

PATCHER_PUTSTATIC_INT ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_FLOAT ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_LONG ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_CELL ( #aRef #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ patchers for statically initializing classes

PATCHER_PUTSTATIC_CLINIT_INT ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_CLINIT_FLOAT ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_CLINIT_LONG ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTSTATIC_CLINIT_CELL ( #aRef #afi ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putstatic_clinit((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTSTATIC_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ patchers for resolving fields

PATCHER_GETFIELD_INT ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETFIELD_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETFIELD_FLOAT ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETFIELD_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETFIELD_LONG ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETFIELD_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_GETFIELD_CELL ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(GETFIELD_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTFIELD_INT ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTFIELD_INT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTFIELD_FLOAT ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *)(IP-3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTFIELD_FLOAT);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTFIELD_LONG ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTFIELD_LONG);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_PUTFIELD_CELL ( #vOffset #auf ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_get_putfield((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(PUTFIELD_CELL);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ other patchers for lazy resolving

PATCHER_MULTIANEWARRAY ( #aClass #iSize #acr ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_builtin_multianewarray((u1 *) (IP - 4));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-4] = INST_ADDR(MULTIANEWARRAY);
SET_IP(IP - 4);
patchersuper_rewrite(IP);

PATCHER_INVOKESTATIC ( #aaTarget #iNargs #aum ... -- ) 0xd8
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_invokestatic_special((u1 *) (IP - 4));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-4] = INST_ADDR(INVOKESTATIC);
SET_IP(IP-4);
patchersuper_rewrite(IP);

PATCHER_INVOKESPECIAL ( #aaTarget #iNargs #aum ... -- ) 0xd8
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_invokestatic_special((u1 *) (IP - 4));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-4] = INST_ADDR(INVOKESPECIAL);
SET_IP(IP-4);
patchersuper_rewrite(IP);

PATCHER_INVOKEVIRTUAL ( #vOffset #iNargs #aum ... -- ) 0xd8
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_invokevirtual((u1 *) (IP - 4));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-4] = INST_ADDR(INVOKEVIRTUAL);
SET_IP(IP-4);
patchersuper_rewrite(IP);

PATCHER_INVOKEINTERFACE ( #iInterfaceoffset #vOffset #iNargs #aum ... -- ) 0xd8
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_invokeinterface((u1 *) (IP - 5));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-5] = INST_ADDR(INVOKEINTERFACE);
SET_IP(IP-5);
patchersuper_rewrite(IP);

PATCHER_CHECKCAST ( #aClass #acr ... -- ) 0xc
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_checkcast_instanceof((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(CHECKCAST);
SET_IP(IP-3);
patchersuper_rewrite(IP);

PATCHER_INSTANCEOF ( #aClass #acr ... -- ) 0xc1
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_checkcast_instanceof((u1 *) (IP - 3));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-3] = INST_ADDR(INSTANCEOF);
SET_IP(IP-3);
patchersuper_rewrite(IP);

\ 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;
  stackframeinfo sfi;
  Cell *acelloldfp;
  Inst *ainstoldip;
  ;
  vm_Cell2acell(sp[1],acelloldfp);
  vm_Cell2ainst(sp[0],ainstoldip);
  global_sp = sp;
  stacktrace_create_extern_stackframeinfo(&sfi, NULL, (u1 *) acelloldfp, (u1 *) ainstoldip, (u1 *) ainstoldip);
  codeptr = (Inst *) jit_compile(am);
  stacktrace_remove_stackframeinfo(&sfi);
  if (codeptr == NULL) {
    fp = acelloldfp;
    SET_IP(ainstoldip); /* set up ip and fp for throw */
    THROW0;
  }
  CLEAR_global_sp;
  IP[-4] = codeptr;
  SET_IP(codeptr);
}

NATIVECALL ( #am #af #addrcif ... acelloldfp ainstoldip -- )
sp = nativecall(af, am, sp, ainstoldip, acelloldfp, addrcif);
fp = acelloldfp;
SET_IP(ainstoldip);
if (*exceptionptr)
  THROW0;


TRACENATIVECALL ( #am #af #addrcif ... acelloldfp ainstoldip -- )
sp = nativecall(af, am, sp, ainstoldip, acelloldfp, addrcif);
fp = acelloldfp;
SET_IP(ainstoldip);
{
Cell v = sp[0];
float f;
vm_Cell2f(v,f);
#if !defined(NDEBUG)
builtin_verbosecall_exit((s8) v, f, f, am);
#endif
}
if (*exceptionptr)
  THROW0;

PATCHER_NATIVECALL ( #am #af #addrcif ... -- )
stackframeinfo sfi;
bool result;
global_sp = sp;
stacktrace_create_stackframeinfo(&sfi, NULL, (u1 *) fp, (u1 *) IP);
result = intrp_patcher_resolve_native((u1 *) (IP - 4));
stacktrace_remove_stackframeinfo(&sfi);
CLEAR_global_sp;
if (!result)
  THROW0;
STORE_ORDER_BARRIER();
IP[-4] = opt_verbosecall ? INST_ADDR(TRACENATIVECALL) : INST_ADDR(NATIVECALL);
SET_IP(IP - 4);
patchersuper_rewrite(IP);

TRACECALL ( #am -- )
#if !defined(NDEBUG)
#if SIZEOF_VOID_P == 4
{
    s8 args[TRACE_ARGS_NUM];
    int i;
    int ofs = 0;
    typedesc *params = am->parseddesc->paramtypes;
    for (i=0; i < TRACE_ARGS_NUM; ++i, ++params) {
        if (IS_2_WORD_TYPE(params->type)) {
            vm_twoCell2l(access_local_cell(ofs), access_local_cell(ofs - SIZEOF_VOID_P), args[i]);
            ofs -= 2*SIZEOF_VOID_P;
        }
        else {
            args[i] = access_local_cell(ofs);
            ofs -= SIZEOF_VOID_P;
        }
    }
    builtin_verbosecall_enter(args[0], args[1], args[2], args[3],  
#if TRACE_ARGS_NUM > 4
                              args[4], args[5],
#endif
#if TRACE_ARGS_NUM == 8
                              args[6], args[7],
#endif
                              am);
}
#else /* SIZEOF_VOID_P */
    builtin_verbosecall_enter(
        access_local_cell(0 * -SIZEOF_VOID_P),
        access_local_cell(1 * -SIZEOF_VOID_P),
        access_local_cell(2 * -SIZEOF_VOID_P),
        access_local_cell(3 * -SIZEOF_VOID_P),
#if TRACE_ARGS_NUM > 4
        access_local_cell(4 * -SIZEOF_VOID_P),
        access_local_cell(5 * -SIZEOF_VOID_P),
#endif
#if TRACE_ARGS_NUM == 8
        access_local_cell(6 * -SIZEOF_VOID_P),
        access_local_cell(7 * -SIZEOF_VOID_P),
#endif
        am);
#endif /* SIZEOF_VOID_P */
#endif /* !defined(NDEBUG) */

TRACERETURN ( #am v -- v )
float f;
vm_Cell2f(v,f);
#if !defined(NDEBUG)
builtin_verbosecall_exit((s8) v, f, f, am);
#endif

TRACELRETURN ( #am l -- l )
Double_Store ds;
ds.l = l;
#if !defined(NDEBUG)
builtin_verbosecall_exit(l, ds.d, ds.d, am);
#endif

END ( ... -- )
global_sp = sp;
SUPER_END;
vm_uncount_block(IP); /* undo the count part of SUPER_END,
			 because there is no fallthrough */
return NULL;

_ALOAD_GETFIELD_CELL_ =  ALOAD GETFIELD_CELL 
_DUP_ICONST_ =  DUP ICONST 
_ALOAD_ALOAD_ =  ALOAD ALOAD 
_ICONST_ICONST_ =  ICONST ICONST 
_DUP_ICONST_ICONST_ =  DUP ICONST ICONST 
_ICONST_CASTORE_ =  ICONST CASTORE 
_ICONST_ICONST_CASTORE_ =  ICONST ICONST CASTORE 
_DUP_ICONST_ICONST_CASTORE_ =  DUP ICONST ICONST CASTORE 
_CASTORE_DUP_ =  CASTORE DUP 
_CASTORE_DUP_ICONST_ =  CASTORE DUP ICONST 
_DUP_ICONST_ICONST_CASTORE_DUP_ =  DUP ICONST ICONST CASTORE DUP 
_ICONST_CASTORE_DUP_ =  ICONST CASTORE DUP 
_ICONST_CASTORE_DUP_ICONST_ =  ICONST CASTORE DUP ICONST 
_ICONST_ICONST_CASTORE_DUP_ =  ICONST ICONST CASTORE DUP 
_ICONST_ICONST_CASTORE_DUP_ICONST_ =  ICONST ICONST CASTORE DUP ICONST 
_CASTORE_DUP_ICONST_ICONST_ =  CASTORE DUP ICONST ICONST 
_CASTORE_DUP_ICONST_ICONST_CASTORE_ =  CASTORE DUP ICONST ICONST CASTORE 
_ICONST_CASTORE_DUP_ICONST_ICONST_ =  ICONST CASTORE DUP ICONST ICONST 
_ASTORE_ALOAD_ =  ASTORE ALOAD 
_ALOAD_GETFIELD_INT_ =  ALOAD GETFIELD_INT 
_ALOAD_ILOAD_ =  ALOAD ILOAD 
_ALOAD_ACONST_ =  ALOAD ACONST 
_ICONST_ACONST_ =  ICONST ACONST 
_AASTORE_DUP_ =  AASTORE DUP 
_AASTORE_DUP_ICONST_ =  AASTORE DUP ICONST 
_ALOAD_ICONST_ =  ALOAD ICONST 
_ILOAD_ICONST_ =  ILOAD ICONST 
_ILOAD_ILOAD_ =  ILOAD ILOAD 
_DUP_ICONST_ACONST_ =  DUP ICONST ACONST 
_GETFIELD_CELL_ALOAD_ =  GETFIELD_CELL ALOAD 
_AASTORE_DUP_ICONST_ACONST_ =  AASTORE DUP ICONST ACONST 
_ACONST_AASTORE_ =  ACONST AASTORE 
_ICONST_ACONST_AASTORE_ =  ICONST ACONST AASTORE 
_DUP_ICONST_ACONST_AASTORE_ =  DUP ICONST ACONST AASTORE 
_ALOAD_GETFIELD_CELL_ALOAD_ =  ALOAD GETFIELD_CELL ALOAD 
_ACONST_AASTORE_DUP_ =  ACONST AASTORE DUP 
_ACONST_AASTORE_DUP_ICONST_ =  ACONST AASTORE DUP ICONST 
_DUP_ICONST_ACONST_AASTORE_DUP_ =  DUP ICONST ACONST AASTORE DUP 
_ICONST_ACONST_AASTORE_DUP_ =  ICONST ACONST AASTORE DUP 
_ICONST_ACONST_AASTORE_DUP_ICONST_ =  ICONST ACONST AASTORE DUP ICONST 
_AASTORE_DUP_ICONST_ACONST_AASTORE_ =  AASTORE DUP ICONST ACONST AASTORE 
_DUP_ACONST_ =  DUP ACONST 
_ACONST_AASTORE_DUP_ICONST_ACONST_ =  ACONST AASTORE DUP ICONST ACONST 
_PUTFIELD_CELL_ALOAD_ =  PUTFIELD_CELL ALOAD 
_ILOAD_ALOAD_ =  ILOAD ALOAD 
_ICONST_ALOAD_ =  ICONST ALOAD 
_DUP_ALOAD_ =  DUP ALOAD 
_ICONST_ISTORE_ =  ICONST ISTORE 
_ASTORE_ALOAD_ALOAD_ =  ASTORE ALOAD ALOAD 
_ALOAD_PUTFIELD_CELL_ =  ALOAD PUTFIELD_CELL 
_PUTFIELD_INT_ALOAD_ =  PUTFIELD_INT ALOAD 
_ALOAD_ALOAD_PUTFIELD_CELL_ =  ALOAD ALOAD PUTFIELD_CELL 
_ICONST_IADD_ =  ICONST IADD 
_ISTORE_GOTO_ =  ISTORE GOTO 
_ACONST_ACONST_ =  ACONST ACONST 
_POP_ALOAD_ =  POP ALOAD 
_ACONST_ICONST_ =  ACONST ICONST 
_ALOAD_ALOAD_ALOAD_ =  ALOAD ALOAD ALOAD 
_ALOAD_ALOAD_GETFIELD_CELL_ =  ALOAD ALOAD GETFIELD_CELL 
_ISTORE_ALOAD_ =  ISTORE ALOAD 
_GETFIELD_CELL_ILOAD_ =  GETFIELD_CELL ILOAD 
_IINC_ILOAD_ =  IINC ILOAD 
_ISTORE_ILOAD_ =  ISTORE ILOAD 
_ALOAD_GETFIELD_CELL_ILOAD_ =  ALOAD GETFIELD_CELL ILOAD 
_ILOAD_ICONST_IADD_ =  ILOAD ICONST IADD 
_CHECKCAST_ASTORE_ =  CHECKCAST ASTORE 
_ASTORE_ACONST_ =  ASTORE ACONST 
_GETFIELD_INT_ALOAD_ =  GETFIELD_INT ALOAD 
_ACONST_ALOAD_ =  ACONST ALOAD 
_ICONST_ISTORE_GOTO_ =  ICONST ISTORE GOTO 
_CHECKCAST_ASTORE_ALOAD_ =  CHECKCAST ASTORE ALOAD 
_ASTORE_GOTO_ =  ASTORE GOTO 
_ALOAD_PUTFIELD_CELL_ALOAD_ =  ALOAD PUTFIELD_CELL ALOAD 
_ALOAD_ALOAD_PUTFIELD_CELL_ALOAD_ =  ALOAD ALOAD PUTFIELD_CELL ALOAD 
_ICONST_PUTFIELD_INT_ =  ICONST PUTFIELD_INT 
_ALOAD_IFNULL_ =  ALOAD IFNULL 
_ALOAD_IFNONNULL_ =  ALOAD IFNONNULL 
_ALOAD_ICONST_PUTFIELD_INT_ =  ALOAD ICONST PUTFIELD_INT 
_ALOAD_GETFIELD_INT_ALOAD_ =  ALOAD GETFIELD_INT ALOAD 
_ALOAD_ILOAD_ILOAD_ =  ALOAD ILOAD ILOAD 
_DUP_ICONST_ALOAD_ =  DUP ICONST ALOAD 
_IADD_ILOAD_ =  IADD ILOAD 
_GETFIELD_CELL_ICONST_ =  GETFIELD_CELL ICONST 
_ILOAD_PUTFIELD_INT_ =  ILOAD PUTFIELD_INT 
_GETFIELD_INT_ALOAD_GETFIELD_INT_ =  GETFIELD_INT ALOAD GETFIELD_INT 
_GETFIELD_CELL_GETFIELD_CELL_ =  GETFIELD_CELL GETFIELD_CELL 
_ALOAD_ACONST_ACONST_ =  ALOAD ACONST ACONST 
_ALOAD_ARRAYLENGTH_ =  ALOAD ARRAYLENGTH 
_GETFIELD_CELL_IFNULL_ =  GETFIELD_CELL IFNULL 
_ICONST_ISUB_ =  ICONST ISUB 
_ALOAD_ILOAD_PUTFIELD_INT_ =  ALOAD ILOAD PUTFIELD_INT 
_ALOAD_GETFIELD_CELL_GETFIELD_CELL_ =  ALOAD GETFIELD_CELL GETFIELD_CELL 
_PUTFIELD_CELL_ALOAD_ALOAD_ =  PUTFIELD_CELL ALOAD ALOAD 
_ILOAD_AALOAD_ =  ILOAD AALOAD 
_ALOAD_MONITOREXIT_ =  ALOAD MONITOREXIT 
_ALOAD_CHECKCAST_ =  ALOAD CHECKCAST 
_ALOAD_GETFIELD_CELL_ICONST_ =  ALOAD GETFIELD_CELL ICONST 
_ICONST_ILOAD_ =  ICONST ILOAD 
_ACONST_ICONST_ICONST_ =  ACONST ICONST ICONST 
_ALOAD_GETFIELD_CELL_IFNULL_ =  ALOAD GETFIELD_CELL IFNULL 
