// execute.cc -- platform-independent execution machinery
//
// Author: Ian.Piumarta@inria.fr
//
// Last edited: Thu Feb 25 20:37:42 1999 by piumarta (Ian Piumarta) on pingu
		     
#include "interp.h"
#include "util.h"
#include "translate.h"
#include "error.h"
		     
#include "primitiveTable.decl"
#include "primitiveTable.defn"

#include "cache.h"

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

#include "execute.h"


///
/// EXECUTION STATE
///

int contextCacheSize= 0;

CachedContext
  *contextCache,
  *lowestCachedContext,
  *activeCachedContext,
  *highestCachedContext;

oop *stackCache;
oop *temporaryPointer;

bool activated;

Context *topStableContext;

TranslatedMethod *translatedMethod;

oop returnResult;
Class *receiverClass= 0;

unsigned int ccBitsBlockContext;
unsigned int ccBitsMethodContext;
unsigned int ccBitsPseudoContext;

#ifndef NDEBUG
extern bool externalRegisters;
#endif

#ifdef GATHER_STATS
extern unsigned long long lastEntry, lastExit;
extern unsigned long long interpMSecs, jitterMSecs;
extern unsigned stat_jitterEntries;
#endif

///
/// DEBUG SUPPORT
///

void printMethodName(bool block, CompiledMethod *meth, oop rcvr)
{
  Class *methodClass= findClassOfMethodforReceiver(meth, rcvr);
  oop methodSel= findSelectorOfMethodforReceiver(meth, rcvr);
  if (block) printf("[] in ");
  printNameOfClasscount(methodClass, 5);
  printf(">");
  printStringOf(methodSel);
}

#include "bcNames.h"

#ifdef TRACE

void noteEntry(oop rcvr, TranslatedMethod *meth, word *ip,
	       CachedContext *ctx, oop *sp)
{
  printf("\nENTER %d\t", ctx - contextCache);
  printMethodName(ctx->isBlock(), meth->compiledMethod(), rcvr);
  printf("\n");
}

void noteActivation(oop rcvr, TranslatedMethod *meth, word *ip,
		    CachedContext *ctx, oop *sp)
{
  printf("\nSEND %d\t", ctx - contextCache);
  printMethodName(ctx->isBlock(), meth->compiledMethod(), rcvr);
  printf("\n");
}

void noteReturn(oop rcvr, TranslatedMethod *meth, word *ip,
		CachedContext *ctx, oop *sp)
{
  printf("\nRETURN %d\t", ctx - contextCache);
  printMethodName(ctx->isBlock(), meth->compiledMethod(), rcvr);
  printf("\n");
}

void noteExecutionState(oop recv, TranslatedMethod *meth, word *ip,
			CachedContext *ctx, oop *sp)
{
  word bc= *ip;
  printf(" %03d %02x %-36s ", meth->bytecodeIndex(ip), bc, bcNames[bc+256]);
  if (((sp - ctx->stack) > 32) || ((sp - ctx->stack) < -1))
    error("stack pointer out of bounds");
  printf("%2d:", sp - ctx->stack + 1);
  for (oop *p= ctx->stack; p <= sp; ++p) {
    printf(" ");
    printObject(*p);
  }
  printf("\n");
}

#endif TRACE


///
/// METHOD ACTIVATION
///


void executeNewMethod(void)
{
  if ((primitiveIndex == 0) || !primitiveResponse()) {
    // ACTIVATE
    int nargs= argumentCount;
    CachedContext *oldContext= activeCachedContext;

    CachedContext *acc= oldContext->child();
    if (acc == lowestCachedContext) {
      flushLowestCachedContext();
    }

    TranslatedMethod *tm=
      translatedMethodFor(newMethod, stackValue(argumentCount));

    {
      oop *frame= stackPointer - nargs;	// location of receiver
      acc->stack= frame + 1;
      acc->receiver= receiver= *frame;	// could suppress this field
      oldContext->sp= frame - 1;
      assert(oldContext->sp >= (oldContext->stack - 1));
      oldContext->ip= instructionPointer;
    }

    CompiledMethod *meth= newMethod;
    unsigned int hdr= meth->methodHeader();
    int ntemps= meth->tempCount(hdr);
    oop nilOop= nilObj;

    acc->pseudo=   0;
    acc->closure=  0;
    acc->translatedMethod= translatedMethod= tm;
#ifdef USE_METHOD
    method= tm->compiledMethod();
#endif

    for (int i= ntemps - nargs; i != 0; --i) push(nilOop);

    instructionPointer= (word *)tm->activatedEntry;
    temporaryPointer= acc->stack;
    activated= true;

    activeCachedContext= acc;

    assert(stackPointer == acc->stack + ntemps - PreInc);

    noteActivation(receiver, tm, instructionPointer, acc, stackPointer);
  }
}


