#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <errno.h>
#include <ucontext.h>

#include "j2c.hh"
#include "config.h"

extern "C" {
#include "engine.h"
#include "signalmanager.h"
#include "nativeScheduling.h"
#include "jvm_constants/throwables.h"
}
#include "ffi.h"

// The size of a thread context and stack (we add an unreadable gaurd
// page at the bottom).
#define CONTEXT_SIZE (56 * 1024)

#undef TRACE_THREADS
#ifdef TRACE_THREADS
# define CTX(format, ...) fprintf(stderr, format, __VA_ARGS__)
#else
# define CTX(format, ...) 
#endif

/*
 * Define some accessors into ucontext and jumpbuf.  Also, capture
 * some assembler/linker differences between ppc architectures
 */
#if defined(OVM_X86)
# ifdef linux
#   define UC_FP  REG_EBP
#   define UC_PC  REG_EIP

#   define JB_FP 4
#   define JB_REG(BUF, N) (BUF)->__jmpbuf[N]
#   define UC_REG(CTX, N) (CTX).uc_mcontext.gregs[N]
#   define UC_SIZE (8 * sizeof(size_t))
# endif
#elif defined(OVM_PPC)
# if defined(linux)
#  define UC_R1  1
#  define UC_R14 14
#  define UC_PC  32
#  define UC_FP  UC_R1
#  define JB_FP 0
#  define JB_REG(BUF, N) (BUF)->__jmpbuf[N]
#  define UC_REG(CTX, N) (CTX).uc_mcontext.uc_regs->gregs[N]
#  define UC_SIZE (32 * sizeof(size_t))

#  define G(S) #S
#  define L(S) "." #S
#  define R(N) "%%r" #N
# elif defined(OSX_BUILD)
#  define G(S) "_" #S
#  define L(S) #S
#  define R(N) "r" #N

#  define JB_FP 0
#  define JB_REG(BUF, N) (BUF)[N]
# endif
#endif

typedef e_java_lang_Object Object;

typedef e_ovm_core_domain_Oop Oop;
typedef e_ovm_core_execution_ValueUnion ValueUnion;
typedef e_ovm_core_services_memory_VM_1Address VM_Address;

typedef e_s3_core_domain_S3ByteCode S3ByteCode;
typedef e_s3_services_j2c_J2cCodeFragment J2cCodeFragment;

// extern e_Array<jint> cf_ranges;
// extern e_Array<J2cCodeFragment *> cf_frags;

static float bits2float(int x) {
  union {
    int i;
    float f;
  } u;

  u.i = x;
  return u.f;
}

static double bits2double(long long x) {
  union {
    long long l;
    double d;
  } u;

  u.l = x;
  return u.d;
}

double _j2c_NaN =   bits2double(0x7ff8000000000000LL);
double _j2c_nzero = bits2double(0x8000000000000000LL);
float _j2c_NaNf =   bits2float(0x7fc00000);
float _j2c_nzerof = bits2float(0x80000000);

int **initializedBlueprints;
J2cContext *currentContext;
J2cContext *mainContext;
int process_argc;
char **process_argv;
extern void boot();

struct pcRange {
  size_t start;
  size_t end;
};
extern e_Array<pcRange> j2c_method_ranges;
extern e_Array<e_s3_services_j2c_J2cCodeFragment *> j2c_method_pointers;
static void compute_range_hints();

int pgsize = getpagesize();

inline void *operator new(size_t sz, void *p) { return p; }

ffi_type *codeToType(int code) {
  switch (code) {
  case 'L':
  case '[':
    return &ffi_type_pointer;
  case 'J':
    return &ffi_type_sint64;
  case 'I':
    return &ffi_type_sint32;
  case 'S':
    return &ffi_type_sint16;
  case 'C':
    return &ffi_type_uint16;
  case 'B':
  case 'Z':
    return &ffi_type_sint8;
  case 'D':
    return &ffi_type_double;
  case 'F':
    return &ffi_type_float;
  case 'V':
    return &ffi_type_void;
  default:
    assert(!"unknown type code");
  }
}

