/**************************************************************************/ /* */ /* OCaml */ /* */ /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */ /* */ /* Copyright 1996 Institut National de Recherche en Informatique et */ /* en Automatique. */ /* */ /* All rights reserved. This file is distributed under the terms of */ /* the GNU Lesser General Public License version 2.1, with the */ /* special exception on linking described in the file LICENSE. */ /* */ /**************************************************************************/ #define CAML_INTERNALS /* The bytecode interpreter */ #include #include "caml/alloc.h" #include "caml/backtrace.h" #include "caml/callback.h" #include "caml/debugger.h" #include "caml/fail.h" #include "caml/fix_code.h" #include "caml/instrtrace.h" #include "caml/instruct.h" #include "caml/interp.h" #include "caml/major_gc.h" #include "caml/memory.h" #include "caml/misc.h" #include "caml/mlvalues.h" #include "caml/prims.h" #include "caml/signals.h" #include "caml/stacks.h" #include "caml/startup_aux.h" /* Registers for the abstract machine: pc the code pointer sp the stack pointer (grows downward) accu the accumulator env heap-allocated environment caml_trapsp pointer to the current trap frame extra_args number of extra arguments provided by the caller sp is a local copy of the global variable caml_extern_sp. */ /* Instruction decoding */ #ifdef THREADED_CODE # define Instruct(name) lbl_##name # if defined(ARCH_SIXTYFOUR) && !defined(ARCH_CODE32) # define Jumptbl_base ((char *) &&lbl_ACC0) # else # define Jumptbl_base ((char *) 0) # define jumptbl_base ((char *) 0) # endif # ifdef DEBUG # define Next goto next_instr # else # define Next goto *(void *)(jumptbl_base + *pc++) # endif #else # define Instruct(name) case name # define Next break #endif /* GC interface */ #define Setup_for_gc \ { sp -= 2; sp[0] = accu; sp[1] = env; caml_extern_sp = sp; } #define Restore_after_gc \ { accu = sp[0]; env = sp[1]; sp += 2; } #define Setup_for_c_call \ { saved_pc = pc; *--sp = env; caml_extern_sp = sp; } #define Restore_after_c_call \ { sp = caml_extern_sp; env = *sp++; saved_pc = NULL; } /* An event frame must look like accu + a C_CALL frame + a RETURN 1 frame */ #define Setup_for_event \ { sp -= 6; \ sp[0] = accu; /* accu */ \ sp[1] = Val_unit; /* C_CALL frame: dummy environment */ \ sp[2] = Val_unit; /* RETURN frame: dummy local 0 */ \ sp[3] = (value) pc; /* RETURN frame: saved return address */ \ sp[4] = env; /* RETURN frame: saved environment */ \ sp[5] = Val_long(extra_args); /* RETURN frame: saved extra args */ \ caml_extern_sp = sp; } #define Restore_after_event \ { sp = caml_extern_sp; accu = sp[0]; \ pc = (code_t) sp[3]; env = sp[4]; extra_args = Long_val(sp[5]); \ sp += 6; } /* Debugger interface */ #define Setup_for_debugger \ { sp -= 4; \ sp[0] = accu; sp[1] = (value)(pc - 1); \ sp[2] = env; sp[3] = Val_long(extra_args); \ caml_extern_sp = sp; } #define Restore_after_debugger { sp += 4; } #ifdef THREADED_CODE #define Restart_curr_instr \ goto *(jumptable[caml_saved_code[pc - 1 - caml_start_code]]) #else #define Restart_curr_instr \ curr_instr = caml_saved_code[pc - 1 - caml_start_code]; \ goto dispatch_instr #endif /* Register optimization. Some compilers underestimate the use of the local variables representing the abstract machine registers, and don't put them in hardware registers, which slows down the interpreter considerably. For GCC, I have hand-assigned hardware registers for several architectures. */ #if defined(__GNUC__) && !defined(DEBUG) && !defined(__INTEL_COMPILER) \ && !defined(__llvm__) #ifdef __mips__ #define PC_REG asm("$16") #define SP_REG asm("$17") #define ACCU_REG asm("$18") #endif #ifdef __sparc__ #define PC_REG asm("%l0") #define SP_REG asm("%l1") #define ACCU_REG asm("%l2") #endif #ifdef __alpha__ #ifdef __CRAY__ #define PC_REG asm("r9") #define SP_REG asm("r10") #define ACCU_REG asm("r11") #define JUMPTBL_BASE_REG asm("r12") #else #define PC_REG asm("$9") #define SP_REG asm("$10") #define ACCU_REG asm("$11") #define JUMPTBL_BASE_REG asm("$12") #endif #endif #ifdef __i386__ #define PC_REG asm("%esi") #define SP_REG asm("%edi") #define ACCU_REG #endif #if defined(__ppc__) || defined(__ppc64__) #define PC_REG asm("26") #define SP_REG asm("27") #define ACCU_REG asm("28") #endif #ifdef __hppa__ #define PC_REG asm("%r18") #define SP_REG asm("%r17") #define ACCU_REG asm("%r16") #endif #ifdef __mc68000__ #define PC_REG asm("a5") #define SP_REG asm("a4") #define ACCU_REG asm("d7") #endif /* PR#4953: these specific registers not available in Thumb mode */ #if defined (__arm__) && !defined(__thumb__) #define PC_REG asm("r6") #define SP_REG asm("r8") #define ACCU_REG asm("r7") #endif #ifdef __ia64__ #define PC_REG asm("36") #define SP_REG asm("37") #define ACCU_REG asm("38") #define JUMPTBL_BASE_REG asm("39") #endif #ifdef __x86_64__ #define PC_REG asm("%r15") #define SP_REG asm("%r14") #define ACCU_REG asm("%r13") #endif #ifdef __aarch64__ #define PC_REG asm("%x19") #define SP_REG asm("%x20") #define ACCU_REG asm("%x21") #define JUMPTBL_BASE_REG asm("%x22") #endif #endif #ifdef DEBUG static intnat caml_bcodcount; #endif /* The interpreter itself */ value caml_interprete(code_t prog, asize_t prog_size) { #ifdef PC_REG register code_t pc PC_REG; register value * sp SP_REG; register value accu ACCU_REG; #else register code_t pc; register value * sp; register value accu; #endif #if defined(THREADED_CODE) && defined(ARCH_SIXTYFOUR) && !defined(ARCH_CODE32) #ifdef JUMPTBL_BASE_REG register char * jumptbl_base JUMPTBL_BASE_REG; #else register char * jumptbl_base; #endif #endif value env; intnat extra_args; struct longjmp_buffer * initial_external_raise; int initial_sp_offset; /* volatile ensures that initial_local_roots and saved_pc will keep correct value across longjmp */ struct caml__roots_block * volatile initial_local_roots; volatile code_t saved_pc = NULL; struct longjmp_buffer raise_buf; #ifndef THREADED_CODE opcode_t curr_instr; #endif #ifdef THREADED_CODE static void * jumptable[] = { # include "caml/jumptbl.h" }; #endif if (prog == NULL) { /* Interpreter is initializing */ #ifdef THREADED_CODE caml_instr_table = (char **) jumptable; caml_instr_base = Jumptbl_base; #endif return Val_unit; } #if defined(THREADED_CODE) && defined(ARCH_SIXTYFOUR) && !defined(ARCH_CODE32) jumptbl_base = Jumptbl_base; #endif initial_local_roots = caml_local_roots; initial_sp_offset = (char *) caml_stack_high - (char *) caml_extern_sp; initial_external_raise = caml_external_raise; caml_callback_depth++; saved_pc = NULL; if (sigsetjmp(raise_buf.buf, 0)) { caml_local_roots = initial_local_roots; sp = caml_extern_sp; accu = caml_exn_bucket; pc = saved_pc; saved_pc = NULL; if (pc != NULL) pc += 2; /* +2 adjustement for the sole purpose of backtraces */ goto raise_exception; } caml_external_raise = &raise_buf; sp = caml_extern_sp; pc = prog; extra_args = 0; env = Atom(0); accu = Val_int(0); #ifdef THREADED_CODE #ifdef DEBUG next_instr: if (caml_icount-- == 0) caml_stop_here (); CAMLassert(sp >= caml_stack_low); CAMLassert(sp <= caml_stack_high); #endif goto *(void *)(jumptbl_base + *pc++); /* Jump to the first instruction */ #else while(1) { #ifdef DEBUG caml_bcodcount++; if (caml_icount-- == 0) caml_stop_here (); if (caml_trace_level>1) printf("\n##%" ARCH_INTNAT_PRINTF_FORMAT "d\n", caml_bcodcount); if (caml_trace_level>0) caml_disasm_instr(pc); if (caml_trace_level>1) { printf("env="); caml_trace_value_file(env,prog,prog_size,stdout); putchar('\n'); caml_trace_accu_sp_file(accu,sp,prog,prog_size,stdout); fflush(stdout); }; CAMLassert(sp >= caml_stack_low); CAMLassert(sp <= caml_stack_high); #endif curr_instr = *pc++; dispatch_instr: switch(curr_instr) { #endif /* Basic stack operations */ Instruct(ACC0): accu = sp[0]; Next; Instruct(ACC1): accu = sp[1]; Next; Instruct(ACC2): accu = sp[2]; Next; Instruct(ACC3): accu = sp[3]; Next; Instruct(ACC4): accu = sp[4]; Next; Instruct(ACC5): accu = sp[5]; Next; Instruct(ACC6): accu = sp[6]; Next; Instruct(ACC7): accu = sp[7]; Next; Instruct(PUSH): Instruct(PUSHACC0): *--sp = accu; Next; Instruct(PUSHACC1): *--sp = accu; accu = sp[1]; Next; Instruct(PUSHACC2): *--sp = accu; accu = sp[2]; Next; Instruct(PUSHACC3): *--sp = accu; accu = sp[3]; Next; Instruct(PUSHACC4): *--sp = accu; accu = sp[4]; Next; Instruct(PUSHACC5): *--sp = accu; accu = sp[5]; Next; Instruct(PUSHACC6): *--sp = accu; accu = sp[6]; Next; Instruct(PUSHACC7): *--sp = accu; accu = sp[7]; Next; Instruct(PUSHACC): *--sp = accu; /* Fallthrough */ Instruct(ACC): accu = sp[*pc++]; Next; Instruct(POP): sp += *pc++; Next; Instruct(ASSIGN): sp[*pc++] = accu; accu = Val_unit; Next; /* Access in heap-allocated environment */ Instruct(ENVACC1): accu = Field(env, 1); Next; Instruct(ENVACC2): accu = Field(env, 2); Next; Instruct(ENVACC3): accu = Field(env, 3); Next; Instruct(ENVACC4): accu = Field(env, 4); Next; Instruct(PUSHENVACC1): *--sp = accu; accu = Field(env, 1); Next; Instruct(PUSHENVACC2): *--sp = accu; accu = Field(env, 2); Next; Instruct(PUSHENVACC3): *--sp = accu; accu = Field(env, 3); Next; Instruct(PUSHENVACC4): *--sp = accu; accu = Field(env, 4); Next; Instruct(PUSHENVACC): *--sp = accu; /* Fallthrough */ Instruct(ENVACC): accu = Field(env, *pc++); Next; /* Function application */ Instruct(PUSH_RETADDR): { sp -= 3; sp[0] = (value) (pc + *pc); sp[1] = env; sp[2] = Val_long(extra_args); pc++; Next; } Instruct(APPLY): { extra_args = *pc - 1; pc = Code_val(accu); env = accu; goto check_stacks; } Instruct(APPLY1): { value arg1 = sp[0]; sp -= 3; sp[0] = arg1; sp[1] = (value)pc; sp[2] = env; sp[3] = Val_long(extra_args); pc = Code_val(accu); env = accu; extra_args = 0; goto check_stacks; } Instruct(APPLY2): { value arg1 = sp[0]; value arg2 = sp[1]; sp -= 3; sp[0] = arg1; sp[1] = arg2; sp[2] = (value)pc; sp[3] = env; sp[4] = Val_long(extra_args); pc = Code_val(accu); env = accu; extra_args = 1; goto check_stacks; } Instruct(APPLY3): { value arg1 = sp[0]; value arg2 = sp[1]; value arg3 = sp[2]; sp -= 3; sp[0] = arg1; sp[1] = arg2; sp[2] = arg3; sp[3] = (value)pc; sp[4] = env; sp[5] = Val_long(extra_args); pc = Code_val(accu); env = accu; extra_args = 2; goto check_stacks; } Instruct(APPTERM): { int nargs = *pc++; int slotsize = *pc; value * newsp; int i; /* Slide the nargs bottom words of the current frame to the top of the frame, and discard the remainder of the frame */ newsp = sp + slotsize - nargs; for (i = nargs - 1; i >= 0; i--) newsp[i] = sp[i]; sp = newsp; pc = Code_val(accu); env = accu; extra_args += nargs - 1; goto check_stacks; } Instruct(APPTERM1): { value arg1 = sp[0]; sp = sp + *pc - 1; sp[0] = arg1; pc = Code_val(accu); env = accu; goto check_stacks; } Instruct(APPTERM2): { value arg1 = sp[0]; value arg2 = sp[1]; sp = sp + *pc - 2; sp[0] = arg1; sp[1] = arg2; pc = Code_val(accu); env = accu; extra_args += 1; goto check_stacks; } Instruct(APPTERM3): { value arg1 = sp[0]; value arg2 = sp[1]; value arg3 = sp[2]; sp = sp + *pc - 3; sp[0] = arg1; sp[1] = arg2; sp[2] = arg3; pc = Code_val(accu); env = accu; extra_args += 2; goto check_stacks; } Instruct(RETURN): { sp += *pc++; if (extra_args > 0) { extra_args--; pc = Code_val(accu); env = accu; } else { pc = (code_t)(sp[0]); env = sp[1]; extra_args = Long_val(sp[2]); sp += 3; } Next; } Instruct(RESTART): { int num_args = Wosize_val(env) - 2; int i; sp -= num_args; for (i = 0; i < num_args; i++) sp[i] = Field(env, i + 2); env = Field(env, 1); extra_args += num_args; Next; } Instruct(GRAB): { int required = *pc++; if (extra_args >= required) { extra_args -= required; } else { mlsize_t num_args, i; num_args = 1 + extra_args; /* arg1 + extra args */ Alloc_small(accu, num_args + 2, Closure_tag); Field(accu, 1) = env; for (i = 0; i < num_args; i++) Field(accu, i + 2) = sp[i]; Code_val(accu) = pc - 3; /* Point to the preceding RESTART instr. */ sp += num_args; pc = (code_t)(sp[0]); env = sp[1]; extra_args = Long_val(sp[2]); sp += 3; } Next; } Instruct(CLOSURE): { int nvars = *pc++; int i; if (nvars > 0) *--sp = accu; if (nvars < Max_young_wosize) { /* nvars + 1 <= Max_young_wosize, can allocate in minor heap */ Alloc_small(accu, 1 + nvars, Closure_tag); for (i = 0; i < nvars; i++) Field(accu, i + 1) = sp[i]; } else { /* PR#6385: must allocate in major heap */ /* caml_alloc_shr and caml_initialize never trigger a GC, so no need to Setup_for_gc */ accu = caml_alloc_shr(1 + nvars, Closure_tag); for (i = 0; i < nvars; i++) caml_initialize(&Field(accu, i + 1), sp[i]); } /* The code pointer is not in the heap, so no need to go through caml_initialize. */ Code_val(accu) = pc + *pc; pc++; sp += nvars; Next; } Instruct(CLOSUREREC): { int nfuncs = *pc++; int nvars = *pc++; mlsize_t blksize = nfuncs * 2 - 1 + nvars; int i; value * p; if (nvars > 0) *--sp = accu; if (blksize <= Max_young_wosize) { Alloc_small(accu, blksize, Closure_tag); p = &Field(accu, nfuncs * 2 - 1); for (i = 0; i < nvars; i++, p++) *p = sp[i]; } else { /* PR#6385: must allocate in major heap */ /* caml_alloc_shr and caml_initialize never trigger a GC, so no need to Setup_for_gc */ accu = caml_alloc_shr(blksize, Closure_tag); p = &Field(accu, nfuncs * 2 - 1); for (i = 0; i < nvars; i++, p++) caml_initialize(p, sp[i]); } sp += nvars; /* The code pointers and infix headers are not in the heap, so no need to go through caml_initialize. */ p = &Field(accu, 0); *p = (value) (pc + pc[0]); *--sp = accu; p++; for (i = 1; i < nfuncs; i++) { *p = Make_header(i * 2, Infix_tag, Caml_white); /* color irrelevant. */ p++; *p = (value) (pc + pc[i]); *--sp = (value) p; p++; } pc += nfuncs; Next; } Instruct(PUSHOFFSETCLOSURE): *--sp = accu; /* fallthrough */ Instruct(OFFSETCLOSURE): accu = env + *pc++ * sizeof(value); Next; Instruct(PUSHOFFSETCLOSUREM2): *--sp = accu; /* fallthrough */ Instruct(OFFSETCLOSUREM2): accu = env - 2 * sizeof(value); Next; Instruct(PUSHOFFSETCLOSURE0): *--sp = accu; /* fallthrough */ Instruct(OFFSETCLOSURE0): accu = env; Next; Instruct(PUSHOFFSETCLOSURE2): *--sp = accu; /* fallthrough */ Instruct(OFFSETCLOSURE2): accu = env + 2 * sizeof(value); Next; /* Access to global variables */ Instruct(PUSHGETGLOBAL): *--sp = accu; /* Fallthrough */ Instruct(GETGLOBAL): accu = Field(caml_global_data, *pc); pc++; Next; Instruct(PUSHGETGLOBALFIELD): *--sp = accu; /* Fallthrough */ Instruct(GETGLOBALFIELD): { accu = Field(caml_global_data, *pc); pc++; accu = Field(accu, *pc); pc++; Next; } Instruct(SETGLOBAL): caml_modify(&Field(caml_global_data, *pc), accu); accu = Val_unit; pc++; Next; /* Allocation of blocks */ Instruct(PUSHATOM0): *--sp = accu; /* Fallthrough */ Instruct(ATOM0): accu = Atom(0); Next; Instruct(PUSHATOM): *--sp = accu; /* Fallthrough */ Instruct(ATOM): accu = Atom(*pc++); Next; Instruct(MAKEBLOCK): { mlsize_t wosize = *pc++; tag_t tag = *pc++; mlsize_t i; value block; if (wosize <= Max_young_wosize) { Alloc_small(block, wosize, tag); Field(block, 0) = accu; for (i = 1; i < wosize; i++) Field(block, i) = *sp++; } else { block = caml_alloc_shr(wosize, tag); caml_initialize(&Field(block, 0), accu); for (i = 1; i < wosize; i++) caml_initialize(&Field(block, i), *sp++); } accu = block; Next; } Instruct(MAKEBLOCK1): { tag_t tag = *pc++; value block; Alloc_small(block, 1, tag); Field(block, 0) = accu; accu = block; Next; } Instruct(MAKEBLOCK2): { tag_t tag = *pc++; value block; Alloc_small(block, 2, tag); Field(block, 0) = accu; Field(block, 1) = sp[0]; sp += 1; accu = block; Next; } Instruct(MAKEBLOCK3): { tag_t tag = *pc++; value block; Alloc_small(block, 3, tag); Field(block, 0) = accu; Field(block, 1) = sp[0]; Field(block, 2) = sp[1]; sp += 2; accu = block; Next; } Instruct(MAKEFLOATBLOCK): { mlsize_t size = *pc++; mlsize_t i; value block; if (size <= Max_young_wosize / Double_wosize) { Alloc_small(block, size * Double_wosize, Double_array_tag); } else { block = caml_alloc_shr(size * Double_wosize, Double_array_tag); } Store_double_flat_field(block, 0, Double_val(accu)); for (i = 1; i < size; i++){ Store_double_flat_field(block, i, Double_val(*sp)); ++ sp; } accu = block; Next; } /* Access to components of blocks */ Instruct(GETFIELD0): accu = Field(accu, 0); Next; Instruct(GETFIELD1): accu = Field(accu, 1); Next; Instruct(GETFIELD2): accu = Field(accu, 2); Next; Instruct(GETFIELD3): accu = Field(accu, 3); Next; Instruct(GETFIELD): accu = Field(accu, *pc); pc++; Next; Instruct(GETFLOATFIELD): { double d = Double_flat_field(accu, *pc); Alloc_small(accu, Double_wosize, Double_tag); Store_double_val(accu, d); pc++; Next; } Instruct(SETFIELD0): caml_modify(&Field(accu, 0), *sp++); accu = Val_unit; Next; Instruct(SETFIELD1): caml_modify(&Field(accu, 1), *sp++); accu = Val_unit; Next; Instruct(SETFIELD2): caml_modify(&Field(accu, 2), *sp++); accu = Val_unit; Next; Instruct(SETFIELD3): caml_modify(&Field(accu, 3), *sp++); accu = Val_unit; Next; Instruct(SETFIELD): caml_modify(&Field(accu, *pc), *sp++); accu = Val_unit; pc++; Next; Instruct(SETFLOATFIELD): Store_double_flat_field(accu, *pc, Double_val(*sp)); accu = Val_unit; sp++; pc++; Next; /* Array operations */ Instruct(VECTLENGTH): { /* Todo: when FLAT_FLOAT_ARRAY is false, this instruction should be split into VECTLENGTH and FLOATVECTLENGTH because we know statically which one it is. */ mlsize_t size = Wosize_val(accu); if (Tag_val(accu) == Double_array_tag) size = size / Double_wosize; accu = Val_long(size); Next; } Instruct(GETVECTITEM): accu = Field(accu, Long_val(sp[0])); sp += 1; Next; Instruct(SETVECTITEM): caml_modify(&Field(accu, Long_val(sp[0])), sp[1]); accu = Val_unit; sp += 2; Next; /* Bytes/String operations */ Instruct(GETSTRINGCHAR): Instruct(GETBYTESCHAR): accu = Val_int(Byte_u(accu, Long_val(sp[0]))); sp += 1; Next; Instruct(SETBYTESCHAR): Byte_u(accu, Long_val(sp[0])) = Int_val(sp[1]); sp += 2; accu = Val_unit; Next; /* Branches and conditional branches */ Instruct(BRANCH): pc += *pc; Next; Instruct(BRANCHIF): if (accu != Val_false) pc += *pc; else pc++; Next; Instruct(BRANCHIFNOT): if (accu == Val_false) pc += *pc; else pc++; Next; Instruct(SWITCH): { uint32_t sizes = *pc++; if (Is_block(accu)) { intnat index = Tag_val(accu); CAMLassert ((uintnat) index < (sizes >> 16)); pc += pc[(sizes & 0xFFFF) + index]; } else { intnat index = Long_val(accu); CAMLassert ((uintnat) index < (sizes & 0xFFFF)) ; pc += pc[index]; } Next; } Instruct(BOOLNOT): accu = Val_not(accu); Next; /* Exceptions */ Instruct(PUSHTRAP): sp -= 4; Trap_pc(sp) = pc + *pc; Trap_link(sp) = caml_trapsp; sp[2] = env; sp[3] = Val_long(extra_args); caml_trapsp = sp; pc++; Next; Instruct(POPTRAP): if (caml_something_to_do) { /* We must check here so that if a signal is pending and its handler triggers an exception, the exception is trapped by the current try...with, not the enclosing one. */ pc--; /* restart the POPTRAP after processing the signal */ goto process_signal; } caml_trapsp = Trap_link(sp); sp += 4; Next; Instruct(RAISE_NOTRACE): if (caml_trapsp >= caml_trap_barrier) caml_debugger(TRAP_BARRIER); goto raise_notrace; Instruct(RERAISE): if (caml_trapsp >= caml_trap_barrier) caml_debugger(TRAP_BARRIER); if (caml_backtrace_active) caml_stash_backtrace(accu, pc, sp, 1); goto raise_notrace; Instruct(RAISE): raise_exception: if (caml_trapsp >= caml_trap_barrier) caml_debugger(TRAP_BARRIER); if (caml_backtrace_active) caml_stash_backtrace(accu, pc, sp, 0); raise_notrace: if ((char *) caml_trapsp >= (char *) caml_stack_high - initial_sp_offset) { caml_external_raise = initial_external_raise; caml_extern_sp = (value *) ((char *) caml_stack_high - initial_sp_offset); caml_callback_depth--; return Make_exception_result(accu); } sp = caml_trapsp; pc = Trap_pc(sp); caml_trapsp = Trap_link(sp); env = sp[2]; extra_args = Long_val(sp[3]); sp += 4; Next; /* Stack checks */ check_stacks: if (sp < caml_stack_threshold) { caml_extern_sp = sp; caml_realloc_stack(Stack_threshold / sizeof(value)); sp = caml_extern_sp; } /* Fall through CHECK_SIGNALS */ /* Signal handling */ Instruct(CHECK_SIGNALS): /* accu not preserved */ if (caml_something_to_do) goto process_signal; Next; process_signal: caml_something_to_do = 0; Setup_for_event; caml_process_event(); Restore_after_event; Next; /* Calling C functions */ Instruct(C_CALL1): Setup_for_c_call; accu = Primitive(*pc)(accu); Restore_after_c_call; pc++; Next; Instruct(C_CALL2): Setup_for_c_call; accu = Primitive(*pc)(accu, sp[1]); Restore_after_c_call; sp += 1; pc++; Next; Instruct(C_CALL3): Setup_for_c_call; accu = Primitive(*pc)(accu, sp[1], sp[2]); Restore_after_c_call; sp += 2; pc++; Next; Instruct(C_CALL4): Setup_for_c_call; accu = Primitive(*pc)(accu, sp[1], sp[2], sp[3]); Restore_after_c_call; sp += 3; pc++; Next; Instruct(C_CALL5): Setup_for_c_call; accu = Primitive(*pc)(accu, sp[1], sp[2], sp[3], sp[4]); Restore_after_c_call; sp += 4; pc++; Next; Instruct(C_CALLN): { int nargs = *pc++; *--sp = accu; Setup_for_c_call; accu = Primitive(*pc)(sp + 1, nargs); Restore_after_c_call; sp += nargs; pc++; Next; } /* Integer constants */ Instruct(CONST0): accu = Val_int(0); Next; Instruct(CONST1): accu = Val_int(1); Next; Instruct(CONST2): accu = Val_int(2); Next; Instruct(CONST3): accu = Val_int(3); Next; Instruct(PUSHCONST0): *--sp = accu; accu = Val_int(0); Next; Instruct(PUSHCONST1): *--sp = accu; accu = Val_int(1); Next; Instruct(PUSHCONST2): *--sp = accu; accu = Val_int(2); Next; Instruct(PUSHCONST3): *--sp = accu; accu = Val_int(3); Next; Instruct(PUSHCONSTINT): *--sp = accu; /* Fallthrough */ Instruct(CONSTINT): accu = Val_int(*pc); pc++; Next; /* Integer arithmetic */ Instruct(NEGINT): accu = (value)(2 - (intnat)accu); Next; Instruct(ADDINT): accu = (value)((intnat) accu + (intnat) *sp++ - 1); Next; Instruct(SUBINT): accu = (value)((intnat) accu - (intnat) *sp++ + 1); Next; Instruct(MULINT): accu = Val_long(Long_val(accu) * Long_val(*sp++)); Next; Instruct(DIVINT): { intnat divisor = Long_val(*sp++); if (divisor == 0) { Setup_for_c_call; caml_raise_zero_divide(); } accu = Val_long(Long_val(accu) / divisor); Next; } Instruct(MODINT): { intnat divisor = Long_val(*sp++); if (divisor == 0) { Setup_for_c_call; caml_raise_zero_divide(); } accu = Val_long(Long_val(accu) % divisor); Next; } Instruct(ANDINT): accu = (value)((intnat) accu & (intnat) *sp++); Next; Instruct(ORINT): accu = (value)((intnat) accu | (intnat) *sp++); Next; Instruct(XORINT): accu = (value)(((intnat) accu ^ (intnat) *sp++) | 1); Next; Instruct(LSLINT): accu = (value)((((intnat) accu - 1) << Long_val(*sp++)) + 1); Next; Instruct(LSRINT): accu = (value)((((uintnat) accu) >> Long_val(*sp++)) | 1); Next; Instruct(ASRINT): accu = (value)((((intnat) accu) >> Long_val(*sp++)) | 1); Next; #define Integer_comparison(typ,opname,tst) \ Instruct(opname): \ accu = Val_int((typ) accu tst (typ) *sp++); Next; Integer_comparison(intnat,EQ, ==) Integer_comparison(intnat,NEQ, !=) Integer_comparison(intnat,LTINT, <) Integer_comparison(intnat,LEINT, <=) Integer_comparison(intnat,GTINT, >) Integer_comparison(intnat,GEINT, >=) Integer_comparison(uintnat,ULTINT, <) Integer_comparison(uintnat,UGEINT, >=) #define Integer_branch_comparison(typ,opname,tst,debug) \ Instruct(opname): \ if ( *pc++ tst (typ) Long_val(accu)) { \ pc += *pc ; \ } else { \ pc++ ; \ } ; Next; Integer_branch_comparison(intnat,BEQ, ==, "==") Integer_branch_comparison(intnat,BNEQ, !=, "!=") Integer_branch_comparison(intnat,BLTINT, <, "<") Integer_branch_comparison(intnat,BLEINT, <=, "<=") Integer_branch_comparison(intnat,BGTINT, >, ">") Integer_branch_comparison(intnat,BGEINT, >=, ">=") Integer_branch_comparison(uintnat,BULTINT, <, "<") Integer_branch_comparison(uintnat,BUGEINT, >=, ">=") Instruct(OFFSETINT): accu += *pc << 1; pc++; Next; Instruct(OFFSETREF): Field(accu, 0) += *pc << 1; accu = Val_unit; pc++; Next; Instruct(ISINT): accu = Val_long(accu & 1); Next; /* Object-oriented operations */ #define Lookup(obj, lab) Field (Field (obj, 0), Int_val(lab)) /* please don't forget to keep below code in sync with the functions caml_cache_public_method and caml_cache_public_method2 in obj.c */ Instruct(GETMETHOD): accu = Lookup(sp[0], accu); Next; #define CAML_METHOD_CACHE #ifdef CAML_METHOD_CACHE Instruct(GETPUBMET): { /* accu == object, pc[0] == tag, pc[1] == cache */ value meths = Field (accu, 0); value ofs; #ifdef CAML_TEST_CACHE static int calls = 0, hits = 0; if (calls >= 10000000) { fprintf(stderr, "cache hit = %d%%\n", hits / 100000); calls = 0; hits = 0; } calls++; #endif *--sp = accu; accu = Val_int(*pc++); ofs = *pc & Field(meths,1); if (*(value*)(((char*)&Field(meths,3)) + ofs) == accu) { #ifdef CAML_TEST_CACHE hits++; #endif accu = *(value*)(((char*)&Field(meths,2)) + ofs); } else { int li = 3, hi = Field(meths,0), mi; while (li < hi) { mi = ((li+hi) >> 1) | 1; if (accu < Field(meths,mi)) hi = mi-2; else li = mi; } *pc = (li-3)*sizeof(value); accu = Field (meths, li-1); } pc++; Next; } #else Instruct(GETPUBMET): *--sp = accu; accu = Val_int(*pc); pc += 2; /* Fallthrough */ #endif Instruct(GETDYNMET): { /* accu == tag, sp[0] == object, *pc == cache */ value meths = Field (sp[0], 0); int li = 3, hi = Field(meths,0), mi; while (li < hi) { mi = ((li+hi) >> 1) | 1; if (accu < Field(meths,mi)) hi = mi-2; else li = mi; } accu = Field (meths, li-1); Next; } /* Debugging and machine control */ Instruct(STOP): caml_external_raise = initial_external_raise; caml_extern_sp = sp; caml_callback_depth--; return accu; Instruct(EVENT): if (--caml_event_count == 0) { Setup_for_debugger; caml_debugger(EVENT_COUNT); Restore_after_debugger; } Restart_curr_instr; Instruct(BREAK): Setup_for_debugger; caml_debugger(BREAKPOINT); Restore_after_debugger; Restart_curr_instr; #ifndef THREADED_CODE default: #if _MSC_VER >= 1200 __assume(0); #else caml_fatal_error_arg("Fatal error: bad opcode (%" ARCH_INTNAT_PRINTF_FORMAT "x)\n", (char *) (intnat) *(pc-1)); #endif } } #endif } void caml_prepare_bytecode(code_t prog, asize_t prog_size) { /* other implementations of the interpreter (such as an hypothetical JIT translator) might want to do something with a bytecode before running it */ CAMLassert(prog); CAMLassert(prog_size>0); /* actually, the threading of the bytecode might be done here */ } void caml_release_bytecode(code_t prog, asize_t prog_size) { /* other implementations of the interpreter (such as an hypothetical JIT translator) might want to know when a bytecode is removed */ /* check that we have a program */ CAMLassert(prog); CAMLassert(prog_size>0); }