/* src/vm/jit/intrp/engine.c - #included by engine1.c and engine2.c

   Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
   C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
   E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
   J. Wenninger, Institut f. Computersprachen - TU Wien

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

   $Id: engine.c 7366 2007-02-15 19:48:11Z twisti $
*/


/* #define VM_DEBUG */

#include "config.h"

#include <assert.h>

#include "vm/types.h"

#include "arch.h"

#include "vm/jit/intrp/intrp.h"

#include "md-abi.h"                           /* required for TRACE_ARGS_NUM */

#include "mm/memory.h"

#if defined(ENABLE_THREADS)
# include "threads/native/threads.h"
#else
# include "threads/none/threads.h"
#endif

#include "vm/builtin.h"
#include "vm/exceptions.h"

#include "vm/jit/methodheader.h"
#include "vm/jit/patcher.h"
#include "vm/jit/stacktrace.h"

#include "vmcore/loader.h"
#include "vmcore/options.h"


#if defined(ENABLE_THREADS)
# ifndef USE_FAKE_ATOMIC_INSTRUCTIONS
#  include "machine-instr.h"
# else
#  include "threads/native/generic-primitives.h"
# endif
#endif

#if !defined(STORE_ORDER_BARRIER) && !defined(ENABLE_THREADS)
#define STORE_ORDER_BARRIER() /* nothing */
#endif


/* threading macros */
#define GCC_PR15242_WORKAROUND
#ifdef GCC_PR15242_WORKAROUND
#  define NEXT_P1_5
#  define DO_GOTO goto before_goto
#else
#  define NEXT_P1_5
#  define DO_GOTO goto *ip[-1]
#endif

#  define NEXT_P0
#  define IP		(ip)
#  define SET_IP(p)	do {ip=(p); NEXT_P0;} while (0)
#  define NEXT_INST	(*IP)
#  define INC_IP(const_inc)	do { ip+=(const_inc);} while (0)
#  define DEF_CA
#  define NEXT_P1	(ip++)
#  define NEXT_P2   do {NEXT_P1_5; DO_GOTO;} while(0)

#define NEXT ({DEF_CA NEXT_P1; NEXT_P2;})
#define IPTOS NEXT_INST

#if defined(__POWERPC__) || defined(__POWERPC64__) || defined(__SPARC__)
# define USE_spTOS
#endif

#if defined(USE_spTOS)
#define IF_spTOS(x) x
#else
#define IF_spTOS(x)
#define spTOS (sp[0])
#endif

#if defined(__I386__)
/* works with gcc-2.95.4 20011002 (Debian prerelease) without static supers */
#define SPREG /* __asm__("%esi") */
#define TOSREG /* __asm__("%ecx") */
#elif defined(__X86_64__)
/* works with gcc-4.0.2 (Debian 4.0.2-2) */
#define SPREG /* __asm__("%r15") */
#define TOSREG
#else
#define SPREG
#define TOSREG
#endif


/* conversion on fetch */

#ifdef VM_PROFILING
#define SUPER_END  vm_count_block(IP)
#else
#define SUPER_END
#define vm_uncount_block(_ip)	/* nothing */
#endif


#define THROW0       goto throw
#if 1
#define THROW(_ball) do { \
                       __asm__(""); /* work around gcc PR 25285 */ \
                       goto *throw_##_ball; \
                     } while (0)
#else
#define THROW(_ball) do { \
                       goto throw_##_ball##1; \
                     } while (0)
#endif