jvalue j2cInvoke(Code *code, InvocationMessage *args) {
  S3ByteCode *bc = (S3ByteCode *) code;
  J2cCodeFragment *cc = (J2cCodeFragment *) bc->ovm_next_1;
  ffi_cif cif;

  if (cc->ovm_index == -1) {
    fprintf(stderr, "reflective call to `unreachable' method \"%.*s\"\n",
	    cc->ovm_cname->ovm_count,
	    cc->ovm_cname->ovm_data->values + cc->ovm_cname->ovm_offset);
    abort();
  }
  int nargs = 1 + args->ovm_inArgs->length;
  ffi_type *at[nargs];
  ffi_type *rt = codeToType(cc->ovm_rtype);
  at[0] = codeToType('L');
  for (int i = 1; i < nargs; i++) {
    at[i] = codeToType(args->ovm_inArgs->values[i-1]->ovm_tag);
  }
  assert(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs, rt, at) == FFI_OK);

  void (*fn) () = (void (*)()) j2c_method_ranges.values[cc->ovm_index].start;
  void *av[nargs];
  typedef union {
    jbyte b;
    jchar c;
    jshort s;
  } narrow;
  narrow av_narrow[nargs];

  av[0] = &args->ovm_receiver;
  for (int i = 1; i < nargs; i++) {
    ValueUnion *ai = args->ovm_inArgs->values[i - 1];
    switch (ai->ovm_tag) {
    case 'L':
    case '[':
      av[i] = (void *) &ai->ovm_reference;
      break;
    case 'I':
    case 'F':
      av[i] = (void *) &ai->ovm_primitive;
      break;
    case 'D':
    case 'J':
      av[i] = &ai->ovm_widePrimitive;
      break;
    case 'Z':
    case 'B':
      av_narrow[i].b = ai->ovm_primitive;
      av[i] = (void *) &av_narrow[i].b;
      break;
    case 'S':
      av_narrow[i].s = ai->ovm_primitive;
      av[i] = (void *) &av_narrow[i].s;
      break;
    case 'C':
      av_narrow[i].c = ai->ovm_primitive;
      av[i] = (void *) &av_narrow[i].c;
      break;
    }
  }
  jvalue ret;
  HEADER *ex;
  try { ffi_call(&cif, fn, &ret, av); }
  // I guess C++ requires this goto: calling throwWildcardException
  // from the handler causes a rethrow of the original exception
  catch (HEADER *ex1) { ex = ex1; goto dead; }
  return ret;
 dead:
  throwWildcardException(ex);
}

jint j2cFail(...) {
  assert(!"abstract/boot-time/native method called\n");
  exit(-1);
}

sigset_t j2cNormalMask;

static inline void getMask(ucontext_t *ctx) {
  j2cNormalMask = ctx->uc_sigmask;

  stack_t stack;
  sigaltstack(0, &stack);
  if (stack.ss_flags & SS_ONSTACK)
    fprintf(stderr, "on stack %p of size %d (flags = %x)\n",
	    stack.ss_sp, stack.ss_size, stack.ss_flags);
}
#if defined(OVM_X86) && defined(linux)
/* Hopefully, this optimization will avoid stack fixup botches.  load
 * and divide instructions don't push arguments on the stack, so a
 * call that returns after such an instruction shouldn't push
 * arguments either.  (Maybe this must be used in combination with
 * -fno-defer-pop.)
 */
extern "C" void generateThrowable_x86(int code)
  __attribute__((regparm(1)));

void generateThrowable_x86(int code) {
  generateThrowable(NULL_POINTER_EXCEPTION);
}

