// ppc-Frame.h -- PowerPC native stack frames
//
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2000-11-22 11:22:02 by piumarta on emilia.rd.wdi.disney.com

#ifndef _j_ppc_frame_h
#define _j_ppc_frame_h


#include <stdio.h>
#include <sys/types.h>

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

#include "archdep.h"
#include "ppc-defs.h"
#include "tramp.h"

extern insn *t_cxFault;
extern insn *t_mxFault;

class Frame;

// PowerPC native frame format

class _Frame
{

public: // Frame must not have knowledge of internal state!

  _Frame	 *sender;	 //	 (SqABI init)		sender's Frame
# ifdef __macintosh__
    void	 *savedCR;
    void	 *cpc;
    void	 *reserved1;
    void	 *reserved2;
    void	 *savedTOC;
    int		  gprs[4];	// *** NEED TO GET RID OF THIS ***
# else // !__macintosh__
    void	 *cpc;		 //	 (reserved)		CABI saved PC
# endif // !__macintosh__
  insn		 *pc;		 //	 (callee saved)		saved insn ptr
  insn		 *opc;		 //	 (iff pc == redirect)	redirected pc
  oop		 *stackp;	 //	 (callee saved)		raw ptr
  oop		  receiver;	 // [GC] (callee saved)
  NativeMethod	 *nMethod;	 //	 (callee saved)		ind: nm->cM()->lits[]
  PseudoContext	 *pcx;		 // [GC] (iff pc == redirect)
  oop		  stack[LargeFrame];	 // [GC] (grows UPWARDS, towards higher memory)

public: // Note: the following valid only for (Frame *)0

  inline void reset(void)
    {
      sender= 0;
      cpc= 0;
      pc= 0;
      opc= 0;
      stackp= 0;
      receiver= 0;
      nMethod= 0;
      pcx= 0;
    }

  inline _Frame *senderFrame(void)
    {
      return sender;
    }

  inline void setSenderFrame(_Frame *newSender)
    {
      sender= newSender;
    }

  inline NativeMethod *nativeMethod(void) const
    {
      return (NativeMethod *)((unsigned)nMethod & ~3UL);
    }

  inline void setNativeMethod(NativeMethod *newNM)
    {
      assert(newNM != 0);
      nMethod= (NativeMethod *)((unsigned)newNM | ((unsigned)nMethod & 3U));
    }

  inline bool isBaseFrame(void)
    {
      return sender == 0;
    }

  inline void beBaseFrame(void)
    {
      setSenderFrame(0);
    }

  inline _Frame *calleeFrame(void)
    {
      return this - 1;	// Ugh!
    }

  inline bool hasFault(void)
    {
      return pc == t_cxFault;
    }

  inline void setPC(insn *npc)
    {
      if (hasFault())
	opc= npc;
      else
	pc= npc;
    }

  inline int vPC(void) const
    {
      if (pc == t_mxFault)
	fatal("attempt to fetch uninitialised pc after method change");
      if (pc == t_cxFault)
	return nativeMethod()->n2vPC(opc);
      else
	return nativeMethod()->n2vPC(pc);
    }

  inline oop *stackFirst(void)
    {
      return stack + LargeFrame - 1;
    }

  inline size_t stackIndex(void)
    {
#    ifdef STACK_GROWS_DOWN
      return stackFirst() - stackp + 1;
#    else
      return stackp - stack + 1;
#    endif
    }

  inline void stackIndex(size_t newIndex)
    {
#    ifdef STACK_GROWS_DOWN
      stackp= stackFirst() - (newIndex - 1);
#    else
      stackp= stack + (newIndex - 1);
#    endif
    }

  inline oop stackValue(int index)
    {
      // stack grows upwards (for the moment at least... ;-)
#    ifdef STACK_GROWS_DOWN
      assert((stackp <= stackFirst()) && (stackp >= stack));
      return stackp[index];
#    else
      assert((stackp >= (stack - 1)) && (stackp < (stack + LargeFrame)));
      return stackp[-index];
#    endif
    }

  inline oop stackRemove(int index)
    {
#    ifdef STACK_GROWS_DOWN
      oop *last= stackp + index;
      oop oldObject= *last;
      for (oop *ptr= last; ptr > stackp; --ptr)
	ptr[0]= ptr[-1];
      ++stackp;
#    else
      oop oldObject= stackp[-index];
      for (oop *ptr= stackp - index; ptr < stackp; ++ptr)
	ptr[0]= ptr[1];
      --stackp;
#    endif
      return oldObject;
    }

