// NativeMethod.h -- native code methods
//
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: Wed Sep 20 02:44:07 2000 by piumarta (Ian Piumarta) on emilia


#ifndef _j_NativeMethod_h
#define _j_NativeMethod_h


#include "Memoizer.h"
#include "PcMap.h"
#include "Cache.h"

#include "archdep.h"
#include "cachebits.h"


class Profiler;

// encapsulated native code method.

class NativeMethod
{
public:
  static NativeMethod *methods;	// comprehensive method list
  NativeMethod *nextMethod;	// previous NM in methods list

public:
  NativeMethod *next;		// next NativeMethod in specialisation chain
protected:
  friend void gen_compile(void);
  insn *start;			// start of native code -- do not use!
  insn *end;			// addr past last insn -- do not use!
public:
  insn *relink;			// start of (non-preactivated!) relink sequence
  insn *controlEntry;		// reload receiver -> activateEntry
  insn *entry;			// cache check, ...
  insn *siEntry;		// cache check, ...
  insn *ccEntry;		// cache check, ...
  insn *ncEntry;		// cache check, ...
  insn *checkedEntry;		// prim, preactivate, ctlPrim, activate, run...
  insn *activateEntry;		// activate, run...
  insn *activatedEntry;		// run...
  unsigned cacheType;		// 0=polyvalent, 1=SI, 2=CC, 3=NC, 4=IX
  MemoIndex methodIndex;	// my index in nativeMemoizer
  MemoIndex selectorIndex;	// index of my selector in selectorMemoizer
  MemoIndex methodClassIndex;	// index of my defining class in classMemoizer
  MemoIndex receiverClassIndex;	// index of my receiver's class in classMemoizer
  unsigned argumentCount;	// number of arguments
  unsigned temporaryCount;	// number of temps (excluding arguments)
  unsigned primitiveIndex;	// prim index or 0
public:
  PcMap *map;			// my PC map
  PcMap *blockMap;		// my block entry map
private:
  insn code[1];			// non-GNU compilers barf at [0]

public:

  static void initialise(void)
    {
      methods= 0;
    }

  static NativeMethod *find(Symbol *sel, int nArgs, Class *rCls, Class *lCls);
  static NativeMethod *find(Symbol *sel, int nArgs, oop rcvr, Class *lCls);
  static NativeMethod *find(Symbol *sel, int nArgs, oop rcvr);
  static NativeMethod *find(MethodContext *mcx);

  inline static NativeMethod *find(const BlockContext *bcx)
    {
      assert(bcx->isBlockContext());
      NativeMethod *nMeth= find(bcx->home);
      assert((bcx->pc->isNil())
	     || ((bcx->pc->isInteger())
		 && nMeth->includesVPC(bcx->pc->integerValue())));
      return nMeth;
    }

  inline static NativeMethod *find(Context *cx)
    {
      return (cx->isMethodContext())
	? find(cx->asMethodContext())
	: find(cx->asBlockContext());
    }

public:
  // instantiate with bytecode size as placement param
  inline void *operator new(size_t lbs, size_t codeSize)
    {
      return codeCache->reserve(lbs + codeSize);
    }

public:
  // call with the number of bytecodes in the CompiledMethod, to allow
  // appropriate sizing of the PC map
  NativeMethod(size_t byteSize)
    // only "next", "start", "map" and "blockMap" are valid on
    // instantiation.  ALL others are client's responsibility...
    : nextMethod(methods), next(0), start(code),
      relink(code), controlEntry(code),
      entry(code), siEntry(code), ccEntry(code), ncEntry(code),
      checkedEntry(code), activateEntry(code), activatedEntry(code),
      map(new(byteSize) PcMap()), blockMap(0)
    {
      methods= this;
    }

  ~NativeMethod(void)
    {
      fatal("this cannot happen");
    }

  // indicate that code generation is complete in the method.
  // MUST be called, to finialise allocation of method and map.

  inline void finalise(insn *endPC) const
    {
      map->finalise();
#     ifndef NDEBUG
      char *base=
#     endif
      codeCache->allocate(sizeof(NativeMethod)-sizeof(code) + (int)endPC - (int)start);
      assert(base == (char *)this);
    }

  // the following are here so that the translator need only hang on to
  // the method, which forwards map operations appropriately.

  // add an entry to the map, associating vPC with nPC

  inline void notePC(int vPC, insn *nPC)
    {
      map->notePC(vPC, nPC);
    }

