// cache.h -- context cache
//
// Author: Ian.Piumarta@inria.fr
//
// last edited: Tue Feb 23 15:17:08 1999 by piumarta (Ian Piumarta) on pingu

#ifndef _cache_h_
#define _cache_h_

#include "util.h"

class CachedContext;


extern int contextCacheSize;

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

extern oop *stackCache;
extern oop *temporaryPointer;

extern Context *topStableContext;

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


class PseudoContext : public Object
{
public:
  oop		cacheAddress;

  inline void *operator new(size_t ignored) {
    return allocateSmall(ClassPseudoContext, LargeContextSize);
  }

  inline CachedContext *cachedContext(void) {
    return (CachedContext *)((int)cacheAddress - 1);
  }

  inline void cachedContext(CachedContext *ccx) {
    cacheAddress= (oop)((int)ccx + 1);
  }

  inline void beMethodContext(void)	{ ccBits(ccBitsMethodContext); }
  inline void beBlockContext(void)	{ ccBits(ccBitsBlockContext); }
};


class CachedContext
{
public:
  word		   *ip;
  oop		   *sp;
  PseudoContext	   *pseudo;
  BlockContext	   *closure;	// ALWAYS stable
  TranslatedMethod *translatedMethod;
  oop		    receiver;
  oop		   *stack;

  inline CachedContext *parent(void) {
    return (this == contextCache) ? highestCachedContext : (this - 1);
  }

  inline CachedContext *child(void) {
    return (this == highestCachedContext) ? contextCache : (this + 1);
  }

  inline bool isBlock(void) { return closure != 0; }
  inline bool isMethod(void) { return closure == 0; }

  inline CompiledMethod *compiledMethod(void) {
    return translatedMethod->compiledMethod();
  }

  inline oop *temporaryPointer(void) {
    if (isMethod()) return stack;
    // BlockContext
    MethodContext *home= closure->home;
    if (home->isPseudo())
      return home->asPseudoContext()->cachedContext()->stack;
    else
      return home->stack;
  }

  inline int stackIndex(void) { return (sp - stack) + OneRel; }

  inline PseudoContext *allocatePseudoContext(void) {
    if (pseudo == 0) {
      PseudoContext *pc= new PseudoContext;
      pc->cachedContext(this);
      return pseudo= pc;
    }
    return pseudo;
  }

private:

  // Note: this method leaves pc indeterminate

  Context *flush(Context *sender) {
    int stackp= stackIndex();
    pushRemappableOop(sender);
    register PseudoContext *cx= allocatePseudoContext();
    sender= popRemappable(Context);
    cx->beRoot();
    // ContextPart
    cx->asContext()->sender= sender;
    cx->asContext()->stackp= Object::integer(stackp);
    if (isBlock()) {
      assert(closure->isBlockContext());
      PRINTF(("flush %p [%d] -> BlockContext\n", pseudo, this - contextCache));
      cx->beBlockContext();
      // BlockContext
      cx->asBlockContext()->nargs   = closure->nargs;
      cx->asBlockContext()->startpc = closure->startpc;
      cx->asBlockContext()->home    = closure->home;
    } else {
      PRINTF(("flush %p [%d] -> MethodContext\n", pseudo, this - contextCache));
      cx->beMethodContext();
      // MethodContext
      cx->asMethodContext()->method   = translatedMethod->compiledMethod();
      cx->asMethodContext()->receiver = receiver;
    }
    // stack
    assert((byte *)(&pseudo->asContext()->stack[stackp]) <
	   (byte *)pseudo + pseudo->sizeBits());
    wordCopy(cx->asContext()->stack, stack, stackp);

    // nil the remainder of the stack to discard temp objects
#if CLEAR_CONTEXTS
    {
      register oop nilOop= nilObj;
      wordFill(cx->asContext()->stack + stackp,
	       nilOop,
	       (((cx->sizeBits() - sizeof(Context)) / sizeof(oop))
		- stackp));
    }
#endif
    return cx->asContext();
  }

public:

  // eject cached context because of return

  inline void unlink(Context *link) {
    if (pseudo != 0) flush(link);
  }

  // eject cached context because of overflow

  inline Context *stabilise(Context *link) {
    // assumes: context regsisters have been stored
    Context *cx= flush(link);
    cx->pc     = Object::integer(translatedMethod->bytecodeIndex(ip));
#ifdef DEBUG
    okayFields(cx);
#endif
    return cx;
  }

  // GC

  inline void mark(void) {
    if (pseudo != 0) pseudo->mark();
    if (closure != 0) closure->mark();
    if (!receiver->isInteger()) receiver->mark();
  }

  inline void remap(void) {
    if (pseudo != 0) pseudo= pseudo->remap()->asPseudoContext();
    if (closure != 0) closure= closure->remap()->asBlockContext();;
    if (!receiver->isInteger()) receiver= receiver->remap();
  }

  // DEBUG

#ifdef DEBUG
  void okayOops(void) {
    if (pseudo != 0) okayFields(pseudo);
    if (closure != 0) okayFields(closure);
    okayFields(translatedMethod->compiledMethod());
    okayFields(receiver);
    oop *limit= sp + 1;
    for (oop *p= stack; p != limit; ++p) {
      okayFields(*p);
    }
  }
#endif
};


#define fetchContextRegisters(ctx) { \
  register MethodContext *home= (ctx)->home(); \
  if ((oop)(ctx) != (oop)home) home->beRoot(); \
  theHomeContext= home; \
  receiver= home->receiver; \
  method= home->method; \
  instructionPointer= (word *)method->bytecodePointer((ctx)->pc->integerValue()); \
  stackPointer= (ctx)->stackPointer(); \
  assert(stackPointer >= (ctx->stack - 1)); \
}

#define storeCachedContextRegisters(ccx) { \
  (ccx)->ip= instructionPointer; \
  (ccx)->sp= stackPointer; \
  assert(stackPointer >= ((ccx)->stack - 1)); \
}

#define internalFetchCachedContextRegisters(ccx) { \
  localIP=	      (ccx)->ip; \
  localSP=	      (ccx)->sp; \
  translatedMethod=   (ccx)->translatedMethod; \
  method=	      (ccx)->translatedMethod->compiledMethod(); \
  receiver=	      (ccx)->receiver; \
  temporaryPointer=   (ccx)->temporaryPointer(); \
  assert(localSP >= ((ccx)->stack - 1)); \
}


extern Context *flushContextCache(void);
extern void flushLowestCachedContext(void);


#endif _cache_h_