  inline void stackInsert(oop newObject, int index)
    {
#    ifdef STACK_GROWS_DOWN
      oop *last= stackp + index;
      for (oop *ptr= stackp; ptr <= last; ++ptr)
	ptr[-1]= ptr[0];
      stackp[index]= newObject;
      --stackp;
#    else
      for (oop *ptr= stackp - index; ptr <= stackp; ++ptr)
	ptr[1]= ptr[0];
      stackp[-index]= newObject;
      ++stackp;
#    endif
    }

  inline void drop(int nItems)
    {
#    ifdef STACK_GROWS_DOWN
      stackp+= nItems;
#    else
      stackp-= nItems;
#    endif
    }

  inline oop pop(void)
    {
#    ifdef STACK_GROWS_DOWN
      return *stackp++;
#    else
      return *stackp--;
#    endif
    }

  inline oop push(oop obj)
    {
#    ifdef STACK_GROWS_DOWN
      return *--stackp= obj;
#    else
      return *++stackp= obj;
#    endif
    }

  inline oop popThenPush(int nItems, oop value)
    {
#    ifdef STACK_GROWS_DOWN
      stackp+= (nItems - 1);
#    else
      stackp-= (nItems - 1);
#    endif
      return *stackp= value;
    }

  inline bool hasPseudoContext(void) const
    {
      return ((unsigned)nMethod & 1) != 0;
    }

  inline PseudoContext *pseudoContext(void) const
    {
      assert(hasPseudoContext());
      return pcx;
    }

  inline void setPseudoContext(PseudoContext *pseudoCtx)
    {
      assert(!hasPseudoContext());
      pcx= pseudoCtx;
    }

  PseudoContext *allocatePseudoMethodContext(void);
  PseudoContext *allocatePseudoBlockContext(void);
  PseudoContext *allocatePseudoContext(void);

  inline void mark(void);
  inline void remap(void);

  void markStack(void);
  void remapStack(void);

  inline _Frame *loadBase(void);
  inline _Frame *load(Context *cx);
  static _Frame *loadStack(Context *ctx, char *stack, size_t stackSize);

  Context *stabiliseWithSenderPC(oop senderObj, oop pcObj);

  inline Context *stabiliseForReturn(void)
    {
      return stabiliseWithSenderPC(nilObj, nilObj);
    }

  Context *stabiliseAll(void);

  inline void setFault(void)
    {
      assert(sender != 0);
      if (sender->pc != t_cxFault)
	{
	  sender->opc= sender->pc;
	  sender->pc= t_cxFault;
	}
    }

  inline void clearFault(void)
    {
      assert(pc == t_cxFault);
      pc= opc;
      opc= 0;
    }

  inline void noteSenderChange(void)
    {
      setFault();
    }

  inline void notePseudoContext(void)
    {
      assert(((unsigned)nMethod & 1) == 0);
      nMethod= (NativeMethod *)((unsigned)nMethod | 1);
      setFault();
    }

  inline bool isBlockFrame(void) const
    {
      return (((int)nMethod & 2) != 0);
    }

  inline bool isMethodFrame(void) const
    {
      return (((int)nMethod & 2) == 0);
    }

  void print(void);
  void printBacktrace(void);
  bool okayOops(void);
  bool okayStackOops(void);

  inline void resume(void)
    {
#    ifndef NDEBUG
      // have to clear activeFrame from reload
      extern Frame *activeFrame;
      activeFrame= 0;
#    endif
      tramp_resume((Frame *)this);
    }

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

};


// for the benefit of PPC native code (reduced to constants at static compile time)
// Note: must NOT be used outside native code!

static const size_t f_sender_off=	memberOffset(_Frame, sender);
static const size_t f_cpc_off=		memberOffset(_Frame, cpc);
static const size_t f_pc_off=		memberOffset(_Frame, pc);
static const size_t f_opc_off=		memberOffset(_Frame, opc);
static const size_t f_stackp_off=	memberOffset(_Frame, stackp);
static const size_t f_receiver_off=	memberOffset(_Frame, receiver);
static const size_t f_nmeth_off=	memberOffset(_Frame, nMethod);
static const size_t f_pcx_off=		memberOffset(_Frame, pcx);
static const size_t f_stack_off=	memberOffset(_Frame, stack);


#endif // _j_ppc_frame_h