static void callGenerateThrowable(ucontext_t *state, int code)
{
  getMask(state);
  int *gregs = state->uc_mcontext.gregs;
  asm("movl 24(%2), %%ebp\n\t"	// "N"(4*REG_EBP) produces $24
      "movl 28(%2), %%esp\n\t"	// which is not allowed in loads
      //"pushl %0\n\t"
      "pushl %1\n\t"
      "jmp generateThrowable_x86"
      : :
      "a"(code), "c"(gregs[REG_EIP]+2), "d"(gregs),
      "D"(gregs[REG_EDI]), "S"(gregs[REG_ESI]), "b"(gregs[REG_EBX]));
}
#elif defined(OVM_PPC) && defined(OSX_BUILD)
static void callGenerateThrowable(ucontext_t *state, int code) {
  getMask(state);
  /* FIXME: this code assumes that callee-saved floating point
   * registers are unchanged. (they are fp14-fp31), and are available
   * in the mcontext.
   */
  ppc_thread_state *ctx = &state->uc_mcontext->ss;
  asm("mr   r3, %0\n\t"
      "mr   r4, %1\n\t"
      "mtlr %2\n\t"
      "mr   r1, %3\n\t"
      "lwz  r13,  0(r4)\n\t"
      "lwz  r14,  4(r4)\n\t"
      "lwz  r15,  8(r4)\n\t"
      "lwz  r16, 12(r4)\n\t"
      "lwz  r17, 16(r4)\n\t"
      "lwz  r18, 20(r4)\n\t"
      "lwz  r19, 24(r4)\n\t"
      "lwz  r20, 28(r4)\n\t"
      "lwz  r21, 32(r4)\n\t"
      "lwz  r22, 36(r4)\n\t"
      "lwz  r23, 40(r4)\n\t"
      "lwz  r24, 44(r4)\n\t"
      "lwz  r25, 48(r4)\n\t"
      "lwz  r26, 52(r4)\n\t"
      "lwz  r27, 56(r4)\n\t"
      "lwz  r28, 60(r4)\n\t"
      "lwz  r29, 64(r4)\n\t"
      "lwz  r30, 68(r4)\n\t"
      "lwz  r31, 72(r4)\n\t"
      "ba   _generateThrowable"
      : : "r"(code),  "r"(&ctx->r13), "r"(ctx->srr0 + 4), "r"(ctx->r1)
      : "r3", "r4");
}
#elif defined(OVM_PPC) && defined(linux)
static void callGenerateThrowable(ucontext_t *state, int code) {
  getMask(state);
  /* This function differs from the Darwin version in terms of
   * register names, global symbol names (undersores), and ucontext_t
   * layout.
   */
  asm("mr   %%r3, %0\n\t"
      "mr   %%r4, %1\n\t"
      "mtlr %2\n\t"
      "mr   %%r1, %3\n\t"
      "lwz  %%r14,  0(%%r4)\n\t"
      "lwz  %%r15,  4(%%r4)\n\t"
      "lwz  %%r16,  8(%%r4)\n\t"
      "lwz  %%r17, 12(%%r4)\n\t"
      "lwz  %%r18, 16(%%r4)\n\t"
      "lwz  %%r19, 20(%%r4)\n\t"
      "lwz  %%r20, 24(%%r4)\n\t"
      "lwz  %%r21, 28(%%r4)\n\t"
      "lwz  %%r22, 32(%%r4)\n\t"
      "lwz  %%r23, 36(%%r4)\n\t"
      "lwz  %%r24, 40(%%r4)\n\t"
      "lwz  %%r25, 44(%%r4)\n\t"
      "lwz  %%r26, 48(%%r4)\n\t"
      "lwz  %%r27, 52(%%r4)\n\t"
      "lwz  %%r28, 56(%%r4)\n\t"
      "lwz  %%r29, 60(%%r4)\n\t"
      "lwz  %%r30, 64(%%r4)\n\t"
      "lwz  %%r31, 68(%%r4)\n\t"
      "lis  %%r5, generateThrowable@ha\n\t"
      "la   %%r4, generateThrowable@l(%%r5)\n\t"
      "mtctr %%r4\n\t" // may be too far away for ba
      "bctr"
      : :
      "r"(code),  "r"(&UC_REG(*state, UC_R14)),
      "r"(UC_REG(*state, UC_PC) + 4), "r"(UC_REG(*state, UC_R1))
      : "%r3", "%r4");
}
#else
static void callGenerateThrowable(ucontext_t *state, int code) {
  assert(!"unabled to convert signal to exception");
}
#endif