///
/// PRIMITIVES
///


void primitiveBlockCopy(void)
{
  CachedContext *acc= activeCachedContext;
  Context *cx= stackValue(1)->asContext();
  MethodContext *home;
  // check the common case first: pushActiveCtx pushConst blockCopy
  if (cx == acc->pseudo->asContext()) {
    if (acc->closure != 0)
      home= acc->closure->home;
    else
      home= cx->asMethodContext();
    pushRemappableOop(home);
    BlockContext *closure= new BlockContext;
    home= popRemappable(MethodContext);
    oop initialIP= Object::integer(translatedMethod->bytecodeIndex(instructionPointer) + 2);
    closure->pc=      initialIP;
    closure->stackp=  Object::integer(0);
    closure->nargs=   stackValue(0);
    closure->startpc= initialIP;
    closure->home=    home;
    drop(2);	// block argument count, rcvr
    push(closure);
    assert(stackPointer >= (activeCachedContext->stack - 1));
    return;
  }
  error("primitiveBlockCopy: receiver is not thisContext");
}


void primitiveValue(void)
{
  BlockContext *closure= stackValue(argumentCount)->asBlockContext();
  assert(closure->isBlockContext());
  successFlag= successFlag && closure->nargs->isInteger();
  int blockArgumentCount= closure->argumentCount();
  successFlag= successFlag &&
    (argumentCount == blockArgumentCount) &&
    (closure->sender == nilObj);
  if (successFlag) {
    // activate block in context cache
    CachedContext *oldContext= activeCachedContext;
    CachedContext *acc= oldContext->child();
    if (acc == lowestCachedContext) {
      pushRemappableOop(closure);
      flushLowestCachedContext();
      closure= popRemappable(BlockContext);
    }
    MethodContext *home= closure->home;
    TranslatedMethod *tm;
    oop *tp;
    oop rp;
    if (home->isPseudo()) {
      CachedContext *ccx= home->asPseudoContext()->cachedContext();
      tm= ccx->translatedMethod;
      tp= ccx->stack;
      rp= ccx->receiver;
    } else {
      if (home->receiverMap == nilObj) {
	pushRemappableOop(closure);
	pushRemappableOop(home);
	tm= translatedMethodFor(home->method, home->receiver);
	home= popRemappable(MethodContext);
	closure= popRemappable(BlockContext);
	home->receiverMap= tm->asOop();
      } else {
	tm= TranslatedMethod::forOop(home->receiverMap);
      }
      tp= home->stack;
      rp= home->receiver;
      home->beRoot();
    }

    acc->pseudo=	   0;
    acc->closure=	   closure;
    acc->translatedMethod= tm;
#ifdef USE_METHOD
    method= tm->compiledMethod();
#endif
    translatedMethod= tm;
    acc->receiver=	   receiver= rp;

    {
      oop *frame= stackPointer - blockArgumentCount;	// locn of block
      acc->stack= frame + 1;
      oldContext->sp= frame - 1;
      assert(oldContext->sp >= (oldContext->stack - 1));
      oldContext->ip= instructionPointer;
    }

    const int startpc= closure->startpc->integerValue();

    instructionPointer= (word *)tm->instructionPointer(startpc);
    temporaryPointer= tp;

    activeCachedContext= acc;

    assert(stackPointer == acc->stack + blockArgumentCount - PreInc);

    noteActivation(receiver, tm, instructionPointer, acc, stackPointer);
  }
}


