// prologue.cc -- method prologue generation			-*- C++ -*-
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2/26/01 by marcus@ira.uka.de

#include "NativeMethod.h"

#include "cachebits.h"
#include "cases.h"
#include "generate.h"
#include "machine.h"

#include "archdep.h"

#include "genCache.h"
#include "genArray.h"
#include "genFloat.h"
#include "genObject.h"
#include "genPrimitive.h"
#include "genQuick.h"
#include "genSmallInteger.h"
#include "genValue.h"

#include ARCHDEP(emit.h)


void genPrologue(NativeMethod *nMeth, bool ctxFlag)
{
  unsigned primIndex= nMeth->primitiveIndex;

  unsigned nArgs= nMeth->argumentCount;
  Class *rCls= nMeth->receiverClass();
  Class *mCls= nMeth->methodClass();

# if defined(CACHED_AT) || defined(INLINED_AT)
  unsigned instSize= rCls->instSize();
  fieldType_t fieldType= fieldTypeOfFormat(rCls->instSpec());

  if ((primIndex == 63) || (primIndex == 64))
    {
      assert(fieldType == byteFields);
      fieldType= charFields;
    }
# endif

  insn *relink= asm_pc, *primFail= nMeth->activateEntry, *enter= nMeth->checkedEntry;

  // this should go at the end of the method: some architectures
  // (e.g. Pentium) assume that backward branches succeed
  nMeth->relink= asm_pc;
  gen_relink(nMeth);

  nMeth->controlEntry= asm_pc;

  emit_move_S_r(nArgs, reg[0]);
  emit_jmp(enter);

  insn *entry= asm_pc;
  nMeth->entry= asm_pc;

  // special cases: primitives that have no cache check

      switch (nMeth->cacheType)
	{
	case pvCacheType:
	  nMeth->siEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genSICacheCheck(nMeth, enter, relink);
	  nMeth->ccEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genCCCacheCheck(nMeth, enter, relink);
	  nMeth->ncEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genNCCacheCheck(nMeth, 0, relink);
	  break;
	case siCacheType:
	  nMeth->siEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genSICacheCheck(nMeth, 0, relink);
	  nMeth->ccEntry= 0;
	  nMeth->ncEntry= 0;
	  break;
	case ccCacheType:
	  nMeth->siEntry= 0;
	  nMeth->ccEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genCCCacheCheck(nMeth, 0, relink);
	  nMeth->ncEntry= 0;
	  break;
	case ncCacheType:
	  nMeth->siEntry= 0;
	  nMeth->ccEntry= 0;
	  nMeth->ncEntry= asm_pc;	emit_move_S_r(nArgs, reg[0]); // !!!
					genNCCacheCheck(nMeth, 0, relink);
	  break;
	case ixCacheType:
	  nMeth->siEntry= 0;
	  nMeth->ccEntry= 0;
	  nMeth->ncEntry= 0;		emit_move_S_r(nArgs, reg[0]); // !!!
					genIXCacheCheck(nMeth, rCls, relink);
	  break;
	default:
	  fatal("this cannot happen");
	  break;
	} // switch (cacheType)

  nMeth->checkedEntry= asm_pc;

# ifdef IC_STATS
  extern unsigned icHits;
  emit_move_v_r(icHits, tmp[1]);
  emit_add_i_r(1, tmp[1]);
  emit_move_r_v(tmp[1], icHits);
# endif

  switch (primIndex)
    {
    case 0:	break;
      // SmallInteger primitives
    case 16:	if (mCls == ClassSmallInteger)	genBitXor(nMeth, primFail);	break;
      // Float Primitives (40-59)
    case 40:	if (mCls == ClassSmallInteger)	genAsFloat(nMeth, primFail);	break;
    case 41: case 42: case 43: case 44: case 45:
    case 46: case 47: case 48: case 49: case 50:
      if (mCls == ClassFloat)
	{
	  switch (primIndex)
	    {
	    case 41: genFloatAdd	    (nMeth, primFail);	break;
	    case 42: genFloatSubtract	    (nMeth, primFail);	break;
	    case 43: genFloatLessThan	    (nMeth, primFail);	break;
	    case 44: genFloatGreaterThan    (nMeth, primFail);	break;
	    case 45: genFloatLessOrEqual    (nMeth, primFail);	break;
	    case 46: genFloatGreaterOrEqual (nMeth, primFail);	break;
	    case 47: genFloatEqual	    (nMeth, primFail);	break;
	    case 48: genFloatNotEqual	    (nMeth, primFail);	break;
	    case 49: genFloatMultiply	    (nMeth, primFail);	break;
	    case 50: genFloatDivide	    (nMeth, primFail);	break;
	    case 51: // primitiveTruncated
	    case 52: // primitiveFractionalPart
	    case 53: // primitiveExponent
	    case 54: // primitiveTimesTwoPower
	    case 55: // primitiveSquareRoot
	    case 56: // primitiveSine
	    case 57: // primitiveArctan
	    case 58: // primitiveLogN
	    case 59: // primitiveExp
	      break;
	    }
	}
      // else FAIL
      break;
    case 60: // primitiveAt
    case 61: // primitiveAtPut
    case 62: // primitiveSize
    case 63: // primitiveStringAt
    case 64: // primitiveStringAtPut
#    if defined(CACHED_AT) || defined(INLINED_AT)
      if (fieldType != noFields)
	{
	  if (!ctxFlag)		// Contexts are dynamically-sized
	    {
	      switch (primIndex)
		{
#	        if defined(CACHED_AT)
		case 60: genCachedAt(nMeth, instSize, fieldType, false, primFail); break;
		case 61: genCachedAt(nMeth, instSize, fieldType, true, primFail);  break;
		case 62: genPrimitiveSize(nMeth, primFail);			   break;
		case 63: genCachedAt(nMeth, instSize, fieldType, false, primFail); break;
		case 64: genCachedAt(nMeth, instSize, fieldType, true, primFail);  break;
#		elif defined(INLINED_AT)
		case 60: genPrimitiveAt(nMeth, false, false, primFail);	break;
		case 61: genPrimitiveAt(nMeth, false, true,  primFail);	break;
		case 62: genPrimitiveSize(nMeth, primFail);		break;
		case 63: genPrimitiveAt(nMeth, true,  false, primFail);	break;
		case 64: genPrimitiveAt(nMeth, true,  true,  primFail);	break;
#		else
		error: this cannot happen
#		endif
		}
	    }
	  else // ctxFlag
	    {
	      genPrimitive(primIndex, nMeth, primFail);
	    }
	}
#    else
      genPrimitive(primIndex, nMeth, primFail);      
#    endif // CACHED_AT || INLINED_AT
      // else FAIL
      break;

    case 70:	genNew(nMeth, primFail);	break;
    case 71:	genNewWithArg(nMeth, primFail);	break;
    case 75:	genAsOop(nMeth, primFail);	break;

    case 81:
      genValue(nMeth);		// falls through to fail...
      genControlPrimitive(primIndex, nMeth, primFail);
      break;

    case  82: // primitiveValueWithArgs
    case  83: // primitivePerform
    case  84: // primitivePerformWithArgs
    case 100: // primitivePerformInSuperclass
    case 118: // primitiveDoPrimitiveWithArgs
      genControlPrimitive(primIndex, nMeth, primFail);
      break;

    case 110: // Object>>==
      genEquivalent(nMeth, primFail);
      break;

    case 122:	genNoop(nMeth, primFail);	break;

      // Quick primitives
    case 256:	genPushSelf(nMeth, primFail);	break;
    case 257:	genPushTrue(nMeth, primFail);	break;
    case 258:	genPushFalse(nMeth, primFail);	break;
    case 259:	genPushNil(nMeth, primFail);	break;
    case 260: // primitivePushMinusOne
    case 261: // primitivePushZero
    case 262: // primitivePushOne
    case 263: // primitivePushTwo
		genPushInteger(nMeth, primFail); break;

    cases256(264): // primitiveLoadInstVar
      if (!ctxFlag)
	genLoadInstVar(nMeth, primFail);
      else
	genPrimitive(primIndex, nMeth, primFail);
      break;

      // Absolutely everything else...
    default:
      genPrimitive(primIndex, nMeth, primFail);
      break;
    }

  nMeth->activateEntry= asm_pc;

  gen_activate(nMeth);	// allocate a frame and enter method body

  nMeth->activatedEntry= asm_pc;
}


void genBlockActivate(NativeMethod *nMeth, size_t nArgs)
{
  gen_blockActivate(nMeth, nArgs);
}