static void segvHandler(int sig, siginfo_t * info, void *_ctx) {
#if 0
  fprintf(stderr, "segvHandler(%d, %p, %p)\n", sig, info, _ctx);
#endif
  ucontext_t *ctx = (ucontext_t *) _ctx;
  char *gaurdPage = ((char *) currentContext->stackBase) - pgsize;
  char *faultAddr = (char *) FAULT_ADDR(info, _ctx);
#if 0
  int reason = info->si_code;
  fprintf(stderr, "segvHandler: fault addr: %d\n", faultAddr);
  switch (reason) {
  case SEGV_MAPERR:   fprintf(stderr, " reason: SEGV_MAPERR\n"); break;
  case SEGV_ACCERR:   fprintf(stderr, " reason: SEGV_ACCERR\n"); break;
  default:  fprintf(stderr, " reason: unknown(%d)\n", reason);
  }
#endif
  if (faultAddr >= gaurdPage)
    // Stack overflow currently triggers a recursive SEGV
    callGenerateThrowable(ctx, STACK_OVERFLOW_ERROR);
  else
    callGenerateThrowable(ctx, NULL_POINTER_EXCEPTION);
}

// don't mangle this label!
extern "C" void startContext(J2cContext *);

void startContext(J2cContext *ctx) {
  CTX("%p starting\n", ctx);
  currentContext = ctx;
  ctx->bottomFrame = getCurrentActivation(0);
  j2cInvoke(ctx->code, ctx->args);
  write(2, "unexpected return from thread's initial method", 46);
  abort();
}

#define MEM2CTX(mem) 							\
  ((void *) (((char *) mem) + CONTEXT_SIZE + pgsize - sizeof(J2cContext)))

#define CTX2MEM(ctx) 							\
  (((char *) ctx) - CONTEXT_SIZE - pgsize + sizeof(J2cContext))