void primitiveValueWithArgs(void)
{
  Array *argumentArray= pop()->asArray();
  BlockContext *closure= pop()->asBlockContext();
  assert(closure->isBlockContext());
  successFlag= successFlag && closure->nargs->isInteger();
  int blockArgumentCount= closure->argumentCount();
  successFlag= successFlag && (argumentArray->isArray());
  if (successFlag) {
    int arrayArgumentCount= argumentArray->wordLength();
    successFlag= successFlag &&
      (arrayArgumentCount == blockArgumentCount) &&
      (closure->sender == nilObj);
  }
  if (successFlag) {
    // activate block in context cache
    CachedContext *oldContext= activeCachedContext;
    CachedContext *acc= oldContext->child();
    if (acc == lowestCachedContext) {
      pushRemappableOop(argumentArray);
      pushRemappableOop(closure);
      flushLowestCachedContext();
      closure= popRemappable(BlockContext);
      argumentArray= popRemappable(Array);
    }
    MethodContext *home= closure->home;
    TranslatedMethod *tm;
    oop *tp;
    oop rp;
    if (home->isPseudo()) {
      CachedContext *ccx= home->asPseudoContext()->cachedContext();
      tm= ccx->translatedMethod;
      tp= ccx->stack;
      rp= ccx->receiver;
    } else {
      if (home->receiverMap == nilObj) {
	pushRemappableOop(argumentArray);
	pushRemappableOop(closure);
	pushRemappableOop(home);
	tm= translatedMethodFor(home->method, home->receiver);
	home= popRemappable(MethodContext);
	closure= popRemappable(BlockContext);
	argumentArray= popRemappable(Array);
	home->receiverMap= tm->asOop();
      } else {
	tm= TranslatedMethod::forOop(home->receiverMap);
      }
      rp= home->receiver;
      tp= home->stack;
      home->beRoot();
    }

    acc->pseudo=   0;
    acc->closure=  closure;
    acc->translatedMethod=   tm;
#ifdef USE_METHOD
    method= tm->compiledMethod();
#endif
    translatedMethod= tm;
    acc->receiver= receiver= rp;

    {
      oop *frame= stackPointer;	// empty local stack
      oldContext->sp= frame;
      assert(oldContext->sp >= (oldContext->stack - 1));
      oldContext->ip= instructionPointer;
      acc->stack= frame + 1;
      oop *arg= argumentArray->oops - 1;
      for (int i= blockArgumentCount; i != 0; --i) push(*++arg);
    }

    wordCopy(acc->stack, argumentArray->oops, blockArgumentCount);

    const int startpc= closure->startpc->integerValue();

    instructionPointer= (word *)tm->instructionPointer(startpc);
    temporaryPointer= tp;

    activeCachedContext= acc;

    assert(stackPointer == acc->stack + blockArgumentCount - PreInc);

    noteActivation(receiver, tm, instructionPointer, acc, stackPointer);
  } else {
    unPop(2);
  }
}


void primitiveDoPrimitiveWithArgs(void)
{
  Array *argumentArray= stackTop()->asArray();
  oop primIdx= stackValue(1);
  int arraySize= argumentArray->wordLength();
  successFlag= successFlag &&
    (arraySize + activeCachedContext->stackIndex() < 32) &&
    (argumentArray->isArray()) &&
    (primIdx->isInteger());
  if (successFlag) {
    drop(2);
    primitiveIndex= primIdx->integerValue();
    argumentCount= arraySize;
    for (int index= 0; index < arraySize; ++index)
      push(argumentArray->at(index));
    pushRemappableOop(argumentArray);
    successFlag= primitiveResponse();
    argumentArray= popRemappable(Array);
    assert((activeCachedContext == 0) ||
	   (stackPointer >= (activeCachedContext->stack - 1)));
    if (successFlag) return;
    drop(arraySize);
    push(primIdx);
    push(argumentArray);
    argumentCount= 2;
  }
}


