// translate.h -- translator declarations
//
// Author: Ian.Piumarta@inria.fr
//
// last edited: Thu Feb 25 19:01:32 1999 by piumarta (Ian Piumarta) on pingu

#ifndef _translate_h_
#define _translate_h_


#include "opcodes.h"


extern void *codeCache;
extern void *codeCacheLimit;

extern void *lastCache;

extern int   cacheCount;
extern int   cacheTotal;
extern int   cacheLossage;

extern void allocateCodeCache(void);

class TranslatedMethod;


class CacheFragment
{
public:
  inline void *operator new(size_t size) {
    if (codeCache + size >= codeCacheLimit) allocateCodeCache();
    void *base= codeCache;
    codeCache+= size;
    return base;
  }

  inline void operator delete(void *base) {
    error("this cannot happen");
  }

  inline CacheFragment(void) {}

  inline CacheFragment(const CacheFragment &other) {
    error("this cannot happen");
  }

  inline ~CacheFragment(void) {
    error("this cannot happen");
  }

  inline const CacheFragment &operator=(const CacheFragment &other) {
    error("this cannot happen");
    return *this;
  }
};


///
/// PC MAP
///


class PcMapping
{
public:
  unsigned short vPC __attribute__((packed));
  unsigned short nPC __attribute__((packed));
  PcMapping(void) {}
  PcMapping(unsigned short v, unsigned short n) : vPC(v), nPC(n) {}
};


class PcMap
{
public:
  int	    size;
  PcMapping mappings[0];	// some compilers won't like this

  inline void *operator new(size_t size) {
    void *base= codeCache;
    codeCache+= size;
    return base;
  }

  inline void addMapping(unsigned short v, unsigned short n) {
    mappings[size++]= PcMapping(v, n);
  }

  inline bool checkPhase(int index, unsigned short v, unsigned short n) {
    if (mappings[index].vPC != v)
      error("phase error for vPC: %d -> %d", mappings[index].vPC, v);
    if (mappings[index].nPC != n)
      error("phase error for nPC: %d -> %d", mappings[index].nPC, n);
    return true;
  }

  inline int vPC(unsigned short nPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (mappings[index].nPC < nPC) low= index + 1; else high= index - 1;
    if ((low < size) && (mappings[low].nPC == nPC)) return mappings[low].vPC;
    error("nPC %d not found in map", nPC);
    return 0;
  }

  inline int nPC(unsigned short vPC) const {
    assert(size > 0);
    register int low= 0;
    register int high= size - 1;
    register int index;
    while ((index= (high + low) >> 1), (low <= high))
      if (mappings[index].vPC < vPC) low= index + 1; else high= index - 1;
    if ((low < size) && (mappings[low].vPC == vPC)) return mappings[low].nPC;
    error("vPC %d not found in map", vPC);
    return 0;
  }
};


///
/// MEMOISERS
///


// Note: object hashes are 12 bits wide, so it's pointless trying to
// use a memoiser with more than 4096 buckets.


template<class elt_t>
class ArrayOf : public Array
{
public:
  inline void *operator new(size_t ignored, size_t indexableSize) {
    return Object::allocate(ClassArray, indexableSize);
  }

  inline elt_t *at(int index) const {
    assert(fetchClass() == ClassArray);
    return (elt_t *)Array::at(index);
  }

  inline elt_t *atPut(int index, elt_t *elt) {
    assert(fetchClass() == ClassArray);
    checkStore(Array::atPut(index, (oop)elt));
    return elt;
  }

  inline elt_t *_atPut(int index, elt_t *elt) {
    assert(fetchClass() == ClassArray);
    Array::atPut(index, (oop)elt);
    return elt;
  }
};


template<class memo_t>
class MemoArray : public ArrayOf<memo_t>
{
public:
  typedef MemoArray<memo_t> memoArray_t ;

  inline int freeIndex(void) const {
    assert(fetchClass() == ClassArray);
    const int size= wordLength();
    for (int i= 0; i < size; ++i)
      if (at(i)->isNil()) return i;
    return -1;
  }

  inline int memoCount(void) const {
    assert(fetchClass() == ClassArray);
    const int size= wordLength();
    int count= 0;
    for (int i= 0; i < size; ++i)
      if (at(i)->notNil()) ++count;
    return count;
  }

  memoArray_t *grow(void) {
    assert(fetchClass() == ClassArray);
    const int oldSize= wordLength();
    const int newSize= (int)(oldSize * MemoArrayFactor) >? (oldSize + 1); 
    pushRemappableOop(this);
    memoArray_t *newArray= new(newSize) memoArray_t;	// GC!
    const memoArray_t *self= (memoArray_t *)popRemappableOop();
    // can use unchecked stores because of beRoot() below...
    for (int i= oldSize - 1; i >= 0; --i) {
      newArray->_atPut(i, self->at(i));
    }
    newArray->beRoot();
    assert(newArray->wordLength() == newSize);
    return newArray;
  }
};


