// i386-Frame.cc -- PowerPC native frame operations
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: 2000-11-10 16:12:35 by piumarta on emilia.rd.wdi.disney.com

//#undef	DEBUG

#include "i386-Frame.h"

#include "machine.h"
#include "NativeProcess.h"

#undef	FAST_CTX_ALLOC

///
/// PseudoContexts
///


PseudoContext *_Frame::allocatePseudoMethodContext(void)
{
  assert(activeFrame != 0);
  assert(isMethodFrame());
  if (!hasPseudoContext())
    {
#     ifdef FAST_CTX_ALLOC
      NativeMethod *nm= nativeMethod();
      pcx= PseudoContext::allocateEmpty();
      notePseudoContext();
      MethodContext *pmx= (MethodContext *)pcx;
      register oop nilOop= nilObj;
      pmx->sender= nilOop->asContext();
      pmx->pc= nilOop;
      pmx->stackp= nilOop;
      pmx->method= nm->compiledMethod();	// Block vs Method
      assert(pmx->method->isCompiledMethod());
      pmx->receiverMap= (oop)((unsigned)nm | 1);
      pmx->receiverMap= nilOop;
      pmx->receiver= receiver;
      pmx->stack[0]= (oop)this;
#     if 0
      for (unsigned i= 1; i < 32; ++i)
	pmx->stack[i]= nilOop;
#     endif
#     else
      pcx= new PseudoContext((Frame *)this);
      notePseudoContext();
      MethodContext *pseudoMethod= (MethodContext *)pcx;
      assert(pseudoMethod->sender == nilObj);
      assert(pseudoMethod->pc == nilObj);
      assert(pseudoMethod->stackp == nilObj);
      pseudoMethod->method= nativeMethod()->compiledMethod();	// Block vs Method
      assert(pseudoMethod->method->isCompiledMethod());
      assert(pseudoMethod->receiverMap == nilObj);
      //pseudoMethod->receiverMap= (oop)((unsigned)nativeMethod() | 1);
      pseudoMethod->receiver= receiver;
#     endif
    }
  else
    {
      assert(pseudoContext() != 0);
      assert(pseudoContext()->frame() == this);
      assert(pseudoContext()->nArgsOrMethod == nativeMethod()->compiledMethod());
      assert(pseudoContext()->startPcOrReceiverMap == nilObj);
      assert(pseudoContext()->homeOrReceiver == receiver);
    }
  return pseudoContext();
}


PseudoContext *_Frame::allocatePseudoBlockContext(void)
{
  assert(activeFrame != 0);
  assert(isBlockFrame());
  if (!hasPseudoContext())
    {
      assert(pcx->isBlockContext());		// our closure
#     ifdef BLOCK_CLOSURES
      pcx= pcx->copy()->asPseudoContext();	// copy-on-value
      assert(pcx->isBlockContext());
#     endif
      pcx->bePseudoContext((Frame *)this);
      notePseudoContext();
      assert(pcx->isPseudoBlockContext());
    }
  else
    {
      assert(pseudoContext() != 0);
      assert(pseudoContext()->isPseudoBlockContext());
      assert(pseudoContext()->frame() == this);
    }
  return pseudoContext();
}


PseudoContext *_Frame::allocatePseudoContext(void)
{
  assert(activeFrame != 0);
  if (hasPseudoContext())
    return pseudoContext();
  return isBlockFrame()
    ? allocatePseudoBlockContext()
    : allocatePseudoMethodContext();
}


///
/// garbage collection
///


inline void _Frame::mark(void)
{
  PRINTF(("mark frame %p\n", this)); fflush(stdout);
  if (!receiver->isInteger())
    receiver->mark();
  if ((pcx != 0) && (hasPseudoContext() || isBlockFrame())) {
    pcx->mark();
  }
#ifdef STACK_GROWS_DOWN
  for (oop *ptr= stackp; ptr <= stackFirst(); ++ptr)
#else
  for (oop *ptr= stack; ptr <= stackp; ++ptr)
#endif
    if (!(*ptr)->isInteger()) {
      (*ptr)->mark();
    }
}

inline void _Frame::remap(void)
{
  PRINTF(("remap frame %p\n", this));
  if (!receiver->isInteger()) {
    receiver= receiver->remap();
}
if ((pcx != 0) && (hasPseudoContext() || isBlockFrame())) { 
    pcx= pcx->remap()->asPseudoContext();
}
#ifdef STACK_GROWS_DOWN
  for (oop *ptr= stackp; ptr <= stackFirst(); ++ptr)
#else
  for (oop *ptr= stack; ptr <= stackp; ++ptr)
#endif 
    {
    if (!(*ptr)->isInteger()) {
      *ptr= (*ptr)->remap();
    }
}
}

void _Frame::markStack(void)
{
  assert(activeFrame != 0);
  PRINTF(("mark stack from %p\n", this));
  for (_Frame *frame= this; !frame->isBaseFrame(); frame= frame->senderFrame())
    frame->mark();
}


