// genArray.cc -- Array method prologue generation		-*- C++ -*-
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2000-12-04 10:55:46 by piumarta on emilia.rd.wdi.disney.com

#ifdef __macintosh__
#	define	WORDS_BIGENDIAN 1
#else
#	include "sqUnixConfig.h"
#endif

#include "NativeMethod.h"

#include "archdep.h"
#include "atcache.h"
#include "generate.h"
#include "machine.h"

#include ARCHDEP(emit.h)

#include "genArray.h"

#define	INLINE_STORECK	// inline the easy store check tests


#if defined(INLINED_AT) || defined(CACHED_AT)


fieldType_t fieldTypeOfFormat(const unsigned instSpec)
{
  switch (instSpec)
    {
    case 0: // no fields
    case 1: // fixed fields only (all containing pointers)
      return noFields;
    case 2: // indexable fields only (all containing pointers)
    case 3: // both fixed and indexable fields (all containing pointers)
    case 4: // both fixed and indexable weak fields (all containing pointers).
      return oopFields;
    case 5: // unused
      fatal("illegal format");
      return noFields;
    case 6: // indexable word fields only (no pointers)
      return wordFields;
    case 7: // unused
      fatal("illegal format");
      return noFields;
    case 8: case 9: case 10: case 11:
      // indexable byte fields only (no pointers) (low 2 bits are low 2 bits of size)
    case 12: case 13: case 14: case 15:
      // compiled methods:
      // # of literal oops specified in method header,
      // followed by indexable bytes (same interpretation of low 2 bits as above)
      return byteFields;
    }
  fatal("this cannot happen");
  return noFields;
}


static oop c_makeLargePositiveInteger(unsigned int value)
{
  assert(activeFrame != 0);
  oop lpi= LargePositiveIntPrototype->copy();
# ifdef WORDS_BIGENDIAN
  ((unsigned char *)lpi)[4]= ((value >>  0) & 0xff);
  ((unsigned char *)lpi)[5]= ((value >>  8) & 0xff);
  ((unsigned char *)lpi)[6]= ((value >> 16) & 0xff);
  ((unsigned char *)lpi)[7]= ((value >> 24) & 0xff);
# else
  ((unsigned int *)lpi)[1]= value;
# endif
  return lpi;
}


#endif // INLINED_AT || CACHED_AT


/// 
/// CACHED RESPONSE FOR ACESS PRIMITIVES
/// 


#ifdef CACHED_AT

// reg[0] = receiver
// reg[1] = cache argument