  inline void checkPC(int vPC, insn *nPC, int index)
    {
      map->checkPC(vPC, nPC, index);
    }

  // answer the vPC corresponding to the given nPC

  inline int n2vPC(insn *nPC) const
    {
      return map->n2vPC(nPC);
    }

  // answer the nPC corresponding to the given vPC

  inline insn *v2nPC(int vPC) const
    {
      return map->v2nPC(vPC);
    }

  inline insn *v2nBlockPC(int vPC) const
    {
      return blockMap->v2nPClinear(vPC);
    }

  inline bool includesVPC(int vPC) const
    {
      return map->includesVPC(vPC);
    }

  inline bool includesBlockVPC(int vPC) const
    {
      return blockMap->includesVPC(vPC);
    }

  inline bool includesNPC(insn *nPC) const
    {
      return map->includesNPC(nPC);
    }

  // answer the CompiledMethod, class, or selector of this NativeMethod

  inline CompiledMethod *compiledMethod(void) const
    {
      return methodMemoizer->elementAt(methodIndex);
    }

  inline Symbol *selector(void) const
    {
      return selectorMemoizer->elementAt(selectorIndex);
    }

  inline Class *methodClass(void) const
    {
      return classMemoizer->elementAt(methodClassIndex);
    }

  inline Class *receiverClass(void) const
    {
      return classMemoizer->elementAt(receiverClassIndex);
    }

  inline oop literalAt(int index)
    {
      return compiledMethod()->literalAt(index);
    }

  inline static unsigned cacheTypeForClass(Class *cls)
    {
      if (cls == ClassSmallInteger)
	return siCacheType;
      if (cls->hasCompactInstances())
	return ccCacheType;
      return ncCacheType;
    }

  inline bool hasCacheForClass(Class *cls)
    {
      if (cacheType == pvCacheType)
	return true;
      if (cacheType == ixCacheType)
	return cls == receiverClass();
      return cacheType == cacheTypeForClass(cls);
    }

  inline NativeMethod *specialisedClass(Class *rcvrClass)
    {
      for (register NativeMethod *nMeth= this; nMeth != 0; nMeth= nMeth->next)
	if (nMeth->receiverClass() == rcvrClass)
	  return nMeth;
      return 0;
    }

  inline NativeMethod *specialisedCache(unsigned cType)
    {
      for (register NativeMethod *nMeth= this; nMeth != 0; nMeth= nMeth->next)
	if (nMeth->cacheType == cType)
	  return nMeth;
      return 0;
    }

  inline NativeMethod *specialisedClassAndCache(Class *rcvrClass, unsigned cType)
    {
      for (register NativeMethod *nMeth= this; nMeth != 0; nMeth= nMeth->next)
	if ((nMeth->receiverClass() == rcvrClass) && (nMeth->cacheType == cType))
	  return nMeth;
      return 0;
    }

  inline NativeMethod *specialised(Class *rcvrClass, unsigned cType)
    {
      if ((rcvrClass != 0) && (cacheType != 0))
	return specialisedClassAndCache(rcvrClass, cType);
      if (rcvrClass != 0)
	return specialisedClass(rcvrClass);
      if (cacheType != 0)
	return specialisedCache(cType);
      return this;
    }

  inline bool canBeLinked(void)
    {
      const Symbol *sel= selector();
      if ((sel == SelectorDoesNotUnderstand) || (sel == SelectorCannotInterpret))
	return false;
      const unsigned cc= receiverClass()->ccBits();
#     if 0
      if (cc == (BlockContextCCI << 12))
	return (primitiveIndex == 81) || (primitiveIndex == 82);
#     endif
#     if 0
      if (cc == (MethodContextCCI << 12))
	return false;
#     endif
      if (cc == (PseudoContextCCI << 12))
	return false;
      return true;
    }


  void invalidate(void);


  inline void print(void)
    {
      methodClass()->print();
      fputs(">>", stdout);
      selector()->print();
    }

  size_t profile(size_t count, size_t observed, size_t expected,
		 float elapsed, bool full);

  static size_t profileAll(void);

}; // class NativeMethod


inline NativeMethod *CompiledMethod::nativeMethod(void)
{
  NativeMethod *nm= nativeMemoizer->atOrNil(methodMemoizer->indexOf(this));
  assert(nm != 0);
  return nm;
}


static const size_t nm_methodIndex_off= memberOffset(NativeMethod, methodIndex);


#endif // _j_NativeMethod_h
