// NativeMethod.cc -- find a native method from some kind of executable something(s)
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: 2000-10-26 18:13:38 by piumarta on emilia.rd.wdi.disney.com


#include "Object.h"
#include "NativeMethod.h"
#include "Profiler.h"

#include "compile.h"
#include "machine.h"

#include "Frame.h"

#include "archdep.h"

#include "generate.h"


#define	MX_CACHED_METHODS


NativeMethod *NativeMethod::methods= 0;


NativeMethod *NativeMethod::find(Symbol *sel, int nArgs, Class *rCls, Class *lCls)
{
  assert(rCls != ClassPseudoContext);
  assert(lCls != ClassPseudoContext);

  ::messageSelector= sel;
  ::argumentCount= nArgs;
  ::receiverClass= rCls;
  ::lkupClass= lCls;

# ifdef DEBUG
  printf("NativeMethod::find\n");
  printf("  sel   = "); sel->print(); printf("\n");
  printf("  nArgs = %d\n", nArgs);
  printf("  rCls  = "); rCls->print(); printf("\n");
  printf("  lCls  = "); lCls->print(); printf("\n");
# endif
  assert(activeFrame != 0);
# ifdef IC_STATS
  extern unsigned mcProbes;
  ++mcProbes;
# endif
  findNewMethodInClass(lCls);	// [rl]Cls and sel trashed by possible GC in j_translateHook
  assert(newNativeMethod->compiledMethod() == newMethod);
  if (!newNativeMethod->hasCacheForClass(::receiverClass))
    {
#     ifdef DEBUG
      printf("NativeMethod::gen_compile for specialised cache\n");
      printf("  sel            = "); sel->print(); printf("\n");
      printf("  nArgs          = %d\n", nArgs);
      printf("  rCls           = "); rCls->print(); printf("\n");
      printf("  lCls           = "); lCls->print(); printf("\n");
      printf("  receiverClass  = "); ::receiverClass->print(); printf("\n");
#     endif
      gen_compile();	// re-specialise for suitable cache in method prologue
      assert(newNativeMethod->hasCacheForClass(::receiverClass));
      assert(newNativeMethod->compiledMethod() == newMethod);
    }
  return newNativeMethod;
}


NativeMethod *NativeMethod::find(Symbol *sel, int nArgs, oop rcvr, Class *lCls)
{
  Class *rCls= rcvr->fetchClass();
  if (rCls == ClassPseudoContext)
    {
      if (rcvr->isPseudoMethodContext())
	{
	  rCls= ClassMethodContext;
	}
      else
	{
	  assert(rcvr->isPseudoBlockContext());
	  rCls= ClassBlockContext;
	}
    }
  return find(sel, nArgs, rCls, lCls);
}


NativeMethod *NativeMethod::find(Symbol *sel, int nArgs, oop rcvr)
{
  Class *rCls= rcvr->fetchClass();
  if (rCls == ClassPseudoContext)
    {
      if (rcvr->isPseudoMethodContext())
	{
	  rCls= ClassMethodContext;
	}
      else
	{
	  assert(rcvr->isPseudoBlockContext());
	  rCls= ClassBlockContext;
	}
    }
  return find(sel, nArgs, rCls, rCls);
}


NativeMethod *NativeMethod::find(MethodContext *mcx)
{
  // NOTE: the following must find the corresponding NativeMethod
  // WITHOUT affecting the method cache!

  if (mcx->isPseudoContext())
    {
      Frame *frame= mcx->asPseudoContext()->frame();
      assert(mcx->method == frame->compiledMethod());
      assert(mcx->receiver == frame->receiver);
      return frame->nativeMethod();
    }

# ifdef MX_CACHED_METHODS

  if (mcx->receiverMap->notNil())
    {
      assert(mcx->receiverMap->isInteger());
      return (NativeMethod *)((unsigned)mcx->receiverMap & ~1);
    }

# endif

  int nArgs= mcx->method->argCount();
  
  mcx->pushRemappable();
  MemoIndex cmIndex= methodMemoizer->indexOf(mcx->method);
  mcx= popRemappable(MethodContext);
  NativeMethod *nMeth= nativeMemoizer->atOrNil(cmIndex);

  ::receiverClass= mcx->receiver->fetchClass();
  if (::receiverClass == ClassPseudoContext)
    {
      if (mcx->receiver->isPseudoMethodContext())
	{
	  ::receiverClass= ClassMethodContext;
	}
      else
	{
	  assert(mcx->receiver->isPseudoBlockContext());
	  ::receiverClass= ClassMethodContext;
	}
    }

  if (nMeth != 0)
    {
      if (nMeth->receiverClass() == ::receiverClass)
	{
	  ::newNativeMethod= nMeth;
	}
      else
	{
	  ::newMethod= mcx->method;
	  ::primitiveIndex= mcx->method->primitiveIndex();
	  ::messageSelector= nMeth->selector();
	  ::methodClass= ::lkupClass= nMeth->methodClass();
	  mcx->pushRemappable();
	  gen_compile(); // possible specialisation for receiverClass
	  mcx= popRemappable(MethodContext);
	}
    }
  else
    {
      ::newMethod= mcx->method;
      ::primitiveIndex= mcx->method->primitiveIndex();
      // Note: the following yield rcvrClass>>doesNotUnderstand: if
      // the method is no longer installed in the hierarchy.  This is
      // safe because any normal send of #doesNotUnderstand: to an
      // object of mcx->receiver's class will go through the method
      // cache, find a different CompiledMethod, and therefore miss
      // mcx->method in the memoizer.
      ::messageSelector= newMethod->selectorForReceiver(mcx->receiver);
      ::methodClass= ::lkupClass= newMethod->mclassForReceiver(mcx->receiver);
      assert(::methodClass != ClassPseudoContext);
      mcx->pushRemappable();
      gen_compile(); // compile for receiverClass
      mcx= popRemappable(MethodContext);
    }
  assert((mcx->pc->isNil())
	 || ((mcx->pc->isInteger())
	     && newNativeMethod->includesVPC(mcx->pc->integerValue())));

# ifdef MX_CACHED_METHODS
  mcx->receiverMap= (oop)((unsigned)newNativeMethod | 1);
# endif

  return newNativeMethod;
}