void genCachedAt(NativeMethod *nMeth, const unsigned instSize,
		 fieldType_t fieldType, bool putFlag, insn *fail)
{
# ifndef NDEBUG
  printf("genCachedAt %p %d %d %d ", asm_pc, instSize, fieldType, putFlag);
  nMeth->receiverClass()->print();
  printf(" -> ");
  nMeth->print();
  printf("\n");
# endif

  const int nArgs= nMeth->argumentCount;
  insn *asm_org= asm_pc;
  insn *retry= asm_pc, *nonsi= asm_pc, *refill= asm_pc, *shrt= asm_pc, *cont= asm_pc;
  insn *resume= asm_pc;
  for (asm_pass= 1;  asm_pass < 3;  ++asm_pass)
    {
      asm_pc= asm_org;
      retry= asm_pc;
      // reg[0] is still the receiver from ixCacheCheck
      emit_bfmsk_i_i_r_r(AtHashLowBit, AtHashBitWidth, reg[0], tmp[0]);
      int atCacheBase= 0;
      if (putFlag) {
	atCacheBase= ((int)j_atCache+(AtCacheEntries*sizeof(AtCacheLine)));
      } else {
	atCacheBase= ((int)j_atCache);
      }
      emit_move_i_r(atCacheBase, tmp[1]);
      emit_add_r_r(tmp[1], tmp[0]);
      // tmp[0] is at cache line for receiver
      emit_get_i_r_r(acl_object_off, tmp[0], tmp[1]);	// acl.object
      emit_cmp_r_r(reg[0], tmp[1]);
      emit_bne(refill);					// miss -> refill cache
      // at cache hit -- ok to trash cache arg
      emit_move_S_r(nArgs - 1, reg[1]);			// index
      emit_get_i_r_r(acl_size_off, tmp[0], tmp[1]);	// cached size
      emit_tagck_r_t(reg[1], fail);			// non-SI index
      // reg[0] = receiver
      // reg[1] = requested index
      // tmp[1] = index of byte after last indexable field relative to base header
      if (byteIndexed(fieldType)) {
	assert(instSize == 0);
	emit_untaggtck_r_t(reg[1], fail);		// non-SI or <= 0
	emit_add_i_r(3, reg[1]);			// + BaseHdrSize - 1rel
      } else {
	emit_bfextshl_i_i_i_r(1, 30, 2, reg[1]);	// index _ (index & ~1 ) << 1
	emit_ble(fail);					// index <= 0
	if (instSize != 0) {
	  assert(fieldType == oopFields);
	  emit_add_i_r((instSize*4), reg[1]);		// skip fixed fields
	}
      }
      // reg[0] = receiver
      // reg[1] = correct byte/word index relative to base header
      // tmp[1] = index of byte after last indexable field relative to base header
      emit_cmp_r_r(reg[1], tmp[1]);
      emit_bge(fail);					// index > size
      if (putFlag)
	{
	  emit_move_S_r(0, reg[2]);			// value
	  // reg[0] = receiver
	  // reg[1] = correct byte/word index relative to base header
	  // reg[2] = value
	  if (byteIndexed(fieldType)) {
	    if (fieldType == byteFields) {
	      emit_tagck_r_t(reg[2], fail);
	      emit_untag_r_r(reg[2], tmp[2]);
	      emit_cmp_r_i(tmp[2], 256);
	      emit_bge(fail);
	      // tmp[2] = byte to store
	    } else {
	      assert(fieldType == charFields);
	      const size_t ch_value_off= memberOffset(Character, value);
	      emit_notagck_r_t(reg[2], fail);		// SmallInteger
	      emit_get_i_r_r(0, reg[2], tmp[0]);	// base header
	      emit_bftst_i_i_r(12, 5, tmp[0]);		// cc index bits
	      emit_bne(fail);				// Characters are not compact
	      emit_get_i_r_r(-4, reg[2], tmp[0]);	// class header
	      emit_move_v_r(specialObjectsOop, tmp[1]);
	      emit_get_i_r_r((4+19*4), tmp[1], tmp[1]);	// ClassCharacter
	      emit_bfclr_i_i_r(0, 2, tmp[0]);		// zap type bits
	      emit_cmp_r_r(tmp[0], tmp[1]);
	      emit_bne(fail);				// value not Character
	      emit_get_i_r_r(ch_value_off, reg[2], tmp[2]);
	      emit_untag_r(tmp[2]);			// ascii value
	      // tmp[2] = byte to store
	    }
	    emit_putb_r_r_r(tmp[2], reg[1], reg[0]);	// store byte
	    emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	    emit_resume();
	  } else {
	    if (fieldType == wordFields) {
	      emit_tagck_r_t(reg[2], nonsi);		// non-SI value
	      emit_lsr_i_r_r(1, reg[2], tmp[2]);	// don't sign extend!
	      emit_put_r_r_r(tmp[2], reg[1], reg[0]);	// store word
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      emit_resume();
	      nonsi= asm_pc;
	      // try to store a LargePositiveInteger
	      emit_get_i_r_r(0, reg[2], tmp[0]);	// base header
	      emit_ccick_i_r(LargePositiveIntegerCCI, tmp[0]);
	      emit_bne(fail);				// Characters are not compact
	      emit_get_i_r_r(4, reg[2], tmp[2]);	// 32-bit LSB-first value
#	      ifdef WORDS_BIGENDIAN
	      emit_rput_r_r_r(tmp[2], reg[1], reg[0]);	// store word byte reversed
#	      else
	      emit_put_r_r_r(tmp[2], reg[1], reg[0]);	// store word
#	      endif
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      emit_resume();
	    } else {
	      assert(fieldType == oopFields);
	      emit_put_r_r_r(reg[2], reg[1], reg[0]);	// store word
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      // store check
#	      if defined(INLINE_STORECK)
	      emit_tagck_r(reg[2]);
	      emit_resume_ne(resume);			// value->isInteger()
	      emit_move_v_r(youngStart, tmp[0]);
	      emit_cmp_r_r(reg[0], tmp[0]);
	      emit_resume_ge(resume);			// dest->isYoung
	      emit_cmp_r_r(reg[2], tmp[0]);
	      emit_resume_lt(resume);			// value->isOld
	      emit_get_i_r_r(0, reg[0], reg[1]);	// base header
	      emit_rootck_r(reg[1]);
	      emit_resume_ne(resume);			// dest->isRoot
	      // record reg[0] as a root
	      emit_move_v_r(rootTableCount, tmp[0]);
	      emit_cmp_r_i(tmp[0], RootTableSize);
	      emit_resume_ge(resume);			// root table overflow
	      emit_add_i_r(1, tmp[0]);
	      emit_move_r_v(tmp[0], rootTableCount);
	      emit_move_i_r((int)rootTable, tmp[1]);
	      emit_lsl_i_r(2, tmp[0]);			// word->byte offset
	      emit_put_r_r_r(reg[0], tmp[0], tmp[1]);	// rootTable[tmp[0]] = reg[0]
	      emit_setroot_r(reg[1]);			// set header root bit
	      emit_put_r_i_r(reg[1], 0, reg[0]);	// update base header
	      resume= asm_pc;
	      emit_resume();
#	      else // !INLINE_STORECK
	      emit_move_r_r(reg[0], tmp[0]);		// affected object
	      emit_move_r_r(reg[2], tmp[1]);		// value stored
	      emit_jmp(g_storeCheck);			// tail call
#	      endif
	    }
	  }
	}
      else // primitiveAt
	{
	  // reg[0] = receiver
	  // reg[1] = correct byte/word index relative to base header
	  if (byteIndexed(fieldType)) {
	    emit_getb_r_r_r(reg[1], reg[0], reg[0]);	// fetch byte
	    if (fieldType == byteFields) {
	      emit_entag_r(reg[0]);
	      // reg[0] = tagged byte
	    } else {
	      assert(fieldType == charFields);
	      emit_move_v_r(specialObjectsOop, tmp[0]);
	      emit_lsl_i_r(2, reg[0]);			// word index -> byte offset
	      emit_get_i_r_r((4+24*4), tmp[0], tmp[0]);	// CharacterTable
	      emit_add_i_r(4, reg[0]);			// skip base header
	      emit_get_r_r_r(reg[0], tmp[0], reg[0]);	// fetch Character
	      // reg[0] = Character
	    }
	  } else {
	    emit_get_r_r_r(reg[1], reg[0], reg[0]);	// fetch pointer
	    if (fieldType == wordFields) {
	      emit_bftst_i_i_r(30, 2, reg[0]);
	      emit_bne(nonsi);				// not +ve integer value
	      emit_entag_r(reg[0]);
	      // reg[0] = tagged integer
	      emit_move_r_popS(reg[0], 1);		// pop: rcv idx; push: field
	      emit_resume();				// success
	      nonsi= asm_pc;
	      // fetch a LargePositiveInteger
	      emit_savepc();
	      emit_extern();
	      emit_gcprotect();
	      emit_mkcargs1();
	      emit_ccall(c_makeLargePositiveInteger);	// regs/tmps trashed!
	      emit_killcargs1();
#	      ifndef NDEBUG
	      emit_gcunprotect();
#	      endif
	      emit_restorepc();
	      // reg[0] = LargePositiveInteger
	    }
	    //else // reg[0] = oop
	  }
	  emit_move_r_popS(reg[0], 1);		// pop: rcv idx; push: field
	  emit_resume();			// success
	}
      refill= asm_pc;
      // reg[0] is receiver
      // tmp[0] is at cache line
      emit_get_i_r_r(0, reg[0], tmp[1]);		// base header
      if (byteIndexed(fieldType)) {
	emit_bfext_i_i_r_r(8, 2, tmp[1], tmp[2]);	// low two format bits
      }
      emit_bftst_i_i_r(0, 2, tmp[1]);			// type bits
      emit_bne(shrt);					// size encdoded in base header
      emit_get_i_r_r(-8, reg[0], tmp[1]);		// size header
      emit_jmp(cont);
      shrt= asm_pc;
      emit_and_i_r(0xfc, tmp[1]);			// mask for size bits
      cont= asm_pc;
      // tmp[1] = size bits; tmp[2] = low 2 format bits
      if (byteIndexed(fieldType)) {
	emit_sub_r_r(tmp[2], tmp[1]);			// true byte size + 4
      }
      // tmp[1] = index of byte after last indexable field
      emit_put_r_i_r(reg[0], acl_object_off, tmp[0]);
      emit_put_r_i_r(tmp[1], acl_size_off, tmp[0]);
      emit_jmp(retry);					// rerun cached response
    }
  asm_pass= 0;
}

