
// SamOp.h -- Squeak Abstract Machine instructions
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: Sat Sep 16 20:36:24 2000 by piumarta (Ian Piumarta) on emilia

#ifndef _j_SamOp_h
#define _j_SamOp_h

#include <stdio.h>

#include "archdep.h"

typedef unsigned short half;

// arg types (should be symbolic, but they're only used internally, so what the hey):
//	0 = no arguments
//	1 = one 8-bit argument
//	2 = two 8-bit arguments
//	3 = SAM destination address
//	4 = integer constant

// with apologies to Don Knuth...
#define _DO_OPS()							\
  /* stack operations */						\
  _DO(Pop, 0) _DO(Dup, 0)						\
  _DO(LdSelf, 0) _DO(LdTrue, 0) _DO(LdFalse, 0) _DO(LdNil, 0)		\
  _DO(LdInt, 4)								\
  _DO(LdThisContext, 0)							\
  _DO(LdLit, 1)								\
  _DO(LdInst, 1) _DO(StInst, 1) _DO(PopInst, 1)				\
  _DO(LdTemp, 1) _DO(StTemp, 1) _DO(PopTemp, 1)				\
  _DO(LdLitInd, 1) _DO(StLitInd, 1) _DO(PopLitInd, 1)			\
  /* jumps */								\
  _DO(Jmp, 3) _DO(JmpF, 3) _DO(JmpT, 3)					\
  /* message sends */							\
  _DO(Super, 2) _DO(Send, 2)						\
  /* returns */								\
  _DO(RetSelf, 0) _DO(RetTrue, 0) _DO(RetFalse, 0) _DO(RetNil, 0)	\
  _DO(RetMethod, 0) _DO(RetBlock, 0)					\
  /* special and arithmetic */						\
  _DO(Add, 0) _DO(Subtract, 0) _DO(Multiply, 0) _DO(Divide, 0)		\
  _DO(Div, 0) _DO(Mod, 0)						\
  _DO(LessThan, 0) _DO(GreaterThan, 0)					\
  _DO(LessOrEqual, 0) _DO(GreaterOrEqual, 0)				\
  _DO(Equal, 0) _DO(NotEqual, 0) _DO(Equivalent, 0)			\
  _DO(BitShift, 0) _DO(BitAnd, 0) _DO(BitOr, 0)				\
  _DO(At, 0) _DO(AtPut, 0) _DO(Size, 0)					\
  _DO(Next, 0) _DO(NextPut, 0) _DO(AtEnd, 0)				\
  _DO(Do, 0)								\
  _DO(Class, 0)								\
  _DO(BlockCopy, 0) _DO(Value, 0) _DO(ValueWithArg, 0)			\
  _DO(New, 0) _DO(NewWithArg, 0)					\
  _DO(MakePoint, 0) _DO(PointX, 0) _DO(PointY, 0)			\
  /* optimised */							\
  _DO(Lambda, 2) _DO(BlockActivate, 2)


enum samOps
{
# define _DO(X,Y) op##X,
  opILLEGAL= 0, _DO_OPS()
  // marker pseudo-ops
  opDeleted,	// insn deleted after optimisation
  opCOUNT
# undef _DO
};


struct samInfo
{
  const char *name;
  const int   args;
} samInfo[]= {
# define _DO(X,Y) { #X, Y },
  { "ILLEGAL", 0 }, _DO_OPS()
# undef _DO
  // marker pseudo-ops
  { "<deleted>",  0 },
  { "<Scombine>", 4 },
  { "<Tcombine>", 4 }
};


class SamOp
{
public:
  enum instructionFlags {
    ckptFlag= 0x01,	// I need a pc map entry
    bendFlag= 0x02,	// I am the last insn in a basic block
    combFlag= 0x04,	// I'm a potential recombination point
    sqshFlag= 0x08	// I'm a jump that was subsumed by a relation
  };

public:
  insn *nPC;				     // my native code address
  byte 	opcode 	   __attribute__((packed));  // up to 256 opcodes
  byte 	flags  	   __attribute__((packed));  // my flags
  half 	vPC    	   __attribute__((packed));  // my virtual code address
  int 	arg;				     // my (possibly packed) argument(s)
  half 	stackDepth __attribute__((packed));  // size of stack *before* I execute
  half	joinCount  __attribute__((packed));  // number of jumps that target me