void _Frame::remapStack(void)
{
  PRINTF(("remap stack from %p\n", this));
  for (_Frame *frame= this; !frame->isBaseFrame(); frame= frame->senderFrame())
    frame->remap();
}


///
/// process initialisation
///


inline _Frame *_Frame::loadBase(void)
{
  sender= 0;
  pc= 0;
  opc= 0;
  stackp= 0;
  receiver= 0;
  nMethod= 0;
  pcx= 0;
  return this;
}

_Frame *_Frame::load(Context *cx)
{
  assert(cx->stackp->isInteger());
  const int stackDepth= cx->stackp->integerValue();

  // copy the stack contents (before bePseudoCx stomps on it)

  if (stackDepth != 0)
    {
      register oop *const in= cx->stack;
      register oop *const out= stack;
      switch (stackDepth)
	{
#        ifdef STACK_GROWS_DOWN
#	  define cp(N) case N: out[LargeFrame-(N)]= in[(N)-1]
#	 else
#	  define cp(N) case N: out[(N)-1]= in[(N)-1]
#	 endif
	                          cp(56); cp(55); cp(54); cp(53); cp(52); cp(51); cp(50);
	  cp(49); cp(48); cp(47); cp(46); cp(45); cp(44); cp(43); cp(42); cp(41); cp(40);
	  cp(39); cp(38); cp(37); cp(36); cp(35); cp(34); cp(33); cp(32); cp(31); cp(30);
	  cp(29); cp(28); cp(27); cp(26); cp(25); cp(24); cp(23); cp(22); cp(21); cp(20);
	  cp(19); cp(18); cp(17); cp(16); cp(15); cp(14); cp(13); cp(12); cp(11); cp(10);
	  cp( 9); cp( 8); cp( 7); cp( 6); cp( 5); cp( 4); cp( 3); cp( 2); cp( 1);
#	undef cp
#	ifndef NDEBUG
	case 0: break;
	default:
	  fatal("illegal stack index");
#	endif
	}
    }

  pcx= 0;	// for possible GC in NM::find()
  activeFrame= (Frame *)this;
  NativeMethod *nMeth= 0;

  // ContextPart
  sender	= this + 1;
#ifdef STACK_GROWS_DOWN
  stackp	= stackFirst() - stackDepth + 1;
#else
  stackp	= stack + stackDepth - 1;
#endif

  cx->pushRemappable();

  if (cx->isMethodContext())
    {
      // MethodContext
      const MethodContext *mx= cx->asMethodContext();
      receiver= mx->receiver;
      assert(activeFrame->okayStackOops());
      nMethod= nMeth= NativeMethod::find((MethodContext *)mx); // lose const
    }
  else
    {
      // BlockContext
      assert(cx->isBlockContext());
      const BlockContext *bx= cx->asBlockContext();
      MethodContext *mx= bx->home;
      receiver= mx;
      assert(activeFrame->okayStackOops());
      if (mx->isPseudoContext())
	nMeth= mx->asPseudoContext()->frame()->nativeMethod();
      else
	nMeth= NativeMethod::find(mx);
      nMethod= (NativeMethod *)((unsigned)nMeth | 2);
    }

  cx= popRemappable(Context);
  pc= nMeth->v2nPC(cx->pc->integerValue());
  pcx= cx->bePseudoContext((Frame *)this);
  notePseudoContext();

  assert(pcx->frame() == (Frame *)this);

  return this;
}

_Frame *_Frame::loadStack(Context *ctx, char *stackBase, size_t stackSize)
{
  PRINTF(("loadStack("));  PRINTLN(ctx); 
  if (ctx->isNil()) {
    return ((_Frame *)(stackBase + stackSize -sizeof(_Frame)))->loadBase();
  }
  return ( (Frame *)((int)(loadStack(ctx->sender, stackBase, stackSize) - 1) 
     ))->load(ctx);
}


///
/// stabilisation
///

