/**
 *  $Id: jitcompile.c 130 2005-02-11 20:45:21Z ko1 $
 * Create : K.S. 04/01/01 03:42:15
 *
 * YARV JIT Compiler: YARV insns => Native Code
 */

#include <ruby.h>
#include <node.h>
#include "yarvcore.h"
#include "insns.inc"

#if 0
#define DEBUGP if(1)printf
#else
#define DEBUGP if(0)printf
#endif

/**
  opt_call_native_compiled 
  operand: 

 */

#if __GNUC__ && __i386__ && OPT_JIT_COMPILE
/* this part may be separeted to another file */

#include <sys/mman.h>

int insn_len(int);

#define DWORD_MEM(m, i) \
  (*((VALUE *)(((unsigned char *)(m)) + (i))))

static int jitcompile_i386(struct iseq_object *iseq){
  VALUE *iseq_orig    = iseq->iseq_dt;
  int i, j, k, bin_size  = 0, ary_size = 0;
  VALUE compiled_ary;
  VALUE insn;
  unsigned char *data, *code, *code2;
  void *copy_insn_table = (void *)thread_eval_body_copy(0);
  // iseq->size = 1;

#if 1
  // check size
  for(i=0; i<iseq->size; i+= insn_len(iseq->iseq[i])){
    int insn = iseq->iseq[i];
/*
    DEBUGP("%d: %x - %x (%d)\n",
         insn,
         yarv_vm_instruction_start_address_table[insn],
         yarv_vm_instruction_end_address_table  [insn],
         yarv_vm_instruction_start_address_table[insn] -
         yarv_vm_instruction_end_address_table  [insn]
         );
 */  
    code = (char*)iseq_orig[i];
    for(j=0; 1; j++){
      if(code[j] == 0xff && code[j+1] == 0xe0){
        break;
      }
    }
    DEBUGP("size: %d\n", j+1);
    bin_size += j+1;
  }
  compiled_ary = rb_ary_new2((bin_size + 3) / 4);
  //RARRAY(compiled_ary)->ptr = mmap(0, (bin_size + 3) / 4,
  //PROT_EXEC|PROT_READ|PROT_WRITE,
  //                               MAP_PRIVATE | MAP_ANONYMOUS| MAP_ANON, 0, 0);
  data = (unsigned char*) RARRAY(compiled_ary)->ptr;

  // copy code
  for(i=0, k=0; i<iseq->size; i+= insn_len(insn)){
    insn = iseq->iseq[i];
    code = (char*)iseq_orig[i];
    code2= (char*)((VALUE*)copy_insn_table)[insn];
    
    for(j=0; 1; j++, k+=1){
      if(code[j] == 0xff && code[j+1] == 0xe0){
        switch(insn){
        case BIN(end):
        case BIN(end_SC_xx_ax):
        case BIN(end_SC_ax_ax):
        case BIN(end_SC_bx_ax):
        case BIN(end_SC_ab_ax):
        case BIN(end_SC_ba_ax):
          data[k] = code[j]; data[k+1] = code[j+1];
          break;
        default:
          k -= 2;
          /* none */
          break;
        }
        break;
      }
      if(code[j] != code2[j]){
        DEBUGP("%d: %02x != %02x\n", j, code[j] & 0xff, code2[j] & 0xff);
      }

      data[k] = code[j];
      DEBUGP("%08x: %02x\n", &code[j], code[j] & 0xff);

      if(code[j] == 0xe8){
        VALUE d1, d2, p, r;
        d1 = DWORD_MEM(code, j+1);
        d2 = DWORD_MEM(code2, j+1);
        if(d1 != d2){
          /* call rel32 */
          p = (VALUE)&code[j+5];
          r = p + d1 - (VALUE)&data[k+5];
          DEBUGP("!! %d, %d, %d\n", d1, d2, r);
          ((VALUE*)(data+k+1))[0] = r;
          k+=4; j+=4;
        }
      }
    }
    DEBUGP("-- %d\n", k);
    for(j=0; j<k+1; j++){
      // DEBUGP("%d: %02x\n", j, data[j] & 0xff);
    }
    DEBUGP("--\n");
  }
  DEBUGP("-- (%d)\n", (bin_size + 3) / 4);
  
  iseq->iseq_dt = ALLOC_N(VALUE, 4);
  iseq->size    = 4;
  iseq->jit_compiled = data;
  iseq->iseq_orig    = iseq_orig;
  rb_ary_push(iseq->iseq_mark_ary, compiled_ary);

  iseq->iseq_dt[0] = (VALUE)((void **)
                             (thread_eval_body(0)))[BIN(opt_call_native_compiled)];
  iseq->iseq_dt[1] = 0;
  iseq->iseq_dt[2] = 0;
  iseq->iseq_dt[3] = 0;
  
DEBUGP("%x08\n", rb_ary_new4);
  
  __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");

  DEBUGP("== %x\n", data);
#endif
  return 0; /* success */
}
#define JITCOMPILE_FUNC jitcompile_i386

#else
/* null function */

static int jitcompile_none(struct iseq_object *iseq){
  rb_warn("JIT Compiler is not implemented");
  return 0;
}

#define JITCOMPILE_FUNC jitcompile_none
#endif

VALUE iseq_jitcompile(VALUE iseq_obj){
  struct iseq_object *iseq;
  GetISeqVal(iseq_obj, iseq);

  if(iseq->jit_compiled != 0){
    rb_bug("alaready compiled");
  }

  JITCOMPILE_FUNC(iseq);
  return iseq_obj;
}

void breakpoint(){
#if __GNUC__ && __i386__
//  __asm__ __volatile__ ("int $03");
#else
  volatile int v;
  v = *((int*)0);
#endif
}