void primitivePerform(void)
{
  //oop performSelector;
  oop newReceiver;

  //performSelector= messageSelector;
  messageSelector= stackValue(argumentCount - 1);
  newReceiver= stackValue(argumentCount);
  argumentCount-= 1;
  lookupMethodInClass(newReceiver->fetchClass());
  successFlag= (successFlag && (newMethod->argCount() == argumentCount));
  if (successFlag) {
    wordCopy(stackPointer - argumentCount,
	     stackPointer - argumentCount + 1,
	     argumentCount);
    drop(1);
    executeNewMethod();
    assert((activeContext == 0) ||
	   (stackPointer >= (activeCachedContext->stack - 1)));
    successFlag= true;
  } else {
    argumentCount+= 1;
    //messageSelector= performSelector;
  }
}


void primitivePerformWithArgs(void)
{
  Array *argumentArray= pop()->asArray();
  int arraySize= argumentArray->wordLength();
  successFlag= successFlag &&
    (activeCachedContext->stackIndex() + arraySize < 32) &&
    (argumentArray->isArray());
  if (successFlag) {
    //oop performSelector= messageSelector;
    messageSelector= pop();
    oop thisReceiver= stackTop();
    argumentCount= arraySize;
    wordCopy(stackPointer + 1,
	     argumentArray->oops,
	     argumentCount);
    stackPointer+= argumentCount;
    lookupMethodInClass(fetchClassOf(thisReceiver));
    successFlag= successFlag && (newMethod->argCount() == argumentCount);
    if (successFlag) {
      executeNewMethod();
      assert((activeCachedContext == 0) ||
	     (stackPointer >= (activeCachedContext->stack - 1)));
      successFlag= true;
    } else {
      drop(argumentCount);
      assert(stackPointer >= (activeCachedContext->stack - 1));
      push(messageSelector);
      push(argumentArray);
      argumentCount= 2;
      //messageSelector= performSelector;
    }
  } else {
    unPop(1);
  }
}


///
/// Jitter entry point
///


/* Activate a new method in the cache interpreter.  Return to caller
 * when there are no more cached contexts in which to execute.  This
 * method is called only when the context cache is empty.
 *
 *	activeContext	= sending Context
 *	newMethod	= CompiledMethod to activate
 *	argumentCount	= number of arguments
 */

inline static void cachedActivateNewMethod(void)
{
  int nargs= argumentCount;

  assert(stackValue(nargs)->fetchClass() != ClassPseudoContext);

  TranslatedMethod *tm=
    translatedMethodFor(newMethod, stackValue(nargs));

  // set up the initial cachedContext

  drop(nargs + 1);	// drop receiver and arguments

  // storeContextRegisters(activeContext);
  activeContext->pc=
    Object::integer(method->bytecodeIndex((byte *)instructionPointer));
  activeContext->stackPointer(stackPointer);

  CachedContext *acc= contextCache;
  activeCachedContext= acc;
  lowestCachedContext= acc;

  CompiledMethod *meth= newMethod;
  unsigned int hdr= meth->methodHeader();
  int ntemps= meth->tempCount(hdr);
  oop nilOop= nilObj;

  topStableContext= activeContext;

  acc->pseudo	= 0;
  acc->closure	= 0;
  acc->translatedMethod= tm;
#ifdef USE_METHOD
  method= tm->compiledMethod();
#endif
  translatedMethod= tm;
  acc->receiver	= receiver= stackValue(-1);

  acc->stack	= stackCache;

  wordCopy(acc->stack, stackPointer + 2, nargs);
  wordFill(acc->stack + nargs, nilOop, ntemps - nargs);

  instructionPointer= (word *)tm->activatedEntry;

  stackPointer= acc->stack + ntemps - OneRel;
  temporaryPointer= acc->stack;
  activated= true;

  noteEntry(receiver, tm, instructionPointer,
	    activeCachedContext, stackPointer);
}


///
/// DEBUG
///


#ifdef DEBUG
void okayJitterObjects(void)
{
  okayInterpreterObjects();
  okayFields(returnResult);
  okayFields(receiverClass);
  CachedContext *ccp= activeCachedContext;
  storeCachedContextRegisters(ccp);
  ccp->okayOops();
  while (ccp != lowestCachedContext) {
    ccp= ccp->parent();
    ccp->okayOops();
  }
  Context *ctx= topStableContext;
  while (ctx->notNil()) {
    okayFields(topStableContext);
    ctx= ctx->sender;
  }
}
#endif