#endif // CACHED_AT


/// 
/// INLINED RESPONSE FOR ACESS PRIMITIVES
/// 


#ifdef INLINED_AT

void genPrimitiveAccessor(int instSize, fieldType_t fieldType, bool putFlag, insn *fail)
{
  insn *asm_org= asm_pc, *shrt= asm_pc, *cont= asm_pc, *nonsi= asm_pc;
  for (asm_pass= 1;  asm_pass < 3;  ++asm_pass)
    {
      asm_pc= asm_org;
      if (putFlag) {
	emit_move_S_r(0, reg[2]);		// value
	emit_move_S_r(1, reg[1]);		// index
	//emit_move_S_r(2, reg[0]);		// receiver
      } else {
	emit_move_S_r(0, reg[1]);		// index
	//emit_move_S_r(1, reg[0]);		// receiver
      }
      emit_bftst_i_i_r(0, 1, reg[1]);
      emit_beq(fail);				// non-SI index
      emit_get_i_r_r(0, reg[0], tmp[0]);	// fetch base header
      if (byteIndexed(fieldType)) {
	emit_bfext_i_i_r_r(8, 2, tmp[0], tmp[1]);	// low two format bits
      }
      emit_bftst_i_i_r(0, 2, tmp[0]);		// type bits
      emit_bne(shrt);				// size encdoded in base header
      emit_get_i_r_r(-8, reg[0], tmp[0]);	// size header
      emit_jmp(cont);
      shrt= asm_pc;
      emit_and_i_r(0xfc, tmp[0]);		// mask for size bits
      cont= asm_pc;
      // tmp[0] = size bits; tmp[1] = low 2 format bits
      if (byteIndexed(fieldType)) {
	emit_sub_r_r(tmp[1], tmp[0]);			// true byte size + 4
	emit_untaggtck_r_t(reg[1], fail);		// untagged index >= 0
	emit_add_i_r(3, reg[1]);			// + BaseHdrSize - 1rel
      } else {
	emit_bfextshl_i_i_i_r(1, 30, 2, reg[1]);	// index _ (index & ~1 ) << 1
	emit_ble(fail);					// index <= 0
	if (instSize != 0) {
	  assert(fieldType == oopFields);
	  emit_add_i_r((instSize*4), reg[1]);		// skip fixed fields
	}
      }
      // reg[1] = correct byte/word index relative to base header
      // tmp[0] = address of byte after last indexable field
      emit_cmp_r_r(reg[1], tmp[0]);
      emit_bge(fail);					// index > size
      if (putFlag)
	{
	  if (byteIndexed(fieldType)) {
	    if (fieldType == byteFields) {
	      emit_tagck_r_t(reg[2], fail);
	      emit_untag_r_r(reg[2], tmp[2]);
	      emit_cmp_r_i(tmp[2], 256);
	      emit_bge(fail);
	      // tmp[2] = byte to store
	    } else {
	      assert(fieldType == charFields);
	      const size_t ch_value_off= memberOffset(Character, value);
	      emit_notagck_r_t(reg[2], fail);		// SmallInteger
	      emit_get_i_r_r(0, reg[2], tmp[0]);	// base header
	      emit_bftst_i_i_r(12, 5, tmp[0]);		// cc index bits
	      emit_bne(fail);				// Characters are not compact
	      emit_get_i_r_r(-4, reg[2], tmp[0]);	// class header
	      emit_move_v_r(specialObjectsOop, tmp[1]);
	      emit_get_i_r_r((4+19*4), tmp[1], tmp[1]);	// ClassCharacter
	      emit_bfclr_i_i_r(0, 2, tmp[0]);		// zap type bits
	      emit_cmp_r_r(tmp[0], tmp[1]);
	      emit_bne(fail);				// value not Character
	      emit_get_i_r_r(ch_value_off, reg[2], tmp[2]);
	      emit_untag_r(tmp[2]);			// ascii value
	      // tmp[2] = byte to store
	    }
	    emit_putb_r_r_r(tmp[2], reg[1], reg[0]);	// store byte
	    emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	    emit_resume();
	  } else {
	    if (fieldType == wordFields) {
	      emit_tagck_r_t(reg[2], nonsi);		// non-SI value
	      emit_lsr_i_r_r(1, reg[2], tmp[2]);	// don't sign extend!
	      emit_put_r_r_r(tmp[2], reg[1], reg[0]);	// store word
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      emit_resume();
	      nonsi= asm_pc;
	      // try to store a LargePositiveInteger
	      emit_get_i_r_r(0, reg[2], tmp[0]);	// base header
	      emit_ccick_i_r(LargePositiveIntegerCCI, tmp[0]);
	      emit_bne(fail);				// Characters are not compact
	      emit_get_i_r_r(4, reg[2], tmp[2]);	// 32-bit LSB-first value
#	      ifdef WORDS_BIGENDIAN
	      emit_rput_r_r_r(tmp[2], reg[1], reg[0]);	// store word byte reversed
#	      else
	      emit_put_r_r_r(tmp[2], reg[1], reg[0]);	// store word
#	      endif
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      emit_resume();
	    } else {
	      assert(fieldType == oopFields);
	      emit_put_r_r_r(reg[2], reg[1], reg[0]);	// store word
	      // store check
	      emit_initcall(g_storeCheck);		// tail call
	      emit_move_r_popS(reg[2], 2);		// pop: rcv idx val; push: val
	      emit_move_r_r(reg[0], tmp[0]);		// affected object
	      emit_move_r_r(reg[2], tmp[1]);		// value stored
	      emit_execjmp(g_storeCheck);		// tail call
	    }
	  }
	}
      else // primitiveAt
	{
	  if (byteIndexed(fieldType)) {
	    emit_getb_r_r_r(reg[1], reg[0], reg[0]);	// fetch byte
	    if (fieldType == byteFields) {
	      emit_entag_r(reg[0]);
	      // reg[0] = tagged byte
	    } else {
	      assert(fieldType == charFields);
	      emit_move_v_r(specialObjectsOop, tmp[0]);
	      emit_lsl_i_r(2, reg[0]);			// word index -> byte offset
	      emit_get_i_r_r((4+24*4), tmp[0], tmp[0]);	// CharacterTable
	      emit_add_i_r(4, reg[0]);			// skip base header
	      emit_get_r_r_r(reg[0], tmp[0], reg[0]);	// fetch Character
	      // reg[0] = Character
	    }
	  } else {
	    emit_get_r_r_r(reg[1], reg[0], reg[0]);	// fetch pointer
	    if (fieldType == wordFields) {
	      emit_bftst_i_i_r(30, 2, reg[0]);
	      emit_bne(nonsi);				// not +ve integer value
	      emit_entag_r(reg[0]);
	      // reg[0] = tagged integer
	      emit_move_r_popS(reg[0], 1);		// pop: rcv idx; push: field
	      emit_resume();				// success
	      nonsi= asm_pc;
	      emit_savepc();
	      emit_extern();
	      emit_gcprotect();
	      emit_mkcargs1();
	      emit_ccall(c_makeLargePositiveInteger);	// regs/tmps trashed!
	      emit_killcargs1();
#	      ifndef NDEBUG
	      emit_gcunprotect();
#	      endif
	      emit_restorepc();
	      // reg[0] = LargePositiveInteger
	    }
	    //else // reg[0] = oop
	  }
	  emit_move_r_popS(reg[0], 1);		// pop: rcv idx; push: field
	  emit_resume();
	}
      // drop into primitive to fail...
      emit_ccall(abort);
    }
  asm_pass= 0;
}