  // Receiver/tag map and stack recombination occur only at at control
  // flow joins (sam[samPC].joinCount > 0), with at least one
  // conditional source (sam[samPC].combFlag != 0), and then ONLY when
  // the previous instruction is an unconditional control transfer
  // (sam[samPC-1].bendFlag != 0).  In all other cases stack tracing
  // is deterministic (at least we cross our fingers and hope so ;-).

public:
  // can be called 4 times before static buffers collide
  char *printString(void)
    {
      static char bufs[4][1024];
      static int bufp= 0;
      char *buf= bufs[bufp++ % 4], *ptr= buf;
      ptr+= sprintf(ptr, "%c%c%c%c%c %d< %3d %p %s",
		    (isComb() ? 'r' : '.'),
		    (isBend() ? 'e' : '.'),
		    (isDest() ? 'd' : '.'),
		    (isCkpt() ? 'c' : '.'),
		    (isSqsh() ? 's' : '.'),
		    joinCount,
		    vPC, nPC, samInfo[opcode].name);
      switch (samInfo[opcode].args)
	{
	case 0:  break;
	case 1:  ptr+= sprintf(ptr, " %d", arg); break;
	case 2:  ptr+= sprintf(ptr, " 0x%x(%d,%d)", arg, arg >> 8, arg & 255); break;
	case 3:  ptr+= sprintf(ptr, " >%d", arg); break;
	case 4:  ptr+= sprintf(ptr, " #%d", arg); break;
	default: fatal("this cannot happen"); break;
	}
      return buf;
    }

public:
  inline bool isCkpt(void) { return flags & ckptFlag; }
  inline bool isBend(void) { return flags & bendFlag; }
  inline bool isComb(void) { return flags & combFlag; }
  inline bool isSqsh(void) { return flags & sqshFlag; }
  inline bool isDest(void) { return joinCount != 0; }

  void print(void) { printf("%s\n", printString()); }

  inline SamOp &beCkpt(void) { flags|= ckptFlag; return *this; }
  inline SamOp &beBend(void) { flags|= bendFlag; return *this; }
  inline SamOp &beComb(void) { flags|= combFlag; return *this; }
  inline SamOp &squash(void) { flags|= sqshFlag; return *this; }

  inline SamOp &noCkpt(void) { flags&= ~ckptFlag; return *this; }
  inline SamOp &noBend(void) { flags&= ~bendFlag; return *this; }
  inline SamOp &noComb(void) { flags&= ~combFlag; return *this; }

  inline SamOp &join(void)
    {
      ++joinCount;
      return *this;
    }

  inline SamOp &unjoin(void)
    {
      --joinCount;
      return *this;
    }

  inline SamOp &beDeleted(void)
    {
      assert(!isDest());
      assert(!isCkpt());
      assert(!isComb());
      opcode= opDeleted;
      arg= 0;
      flags= 0;
      return *this;
    }

  inline bool isCondJump(void)
    {
      return (opcode == opJmpF)
	 ||  (opcode == opJmpT);
    }

  inline int jumpType(void)
    {
      switch (opcode)
	{
	case opJmpF: return -1;
	case opJmpT: return  1;
	default: fatal("this cannot happen");
	}
      return 0;
    }

  inline bool isControl(void)
    {
      return (opcode == opJmp)
	 ||  (opcode == opRetMethod)
	 ||  (opcode == opRetBlock)
	 ||  (opcode == opRetSelf)
	 ||  (opcode == opRetTrue)
	 ||  (opcode == opRetFalse)
	 ||  (opcode == opRetNil);
    }

  inline void recombine(half sDepth)
    {
      if (isComb())
	{
	  if (stackDepth != sDepth) {
	    fatal("recombination conflict: %d %d", stackDepth, sDepth);
	  }
	}
      else
	{
	  beComb();
	  stackDepth= sDepth;
	}
    }

public:
  // explicit initialisation by compiler (read: bzero()ed en masse ;-)
  SamOp(void) {}
};


#endif // _j_SamOp_h