///
/// COMPILER HOOKS
///


#include "sqVirtualMachine.h"
#include "hooks.h"


void j_initialiseHook(void)
{
  if (contextCacheSize == 0) {
    // first-time initialisation
    extern void printVersion(void);
    printVersion();
    if (AllocsBetweenGCs != allocationsBetweenGCs) {
      allocationsBetweenGCs= AllocsBetweenGCs;
      printf("*** WARNING: GC interval: %d\n", allocationsBetweenGCs);
    }
  }

  contextCacheSize= ContextCacheSize;

  if (contextCacheSize < 16)
      printf("*** WARNING: ccache size: %d\n", contextCacheSize);

  contextCache= (CachedContext *)
    malloc(contextCacheSize * sizeof(CachedContext));

  highestCachedContext= contextCache + contextCacheSize - 1;

  stackCache= (oop *)malloc(contextCacheSize * 32 * sizeof(oop));

  if (contextCache == 0 || stackCache == 0)
    error("cannot allocate cache memory\n");

  PRINTF(("sizeof(oop)   %d\n", sizeof(oop)));
  PRINTF(("sizeof(Obj)   %d\n", sizeof(Object)));
  PRINTF(("sizeof(sCx)   %d\n", SmallContextSize));
  PRINTF(("sizeof(lCx)   %d\n", LargeContextSize));
  PRINTF(("contextCache  %p [%d]\n", contextCache, contextCacheSize));

  temporaryPointer= 0;
  topStableContext= nilObj->asContext();
  returnResult= nilObj;
  receiverClass= nilObj->asClass();

  ccBitsPseudoContext= ClassPseudoContext->ccBits();
  ccBitsMethodContext= ClassMethodContext->ccBits();
  ccBitsBlockContext= ClassBlockContext->ccBits();

  PRINTF(("BlockContext  cc=0x%05x\n", ccBitsBlockContext));
  PRINTF(("MethodContext cc=0x%05x\n", ccBitsMethodContext));
  PRINTF(("PseudoContext cc=0x%05x\n", ccBitsPseudoContext));

  if (ccBitsPseudoContext == 0) error("PseudoContext is not compact");
  if (ccBitsMethodContext == 0) error("MethodContext is not compact");
  if (ccBitsBlockContext  == 0) error("BlockContext is not compact");

  initialiseTranslator();

  topStableContext= nilObj->asContext();
  receiverClass= nilObj->asClass();

  // clear all receiverMap slots to nil
  oop obj= Object::firstObject();
  while (obj != 0) {
    if (obj->isMethodContext()) {
      //obj->clearRootBit();
      obj->asMethodContext()->receiverMap= nilObj;
    }
    obj= obj->nextObject();
  }
  assert(externalRegisters);
  runJitter(true);			// initialise opcode table
  assert(externalRegisters);

  printf("compiler initialised\n");
#ifdef SPY
  extern void spyInitialise(void);
  spyInitialise();
#endif
}


void j_releaseHook(void)
{
  if (contextCache) free(contextCache);
  if (stackCache) free(stackCache);
  releaseTranslator();
  // clear all receiverMap slots to nil
  oop obj= Object::firstObject();
  while (obj != nilObj) {
    if (obj->isMethodContext()) {
      obj->clearRootBit();
      obj->asMethodContext()->receiverMap= nilObj;
      obj= obj->nextObject();
    }
  }
  printf("compiler released\n");
#ifdef SPY
  extern void spyRelease(void);
  spyRelease();
#endif
}


int inCsq= 0;
int inSnapshot= 0;