void NativeMethod::invalidate(void)
{
  if (  entry != 0) gen_flushEntry(  entry, relink);
  if (siEntry != 0) gen_flushEntry(siEntry, relink);
  if (ccEntry != 0) gen_flushEntry(ccEntry, relink);
  if (ncEntry != 0) gen_flushEntry(ncEntry, relink);
  entry= siEntry= ccEntry= ncEntry= relink;
}


/// 
/// PROFILING
/// 


inline static void printProfileHeadings(void)
{
  printf("%5s %7s %7s %7s  %s\n", "count", "   time", "elapsed", "sampled", "method");
  printf("%5s %7s %7s %7s  %s\n", "-----", "-------", "-------", "-------", "------");
}


size_t NativeMethod::profile(size_t nTotal, size_t dynamic, size_t expected,
			     float elapsed, bool full)
{
  printf("%5d %6.2fs %6.2f%% %6.2f%%  ",
	 nTotal,
	 (float)nTotal / (float)expected * elapsed,
	 percent(nTotal, expected),
	 percent(nTotal, dynamic));
  receiverClass()->print();
  if (receiverClass() != methodClass())
    {
      printf("(");
      methodClass()->print();
      printf(")");
    }
  printf(">>");
  selector()->print();
  printf("\n");

  if (!full)
    return nTotal;

  const size_t mapEnd= map->size;
  insn *bEnd= map->map[mapEnd - 1].nPC;

  size_t nRelink= Profiler::total(relink, controlEntry);
  size_t nCtrl=   Profiler::total(controlEntry, entry);
  size_t nIcache= Profiler::total(entry, checkedEntry);
  size_t nPrim=   Profiler::total(checkedEntry, activateEntry);
  size_t nActiv=  Profiler::total(activateEntry, activatedEntry);
  size_t nProlog= nRelink + nCtrl + nIcache + nPrim + nActiv;
  size_t nBody=   Profiler::total(activatedEntry, bEnd);
  size_t nDefer=  Profiler::total(bEnd, end);
  size_t nExec=   nBody + nDefer;
  size_t overall= nProlog + nExec;

  Profiler::addTotals(nRelink, nIcache, cacheType,
		      nPrim, nCtrl,
		      nActiv, nBody, nDefer);

# define report(who, start, end, count)						\
    if (start != end)								\
      {										\
        size_t nInsns= (end - 1) - start;					\
        printf("  %10s [%p %p] %3d total", #who, start, end - 1, count);	\
        printf(" = %6.2f%%, %6.2f per insn\n",					\
	       percent(count, overall), (float)count / (float)nInsns);		\
  }

  putchar('\n');
  report(prologue, relink, activatedEntry, nProlog);
  report(method,   activatedEntry, bEnd, nBody);
  report(deferred, bEnd, end, nDefer);
  putchar('\n');

  report(relink, relink, entry, nRelink);
  switch (cacheType)
    {
    case pvCacheType: report(pvCache, entry, checkedEntry, nIcache); break;
    case siCacheType: report(siCache, entry, checkedEntry, nIcache); break;
    case ncCacheType: report(ncCache, entry, checkedEntry, nIcache); break;
    case ccCacheType: report(ccCache, entry, checkedEntry, nIcache); break;
    case ixCacheType: report(ixCache, entry, checkedEntry, nIcache); break;
    }
  report(control,   controlEntry, entry, nCtrl);
  report(primitive, checkedEntry, activateEntry, nPrim);
    report(activate,  activateEntry, activatedEntry, nActiv);

# undef report

  if (nBody != 0)
    {
      for (size_t i= 1; i < mapEnd; ++i)
	{
	  insn *nStart= map->map[i-1].nPC;
	  insn *nEnd= map->map[i].nPC;
	  int nInsns= nEnd - nStart;
	  if (nInsns != 0)
	    {
	      int vStart= map->map[i-1].vPC;
	      int vEnd= map->map[i].vPC;
	      size_t vBytes= vEnd - vStart;
	      size_t count= Profiler::total(nStart, nEnd);
	      printf("     %3d %3d [%p %p] %3d total",
		     vStart, vEnd - 1, nStart, nEnd - 1, count);
	      printf(" = %6.2f%%, %6.2f per insn\n",
		     percent(count, nExec),
		     (float)count / (float)nInsns);
	    }
	}
    }

  if (nDefer != 0)
    {
      size_t dInsns= end - bEnd;
      printf("    deferred [%p %p] %3d total", bEnd, end - 1, nDefer);
      printf(" = %6.2f%%, %6.2f per insn\n",
	     percent(nDefer, nExec),
	     (float)nDefer / (float)dInsns);
    }

  printf("\n");

  return nTotal;
}


size_t NativeMethod::profileAll(void)
{
  size_t recorded= 0;
  for (NativeMethod *nm= methods; nm != 0; nm= nm->nextMethod)
    {
      if (Profiler::includes(nm->start) && Profiler::includes(nm->end))
	{
	  size_t count= Profiler::total(nm->start, nm->end);
	  if (count > 0)
	    {
	      Profiler::add(nm, count);
	      recorded+= count;
	    }
	}
    }
  printProfileHeadings();
  return recorded;
}