Context *_Frame::stabiliseWithSenderPC(oop senderObj, oop pcObj)
{
  assert(hasPseudoContext());
  assert(pseudoContext() != 0);
  size_t stackDepth= stackIndex();
  if (isBlockFrame())
    {
      assert(pcx->isPseudoBlockContext());
      // BlockContext
      BlockContext *bcx= pseudoContext()->beBlockContext();
      bcx->sender= senderObj->asContext();
      bcx->stackp= Object::integer(stackDepth);
      bcx->pc=     pcObj;
      // the rest we don't touch...
      assert(bcx->nargs->isInteger());
      assert(bcx->startpc->isInteger());
      assert((bcx->home->isMethodContext()) || (bcx->home->isPseudoMethodContext()));
    }
  else
    {
      assert(pcx->isPseudoMethodContext());
      // MethodContext
      MethodContext *mcx= pseudoContext()->beMethodContext();
      mcx->sender= 	senderObj->asContext();
      mcx->stackp= 	Object::integer(stackDepth);
      mcx->pc=     	pcObj;
      assert(mcx->method->isCompiledMethod());
      assert(mcx->method == nativeMethod()->compiledMethod());
      //assert(mcx->receiverMap == nilObj);
      assert(mcx->receiver == receiver);
    }
  // copy the stack
  if (stackDepth != 0)
    {
      register oop *const in= stack;
      register oop *const out= pseudoContext()->stack;
      switch (stackDepth)
	{
#        ifdef STACK_GROWS_DOWN
#	  define cp(N) case N: out[(N)-1]= in[LargeFrame-(N)]
#	 else
#	  define cp(N) case N: out[(N)-1]= in[(N)-1]
#	 endif
	                          cp(56); cp(55); cp(54); cp(53); cp(52); cp(51); cp(50);
	  cp(49); cp(48); cp(47); cp(46); cp(45); cp(44); cp(43); cp(42); cp(41); cp(40);
	  cp(39); cp(38); cp(37); cp(36); cp(35); cp(34); cp(33); cp(32); cp(31); cp(30);
	  cp(29); cp(28); cp(27); cp(26); cp(25); cp(24); cp(23); cp(22); cp(21); cp(20);
	  cp(19); cp(18); cp(17); cp(16); cp(15); cp(14); cp(13); cp(12); cp(11); cp(10);
	  cp( 9); cp( 8); cp( 7); cp( 6); cp( 5); cp( 4); cp( 3); cp( 2); cp( 1);
#	undef cp
#	ifndef NDEBUG
	case 0: break;
	default:
	  fatal("illegal stack index");
#	endif
	}
    }
#ifdef STACK_GROWS_DOWN
  stackp= stack + LargeFrame;
#else
  stackp= stack - 1;
#endif
  pseudoContext()->beRoot();			// may contain young pointers...
  return pseudoContext()->asContext();
}


// I hate like DAMN to write this thing recursively...
// (At the very least we should switch back to the C stack for the duration!)
Context *_Frame::stabiliseAll(void)
{
  assert(activeFrame != 0);
  if (isBaseFrame())
    {
      return nilObj->asContext();
    }
  if (!hasPseudoContext())
    {
      allocatePseudoContext();
    }
  Context *senderCtx= sender->stabiliseAll();
  assert(senderCtx->okayFields());
  assert(senderCtx->stackp->isInteger());
  
  return stabiliseWithSenderPC(senderCtx, Object::integer(vPC()));
}


///
/// debugging
///

void _Frame::print(void)
{
  printf("Frame %p:\n", this);
  printf("  sender       %p\n", sender);
  printf("  pc           %p [%d]\n", pc, (nativeMethod()->includesNPC(pc) ? vPC() : 0));
  printf("  opc          %p\n", opc);
#ifdef STACK_GROWS_DOWN
  printf("  stackp       %p [%d]\n", stackp, stackFirst() - stackp + 1);
#else
  printf("  stackp       %p [%d]\n", stackp, stackp - stack + 1);
#endif
  printf("  receiver     %p ", receiver);
  receiver->print(); putchar('\n');
  printf("  nativeMethod %p ", nMethod);
  nativeMethod()->print();
  putchar('\n');
  if (hasPseudoContext())
    {
      printf("  pseudo       %p ", pseudoContext());
      pseudoContext()->print();
      putchar('\n');
    }
  else
    {
      printf("  <volatile>\n");
    }
#ifdef STACK_GROWS_DOWN
  for (oop *ptr= stackFirst(); ptr >= stackp; --ptr)
#else
  for (oop *ptr= stack; ptr <= stackp; ++ptr)
#endif
    {
#    ifdef STACK_GROWS_DOWN
      printf("  [%2d] ", stackFirst() - ptr);
#    else
      printf("  [%2d] ", ptr - stack);
#    endif
      (*ptr)->print();
      putchar('\n');
    }
}

void _Frame::printBacktrace(void)
{
  for (_Frame *frame= this;  !frame->isBaseFrame();  frame= frame->sender)
    frame->print();
}

bool _Frame::okayOops(void)
{
# ifdef NDEBUG
  fatal("NDEBUG is defined");
# endif
  receiver->okayFields();
  if ((pcx != 0) && hasPseudoContext()) pseudoContext()->okayFields();
#ifdef STACK_GROWS_DOWN
  for (oop *ptr= stackp; ptr <= stackFirst(); ++ptr)
#else
  for (oop *ptr= stack; ptr <= stackp; ++ptr)
#endif
    (*ptr)->okayFields();
  return true;
}

bool _Frame::okayStackOops(void)
{
# ifdef NDEBUG
  fatal("NDEBUG is defined");
# endif
  for (_Frame *frame= this;  !frame->isBaseFrame();  frame= frame->senderFrame())
    frame->okayOops();
  return true;
}