int j_activateMethodHook(void)
{
  assert(inCsq == 0);
  assert(activeCachedContext == 0);
#ifdef GATHER_STATS
  lastEntry= ioMicroMSecs();
  if (lastExit != 0) {
    interpMSecs+= lastEntry - lastExit;
  }
#endif
  ++inCsq;
  cachedActivateNewMethod();
  assert(activeCachedContext != 0);
  activeContext= nilObj->asContext();
  theHomeContext= nilObj->asMethodContext();
  returnResult= nilObj;
  receiverClass= nilObj->asClass();
#ifndef USE_METHOD
  method= nilObj->asCompiledMethod();
#endif
#ifdef GATHER_STATS
  ++stat_jitterEntries;
#endif

  runJitter(false);	// run the dynamic compiler

  assert(inCsq == 1);
  --inCsq;
#ifdef GATHER_STATS
  lastExit= ioMicroMSecs();
  jitterMSecs+= lastExit - lastEntry;
#endif
  assert(activeCachedContext == 0);
  assert(activeContext->isMethodContext() || activeContext->isBlockContext());
  assert(theHomeContext->isMethodContext());
  assert(method == theHomeContext->method);
  assert(receiver == theHomeContext->receiver);
  reclaimableContextCount= 0;
  return true;	// true => method already activated
}


int j_flushCacheHook(oop target)
{
  if (target->isCompiledMethod()) {
    TranslatedMethod *tm=
      methodMemoizer->translatedMethodFor(target->asCompiledMethod());
    if (tm != 0) tm->invalidate();
  } else {
    dumpObject("unknown target class in flushCache: ", target);
    error("moritori te salutant");
  }
  return 0;
}


int j_preGCHook(int fullGCFlag)
{
#ifdef GCTRACE
  printf("<%d", fullGCFlag);
  fflush(stdout);
#endif
  if (inSnapshot) return 0;
  CachedContext *ccx= activeCachedContext;
  if (ccx != 0) {
    storeCachedContextRegisters(ccx);
    PRINTF(("activeCachedContext %p\n", activeCachedContext));
    PRINTF(("stackPointer        %p\n", stackPointer));
    PRINTF(("instructionPointer  %p\n", instructionPointer));
  }
#ifdef DEBUG
  okayJitterObjects();
#endif
  return 0;
}


int j_markHook(void)
{
  if (inSnapshot) return 0;
#ifdef GCTRACE
  printf("?");
  fflush(stdout);
#endif
  CachedContext *ccx= activeCachedContext;
  assert(((ccx == 0) && (activeContext->notNil())) ||
	 ((ccx != 0) && (activeContext->isNil())));
  if (ccx != 0) {
    // mark internal registers
    if (!returnResult->isInteger())	returnResult->mark();
    if (receiverClass->notNil())	receiverClass->mark();
    assert(topStableContext->isMethodContext() ||
	   topStableContext->isBlockContext());
    topStableContext->mark();
    // mark Interpreter registers that are ignored because
    // activeContext == nil (see Interpreter>>markAndTraceInterpreterOops)
    if (!receiver->isInteger()) receiver->mark();
    method->mark();
    assert(theHomeContext->isNil());
    // mark the context cache
    for (;;) {
      ccx->mark();
      if (ccx == lowestCachedContext) break;
      ccx= ccx->parent();
    }
    // mark the stack cache
    {
      const oop *limit= stackPointer + 1;
      for (oop *p= stackCache; p != limit; ++p) {
	oop o= *p;
	if (!o->isInteger()) o->mark();
      }
    }
  }
  // mark the pervasive translator state
  methodMemoizer->mark();
  classMemoizer->mark();
  return 0;
}


int j_mapHook(int memStart, int memEnd)
{
  if (inSnapshot) return 0;
#ifdef GCTRACE
  printf("@");
  fflush(stdout);
#endif
  CachedContext *ccx= activeCachedContext;
  if (ccx != 0) {
    // remap internal registers
    if (!returnResult->isInteger()) returnResult= returnResult->remap();
    receiverClass= receiverClass->remap()->asClass();
    topStableContext= topStableContext->remap()->asContext();
    // receiver/method/theHomeContext remapped in Interpreter
    // remap the context cache
    for (;;) {
      ccx->remap();
      if (ccx == lowestCachedContext) break;
      ccx= ccx->parent();
    }
    // remap the stack cache
    {
      const oop *limit= stackPointer + 1;
      for (oop *p= stackCache; p != limit; ++p) {
	oop o= *p;
	if (!o->isInteger()) *p= o->remap();
      }
    }
  }
  // mark the pervasive translator state
  methodMemoizer= (MethodMemoizer *)methodMemoizer->remap();
  classMemoizer= (ClassMemoizer *)classMemoizer->remap();
  return 0;
}