class MemoIndex
{
public:
  unsigned short major __attribute__((packed));
  unsigned short minor __attribute__((packed));

  MemoIndex(unsigned short maj= 0, unsigned short min= 0)
    : major(maj), minor(min) {}

  inline bool operator==(MemoIndex &other) {
    // compare two 16-bit indices in one 32-bit operation
    return *(unsigned long *)this == *(unsigned long *)&other;
  }
};


template<class memo_t>
class Memoizer : public ArrayOf< MemoArray<memo_t> >
{
public:
  typedef Memoizer<memo_t> memoizer_t;
  typedef MemoArray<memo_t> memoArray_t;

  inline memo_t *memoAt(MemoIndex index) {
    return at(index.major)->at(index.minor);
  }

  inline int majorIndexFor(memo_t *memo) const {
    assert(fetchClass() == ClassArray);
    return memo->_hashBits() % wordLength();
  }

  MemoIndex add(memo_t *memo) {
    assert(fetchClass() == ClassArray);
    memoizer_t *self= this;
    int major= majorIndexFor(memo);
    memoArray_t *memoArray= at(major);
    if (memoArray->isNil()) {
      // no memo array
      pushRemappableOop(this);
      pushRemappableOop(memo);
      memoArray= new(MemoArraySize) memoArray_t;		// GC!
      memo=     (memo_t *)popRemappableOop();
      self= (memoizer_t *)popRemappableOop();
      self->atPut(major, memoArray);
    }
    int minor= memoArray->freeIndex();
    if (minor == -1) {
      // memo array overflow
      pushRemappableOop(self);
      pushRemappableOop(memo);
      memoArray= memoArray->grow();		// GC!
      memo=     (memo_t *)popRemappableOop();
      self= (memoizer_t *)popRemappableOop();
      self->atPut(major, memoArray);
      minor= memoArray->freeIndex();
      assert(minor != -1);
    }
    memoArray->atPut(minor, memo);
    return MemoIndex(major, minor);
  }
};


class MethodMemo : public Array
{
public:
  CompiledMethod *compiledMethod;
  oop		  translatedMethod;	// SmallInteger

  inline void *operator new(size_t ignored) {
    return Object::allocateSmall(ClassArray, sizeof(MethodMemo));
  }

  MethodMemo(void) {
    assert(fetchClass() == ClassArray);
    assert(wordLength() == 2);
  }

  // MethodMemos must hash on their CompiledMethod
  inline unsigned int _hashBits(void) const {
    assert(fetchClass() == ClassArray);
    return compiledMethod->_hashBits();
  }
};


class MethodMemoizer : public Memoizer<MethodMemo>
{
public:
  MethodMemoizer(void) {
    assert(fetchClass() == ClassArray);
    assert(wordLength() == MethodMemoizerSize);
  }

  inline CompiledMethod *compiledMethodAt(MemoIndex index) {
    return memoAt(index)->compiledMethod;
  }

  TranslatedMethod *translatedMethodFor(CompiledMethod *cm);

  inline void remove(MemoIndex index) const {
    assert(wordLength() > index.major);
    assert(at(index.major)->isArray());
    assert(at(index.major)->wordLength() > index.minor);
    assert(at(index.major)->at(index.minor)->isArray() ||
	   // Note: we can remove a method more than once if it's been linked
	   // at more than one send site, so expect occasional nils in here...
	   at(index.major)->at(index.minor)->isNil());
    at(index.major)->atPut(index.minor, (MethodMemo *)nilObj);
  }
};


class ClassMemoizer : public Memoizer<Class>
{
public:
  ClassMemoizer(void) {
    assert(fetchClass() == ClassArray);
    assert(wordLength() == ClassMemoizerSize);
  }

  inline Class *classAt(MemoIndex index) { return memoAt(index); }

  // this almost certainly should be inline
  MemoIndex indexFor(Class *cls) {
    assert(fetchClass() == ClassArray);
    int major= cls->_hashBits() % wordLength();
    memoArray_t *memoArray= at(major);
    int size; // declare before goto to avoid compiler warning
    if (memoArray->isNil()) goto fail;
    size= memoArray->wordLength();
    for (int minor= 0; minor < size; ++minor) {
      Class *probe= memoArray->at(minor);
      if (probe->isNil()) goto fail;
      if (probe == cls) return MemoIndex(major, minor);
    }
  fail:
    return add(cls);
  }
};


extern MethodMemoizer *methodMemoizer;
extern ClassMemoizer *classMemoizer;


///
/// InlineCache
///


class InlineCache : public CacheFragment
{
public:
  void		   *entry;
  bool		    supered;
  MemoIndex	    classIndex;
  word		    ccIndex;
  TranslatedMethod *translatedMethod;
  int		    argCount;
  InlineCache	   *nextCache;