void genPrimitiveAt(NativeMethod *nMeth, bool stringy, bool putFlag, insn *fail)
{
# ifndef NDEBUG
  if (putFlag) {
    assert(nMeth->argumentCount == 2);
  } else {
    assert(nMeth->argumentCount == 1);
  }
# endif

  const Class *rcvrClass= nMeth->receiverClass();
  const unsigned instSpec= rcvrClass->instSpec();

  switch (instSpec)
    {
    case 0: // no fields
    case 1: // fixed fields only (all containing pointers)
      printf("WARNING: ignoring primitive #at:[put:] for non-indexable receiver");
      return;
    case 2: // indexable fields only (all containing pointers)
    case 3: // both fixed and indexable fields (all containing pointers)
    case 4: // both fixed and indexable weak fields (all containing pointers).
      assert(!stringy);
      genPrimitiveAccessor(rcvrClass->instSize(), oopFields, putFlag, fail);
      return;
    case 5: // unused
      fatal("illegal format");
      return;
    case 6: // indexable word fields only (no pointers)
      assert(rcvrClass->instSize() == 0);
      assert(!stringy);
      genPrimitiveAccessor(0, wordFields, putFlag, fail);
      return;
    case 7: // unused
      fatal("illegal format");
      return;
    case 8: case 9: case 10: case 11:
      // indexable byte fields only (no pointers) (low 2 bits are low 2 bits of size)
    case 12: case 13: case 14: case 15:
      // compiled methods:
      // # of literal oops specified in method header,
      // followed by indexable bytes (same interpretation of low 2 bits as above)
      assert(rcvrClass->instSize() == 0);
      if (stringy)
	genPrimitiveAccessor(0, charFields, putFlag, fail);
      else
	genPrimitiveAccessor(0, byteFields, putFlag, fail);
      return;
    }
}


