// dcg.h -- simplified form of Delayed Code Generation -- see [1]
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: 2000-11-10 16:42:10 by piumarta on emilia.rd.wdi.disney.com

#ifndef _j_dcg_h
#define _j_dcg_h


#include "cachebits.h"


// maximum number of deferred sends per method
#define MAX_DEFERRED	1024


extern void dcg_initialise(void);


///
/// Descriptor classes
/// 



class Descriptor
{
public:
  int type;
  int value;
  int flags;
  enum flags {
    tagFlag=  0x01,
    selfFlag= 0x02
  };
public:
  Descriptor(int t= 0, int v= 0, int f= 0) : type(t), value(v), flags(f) {}

public:  
  char *printString(void) const;

  inline bool isStack(void) const;
  inline bool isInteger(void) const;
  inline bool isRegister(void) const;
  inline bool isCondition(void) const;

  inline bool isTrue(void) const;
  inline bool isFalse(void) const;

  inline Descriptor &setFlags(int f)
    {
      flags= f;
      return *this;
    }

  inline void beStack(void);
  inline void beRegister(int regNo);

  inline Descriptor &beTagged(void)
    {
      flags|= tagFlag;
      return *this;
    }

  inline bool isTagged(void) const
    {
      return (flags & tagFlag);
    }

  inline Descriptor &beSelf(void)
    {
      flags|= selfFlag;
      return *this;
    }

  inline bool isSelf(void) const
    {
      return (flags & selfFlag);
    }
};



class Stack : public Descriptor
{
public:
  static const int Type= 1;
public:
  Stack(void) : Descriptor(Type) {}
};


inline void Descriptor::beStack(void)
{
  type= Stack::Type;
  value= 0;
}


inline bool Descriptor::isStack(void) const
{
  return type == Stack::Type;
}


class Integer : public Descriptor
{
public:
  static const int Type= 2;
public:
  Integer(int val) : Descriptor(Type, val)
    {
      beTagged();
    }
};


inline bool Descriptor::isInteger(void) const
{
  return type == Integer::Type;
}


class Register : public Descriptor
{
public:
  static const int Type= 3;
public:
  Register(int val, int flg= 0) : Descriptor(Type, val, flg) {}
};


inline void Descriptor::beRegister(int regNo)
{
  type= Register::Type;
  value= regNo;
  // flags are unaffected
}


inline bool Descriptor::isRegister(void) const
{
  return type == Register::Type;
}


class Condition : public Descriptor
{
public:
  static const int Type= 4;
public:
  // the following is inexcusable, but ANSI refuses to let me initialise
  // the array as a static const member.  hurrah for standardisation.
  static inline const char *name(int i)
    {
      static char *cc[]= {"t","lt","le","eq","ne","gt","ge","f" };
      return cc[i - TR];
    };
  enum ccode		 { TR, LT,  LE,  EQ,  NE,  GT,  GE,  FA };
  static Condition	   Tr, Lt,  Le,  Eq,  Ne,  Gt,  Ge,  Fa;

  static void initialise(void);

public:
  Condition(ccode val) : Descriptor(Type, val) {}
  inline Condition inverted(void);
  inline static ccode inverted(const ccode cc)
    {
      return (ccode)(TR + FA - cc);
    }
  inline static ccode invertedInequality(const ccode cc)
    {
      static ccode invIneq[8]= {
      //TR, LT, LE, EQ, NE, GT, GE, FA
	TR, GT, GE, EQ, NE, LT, LE, FA
      };  
      return invIneq[cc - TR];
    }
};


inline bool Descriptor::isCondition(void) const
{
  return type == Condition::Type;
}


inline bool Descriptor::isTrue(void) const
{
  return (type == Condition::Type) && (value == Condition::TR);
}


inline bool Descriptor::isFalse(void) const
{
  return (type == Condition::Type) && (value == Condition::FA);
}


inline Condition Condition::inverted(void)
{
  return Condition(Condition::inverted((ccode)value));
}


/// 
/// Delayed Code Generation
/// 



static Descriptor stack[LargeFrame];
static int stackp= -1;

#define stackDescriptor(N)	(stack[stackp - (N)])