  inline InlineCache(TranslatedMethod *tm, MemoIndex clsIdx,
		     bool super, InlineCache *next);

  inline Class *receiverClass(void) {
    return classMemoizer->classAt(classIndex);
  }

  inline CompiledMethod *compiledMethod(void) const;
};


///
/// TRANSLATED METHOD
///


class TranslatedMethod : public CacheFragment
{
public:
  void	      *code;
  void	      *activatedEntry;
  PcMap	      *map;
  MemoIndex    memoIndex;
  InlineCache *cacheList;

  // these must agree with the prologue generated in translateMethodFor()
  static const int ClassEntryOffset=	 0;
  static const int CompactEntryOffset=	 4;
  static const int ImmediateEntryOffset= 8;
  static const int UncheckedEntryOffset= 12;

  // these must agree with the prologue generated in translateMethodFor()
  static const int ClassEntrySkip=	2;
  static const int CompactEntrySkip=	1;
  static const int ImmediateEntrySkip=	0;

  TranslatedMethod(void *c, PcMap *m) : code(c), map(m), cacheList(0) {}

  inline oop asOop(void) const {
    return (oop)((int)this + 1);
  }

  static inline TranslatedMethod *forOop(oop obj) {
    return (TranslatedMethod *)((int)obj - 1);
  }

  inline CompiledMethod *compiledMethod(void) const {
    return methodMemoizer->compiledMethodAt(memoIndex);
  }

  inline int argCount(void) const {
    return compiledMethod()->argCount();
  }

  inline int bytecodeIndex(void *insnPtr) const {
    return map->vPC(insnPtr - code);
  }

  inline void *instructionPointer(int byteIdx) const {
    return code + map->nPC(byteIdx);
  }

  inline void *classEntry(void) const {
    return code + ClassEntryOffset;
  }
  inline void *compactEntry(void) const {
    return code + CompactEntryOffset;
  }
  inline void *immediateEntry(void) const {
    return code + ImmediateEntryOffset;
  }

  inline void *uncheckedEntry(void) const {
    return code + UncheckedEntryOffset;
  }

  inline InlineCache *inlineCacheFor(Class *cls, bool supered) {
    MemoIndex index= classMemoizer->indexFor(cls);
    for (InlineCache *ic= cacheList; ic != 0; ic= ic->nextCache)
      if (ic->classIndex == index && ic->supered == supered)
	// SHOULD PROMOTE THIS TO HEAD OF LIST!!!
	return ic;
    // create a new inline cache
    return cacheList= new InlineCache(this, index, supered, cacheList);
  }

  inline void invalidate(void) {
    *(word *)classEntry()=     OP(OpRelink);
    *(word *)compactEntry()=   OP(OpRelink);
    *(word *)immediateEntry()= OP(OpRelink);
    *(word *)uncheckedEntry()= OP(OpRelink);
    // memoiser invalidation must be deferred until next relink of a
    // send to the invalidated method, since invalidating an executing
    // method causes problems.
    // (We tacitly assume that CompiledMethod>>flushCache is not a
    // recursive method. ;^p)
  }
};


inline InlineCache::InlineCache(TranslatedMethod *tm,
				MemoIndex clsIdx,
				bool super,
				InlineCache *next)
{
  classIndex= clsIdx;
  translatedMethod= tm;
  argCount= tm->argCount();
  supered= super;
  nextCache= next;
  Class *cls= classMemoizer->classAt(clsIdx);
  ccIndex= cls->ccBits();
  if (super)
    entry= tm->uncheckedEntry();
  else if (ccIndex != 0)
    entry= tm->compactEntry();
  else if (cls == ClassSmallInteger)
    entry= tm->immediateEntry();
  else
    entry= tm->classEntry();
}

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

///
/// FUNCTIONS
///


extern void initialiseTranslator(void);
extern void releaseTranslator(void);

extern TranslatedMethod *translateMethodFor(CompiledMethod *meth, oop rcvr);

static inline TranslatedMethod *translatedMethodFor(CompiledMethod *vMeth,
						    oop rcvr)
{
  TranslatedMethod *tMeth= methodMemoizer->translatedMethodFor(vMeth);
  if (tMeth) {
    assert(methodMemoizer->compiledMethodAt(tMeth->memoIndex) == vMeth);
    // ** Note: here we should test if tMeth method was translated for
    //	  a receiver of the same class as the current receiver.  If not
    //	  we allocate a new TranslatedMethod for the current receiver
    //	  and share the pcMap and translated code of tMeth, WITHOUT
    //	  GENERATING ANY NEW CODE, and then append the new translated
    //	  method to the method list of the memoised translated method.
    return tMeth;
  }
  return translateMethodFor(vMeth, rcvr);
}

#endif _translate_h_