int j_postGCHook(void)
{
#ifdef GCTRACE
  printf(">\n");
  fflush(stdout);
#endif
  if (inSnapshot) return 0;
  CachedContext *ccx= activeCachedContext;
  if (ccx != 0) {
    instructionPointer= ccx->ip;
    stackPointer= ccx->sp;
    assert(stackPointer >= (ccx->stack - 1));
    temporaryPointer= ccx->temporaryPointer();
    PRINTF(("activeCachedContext %p\n", activeCachedContext));
    PRINTF(("stackPointer        %p\n", stackPointer));
    PRINTF(("instructionPointer  %p\n", instructionPointer));
  }
#ifdef DEBUG
  okayJitterObjects();
#endif

  UPDATE_SPY();
  return 0;
}


int j_processChangeHook(void)
{
  if (activeCachedContext != 0) {
    activeContext= flushContextCache();
    fetchContextRegisters(activeContext);
  }
  UPDATE_SPY();
  return 0;
}


int j_preSnapshotHook(void)
{
  PRINTF(("preSnapshotHook\n"));
  if (inCsq) {
    activeContext= flushContextCache();
    // setup for storeContextRegisters in primitiveSnapshot
    fetchContextRegisters(activeContext);
  }
  j_releaseHook();
  inSnapshot= 1;
  return 0;
}


int j_postSnapshotHook(void)
{
  PRINTF(("postSnapshotHook\n"));
  j_initialiseHook();
  inSnapshot= 0;
  assert(activeCachedContext == 0);
  return 0;
}


///
/// EXECUTION STATISTICS
///


#ifdef GATHER_STATS
double icRate= 0.0, icNow= 0.0;
double mcRate= 0.0, mcNow= 0.0;
double acRate= 0.0, acNow= 0.0;
double linkedRate= 0.0, linkedNow= 0.0;
double jitterRate= 0.0, jitterNow= 0.0;
double quickRate= 0.0, quickNow= 0.0;
#endif

#define PC(X, Y)	((double)(X)/(double)(Y) * 100.0)