#define stackFlags(N)		(stackDescriptor(N).flags)
#define stackType(N)		(stackDescriptor(N).type)
#define stackValue(N)		(stackDescriptor(N).value)
#define stackString(N)		(stackDescriptor(N).printString())

#define stackDepth()		(stackp + 1)

#define tos()			(stack[stackp])

#define types(L,R)		(    ((L).type << 8) | (R).type )
#define typeCases(L,R)		case ((L::Type << 8) | (R::Type))


// push a descriptor onto the simulation stack

static inline void push(const Descriptor &d)
{
  PRINTF(("%d push: %s\n", stackp + 1, d.printString()));
  stack[++stackp]= d;
}


// pop a descriptor off the simulation stack

static inline void pop(int n)
{
  assert(n >= 0);
  PRINTF(("%d pop(%d):\n", stackp + 1, n));
  while (n--)
    {
      PRINTF(("  %d: %s\n", stackp + 1, stack[stackp].printString()));
      --stackp;
    }
}


static Register moveToReg(const Descriptor &src, int regNo= -1);


// answer whether the runtime stack is consistent with the simulation stack

static bool stackIsStable(int offset= 0)
{
  const int top= stackp - offset;
  for (int i= 0; i <= top; ++i)
    if (stack[i].type != Stack::Type)
      return false;
  return true;
}


// flush the given descriptor onto the runtime stack
static void flush(const Descriptor &d)
{
  switch (d.type)
    {
    case Stack::Type:
      break;
    case Integer::Type:
      emit_move_i_r((int)Object::integer(d.value), tmp[0]);
      emit_push_r(tmp[0]);
      break;
    case Register::Type:
      emit_push_r(d.value);
      break;
    case Condition::Type:
      moveToReg(d, tmp[0]);
      emit_push_r(tmp[0]);
      break;
    }
}


// flush the simulation stack onto the runtime stack

static void flushStack(int offset= 0)
{
  const int top= stackp - offset;
  PRINTF(("flushStack %d %d\n", top, stackp));
  int i= 0;
  while ((i <= top) && (stack[i].isStack()))
    ++i;
  while (i <= top)
    {
      PRINTF(("%d flush: %s\n", i, stack[i].printString()));
      if (stack[i].isStack())
	{
	  fatal("fragmented runtime stack");
	}
      flush(stack[i]);
      stack[i].beStack();
      ++i;
    }
  assert(stackIsStable(offset));
}


// allocate the next general-purpose regsister

static int allocReg(void)
{
  PRINTF(("%p allocReg:\n", asm_pc));
  int regNo= 0, topReg= -1;
  for (int i= 0; i <= stackp; ++i)
    {
      if (stack[i].type == Register::Type)
	{
	  PRINTF(("  stack[%d] = reg %d\n", i, stack[i].value));
	  assert(stack[i].value == reg[regNo]);
	  ++regNo;
	  topReg= i;
	}
    }
  if ((topReg == -1) || (reg[regNo] != -1))
    {
      PRINTF(("  => reg[%d] => %d\n", regNo, reg[regNo]));
      return reg[regNo];
    }
  // spill registers
  PRINTF(("SPILLING REGISTERS\n"));
  flushStack();
  return reg[0];
}


// generate a conditional jump based on a Condition value

static void genCondJump(const int cc, const insn *dest)
{
  switch (cc)
    {
    case Condition::TR:
      if (dest == asm_pc)
	fprintf(stderr, "WARNING: infinite loop detected at %p\n", asm_pc);
      if (dest <= asm_pc) {
	emit_jmpck(dest);
	emit_call_l(g_Jmp, dest);
      } else {
	emit_jmp(dest);
      }
      break;
    case Condition::LT: emit_blt(dest); break;
    case Condition::LE: emit_ble(dest); break;
    case Condition::EQ: emit_beq(dest); break;
    case Condition::NE: emit_bne(dest); break;
    case Condition::GT: emit_bgt(dest); break;
    case Condition::GE: emit_bge(dest); break;
    case Condition::FA: /* no-op */	break;
    default: fatal("this cannot happen");
    }
}


// generate an inverted conditional jump based on a Condition value

static inline void genInvertedJump(const int cc, const insn *dest)
{
  genCondJump(Condition::inverted((Condition::ccode)cc), dest);
}


// move a descriptor into a general purpose register

