/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This source file is part of SableVM. * * * * See the file "LICENSE" for the copyright information and for * * the terms and conditions for copying, distribution and * * modification of this source file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* on Linux/Intel, we'll need to put FPU into double precision mode */ #if defined(__i386__) && defined(__linux__) #include #endif #ifdef _SABLEVM_VERBOSE_INSTRUCTIONS_INLINED #define _svmm_verbose_instructions_inlined(a,b,c) \ _svmf_verbose_instructions_inlined_ptr(a,b,c) void _svmf_verbose_instructions_inlined (_svmt_JNIEnv *env, _svmt_code *pc, char *name) { if (env->vm->verbose_instructions == JNI_TRUE) { _svmf_printf (env, stderr, "T%d: [verbose instructions: executing @%p %s]\n", env->thread.id, (void *) (pc - 1), name); fflush (NULL); } } void (*_svmf_verbose_instructions_inlined_ptr) (_svmt_JNIEnv *, _svmt_code *, char *); #else #define _svmm_verbose_instructions_inlined(a,b,c) #endif #ifdef _SABLEVM_INLINED_THREADED_INTERPRETER #define _svmm_fmod(x,y) _svmf_fmod_ptr(x,y) #else #define _svmm_fmod(x,y) fmod(x,y) #endif /* static int delayCounter=1000000; */ static int delayCounter=200000; static int selectiveTracesCounter=0; inline svm_static jint _svmf_tree_compare_branch_correlation (Branch_Correlation_node * value1, Branch_Correlation_node * value2) { if (value1->bc->target > value2->bc->target) return 1; if (value1->bc->target < value2->bc->target) return -1; return 0; } Branch_Context * _svmf_saved_pc_commit (_svmt_code **saved_pc, _svmt_word amount, Branch_Context * context) { _svmt_word j; int i; /* Branch_Correlation bc_temp; Branch_Correlation_node find_temp; find_temp.bc = &bc_temp; */ return context; /* TODO: We could have specialized version of this function * that does not have this check. */ if (delayCounter > 0) { delayCounter -= amount; if (delayCounter < 0) { delayCounter = 0; } return context; } /* Try to lower the overhead of traces processing by 10. */ /* if (++selectiveTracesCounter % 10) { return context; }*/ /* printf("C"); fflush(NULL); */ for (j = 0; j < amount; j++) { _svmt_code *pc = saved_pc[j]; Branch_Correlation * temp; Branch_Correlation_node *tree_found; #ifdef BRANCH_CORRELATION_PROFILING_STATS myInstCounter++; #endif if (context->countBranch-- == 0) { decay_Branch(context); } temp = context->maxCorrelation; #ifdef BRANCH_CORRELATION_PROFILING_STATS context->counter++; i = 1 + (int)(temp->next); executedInstructions[i]++; #endif if (temp->target != pc) { Branch_Correlation_node *new_node; # ifdef BRANCH_CORRELATION_PROFILING_STATS missCounter++; # endif /* First use the splay tree, should be faster if it's there. */ /* FIXME: experiments show that it is NOT faster! */ /***** bc_temp.target = pc; tree_found = _svmm_tree_find_branch_correlation (context->correlations_tree_root, &find_temp); if (tree_found) { if (tree_found->bc->needs_freeing) { _svmm_tree_remove_branch_correlation (context->correlations_tree_root, tree_found); } else{ temp = tree_found->bc; goto found; } } ****/ temp = (Branch_Correlation *)context; while (temp->next) { temp=temp->next; if(pc==temp->target) { goto found; } } temp=get_Branch_Correlation(pc,context,temp); /* new_node = (Branch_Correlation_node*) malloc(sizeof(Branch_Correlation_node)); memset(new_node, 0, sizeof(Branch_Correlation_node)); new_node->bc = temp; */ /* _svmm_tree_insert_branch_correlation (context->correlations_tree_root, new_node);*/ } #ifdef BRANCH_CORRELATION_PROFILING_STATS else { predictedInstructions[i]++; } #endif found: temp->count++; context = temp->context; } return context; } /* ---------------------------------------------------------------------- _svmf_interpreter ---------------------------------------------------------------------- */ void DumpStats(void); svm_static jint _svmf_interpreter (_svmt_JNIEnv *_env) { _svmt_JNIEnv *volatile env = _env; register _svmt_stack_value *stack = NULL; register _svmt_stack_value *locals = NULL; register _svmt_code *pc = NULL; register jint stack_size; Branch_Context * context=(Branch_Context *)&INITIAL_BRANCH_CONTEXT; static _svmt_code *saved_pc[0x10000]; static _svmt_word saved_pc_next_index = 0; /* exception addresses * * The idea is that normal gotos/calls might be compiled to relative * jumps, preventing their usage in inlined-threaded code. Using a * goto is an elegant way to avoid using method calls in ATHROW * (necessary to create a new null pointer exception). * [ NOTE: fatal_error_handler_ptr is currently not used because for GCC ] * [ 3.x we needed arch-specific assembly implementation of goto anyway. ] * [ But it's kept here in case it was useful with other compilers and ] * [ as a well documented and neat idea that once was implemented. ] * * A last note:We must guard against a too clever compiler that * would detect a unique value in a variable; we do this by * initializing goto address variable to NULL, and changing its * value to the correct one when initializing the instruction array. * Ahh! So many little details to get right for things to work... */ static void (*decayBranch)(Branch_Context *)=&decay_Branch; static Branch_Correlation * (*getBranchCorrelation)(void *,Branch_Context *,Branch_Correlation *)=&get_Branch_Correlation; static void (* traceProfile)(Branch_Context *,void *)=&trace_Profile2; static Branch_Context * (*_svmfp_saved_pc_commit) (_svmt_code **saved_pc, _svmt_word amount, Branch_Context * context) = &_svmf_saved_pc_commit; env->vm->fp_periodic_check = &_svmf_periodic_check_real_body; #ifdef _SABLEVM_INLINABILITY_TESTING static _svmt_code *(*no_inlining_increment_pc) (_svmt_JNIEnv *, _svmt_word, _svmt_code *) = NULL; #endif #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) || defined (_SABLEVM_INLINABILITY_TESTING) sigjmp_buf *volatile previous_signal_handler = env->signal_handler; sigjmp_buf signal_handler; #endif /* printf("Delay %d\n",delayCounter); DumpStats(); */ getPathv=&getPath; #ifdef _SABLEVM_INLINED_THREADED_INTERPRETER /* static void *fatal_error_handler_ptr = NULL; */ double (*_svmf_fmod_ptr) (double x, double y) = &fmod; #endif #ifdef _SABLEVM_VERBOSE_INSTRUCTIONS_INLINED _svmf_verbose_instructions_inlined_ptr = &_svmf_verbose_instructions_inlined; #endif #ifdef _SABLEVM_INLINABILITY_TESTING env->inlinability_testing.sigsegv_expected = JNI_FALSE; #endif assert (*(env->throwable) == NULL); #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) || defined (_SABLEVM_INLINABILITY_TESTING) /* set signal handler */ if (sigsetjmp (signal_handler, 1)) { jint signal_code = env->signal_code; #ifndef NDEBUG env->signal_code = SVM_SIGNAL_NONE; #endif switch (signal_code) { #if defined(_SABLEVM_INLINABILITY_TESTING) case SVM_SIGNAL_INLINING_FAILURE: { /* note failed bytecode test if we know which one was that */ _svmt_method_info *method = env->stack.current_frame->method; int byname = _svmf_get_bytecode_by_method_name (env, DREF (method-> name, value)); if (byname >= 0) { _svmf_printf (env, stderr, "REGISTERING failed bytecode: %s\n", env->vm->instructions[byname].name); env->inlinability_testing.bytecode_inlinability[byname] |= SVM_BCI_FAILING; } else { _svmf_printf (env, stderr, "UNABLE to find which bytecode failed!\n"); } _svmf_printf (env, stderr, "UNEXPECTED SIGNAL CAUGHT Class: %s - REVERSED BACKTRACE FOLLOWS:\n", env->stack.current_frame->method->class_info->name); _svmf_no_inlining (env, 0, SVM_IS_INLINED_GET_BACKTRACE); { /* pop stack frame */ _svmt_stack_frame *frame = env->stack.current_frame; env->stack.current_frame = (_svmt_stack_frame *) (((char *) frame) - frame->previous_offset); /* is method synchronized? */ if (method->synchronized) { /* release monitor */ if (_svmf_exit_object_monitor (env, frame->this) != JNI_OK) { goto exception_handler; } } assert (*(env->throwable) == NULL); goto inliningexception_handler; } } break; #endif #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) case SVM_SIGNAL_NULL_POINTER_EXCEPTION: { /* if initializing this vm, we store into _svmv_codes the address of the initialized static local array */ if (env->stack.current_frame == NULL) { goto initialize_instructions; } else { goto nullpointerexception_handler; } } break; #endif #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) || defined (_SABLEVM_INLINABILITY_TESTING) /* Exceptionally - even in inlinability testing mode w/o signals_for_excpetions * we want this signal to be catched or else - it'd kill JVM. * Most important goal is to let the *inlinability* tests be completed. * And we'll clearly indicate that these bytecodes need to be fixed. */ case SVM_SIGNAL_ARITHMETIC_EXCEPTION: { #if (!defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS)) && defined (_SABLEVM_INLINABILITY_TESTING) /* note failed bytecode test if we know which one was that */ _svmt_method_info *method = env->stack.current_frame->method; int byname = _svmf_get_bytecode_by_method_name (env, DREF (method-> name, value)); if (byname >= 0) env->inlinability_testing.bytecode_inlinability[byname] |= SVM_BCI_SIGFPE; #endif goto arithmeticexception_handler; } break; #endif default: { #if defined (_SABLEVM_INLINABILITY_TESTING) _svmf_printf (env, stderr, "KILLER ERROR: imposible control flow; signal_code is %i\n", signal_code); fflush (NULL); goto inliningexception_handler; #endif _svmm_fatal_error ("impossible control flow"); } break; } } env->signal_handler = &signal_handler; #endif #if !defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) if (env->stack.current_frame == NULL) { goto initialize_instructions; } #endif /* ! _SABLEVM_SIGNALS_FOR_EXCEPTIONS */ { /* When _SABLEVM_SIGNALS_FOR_EXCEPTIONS is defined, the following code causes a sigfault at the time of initialization of vm->instructions, as env->stack.current_frame is NULL at that point. The advantage of using this weird control flow on initialization is to eliminate "initialization test" overhead from normal _svmf_interpreter execution path. Not a thing you should teach cs-101 students ;-) */ _svmt_stack_frame *frame = env->stack.current_frame; _svmt_method_info *method = frame->method; locals = (_svmt_stack_value *) (void *) (((char *) frame) - method->frame_info->start_offset); stack = (_svmt_stack_value *) (void *) (((char *) frame) + _svmv_stack_offset); assert (env->stack.current_frame->this != NULL); assert (env->stack.current_frame->stack_size == 0); /* check */ _svmf_periodic_check (env); } /* on Linux/Intel, put FPU into double precision mode */ #if defined(__i386__) && defined(__linux__) /* This puts the X86 FPU in 64-bit precision mode as opposed to the default 80-bit mode (used by Linux). For more explanations see: http://www.srware.com/linux_numerics.txt Originally it was put into lib_init.c but apparently some code executed later resets the flag so we set it to "right" value on each interpreter invocation. TODO: find out where/what exacly resets the flag. */ { fpu_control_t fpu_control; _FPU_GETCW (fpu_control); fpu_control &= ~_FPU_EXTENDED; fpu_control |= _FPU_DOUBLE; _FPU_SETCW (fpu_control); } #endif /* i386 && linux */ #if !defined(NDEBUG) _svmf_verbose_methods_enter (env); #endif pc = env->stack.current_frame->pc; stack_size = 0; #if defined(_SABLEVM_INLINED_THREADED_INTERPRETER) || defined(_SABLEVM_DIRECT_THREADED_INTERPRETER) goto *((pc++)->implementation); #else dispatch: assert (*(env->throwable) == NULL); assert (stack_size >= 0); assert (stack_size == 0 || env->stack.current_frame->method == &env->vm->internal_call_method || env->stack.current_frame->method == &env->vm->vm_initiated_call_method || stack_size <= env->stack.current_frame->method->data.code_attribute->max_stack); switch ((pc++)->jint) { #include "instructions_switch.c" default: { _svmm_fatal_error ("impossible control flow"); } break; } #endif /* defined(_SABLEVM_INLINED_THREADED_INTERPRETER) || defined(_SABLEVM_DIRECT_THREADED_INTERPRETER) */ _svmm_fatal_error ("impossible control flow"); athrow_handler: if (*(env->throwable) == NULL) { goto nullpointerexception_handler; } goto exception_handler; arithmeticexception_handler: _svmf_error_ArithmeticException (env); goto exception_handler; classcastexception_handler: _svmf_error_ClassCastException (env); goto exception_handler; nullpointerexception_handler: _svmf_error_NullPointerException (env); goto exception_handler; #if defined(_SABLEVM_INLINABILITY_TESTING) inliningexception_handler: _svmf_error_InliningException (env); goto exception_handler; #endif arrayindexoutofboundsexception_handler: _svmf_error_ArrayIndexOutOfBoundsException (env); goto exception_handler; arraystoreexception_handler: _svmf_error_ArrayStoreException (env); goto exception_handler; abstractmethoderror_handler: _svmf_error_AbstractMethodError (env); goto exception_handler; exception_handler: assert (*(env->throwable) != NULL); #if !defined(NDEBUG) /* dump stack trace on exception */ if (env->vm->verbose_exceptions) { _svmf_printf (env, stderr, "** thrown on thread %d: instance of type %s\n", env->thread.id, (*(env->throwable))->vtable->type->name); _svmf_dump_stack_trace (env); } #endif #ifdef COMMENT if (strcmp ((*(env->throwable))->vtable->type->name, "java/lang/ClassNotFoundException") != 0 && strcmp ((*(env->throwable))->vtable->type->name, "java/lang/NoClassDefFoundError") != 0) { _svmf_printf (env, stderr, "** (on thread %d) exception: %s\n", env->thread.id, (*(env->throwable))->vtable->type->name); _svmf_dump_stack_trace (env); } #endif { jint i; jint table_length; _svmt_exception_table *table; _svmt_stack_frame *frame = env->stack.current_frame; _svmt_method_info *method = frame->method; exception_loop: pc = frame->pc; frame->stack_size = 0; if (_svmf_is_set_flag (method->access_flags, SVM_ACC_INTERNAL)) { assert (method == &env->vm->internal_call_method || method == &env->vm->vm_initiated_call_method); #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) || defined (_SABLEVM_INLINABILITY_TESTING) env->signal_handler = previous_signal_handler; #endif return JNI_ERR; } assert (!_svmf_is_set_flag (method->access_flags, SVM_ACC_NATIVE)); table_length = method->data.code_attribute->exception_table_length; table = method->data.code_attribute->exception_table; for (i = 0; i < table_length; i++) { if ((pc >= table[i].normal_start && pc <= table[i].normal_end) || (pc >= table[i].prepare_start && pc <= table[i].prepare_end)) { /* resolve and link type, if necessary */ if (CAN_DREF (table[i].catch_type)) { jobject pending_throwable; if (_svmm_new_native_local (env, pending_throwable) != JNI_OK) { /* to avoid infinite loops! */ goto pop_stack_frame; } /* save throwable aside, so that we can run Java code (i.e. java/lang/Class.) if necessary */ *pending_throwable = *(env->throwable); *(env->throwable) = NULL; if (_svmf_resolve_CONSTANT_Class (env, env->stack.current_frame->method->class_info, *(table[i].catch_type)) != JNI_OK) { _svmm_free_native_local (env, pending_throwable); goto pop_stack_frame; } if (_svmf_link_type (env, DREF (table[i].catch_type, type)) != JNI_OK) { _svmm_free_native_local (env, pending_throwable); goto pop_stack_frame; } /* restore pending throwable */ *(env->throwable) = *pending_throwable; _svmm_free_native_local (env, pending_throwable); frame = env->stack.current_frame; assert (method == frame->method); } /* if "any" (null) or matching type */ if (CANNOT_DREF (table[i].catch_type) || _svmf_is_assignable_from (env, (*(env->throwable))->vtable->type, DREF (table[i].catch_type, type))) { locals = (_svmt_stack_value *) (void *) (((char *) frame) - method->frame_info->start_offset); stack = (_svmt_stack_value *) (void *) (((char *) frame) + _svmv_stack_offset); pc = table[i].handler; stack_size = 1; stack[0].reference = *(env->throwable); *(env->throwable) = NULL; #if defined(_SABLEVM_INLINED_THREADED_INTERPRETER) || defined(_SABLEVM_DIRECT_THREADED_INTERPRETER) goto *((pc++)->implementation); #else goto dispatch; #endif } } } /* structured locking */ if (env->stack.current_frame->lock_count != 0) { /* pop stack frame */ frame = env->stack.current_frame; env->stack.current_frame = (_svmt_stack_frame *) (void *) (((char *) frame) - frame->previous_offset); _svmf_error_IllegalMonitorStateException (env); goto exception_handler; } #if !defined(NDEBUG) _svmf_verbose_methods_exit (env); #endif pop_stack_frame: /* pop stack frame */ frame = env->stack.current_frame; env->stack.current_frame = (_svmt_stack_frame *) (void *) (((char *) frame) - frame->previous_offset); /* is method synchronized? */ if (method->synchronized) { /* release monitor */ if (_svmf_exit_object_monitor (env, frame->this) != JNI_OK) { goto exception_handler; } } frame = env->stack.current_frame; method = frame->method; goto exception_loop; } _svmm_fatal_error ("impossible control flow"); #ifdef COMMENT #ifdef _SABLEVM_INLINED_THREADED_INTERPRETER fatal_error_handler: _svmm_fatal_error ("impossible control flow"); return JNI_ERR; #endif #endif /* COMMENT */ initialize_instructions: { jint instr; for (instr = 0; instr < SVM_INSTRUCTION_COUNT; instr++) { switch (instr) { #ifdef _SABLEVM_INLINED_THREADED_INTERPRETER #include "instructions_preparation_inlined_threaded.c" #endif #ifdef _SABLEVM_DIRECT_THREADED_INTERPRETER #include "instructions_preparation_direct_threaded.c" #endif #ifdef _SABLEVM_SWITCH_THREADED_INTERPRETER #include "instructions_preparation_switch_threaded.c" #endif case SVM_INSTRUCTION_UNDEFINED_186: { _svmm_zero_memory (env->vm->instructions[instr]); } break; default: { _svmm_fatal_error ("impossible control flow"); } break; } } /* initialize goto/call addresses */ #ifdef _SABLEVM_INLINED_THREADED_INTERPRETER /* fatal_error_handler_ptr = &&fatal_error_handler; */ if (!_svmv_true) { /* we have to play this trick to assure function pointers * won't get optimized somewhere under way */ _svmf_fmod_ptr = NULL; env->vm->fp_periodic_check = NULL; _svmfp_saved_pc_commit = NULL; #ifdef _SABLEVM_VERBOSE_INSTRUCTIONS_INLINED _svmf_verbose_instructions_inlined_ptr = NULL; #endif } #endif decayBranch=&decay_Branch2; getBranchCorrelation=&get_Branch_Correlation2; traceProfile=&trace_ProfilerX; getPathv=&getPath2; #ifdef _SABLEVM_INLINABILITY_TESTING no_inlining_increment_pc = &_svmf_no_inlining_increment_pc; #endif #if defined(_SABLEVM_SIGNALS_FOR_EXCEPTIONS) || defined(_SABLEVM_INLINABILITY_TESTING) env->signal_handler = previous_signal_handler; #endif /* _SABLEVM_SIGNALS_FOR_EXCEPTIONS || _SABLEVM_INLINABILITY_TESTING */ return JNI_OK; } }