void genPrimitiveSize(NativeMethod *nMeth, insn *fail)
{
  const Class *rcvrClass= nMeth->receiverClass();
  const size_t instSize= rcvrClass->instSize();
  const unsigned instSpec= rcvrClass->instSpec();

  PRINTF(("%p genPrimitiveSize instSize=%d, instSpec=%d\n", asm_pc, instSize, instSpec));

  if (instSpec < 2)	// no indexable fields
    return;

  insn *asm_org= asm_pc, *shrt= asm_pc, *cont= asm_pc;
  for (asm_pass= 1; asm_pass < 3; ++asm_pass)
    {
#if 1
      asm_pc= asm_org;
      //emit_move_S_r(0, reg[0]);			// pick receiver
      emit_notagck_r_t(reg[0], fail); // !!!
      emit_get_i_r_r(0, reg[0], tmp[0]);	// fetch base header
      if (instSpec >= 8) {
	// need to preserve low 2 format bits
	emit_bfext_i_i_r_r(8, 2, tmp[0], tmp[1]);
      }
      emit_bftst_i_i_r(0, 2, tmp[0]);		// test for size header
      emit_bne(shrt);
      emit_get_i_r_r(-8, reg[0], tmp[0]);	// fetch size header
      emit_jmp(cont);
      shrt= asm_pc;
      emit_and_i_r(0xfc, tmp[0]);		// base header size bits
      cont= asm_pc;

      switch (instSpec)
	{
	case 0: // no fields
	case 1: // fixed fields only (all containing pointers)
	  // refuse to compile code for non-indexable classes
	case 5: // unused
	case 7: // unused
	  fatal("illegal format");
	  return;
	case 2: // indexable fields only (all containing pointers)
	case 3: // both fixed and indexable fields (all containing pointers)
	case 4: // both fixed and indexable weak fields (all containing pointers).
	  if (instSize > 0)
	    {
	      emit_sub_i_r((instSize << 2), tmp[0]);
	    }
	  // fall through...
	case 6: // indexable word fields only (no pointers)
	  emit_lsr_i_r(1, tmp[0]);		// untagged byte -> tagged word size
	  emit_sub_i_r(1, tmp[0]);		// less base header; set tag bit
	  emit_move_r_S(tmp[0], 0);
	  emit_resume();
	  break;
	case 8: case 9: case 10: case 11:
	  // indexable byte fields only (no pointers) (low 2 bits are low 2 bits of size)
	case 12: case 13: case 14: case 15:
	  // compiled methods:
	  // # of literal oops specified in method header,
	  // followed by indexable bytes (same interpretation of low 2 bits as above)
	  assert(instSize == 0);
	  // low 2 format bits are in tmp[1]
	  emit_sub_r_r(tmp[1], tmp[0]);
	  emit_lsl_i_r(1, tmp[0]);		// untagged byte -> tagged byte size
	  emit_sub_i_r(7, tmp[0]);		// less base header; set tag bit
	  emit_move_r_S(tmp[0], 0);
	  emit_resume();
	  break;
	}
#else
      asm_pc= asm_org;
      emit_move_S_r(0, tmp[0]);			// pick receiver
      emit_notagck_r_t(tmp[0], fail);
      emit_get_i_r_r(0, tmp[0], reg[0]);	// fetch base header
      if (instSpec >= 8) {
	// need to preserve low 2 format bits
	emit_bfext_i_i_r_r(8, 2, reg[0], tmp[1]);
      }
      emit_bftst_i_i_r(0, 2, reg[0]);		// test for size header
      emit_bne(shrt);
      emit_get_i_r_r(-8, tmp[0], reg[0]);	// fetch size header
      emit_jmp(cont);
      shrt= asm_pc;
      emit_and_i_r(0xfc, reg[0]);		// base header size bits
      cont= asm_pc;

      switch (instSpec)
	{
	case 0: // no fields
	case 1: // fixed fields only (all containing pointers)
	  // refuse to compile code for non-indexable classes
	case 5: // unused
	case 7: // unused
	  fatal("illegal format");
	  return;
	case 2: // indexable fields only (all containing pointers)
	case 3: // both fixed and indexable fields (all containing pointers)
	case 4: // both fixed and indexable weak fields (all containing pointers).
	  if (instSize > 0)
	    {
	      emit_sub_i_r((instSize << 2), reg[0]);
	    }
	  // fall through...
	case 6: // indexable word fields only (no pointers)
	  emit_lsr_i_r(1, reg[0]);		// untagged byte -> tagged word size
	  emit_sub_i_r(1, reg[0]);		// less base header; set tag bit
	  emit_move_r_S(reg[0], 0);
	  emit_resume();
	  break;
	case 8: case 9: case 10: case 11:
	  // indexable byte fields only (no pointers) (low 2 bits are low 2 bits of size)
	case 12: case 13: case 14: case 15:
	  // compiled methods:
	  // # of literal oops specified in method header,
	  // followed by indexable bytes (same interpretation of low 2 bits as above)
	  assert(instSize == 0);
	  // low 2 format bits are in tmp[1]
	  emit_sub_r_r(tmp[1], reg[0]);
	  emit_lsl_i_r(1, reg[0]);		// untagged byte -> tagged byte size
	  emit_sub_i_r(7, reg[0]);		// less base header; set tag bit
	  emit_move_r_S(reg[0], 0);
	  emit_resume();
	  break;
	}
#endif
    }
  asm_pass= 0;
}

#endif // INLINED_AT