static Register moveToReg(const Descriptor &src, int regNo)
{
  if (src.isRegister()
      && ((src.value == regNo) || (regNo == -1)))
    return Register(src.value, src.flags);
  if (regNo == -1)
    regNo= allocReg();
  // assert((regNo >= 3) && (regNo <= 8)); // CHNAGE MD
  switch (src.type)
    {
    case Stack::Type:
      {
	emit_pop_r(regNo);
	break;
      }
    case Integer::Type:
      {
	emit_move_i_r((int)Object::integer(src.value), regNo);
	break;
      }
    case Register::Type:
      {
	if (src.value != regNo)
	  emit_move_r_r(src.value, regNo);
	break;
      }
    case Condition::Type:
      {
	switch (src.value)
	  {
	  case Condition::TR:
	    emit_move_1_r(regNo);
	    break;
	  case Condition::FA:
	    emit_move_0_r(regNo);
	    break;
	  default:
	    fatal("this cannot happen");
	    break;
	  }
      }
    }
  return Register(regNo, src.flags);
}



///
/// Deferred sends
///



class DeferredSend
{
public:

  insn *entryIP;	// address of deferred send sequence
  insn *resumeIP;	// address of insn following inline send site
  insn *patchIP;	// address of insn following deferred send site
  int index;		// deferred send selector index
  Descriptor receiver;	// deferred send receiver descriptor
  Descriptor argument;	// deferred send argument descriptor
  bool annulled;

  DeferredSend(void) {}

  // called from allocation
  inline void init(int si, const Descriptor &r, const Descriptor &a, insn *entry)
    {
      entryIP=  entry;	// correct
      resumeIP= entry;	// safe default -- override in resume()
      patchIP=  entry;	// safe default -- override in generate()
      index=    si;	// correct
      receiver= r;	// correct
      argument= a;	// correct
      annulled= false;	// correct
    }

  inline void resume(int pass)
    {
      PRINTF(("#%d: resume %p %p = %p\n", pass, this, resumeIP, asm_pc));
      if (pass == 1) {
	resumeIP= asm_pc;
      } else {
	assert(resumeIP == asm_pc);
      }
    }

  inline insn *enter(void)
    {
      PRINTF(("#?: entry %p -> %p\n", this, entryIP));
      return entryIP;
    }

  void generate(int pass)
    {
      if (!annulled)
	{
	  PRINTF(("#%d: generate %p @ %p\n", pass, this, asm_pc));
	  if (pass == 1) {
	    entryIP= asm_pc;
	  } else {
	    assert(entryIP == asm_pc);
	  }
	  flush(receiver);
	  flush(argument);
	  emit_lcall_l(resumeIP, patchIP,
		       g_SpecialDeferred, ((index << 8) | 1));
	  if (pass == 1) {
	    patchIP= asm_pc;
	  } else {
	    assert(patchIP == asm_pc);
	  }
	}
    }

  inline char *printString(void)
    {
      static char buf[256];
      sprintf(buf, "DB.%p=(%d %s %s -> resmue %p, enter %p, patch %p)",
	      this, index, receiver.printString(), argument.printString(),
	      resumeIP, entryIP, patchIP);
      return buf;
    }

  // class variables and methods

  static DeferredSend deferredSends[];
  static size_t deferredSendCount;

  inline static void reset(void)
    {
      deferredSendCount= 0;
    }

  inline static DeferredSend &allocate(int selIndex, int pass)
    {
      assert(deferredSendCount < MAX_DEFERRED);
      DeferredSend &ds= deferredSends[deferredSendCount++];
      if (pass == 1)
	{
	  ds.init(selIndex, stackDescriptor(1), stackDescriptor(0), asm_pc);
	  PRINTF(("#%d: allocDB ->%s\n", pass, ds.printString()));
	}
      else
	{
	  PRINTF(("#%d: allocDB -> %s\n", pass, ds.printString()));
	  assert(ds.index == selIndex);
	}
      return ds;
    }

  inline void annul(void)
    {
      annulled= true;
    }

  inline static void generateAll(int pass)
    {
      for (size_t i= 0;  i < deferredSendCount;  ++i)
	deferredSends[i].generate(pass);
    }
};



#endif // _j_dcg_h
