/**
 * $Id: vm.h 154 2005-03-04 04:47:43Z ko1 $
 * Create : K.S. 04/01/01 16:56:59
 */

#ifndef _VM_H_INCLUDED_
#define _VM_H_INCLUDED_

#include "version.h"

#if YARVDEBUG > VMDEBUG
#undef  VMDEBUG
#define VMDEBUG YARVDEBUG
#endif

typedef long OFFSET;

/**
 * VM Debug Level
 *
 * debug level:
 *  0: no debug output
 *  1: show instruction name
 *  2:
 *  3: show stack status
 *  4: show register
 *  5:
 * 10: gc check
 */

//#define VMDEBUG 1
//#define VMDEBUG 5
//#define VMDEBUG 10

//#define COLLECT_USAGE_ANALYSIS

#ifdef COLLECT_USAGE_ANALYSIS
#define USAGE_ANALYSIS_INSN(insn)           vm_analysis_insn(insn)
#define USAGE_ANALYSIS_OPERAND(insn, n, op) vm_analysis_operand(insn, n, (VALUE)op)
#define USAGE_ANALYSIS_REGISTER(reg, s)     vm_analysis_register(reg, s)
#else
#define USAGE_ANALYSIS_INSN(insn)            0
#define USAGE_ANALYSIS_OPERAND(insn, n, op)  0
#define USAGE_ANALYSIS_REGISTER(reg, s)      0

#endif

#if VMDEBUG > 0

#define debugs printf

#define DEBUG_ENTER_INSN(insn)                                      \
  debug_print_pre(th, GET_ISEQ()->iseq, GET_PC_COUNT(), GET_ISEQ(), \
                  GET_SP(), GET_LFP(), GET_DFP(), GET_CFP());

#ifdef OPT_STACK_CACHING
#define SC_REGS() , reg_a, reg_b
#else
#define SC_REGS()
#endif

#define DEBUG_END_INSN()                                            \
  debug_print_post(th, GET_ISEQ(), GET_PC_COUNT(), GET_SP(),        \
                   GET_LFP(), GET_DFP(), GET_CFP() SC_REGS());


#else

#define debugs
#define DEBUG_ENTER_INSN(insn)
#define DEBUG_END_INSN()
#endif

#define throwdebug if(0)printf
//#define throwdebug printf

#define STACKDUMP() \
  stack_dump_raw(th, GET_SP(), GET_LFP(), GET_DFP(), GET_CFP());


/************************************************/
#if   defined(DISPATCH_XXX)
error!

/************************************************/
#elif defined(DISPATCH_THREADED_CODE)
/* threaded code with gcc */

#define LABEL(x)  INSN_LABEL_##x
#define ELABEL(x) INSN_ELABEL_##x

#define INSN_ENTRY(insn) \
  LABEL(insn):

/**********************************/
#ifdef DISPATCH_DIRECT_THREADED_CODE

/* direct threaded code */
#define TC_DISPATCH(insn)   \
  goto *GET_CURRENT_INSN();

#define GET_SEQ(iseq) (iseq)->iseq_dt

/**********************************/
#else /* DISPATCH_DIRECT_THREADED_CODE */
#error "unsupported"
/* indirect threaded code */
#define TC_DISPATCH()                            \
  goto *insns_address_table[GET_CURRENT_INSN()]; \
  rb_bug("tc error");

#define GET_SEQ(is) (is)->iseq

#endif /* DISPATCH_DIRECT_THREADED_CODE */

#define END_INSN(insn)      \
  GC_CHECK();               \
  DEBUG_END_INSN();         \
  ELABEL(insn):             \
  TC_DISPATCH(insn);        \

#define INSN_DISPATCH()   \
  TC_DISPATCH(__START__)  \
  {

#define END_INSNS_DISPATCH()    \
      rb_bug("unknown insn: %d", GET_CURRENT_INSN());   \
  }   /* end of while loop */   \

#define NEXT_INSN() TC_DISPATCH(__NEXT_INSN__)

/************************************************/
#else
/* most common method */

#define GET_SEQ(is) (is)->iseq

#define INSN_ENTRY(insn) \
case BIN(insn):

#define END_INSN(insn)                        \
  GC_CHECK();                                 \
  DEBUG_END_INSN();                           \
  break;


#define INSN_DISPATCH()         \
  while(1){                     \
    switch(GET_CURRENT_INSN()){ 

#define END_INSNS_DISPATCH()    \
default:                        \
      rb_bug("unknown insn: %d", GET_CURRENT_INSN());   \
    } /* end of switch */       \
  }   /* end of while loop */   \

#define NEXT_INSN() goto first

#endif


/************************************************/


/*****************************/
/*  stack frame constructor  */
/*****************************/

struct continuation_frame{
  VALUE magic; /* dfp direct this variable */
  VALUE *pc;
  VALUE *cfp;
  VALUE *lfp;
  VALUE *dfp;
};

struct control_frame{
  VALUE self;
  VALUE iseq;
  struct continuation_frame continuation_frame;
};

struct method_frame{
  struct vm_block_object* block_ptr;
  struct control_frame control_frame;
};

struct block_frame{
  VALUE *prev_dfp;
  struct control_frame control_frame;
};

struct class_frame{
  VALUE dummy;
  struct control_frame control_frame;
};

#define PER_VALUE(type) (sizeof(type) / sizeof(VALUE))
#define FRAME_WC()      (PER_VALUE(struct method_frame))
#define CF_DIFF_WC()    (1)
#define CC_DIFF_WC()    (2)

