/// translate.cc -- CompiledMethods -> TranslatedMethods
///
/// Author: Ian.Piumarta@inria.fr
///
/// Last edited: Thu Feb 25 20:37:38 1999 by piumarta (Ian Piumarta) on pingu

// This file implements an architecture-independent dynamic translator from
// bytecoded CompiledMethods into an unspecified executable representation.
// Native code is actually generated by the opaque gen_*() macros/functions,
// provided by platform[cpu/compiler]-dependent header/source files
// according to the "abstract" instruction set declared in "opcodes.h".

#include "interp.h"
#include "translate.h"
#include "debug.h"
#include "error.h"

#include <stdio.h>
#include <stdlib.h>


#ifdef TRACE
# include "bcNames.h"
# define PRINTF(FMT_ARGS) printf FMT_ARGS
#else
# define PRINTF(FMT_ARGS)
#endif


MethodMemoizer *methodMemoizer= 0;
ClassMemoizer *classMemoizer= 0;

void *lastCache= 0;
void *codeCache= 0;
void *codeCacheLimit= 0;

int   cacheCount= 0;
int   cacheTotal= 0;
int   cacheLossage= 0;


TranslatedMethod *MethodMemoizer::translatedMethodFor(CompiledMethod *cm)
{
  assert(fetchClass() == ClassArray);
  int major= cm->_hashBits() % wordLength();
  memoArray_t *memoArray= at(major);
  if (memoArray->isNil()) return 0;
  int size= memoArray->wordLength();
  for (int minor= 0; minor < size; ++minor) {
    MethodMemo *memo= memoArray->at(minor);
    if (memo->compiledMethod == cm) {
      return TranslatedMethod::forOop(memo->translatedMethod);
    }
  }
  return 0;
}


void translatorMarkHook(void)
{
#ifdef GCTRACE
  printf("["); fflush(stdout);
#endif
  PRINTF(("translatorMarkHook\n"));
  methodMemoizer->mark();
  classMemoizer->mark();
}


void translatorMapHook(void)
{
  PRINTF(("translatorMapHook\n"));
  methodMemoizer= (MethodMemoizer *)methodMemoizer->remap();
  classMemoizer= (ClassMemoizer *)classMemoizer->remap();
#ifdef GCTRACE
  printf("]"); fflush(stdout);
#endif
}


extern void memoStats(void);


void initialiseTranslator(void)
{
  codeCache= 0;
  lastCache= 0;
  codeCacheLimit= 0;
  cacheCount= 0;
  cacheTotal= 0;
  cacheLossage= 0;
  // one-time atexit initialisation
  if (methodMemoizer == 0) atexit(memoStats);
  methodMemoizer= new(MethodMemoizerSize) MethodMemoizer;
  classMemoizer= new(ClassMemoizerSize) ClassMemoizer;
  printf("translator initialised\n");
}


void releaseTranslator(void)
{
  if (codeCache == 0) return;
  memoStats();
  methodMemoizer= (MethodMemoizer *)nilObj;
  classMemoizer= (ClassMemoizer *)nilObj;
  while (lastCache != 0) {
    void *thisCache= lastCache;
    lastCache= *(void **)lastCache;
    printf("code cache dealloc: %p\n", thisCache);
    free(thisCache);
  }
  codeCache= 0;
  printf("translator released\n");
}


void allocateCodeCache(void)
{
  cacheTotal+= codeCache - lastCache;
  cacheLossage+= codeCacheLimit - codeCache;
  codeCache= malloc(CodeCacheSize);
  if (codeCache == 0) error("could not allocate code cache");
  codeCacheLimit= codeCache + CodeCacheSize;
  printf("code cache alloc: %p + %dk\n", codeCache, CodeCacheSize/1024);
  // caches form a 0-terminated linked list through their first word
  (*(void **)codeCache)= lastCache;
  lastCache= codeCache;
  codeCache+= sizeof(lastCache);
  ++cacheCount;
}


///
/// TRANSLATOR
///


#include "generate.h"


// answer a TranslatedMethod for the given receiver