int newNativeContext(Context *appCtx) {
  // allocate stack-gaurd page
  void *mem = mmap(0, CONTEXT_SIZE + pgsize,
		   PROT_READ|PROT_WRITE|PROT_EXEC,
		   MAP_PRIVATE|MAP_ANON, -1, 0);

  // Place context at the end of the allocation
  J2cContext *ctx;
  void *_ctx = MEM2CTX(mem);
  ctx = new(_ctx) J2cContext(appCtx);

  // Stack starts just below the context and ends just above the gaurd page
  ctx->stackBase = (void *) (((size_t) ctx) - 4);
  ctx->stackTop = (void *) (((char *) mem) + pgsize);
  CTX("thread %p has stack %p - %p\n", ctx, ctx->stackTop, ctx->stackBase);
  mprotect((void *) mem, pgsize, PROT_NONE);

#ifdef THREAD_UCONTEXT
  getcontext(&ctx->uc);
  // in which direction does the stack grow?  What is expected here?
  ctx->uc.uc_stack.ss_sp = ctx->stackTop;
  ctx->uc.uc_stack.ss_flags = 0;
  ctx->uc.uc_stack.ss_size = CONTEXT_SIZE - sizeof(*ctx);
  ctx->uc.uc_link = 0;
  makecontext(&ctx->uc, (void (*)()) startContext, 1, ctx);
#else
#define START(callee_save1, callee_save2, start_asm) {			\
    /* setjmp is going to return 1 long after this frame has died.	\
     * Only use one variable, ctx1, stored in a callee-save register.	\
     */									\
    register void *stackBase asm (#callee_save1) = ctx->stackBase;	\
    register J2cContext *ctx1 asm (#callee_save2) = ctx;		\
									\
    if (setjmp(ctx1->buf))						\
      {									\
	/* FIXME: need underscore? factor into x86-specific header? */	\
	asm(start_asm  : : "r"(stackBase), "r"(ctx1));			\
	write(2, "unexpected return from startContext\n", 36);		\
	abort();							\
      }									\
  }
    
# if defined(OVM_X86)
  START(ebx, esi,
	"movl %0, %%esp\n"
	"movl %0, %%ebp\n"
	"pushl %1\n"
	"call startContext");
# elif defined(OVM_PPC)
  /*
   * Here's what I know:
   * r3-r12 are argument registers
   * r13    is the first callee-save register
   * r1     is the stack pointer
   *
   * bl places the return address in a special reg `lr'
   * gcc's prologue saves it at 8(r1) (in the caller's frame), and
   * stores the caller's stack pointer at 0(r1) (in the callee's
   * frame).
   *
   * We need to allocate some space in the bottom frame. We need
   * enough space to hold the frame information and spill the three
   * arguments to write.  I guess that would be 5 words, but it should
   * be rounded up to 8 to satisfy any alignment requirements.
   * (It looks like gcc aligns r1 to quadword boundaries.)
   *
   * I don't understand the PIC magic to get the function's base
   * address into r31
   */
#  ifdef OSX_BUILD
  START(r14, r15,
	"addi r1, %0, -32\n"
	"stw  r1, 0(r1)\n"
	"mr   r3, %1\n" 
	"bl   _startContext");
#  else
  /* The thread-launching asm for ppc-linux seems to work, but unless
   * we use swapcontext, certain library functions such as malloc can
   * only be called from the main thread.
   */
  START(%r14, %r15,
	"addi %%r1, %0, -32\n"
	"stw  %%r1, 0(%%r1)\n"
	"mr   %%r3, %1\n" 
	"bl   startContext");
#  endif
# else
#  error "Don't know how to start threads on this system"
# endif
#endif

  /* GC will examine the value returned by getStackTop, which won't be
     valid until after a thread has actually executed. So we set the initial
     value returned by getStackTop to be the same as the stack base, which
     will cause the GC to not walk through the stack at all.
  */
#ifdef THREAD_UCONTEXT
  UC_REG(ctx->uc, UC_FP) = (int) ctx->stackBase;
#else
  JB_REG(ctx->buf, JB_FP) = (int) ctx->stackBase;
#endif
#if 0
  fprintf(stderr,"returning context %p has stack %u - %u and top %u\n", ctx, ctx->stackTop, ctx->stackBase, getStackTop((int)ctx));
#endif

  return (int) ctx;
}

void destroyNativeContext(int _ctx) {
  J2cContext *ctx = (J2cContext *) _ctx;
  if (ctx != mainContext)
    munmap(CTX2MEM(ctx), CONTEXT_SIZE + pgsize);
}

void run(int processorHandle, int _ctx) {
  J2cContext *ctx = (J2cContext *) _ctx;
  assert(processorHandle == 0);
  if (ctx == currentContext)
    {
      CTX("skipping switch %p -> %p\n", ctx, ctx);
      return;
    }

  J2cContext *me = currentContext;
  // topFrame is run(), top java frame's PC available here
  me->topFrame = getCurrentActivation(0);


  CTX("swap %p -> %p\n", me, ctx);
#ifdef THREAD_SETJMP
  if (setjmp(me->buf))
    currentContext = me;
  else
    longjmp(ctx->buf, 1);
#else
  swapcontext(&me->uc, &ctx->uc);
  currentContext = me;
#endif
  CTX("%p waking up\n", me);
}

jint makeActivation(jint _ctx,
		    Code *cf,
		    InvocationMessage *invocation)
{
  J2cContext  *ctx = (J2cContext *) _ctx;
  /*
  if (ctx->code)
    {
      fprintf(stderr, "makeActivation called after thread start\n");
      abort();
    }
  */
  ctx->code = cf;
  ctx->args = invocation;
  return 0;
}

void launchContext(void *ctx) {
  assert(!"not supported");
}

#ifdef BOOTBASE
static ImageFormat *image = (ImageFormat *) BOOTBASE;
#else
static ImageFormat *image;
#endif

void *getImageBaseAddress() {
  return (void*) ((char*)image->data + 4); /* first word is null */
}

void *getImageEndAddress() {
  int off = image->usedMemory - 4; /* last word is magic number */
  return (void *) ((char *)image->data + off);
}

const int signalsReservedByExecEngine[] = {
  /* add comma-separated list of signals that J2c uses for aborting or
   * regular execution here, but before the 0.  see interpreter/main.c
   * for an example. */
  SIGSEGV, SIGBUS, 0
};

jlong getNow() {
// This code is similar to getCurrentTime()
// in src/native/common/native_helpers.c
#if _POSIX_TIMERS > 0
    struct timespec tm;
    int status = clock_gettime(CLOCK_REALTIME, &tm);
    jlong nanos_per_sec = 1000L * 1000 * 1000;
    jlong now = tm.tv_sec * nanos_per_sec + tm.tv_nsec;
    if (status != 0) {
        ASSERT(status == -1 && errno == EINVAL, "unexpected error from clock_getttime");
        return status;
    }
    return now;
#else
     struct timeval now;
     jlong micros_per_sec = 1000000;
     jlong nowNanos = 0;
     gettimeofday(&now, NULL);
/*      printf("Using gettimeofday()\n");  */
     nowNanos = (now.tv_usec + now.tv_sec * micros_per_sec) * 1000;
     return nowNanos;
#endif
}

int main(int argc, char **argv) {

  long long now=getNow();
  printf("Initial nanoseconds from the Epoch: %lld  (0x%016llX)\n",now,now);
  fflush(stdout);

  process_argc = argc;
  process_argv = argv;

#ifndef BOOTBASE
  if (argc < 2) {
    fprintf(stderr, "usage: %s <image> arguments\n", argv[0]);
    exit(1);
  }
  int fd = open(argv[1], O_RDONLY);
  if (fd < 0) {
    perror("opening image file");
  }
  process_argv++;
  process_argc--;
  ImageFormat tmpImage;
  read(fd, &tmpImage, sizeof(tmpImage));
  lseek(fd, 0, SEEK_SET);
  int length = lseek(fd, 0, SEEK_END);
  /* Round up request to a multiple of page size.  The remainder of
   * the last page will be zero-filled.  Not rounding up may cause
   * the OS to round down.
   */
  length = (length + pgsize - 1) & ~(pgsize - 1);
  void *_image = mmap((void *) tmpImage.baseAddress,
		      length,
		      PROT_READ|PROT_WRITE|PROT_EXEC,
		      MAP_PRIVATE,
		      fd,
		      0);
  if (_image != (void *) tmpImage.baseAddress) {
    perror("could not map image to base address");
    exit(1);
  }

  image = (ImageFormat *) _image;
  if (image->mainObject != &mainObject
      || image->mainMethod != &mainMethod
      || image->coreServicesAccess != &coreServicesAccess
      || image->bootContext != &bootContext) {
    fputs("Image version does not agree with compiled code\n", stderr);
    exit(-1);
  }
    
#else
  // malloc will return memory immediately after the bootimage.  This
  // makes MostlyCopyingWB cringe
  malloc(getpagesize());
#endif
  initializedBlueprints = (int **) malloc(3 * sizeof(int));
  initializedBlueprints[1] =
    (int *) malloc(u1.ovm_context_1->ovm_blueprintCount/8);
  memset(initializedBlueprints[1], 0, u1.ovm_context_1->ovm_blueprintCount/8);
  initializedBlueprints[2] =
    (int *) malloc(u1.ovm_appContext_1->ovm_blueprintCount/8);
  memset(initializedBlueprints[2], 0,
	 u1.ovm_appContext_1->ovm_blueprintCount/8);
  EXIT_ERRNO(initSignalHandling(),"initSignalHandling");

  compute_range_hints();
  
  initializeNativeScheduling();

  struct sigaction act;
  act.sa_sigaction = segvHandler;
  sigfillset(&act.sa_mask);
  act.sa_flags = SA_SIGINFO;
  if (sigaction(SIGSEGV, &act, 0) < 0)
    perror("installing SEGV handler");
  if (sigaction(SIGBUS, &act, 0) < 0)
    perror("installing BUS handler");

  J2cContext c(&bootContext);
  currentContext = mainContext = &c;
  mainContext->bottomFrame = getCurrentActivation(0);
  mainContext->stackBase = (void *) getCurrentActivation(0);
  bootContext.ovm_nativeContextHandle_1 = (jint) mainContext;
  
  boot();
}

inline static int is_array(S3Blueprint* bpt) {
    return bpt->ovm_arrayDepth_1 > 0;
}

static S3Blueprint* strip_array_blueprint(S3Blueprint* bpt, int howManyTimes) {
    int i;
    assert(howManyTimes <= bpt->ovm_arrayDepth_1);
    for (i = 0; i < howManyTimes; i++) {
	bpt = (S3Blueprint *)
	  ((e_s3_core_domain_S3Blueprint_Array*)bpt)->ovm_componentBlueprint_1;
    }
    return bpt;
}

jboolean is_subtype_of(S3Blueprint* client, S3Blueprint* provider) {
    if (client == provider) { /* equality should work for primitives */
	return 1;
    }
    assert(client->ovm_typeDisplay_1);

    // if client is primitive, typeDisplay_.length == 0
    if (provider->ovm_typeBucket_1 >= client->ovm_typeDisplay_1->length)
      return 0;
    
    if (client->ovm_typeDisplay_1->values[provider->ovm_typeBucket_1] 
	== provider->ovm_typeBucketID_1) {
	return 1;
    } 
    /*	printf("failed display[%d] = %d != %d\n",
 	provider->typeBucket_,
 	client->typeDisplay_->values[provider->typeBucket_]
 	provider->typeBucketID_);
    */
    
    if (!is_array(provider)) {
	return 0; /* the test was subsumed */ 
    } else { /* provider is an array, test doesn't matter */
	if (!is_array(client)) {
	    return 0;
	} else {
	    int cdepth = client->ovm_arrayDepth_1;
	    int pdepth = provider->ovm_arrayDepth_1;
	    if (pdepth > cdepth) { 
		return 0;
	    } else { /* pdepth <= cdepth */
		return is_subtype_of(strip_array_blueprint(client, pdepth),
				     strip_array_blueprint(provider, pdepth));
	    }
	}
    } 
}

void did_init(jint ctx, jint bp) {
  // Repeatedly do CSA calls during startup so that the test in
  // needs_init will be fast later on.
  if (!((CSA *) u1.ovm_myCSA)->ovm_classInitializationEnabled)
    return;
  int w = bp >> 5;
  int b = 1 << (bp & 31);
  initializedBlueprints[ctx][w] |= b;
}

int stacktrace(NativeContext *_) {
  abort();
  return 0;
}

static size_t range_offset;	// first java bytecode pc
static size_t range_size;	// last java bytecode pc - range_offset

/*
 * range_hints is essentially a hashtable that tells us the first
 * method that a pc may be in.  RANGE_HASH(pc - range_offset) gives us
 * the higher-order bits of the difference between pc and the start of
 * the first java method, and
 * range_hints[RANGE_HASH(pc - range_offset) give us the index of the
 * first java method that shares higher-order bits with pc.
 *
 * The number 7 was chosen based on `gen-ovm -opt=run' on an x86.
 * In a few places there where long sequences of methods 16 bytes
 * apart, and it was more common to see methods 32 or 48 bytes apart.
 * shifting 7 means that we should never have to search more than 8 or
 * 9 entries in the range table, and the average will be very close to
 * 1.  It uses quite a bit of memory: 3% of range_size.
 */
#define RANGE_HASH(X) ((X) >> 7)
static int *range_hints;

static void compute_range_hints() {
  range_offset = j2c_method_ranges.values[0].start;
  range_size = (j2c_method_ranges.values[j2c_method_pointers.length-1].end
		- range_offset);

  int n_hints = RANGE_HASH(range_size) + 1;
  range_hints = new int[n_hints];
#if 1
  fprintf(stderr, "allocating %d range hints\n", n_hints);
#endif

  int ridx = 0;
  for (int hidx = 0; hidx < n_hints; hidx++) {
    while (ridx < j2c_method_pointers.length 
	   && RANGE_HASH(j2c_method_ranges.values[ridx].end - range_offset) < hidx)
      ridx++;
    range_hints[hidx] = ridx;
  }
}
  
void* j2c_getCode(void *_pc) {
  size_t pc = (size_t) _pc;
  size_t delta = pc - range_offset;
  if (delta >= range_size) {
#if 0
    fprintf(stderr, "getCode(%p) => <null>, out of range\n", _pc);
#endif
    return 0;
  }
  int idx = range_hints[RANGE_HASH(delta)];
  while (j2c_method_ranges.values[idx].end <= pc)
    idx++;
  J2cCodeFragment *ret = (j2c_method_ranges.values[idx].start <= pc
			  ? j2c_method_pointers.values[idx]
			  : 0);
#if 0
  fprintf(stderr, "getCode(%p) => %s@%p idx = %d, distance = %d\n",
	  _pc,
	  (ret
	   ? ((char *) (ret->ovm_cname->ovm_data->values
			+ ret->ovm_cname->ovm_offset))
	   : "<null>"),
	  ret,
	  idx,
	  idx - range_hints[RANGE_HASH(delta)] );
#endif
  return ret;
}

jint j2c_topFrame(jint nc) {
  return ((J2cContext *) nc)->topFrame;
}

jint j2c_bottomFrame(jint nc) {
  return ((J2cContext *) nc)->bottomFrame;
}

// analogous to BCdead
#define RTdead { assert(!"unsupported operation"); exit(-1); }

void *getOVMContextLoc(jint nc) {
  return (void *) &((J2cContext *) nc)->ovmContext;
}

void *getInvocationArgsLoc(jint nc) {
  return (void *) &((J2cContext *) nc)->args;
}

#ifdef PRECISE_GC
jint getNextGcRecord(jint _gcFrame) {
  return (jint) ((GCFrame *) _gcFrame)->next;
}

jint getGcRecord(int nc, jint callee) {
  J2cContext *ctx = (J2cContext *) nc;
  GCFrame *fr = ctx->gcTop;

  if (ctx != currentContext && callee == ctx->topFrame)
    return (jint) fr;

  while (fr && (jint) fr < callee)
    fr = fr->next;
  return (jint) fr;
}
  
e_Array<Oop *> *getLocalReferences(jint gcFrame) {
  return (e_Array<Oop *> *) &((GCFrame *) gcFrame)->arrayHeader;
}
#else
jint getNextGcRecord(jint _) RTdead
jint getGcRecord(jint _, jint __) RTdead
e_Array<Oop *> *getLocalReferences(jint frameHandle) RTdead

void *getStackBase(jint nc) {
  J2cContext *ctx = (J2cContext *) nc;
  return ctx->stackBase;
}

void *getStackTop(jint nc) {
  J2cContext *ctx = (J2cContext *) nc;
  if (ctx == currentContext)
    // skip invoke_native() => getStackTop()
    return callerLocalsEnd(getCurrentActivation(2));
  else
#ifdef THREAD_UCONTEXT
    return (void *) UC_REG(ctx->uc, UC_FP);
#else
    return (void *) JB_REG(ctx->buf, JB_FP);
#endif
}

void *getSavedRegisters(jint _ctx) {
  J2cContext *ctx = (J2cContext *) _ctx;
#ifdef THREAD_UCONTEXT
  return (void *) &UC_REG(ctx->uc, 0);
#else
  return ctx->buf;
#endif
}

jint getSavedRegisterSize() {
#ifdef THREAD_UCONTEXT
  return UC_SIZE;
#else
  return sizeof(jmp_buf);
#endif
}

jboolean stackGrowsUp() { return 0; }
#endif

// Should not be here!
void C(jbyte *, ...) { }