void printMemoStats(char *buf)
{
  if (codeCache == 0) return;

  int localTotal= codeCache - lastCache + cacheTotal;

  buf+= sprintf
    (buf,
     "code cache: %d bytes in %d cache(s), %.2f%% fragmentation\n",
     localTotal, cacheCount,
     (cacheCount == 1)
       ? 0.0
       : PC(cacheLossage, ((cacheCount - 1) * CodeCacheSize)));

  int memoMax= 0;
  int arrayMax= 0;
  int memoLists= 0;
  int memoTotal= 0;
  int memoBytes= 0;
  int arrayTotal= 0;

  memoBytes= (3 + methodMemoizer->wordLength()) * sizeof(oop);

  for (int i= 0; i < MethodMemoizerSize; ++i) {
    MemoArray<MethodMemo> *memoArray= methodMemoizer->at(i);
    int memoSize= 0;
    int arraySize= 0;
    if (memoArray->notNil()) {
      assert(memoArray->fetchClass() == ClassArray);
      ++memoLists; 
      memoSize= memoArray->memoCount();
      arraySize= memoArray->wordLength();
      memoTotal+= memoSize;
      arrayTotal+= arraySize;
      memoBytes+= (1 + arraySize) * sizeof(oop)
	+ memoSize * sizeof(MethodMemo);
    }
    if (memoSize > memoMax) memoMax= memoSize;
    if (arraySize > arrayMax) arrayMax= arraySize;
  }

  buf+= sprintf
    (buf,
     "m-memoiser: %d/%d entries (%.1f%%) in %d/%d buckets, %d bytes total\n",
     memoTotal, arrayTotal, PC(memoTotal, arrayTotal),
     memoLists, methodMemoizer->wordLength(), memoBytes);

  buf+= sprintf
    (buf,
     "            depth %d/%d (max) %.2f (avg)\n",
     memoMax, arrayMax, (float)memoTotal/(float)memoLists);

  memoMax= 0;
  arrayMax= 0;
  memoLists= 0;
  memoTotal= 0;
  memoBytes= 0;
  arrayTotal= 0;

  memoBytes= (3 + classMemoizer->wordLength()) * sizeof(oop);

  for (int i= 0; i < ClassMemoizerSize; ++i) {
    MemoArray<Class> *memoArray= classMemoizer->at(i);
    int memoSize= 0;
    int arraySize= 0;
    if (memoArray->notNil()) {
      assert(memoArray->fetchClass() == ClassArray);
      ++memoLists; 
      memoSize= memoArray->memoCount();
      arraySize= memoArray->wordLength();
      memoTotal+= memoSize;
      arrayTotal+= arraySize;
      memoBytes+= (1 + arraySize) * sizeof(oop);
    }
    if (memoSize > memoMax) memoMax= memoSize;
    if (arraySize > arrayMax) arrayMax= arraySize;
  }

  buf+= sprintf
    (buf, 
     "c-memoiser: %d/%d entries (%.1f%%) in %d/%d buckets, %d bytes total\n",
     memoTotal, arrayTotal, PC(memoTotal, arrayTotal),
     memoLists, classMemoizer->wordLength(), memoBytes);

  buf+= sprintf
    (buf,
     "            depth %d/%d (max) %.2f (avg)\n",
     memoMax, arrayMax, (float)memoTotal/(float)memoLists);

#ifdef GATHER_STATS

  extern unsigned stat_linkedSends, stat_unlinkedSends;

  linkedNow= PC(stat_linkedSends, stat_linkedSends + stat_unlinkedSends);

  if (finite(linkedNow))
    linkedRate= linkedRate * (1.0 - STAT_DECAY) + linkedNow * STAT_DECAY;
  //  else
  //    linkedNow= 100.0;
		
  extern unsigned long long interpMSecs, jitterMSecs;

  jitterNow= PC(jitterMSecs, jitterMSecs + interpMSecs);
  if (finite(jitterNow))
    jitterRate= jitterRate * (1.0 - STAT_DECAY) + jitterNow * STAT_DECAY;

  extern unsigned stat_normalActivations, stat_quickActivations;

  quickNow= PC(stat_quickActivations,
	       stat_quickActivations + stat_normalActivations);
  if (finite(quickNow))
    quickRate= quickRate * (1.0 - STAT_DECAY) + quickNow * STAT_DECAY;

  buf+= sprintf(buf, "send insns: %.1f%% linked, %.1f%% lookup, %.1f%% quick; jitter rate: %.1f%%\n",
		linkedRate, 100.0 - linkedRate, quickRate, jitterRate);

  // reset counters
  stat_linkedSends= stat_unlinkedSends= 0;
  jitterMSecs= interpMSecs= 0;
  stat_normalActivations= stat_quickActivations= 0;

  extern unsigned stat_icHits, stat_icMisses;
  extern unsigned stat_mcHits, stat_mcMisses;
  extern unsigned stat_acHits, stat_acMisses;

  icNow= PC(stat_icHits, stat_icHits + stat_icMisses);
  mcNow= PC(stat_mcHits, stat_mcHits + stat_mcMisses);
  acNow= PC(stat_acHits, stat_acHits + stat_acMisses);

  if (finite(icNow)) icRate= icRate * (1.0 - STAT_DECAY) + icNow * STAT_DECAY;
  //  else icNow= 100.0;
  if (finite(mcNow)) mcRate= mcRate * (1.0 - STAT_DECAY) + mcNow * STAT_DECAY;
  //  else mcNow= 100.0;
  if (finite(acNow)) acRate= acRate * (1.0 - STAT_DECAY) + acNow * STAT_DECAY;
  //  else acNow= 100.0;

  buf+= sprintf(buf,
		"cache hits: %.1f%% inline, %.1f%% method, %.1f%% accessors\n",
		icRate, mcRate, acRate);

  // reset counters
  stat_icHits= stat_icMisses=
    stat_mcHits= stat_mcMisses=
    stat_acHits= stat_acMisses= 0;
#endif
}


void memoStats(void)
{
  char buf[80*8];	// enough room for 6 lines of 80 cols
  printMemoStats(buf);
  fputs(buf, stdout);
}