TranslatedMethod *translateMethodFor(CompiledMethod *vMeth, oop rcvr)
{
  assert(methodMemoizer->translatedMethodFor(vMeth) == 0);

#ifdef SPY
  extern void spyTranslation(Class *cls, oop selector);
  spyTranslation(rcvr->fetchClass(), messageSelector);
#endif

  const int primIndex=	 vMeth->primitiveIndex();
  const int nArgs=	 vMeth->argCount();
  const int nTemps=	 vMeth->tempCount() - nArgs;
  const int startPC=	 vMeth->initialPC();	// Smalltalk coordinates!
  const int endPC=	 vMeth->endPC();	// Smalltalk coordinates!
  const byte *bytecodes= vMeth->bytes - OneRel;	// Smalltalk coordinates!

#ifdef TRACE
  extern void printMethodName(bool b, CompiledMethod *m, oop r);
  printf("TRANSLATE: ");
  printMethodName(false, vMeth, rcvr);
  printf(" %d -> %d\n", startPC, endPC);
  fflush(stdout);
#endif

  // (over)estimate the maximum space required for the PcMap
  const int mSize= sizeof(PcMap) + (endPC - startPC + 8) * sizeof(PcMapping);

  if (codeCache + mSize >= codeCacheLimit) // no room for PcMap
    allocateCodeCache();

  // allocate PC map
  PcMap *pcMap= new PcMap;
  pcMap->size= 0;
  
  // initialisations avoid compiler gripes
  int	mapIndex=	0;	// pcMap index of next entry
  int	vPC=		0;	// current virtual (bytecode) pc
  void *nPC=		0;	// current native/treaded pc
  void *code=		0;	// addr 1st insn of translated method
  void *activatedEntry=	0;	// addr of insn after activate/reserve opcodes

  // ** Note: we could optimise space by eliminating all pcMap
  //	entries that are not potential stabilisation points.
  //	All we really need to keep are:
  //		first insn
  //		insns following sends (including all special bytecodes!)
  //		backward jump destinations
  //		insns following conditional backward jumps

  for (int pass= 0; pass < 2; ++pass) {
    // pass 0:	codeCache -> first map entry, no code generated
    //		bytecodes do: [fill pcMap]
    // pass 1:	codeCache -> after map entries, code generated
    //		bytecodes do: [generate code]
    if (pass == 0) {
      code= 0;		// Note: SEGV if pass 0 attempts to generate code :^p
    } else { // pass == 1
      codeCache+= pcMap->size * sizeof(PcMapping);
      assert(codeCache == (void *)pcMap + sizeof(PcMap)
			     + pcMap->size * sizeof(PcMapping));
      assert(codeCache < codeCacheLimit);
      code= codeCache;
      // calculate space required for code (nPC is now size of code in bytes)
      if (codeCache + (int)nPC >= codeCacheLimit) {
	allocateCodeCache();
	code= codeCache; // Note: code and pcMap are in DIFFERENT CACHES!
      }
      mapIndex= 0;
    }

    vPC= startPC;
    nPC= code;		// this is 0 during pass 0


#   define fetchByte()	(bytecodes[vPC++])
#   define peekByte(pc)	(bytecodes[pc])


#   define notePC(vpc, npc) { \
      PRINTF(("%03d %3d:%p ", (npc) - code, (vpc), (npc))); \
      if (pass == 0) pcMap->addMapping((vpc), (int)(npc)); \
      else assert(pcMap->checkPhase(mapIndex++, (vpc), (npc) - code)); \
    }


#   ifdef TRACE
#     define tracePC(V,N)	notePC(0,N)
#   else
#     define tracePC(V,N)
#   endif


    //// PROLOGUE: enter with icbm, savedIP, and rcvr set

    tracePC(vPC, nPC);		genCheckClass();
    tracePC(vPC, nPC);		genCheckCompact();

    if (primIndex == 0 &&
	nArgs == 0 &&
	nTemps == 0) {
      // this is DEPRECATED!
      tracePC(vPC, nPC);	genCheckImmediate_0_0();
    } else {
      tracePC(vPC, nPC);	genCheckImmediate();
    }

    //// PRIMITIVE RESPONSE

    if (primIndex != 0) {
      tracePC(vPC, nPC);
      switch (primIndex) {
	// quick push const methods (should ignore body after one of these)
      case 256:			genPrimitivePushSelf();			break;
      case 257:			genPrimitivePushTrue();			break;
      case 258:			genPrimitivePushFalse();		break;
      case 259:			genPrimitivePushNil();			break;
      case 260:			genPrimitivePushMinusOne();		break;
      case 261:			genPrimitivePushZero();			break;
      case 262:			genPrimitivePushOne();			break;
      case 263:			genPrimitivePushTwo();			break;
	// quick push inst var methods -- DEPRECATED!
      case 264 ... 519:		genPrimitiveLoadInstVar(primIndex-264);	break;
	// other primitives
      default:			genPrimitive(primIndex);		break;
      }
    }

    //// QUICK RESPONSE

#ifdef QUICK_METHODS

    void *quickEntry= nPC;
    int   quickMapIndex= pcMap->size;

#   define abortQuickResponse() \
      nPC= quickEntry; \
      pcMap->size= quickMapIndex; \
      goto translateActivation;

    tracePC(vPC, nPC);		genQkActivate(nArgs);

    if (nTemps > 0) {
      // should be:	nTemps timesRepeat: [internalPush: nilOop]
      abortQuickResponse();
    }

    while (vPC <= endPC) {
      int bc;
      tracePC(vPC, nPC);
      PRINTF(("%3d %02x %s\n", vPC, bytecodes[vPC], bcNames[bytecodes[vPC]]));
      switch (bc= fetchByte()) {
      case  16 ...  31:		genLdTemp(bc & 15);			break;
      case 120:			genQkRetSelf();				break;
      case 121:			genQkRetTrue();				break;
      case 122:			genQkRetFalse();			break;
      case 123:			genQkRetNil();				break;
      case 124:			genQkRetTop();				break;
      default:			abortQuickResponse();			break;
      } //switch
    } //while

  translateActivation:

#endif

    tracePC(vPC, nPC);		genActivate(nArgs);

    if (nTemps > 0) {
      tracePC(vPC, nPC);	genReserve(nTemps);
    }

    activatedEntry= nPC;

    //// ACTIVATED RESPONSE

    vPC= startPC;

    while (vPC <= endPC) {
      int bc;
      notePC(vPC, nPC);
      PRINTF(("%3d %02x %s\n", vPC, bytecodes[vPC], bcNames[bytecodes[vPC]]));
      switch (bc= fetchByte()) {

      case   0 ...  15:		genLdInst(   bc & 15);			break;
      case  16 ...  31:		genLdTemp(   bc & 15);			break;
      case  32 ...  63:		genLdLit(    bc & 31);			break;
      case  64 ...  95:		genLdLitInd( bc & 31);			break;
      case  96 ... 103:		genPopInst(  bc &  7);			break;
      case 104 ... 111:		genPopTemp(  bc &  7);			break;

      case 112:			genLdSelf();				break;
      case 113:			genLdTrue();				break;
      case 114:			genLdFalse();				break;
      case 115:			genLdNil();				break;
      case 116:			genLdMinusOne();			break;
      case 117:			genLdZero();				break;
      case 118:			genLdOne();				break;
      case 119:			genLdTwo();				break;

      case 120:			genRetSelf();				break;
      case 121:			genRetTrue();				break;
      case 122:			genRetFalse();				break;
      case 123:			genRetNil();				break;
      case 124:			genMethodRet();				break;
      case 125:			genBlockRet();				break;

      case 126 ... 127:	error("unknown bytecode");			break;

      case 128: { // extendedPush
	const int descriptor= fetchByte();
	const int variableType= (descriptor >> 6) & 0x3;
	const int variableIndex= descriptor & 0x3F;
	switch (variableType) {
	case 0:		genLdInst(  variableIndex);			break;
	case 1:		genLdTemp(  variableIndex);			break;
	case 2:		genLdLit(   variableIndex);			break;
	case 3:		genLdLitInd(variableIndex);			break;
	}
	break;
      }

      case 129: { // extendedStore
	const int descriptor=  fetchByte();
	const int variableType= (descriptor >> 6) & 0x3;
	const int variableIndex= descriptor & 0x3F;
	switch (variableType) {
	case 0:		genStInst(variableIndex);			break;
	case 1:		genStTemp(variableIndex);			break;
	case 2:		error("illegal store");				break;
	case 3:		genStLitInd(variableIndex);			break;
	}
	break;
      }

      case 130: { // extendedStoreAndPop
	const int descriptor=  fetchByte();
	const int variableType= (descriptor >> 6) & 0x3;
	const int variableIndex= descriptor & 0x3F;
	switch (variableType) {
	case 0:		genPopInst(variableIndex);			break;
	case 1:		genPopTemp(variableIndex);			break;
	case 2:		error("illegal store");				break;
	case 3:		genPopLitInd(variableIndex);			break;
	}
	break;
      }

      case 131: { // singleExtendedSend
	const int descriptor= fetchByte();
	const int selIndex= descriptor & 0x1F;
	const int argCount= descriptor >> 5;
			genSend(argCount, selIndex);			break;
      }

      case 132: { // doubleExtendedDoAnything
	const int byte2= fetchByte();
	const int byte3= fetchByte();
	const int opType= byte2 >> 5;
	switch (opType) {
	case 0: {
	  
	  const int argCount= byte2 & 0x1F;
			genSend(argCount, byte3);			break;
	}
	case 1: {
	  
	  const int argCount= byte2 & 0x1F;
			genSuper(argCount, byte3);			break;
	}
	case 2:		genLdInst(  byte3);				break;
	case 3:		genLdLit(   byte3);				break;
	case 4:		genLdLitInd(byte3);				break;
	case 5:		genStInst(  byte3);				break;
	case 6:		genPopInst( byte3);				break;
	case 7:		genStLitInd(byte3);				break;
	}
	break;
      }

      case 133: { // singleExtendedSuper
	const int descriptor= fetchByte();
	const int selIndex= descriptor & 0x1F;
	const int argCount= descriptor >> 5;
			genSuper(argCount, selIndex);			break;
      }

      case 134: { // secondExtendedSend
	const int descriptor= fetchByte();
	const int selIndex= descriptor & 0x3F;
	const int argCount= descriptor >> 6;
			genSend(argCount, selIndex);			break;
      }

      case 135:		genPop();					break;
      case 136:		genDup();					break;
      case 137:		genLdThisContext();				break;

      case 138 ... 143:	error("experimental bytecode");			break;

      case 144 ... 151:	{ // shortUnconditionalJump
	const int dest= vPC + (bc & 7) + 1;
	if (bytecodes[dest] == 124) {
			genMethodRet();
	} else {
			genJmp(dest);
	}								break;
      }

      case 152 ... 159: { // shortConditionalJump
	const int dest= vPC + (bc & 7) + 1;
			genJmpF(dest);					break;
      }

      case 160 ... 167:	{ // longUnconditionalJump
	const int offset= fetchByte();
	const int dest= vPC + ((bc & 7) - 4) * 256 + offset;
	if (dest < vPC)	{
			genJmpChk(dest);
	} else {
	  if (bytecodes[dest] == 124) {
			genMethodRet();
	  } else {
			genJmp(dest);
	  }
	}								break;
      }

      case 168 ... 171: { // longJumpIfTrue
	const int offset= fetchByte();
	const int dest= vPC + (bc & 3) * 256 + offset;
			genJmpT(dest);					break;
      }

      case 172 ... 175: { // longJumpIfFalse
	const int offset= fetchByte();
	const int dest= vPC + (bc & 3) * 256 + offset;
			genJmpF(dest);					break;
      }

      case 176: 	genAdd();					break;
      case 177:		genSubtract();					break;
      case 178:		genLess();					break;
      case 179:		genGreater();					break;
      case 180:		genLessEqual();					break;
      case 181:		genGreaterEqual();				break;
      case 182:		genEqual();					break;
      case 183:		genNotEqual();					break;
      case 184:		genMultiply();					break;
      case 185:		genDivide();					break;
      case 186:		genMod();					break;
      case 187:		genMkPoint();					break;
      case 188:		genBitShift();					break;
      case 189:		genDiv();					break;
      case 190:		genBitAnd();					break;
      case 191:		genBitOr();					break;
      case 192:		genAt();					break;
      case 193:		genAtPut();					break;
      case 194:		genSize();					break;
      case 195:		genNext();					break;
      case 196:		genNextPut();					break;
      case 197:		genAtEnd();					break;
      case 198:		genEquivalent();				break;
      case 199:		genClass();					break;
      case 200:		genBlockCopy();					break;
      case 201:		genValue();					break;
      case 202:		genValueArgs();					break;
      case 203:		genDo();					break;
      case 204:		genNew();					break;
      case 205:		genNewArg();					break;
      case 206:		genX();						break;
      case 207:		genY();						break;

      case 208 ... 255: { // sendLiteralSelector
	const int selIndex=  bc & 15;
	const int argCount= ((bc >> 4) & 3) - 1;
			genSend(argCount, selIndex);			break;
      }
      } //switch
    } //while
    notePC(vPC, nPC);	// map location beyond last insn (for #cannotReturn:)
    PRINTF(("       <end>\n"));
  } //for

  assert(mapIndex == pcMap->size);

  // code -> first generated insn
  // nPC  -> next free codeCache locn

  // advance code cache past last insn
  codeCache= nPC;

  if (codeCache + sizeof(TranslatedMethod) >= codeCacheLimit)
    // Note: translated method is in different cache to pcMap and code
    allocateCodeCache();

  // allocate TranslatedMethod
  TranslatedMethod *tMeth= new TranslatedMethod(code, pcMap);
  tMeth->activatedEntry= activatedEntry;

  assert(codeCache < codeCacheLimit);

  // create method memo
  pushRemappableOop(vMeth);
  pushRemappableOop(rcvr);
  MethodMemo *memo= new MethodMemo();
  rcvr= popRemappableOop();
  vMeth= popRemappable(CompiledMethod);
  memo->checkStore(memo->compiledMethod= vMeth);
  memo->translatedMethod= tMeth->asOop();
  MemoIndex index= methodMemoizer->add(memo);
  tMeth->memoIndex= index;

#ifdef TRACE
  printf("map size:  %d bytes\n", pcMap->size);
  printf("code size: %d bytes\n", nPC - code);
  printf("memo index %d:%d hash %d\n",
	 index.major, index.minor, vMeth->_hashBits());
#endif

  return tMeth;
}


// %sends  %cumul  #args #tmps(=tempCount-argCount)
// ------  ------  ----- -----
//  36.84   36.84    0	   0
//  33.37   70.21    1	   0
//  19.46   89.67    2	   0
//   3.34   93.01    1	   1
//   1.86   94.87    0	   1
//   0.87   95.74    1	   3 // approx 4% sends use "general case"