#define FRAME_MAGIC_METHOD 0xfaffffa1
#define FRAME_MAGIC_BLOCK  0xfaffffa3
#define FRAME_MAGIC_CLASS  0xfaffffa5
#define FRAME_MAGIC_TOP    0xfaffffa7

#define CHECK_FRAME_MAGIC(magic)                   \
{                                                  \
  if((magic & 0xfffffff0) != 0xfaffffa0){          \
    rb_bug("YARV Stack frame error: %08x", magic); \
  }                                                \
}



/*
 * Env in HEAP image
 *
 * env{
 *   VALUE arg1;
 *   VALUE ...;
 *   VALUE argM;
 *   VALUE loc1;
 *   VALUE ...;
 *   VALUE locN;
 *   VALUE block or prev_env; // frame specific value
 *                            // <- lfp and dfp point this value
 *   VALUE orphan_sign;       // dfp[1] ## Qundef or not
 *   VALUE env_in_heap_sign;  // dfp[2] ## Qundef or not
 *   VALUE prev_env_object;   // dfp[3] ## 
 * }
 */


#define BLOCK_WC() (PER_VALUE(struct vm_block_object))

#define BLOCK_WC_IF_GIVEN() \
  ((GET_DFP()[0] == (VALUE)(GET_CFP() + FRAME_WC())) ? BLOCK_WC() : 0)


#define GET_ENV_CTRL(dfp) ((struct vm_env_control*)((dfp)+1))

#define FRAME_IS_ORPHAN(dfp)  (GET_ENV_CTRL(dfp)->is_orphan == Qundef)
#define FRAME_IS_IN_HEAP(dfp) (GET_ENV_CTRL(dfp)->is_in_heap== Qundef)

#define CURRENT_FRAME_IS_ORPHAN()  FRAME_IS_ORPHAN(GET_DFP())
#define CURRENT_FRAME_IS_IN_HEAP() FRAME_IS_IN_HEAP(GET_DFP())

struct vm_env_control{
  VALUE prev_env;   /* cfg[0] */
  VALUE is_in_heap; /* cfg[1] */
  VALUE is_orphan;  /* cfg[2] */
  VALUE self;       /* cfg[3] */
};

#define GET_FRAME_CONTROL_BUFF_WC() (PER_VALUE(struct vm_env_control) + 1)
#define GET_FRAME_ENV_VAL(dfp) (GET_ENV_CTRL(dfp)->self)


/**********************************************************
  method frame:

  VALUE a1, a2, ... , aM;
  VALUE l1, l2, ... , lN

  struct method_frame:        <- lfp <- dfp
    VALUE block;          // lfp[0]
  
  struct control_frame:       <- cfp
    VALUE self;           // cfp[0]
    VALUE iseq;           // cfp[1]
    struct continuation_frame:
    VALUE magic; // MAGIC_METHOD
      VALUE pc;           // cfp[2]
      VALUE cfp;          // cfp[3]
      VALUE lfp;          // cfp[4]
      VALUE dfp;          // cfp[5]
  

  block frame:

  VALUE a1, a2, ... , aM;
  VALUE l1, l2, ... , lN; // zero clear
                    // block local variables will
                    // be vanished at Ruby 2.0

  struct block_frame:         <- dfp
    VALUE prev_dfp;       // dfp[0]

  struct control_frame:       <- cfp
    VALUE magic; // MAGIC_BLOCK
    VALUE self;           // cfp[0]
    VALUE iseq;           // cfp[1]
    struct continuation_frame:
      VALUE pc;           // cfp[2]
      VALUE cfp;          // cfp[3]
      VALUE lfp;          // cfp[4]
      VALUE dfp;          // cfp[5]


  class frame:

  VALUE a1, a2, ... , aM;
  VALUE l1, l2, ... , lN; // zero clear

  struct class_frame:         <- lfp, dfp
    VALUE dummy;          // lfp[0]
  
  struct control_frame:       <- cfp
    VALUE magic; // MAGIC_CLASS
    VALUE self;           // cfp[0]
    VALUE iseq;           // cfp[1]
    struct continuation_frame:
      VALUE pc;           // cfp[2]
      VALUE cfp;          // cfp[3]
      VALUE lfp;          // cfp[4]
      VALUE dfp;          // cfp[5]


  Proc Object

  struct ProcObject:  // native type object?
    VALUE iseq;
    VALUE self;
    VALUE *env_ptr;
    VALUE iseqobj;

  Block Object

  struct BlockObject: // native type object?
    VALUE *self_ptr;
    VALUE *env_ptr;
    VALUE iseqobj;

  
  
  Method     Block     Block
  Env1    <- Env2   <- Env3
  
  A_ProcOBject{
    self_ptr
    env_ptr -> Env3
    
  }
  
  
**********************************************************/

struct cmethod_info{
  VALUE sig; /* must be null */
  ID id;
  VALUE klass;
};

#define CMETHOD_INFO_P(ptr) (*((VALUE*)(ptr)) == 0)

/*
 * Excception
 */

#define NEW_THROW_OBJECT(val, pt, st) NEW_NODE(NODE_LIT, (val), (pt), (st))
#define GET_THROWOBJ_VAL(obj)         ((VALUE)RNODE((obj))->u1.value)
#define GET_THROWOBJ_CATCH_POINT(obj) ((VALUE*)RNODE((obj))->u2.value)
#define GET_THROWOBJ_STATE(obj)       ((int)RNODE((obj))->u3.value)

#define SET_THROWOBJ_CATCH_POINT(obj, val) \
  (RNODE((obj))->u2.value = (val))
#define SET_THROWOBJ_STATE(obj, val) \
  (RNODE((obj))->u3.value = (val))

#define SCREG(r) (reg_##r)

#endif	// _VM_H_INCLUDED_