#define THROWCODE(_ball) \
    throw_##_ball##1: \
        global_sp = sp; \
        *exceptionptr = (stacktrace_inline_##_ball(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP)); \
        CLEAR_global_sp; \
        THROW0;

#define CHECK_NULL_PTR(ptr) \
    { \
        if ((ptr) == NULL) { \
            THROW(nullpointerexception); \
	    } \
	}

#define THROW_CLASSCASTEXCEPTION(o) \
    { \
		classcastexception_object = o; \
        THROW(classcastexception); \
	}

#define CHECK_OUT_OF_BOUNDS(_array, _idx)              \
        {                                            \
          if (length_array(_array) <= (u4) (_idx)) { \
                arrayindexoutofbounds_index = (_idx); \
				THROW(arrayindexoutofboundsexception); \
          } \
	}

#define CHECK_ZERO_DIVISOR(_divisor) \
  { if (_divisor == 0) \
      THROW(arithmeticexception); \
  } 

#if 0
/* !! alignment bug */
#define access_local_long(_offset) \
        ( *(s8 *)(((u1 *)fp) + (_offset)) )
#endif

#define length_array(array)                          \
        ( ((java_arrayheader*)(array))->size )

#define access_array_int(array, index)               \
        ((((java_intarray*)(array))->data)[index])

#define access_array_long(array, index)               \
        ((((java_longarray*)(array))->data)[index])

#define access_array_char(array, index)               \
        ((((java_chararray*)(array))->data)[index])

#define access_array_short(array, index)               \
        ((((java_shortarray*)(array))->data)[index])

#define access_array_byte(array, index)               \
        ((((java_bytearray*)(array))->data)[index])

#define access_array_addr(array, index)               \
        ((((java_objectarray*)(array))->data)[index])

#define access_array_float(array, index)               \
        ((((java_floatarray*)(array))->data)[index])

/* (see createcompilerstub in codegen.c) */
#define FRAMESIZE(stub) (((Cell *)stub)[1])

#if 0
#define CLEARSTACK(_start, _end) \
	 do {Cell *__start=(_start); MSET(__start,0,u1,(_end)-__start); } while (0)
#else
#define CLEARSTACK(_start, _end)
#endif


#ifdef VM_DEBUG
#define NAME(_x) if (vm_debug) {fprintf(vm_out, "%lx: %-20s, ", (long)(ip-1), _x); fprintf(vm_out,"fp=%p, sp=%p", fp, sp);}
#else
#define NAME(_x)
#endif

#define LABEL2(name) J_##name: __asm__("");
#define LABEL3(name) K_##name: __asm__("");


java_objectheader *
engine(Inst *ip0, Cell * sp0, Cell * fp)
{
  Inst *ip;
  register Cell *sp SPREG = sp0;
  /* Inst ca1; XXX unused? */ /* code address; this is the next dispatched instruction */
  IF_spTOS(register Cell spTOS TOSREG;)
  static Inst   labels[] = {
#define INST_ADDR(_inst) (&&I_##_inst)
#include <java-labels.i>
#undef INST_ADDR
	  NULL,
#define INST_ADDR(_inst) (&&J_##_inst)
#include <java-labels.i>
#undef INST_ADDR
#define INST_ADDR(_inst) (&&K_##_inst)
#include <java-labels.i>
#undef INST_ADDR
    (Label)&&after_last,
    (Label)&&before_goto,
    (Label)&&after_goto,
#define INST_ADDR(_inst) (&&H_##_inst)
#include <java-labels.i>
#undef INST_ADDR
  };
  /* local variables for the various throw codes; this helps make
	 potentially throwing instructions relocatable (instead of a
	 non-relocatable direct jump, they perform an indirect jump) */
  Label throw_arithmeticexception            = &&throw_arithmeticexception1;
  Label throw_arrayindexoutofboundsexception = &&throw_arrayindexoutofboundsexception1;
  Label throw_classcastexception 			 = &&throw_classcastexception1;  
  Label throw_nullpointerexception 		     = &&throw_nullpointerexception1;
  Label throw_arraystoreexception            = &&throw_arraystoreexception1;
  java_objectheader *classcastexception_object = NULL;
  s4 arrayindexoutofbounds_index = 0; /* pass the index to the throw code */

  if (vm_debug)
      fprintf(vm_out,"entering engine(%p,%p,%p)\n",ip0,sp,fp);
  if (ip0 == NULL) {
    return (java_objectheader *)labels;
  }

  if (0) {
  before_goto:
	  goto *ip[-1];
  after_goto:
	  /* ensure that gcc does not constant-propagate the contents of
		 these variables and thus undo our relocatability work */
	  throw_arithmeticexception = 0;
	  throw_arrayindexoutofboundsexception = 0;
	  throw_classcastexception = 0;
	  throw_nullpointerexception = 0;
	  throw_arraystoreexception = 0;

      /* the actual codes jumped to through the ...exception variables */
	  THROWCODE(arithmeticexception);
	  THROWCODE(nullpointerexception);
	  THROWCODE(arraystoreexception);

  throw_classcastexception1:
	  global_sp = sp;
	  *exceptionptr = stacktrace_inline_classcastexception(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP, classcastexception_object);
	  CLEAR_global_sp;
	  THROW0;

  throw_arrayindexoutofboundsexception1:
	  global_sp = sp;
	  *exceptionptr = stacktrace_inline_arrayindexoutofboundsexception(NULL, (u1 *) fp, (u1 *) IP, (u1 *) IP, arrayindexoutofbounds_index);
	  CLEAR_global_sp;
	  THROW0;
  }

  /* I don't have a clue where these things come from,
     but I've put them in macros.h for the moment */
  IF_spTOS(spTOS = sp[0]);

  SET_IP(ip0);
  NEXT;

#define INST_ADDR(_inst) (&&I_##_inst)
#include <java-vm.i>
#undef NAME
 after_last: return NULL;
}


/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 * vim:noexpandtab:sw=4:ts=4:
 */
