commit d48cdeb4bc7aacbb09bb52c9cd98bbba8fdad0dc Author: Pavel Rebriy Date: Fri Apr 11 12:10:38 2008 +0400 HARMONY-5322 Lazy resolution in JET needs to be implemented on x86_64 diff --git a/vm/jitrino/src/jet/cg_fld_arr.cpp b/vm/jitrino/src/jet/cg_fld_arr.cpp index 7b3fa86..f369194 100644 --- a/vm/jitrino/src/jet/cg_fld_arr.cpp +++ b/vm/jitrino/src/jet/cg_fld_arr.cpp @@ -244,7 +244,6 @@ Opnd CodeGen::get_field_addr(const FieldOpInfo& fieldOp, jtype jt) { } else { //field is not resolved -> generate code to request offset SYNC_FIRST(static const CallSig cs_get_offset(CCONV_HELPERS, iplatf, iplatf, i32, i32)); gen_call_vm(cs_get_offset, rt_helper_field_get_offset_withresolve, 0, fieldOp.enclClass, fieldOp.cpIndex, fieldOp.isPut()); - runlock(cs_get_offset); AR gr_ret = cs_get_offset.ret_reg(0); rlock(gr_ret); Val& ref = vstack(ref_depth, true); @@ -259,7 +258,6 @@ Opnd CodeGen::get_field_addr(const FieldOpInfo& fieldOp, jtype jt) { } else { //field is not resolved -> generate code to request address SYNC_FIRST(static const CallSig cs_get_addr(CCONV_HELPERS, iplatf, iplatf, i32, i32)); gen_call_vm(cs_get_addr, rt_helper_field_get_address_withresolve, 0, fieldOp.enclClass, fieldOp.cpIndex, fieldOp.isPut()); - runlock(cs_get_addr); AR gr_ret = cs_get_addr.ret_reg(0); where = Opnd(jt, gr_ret, 0); } diff --git a/vm/jitrino/src/jet/cg_meth.cpp b/vm/jitrino/src/jet/cg_meth.cpp index e3ae58d..1e8dd66 100644 --- a/vm/jitrino/src/jet/cg_meth.cpp +++ b/vm/jitrino/src/jet/cg_meth.cpp @@ -754,7 +754,6 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short rlock(cs); const bool is_static = opcod == OPCODE_INVOKESTATIC; - Val thiz = is_static ? Val() : vstack(thiz_depth, true); if (meth == NULL && !m_lazy_resolution) { runlock(cs); // was just locked above - unlock gen_call_throw(ci_helper_linkerr, rt_helper_throw_linking_exc, 0, @@ -771,35 +770,6 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short return; } - if (!is_static) { - rlock(thiz); - } - - // INVOKEINTERFACE and lazy resolution must have VM helper call first, so will place - // its args later, after the call, to avoid destruction of args - // on registers. - if (opcod != OPCODE_INVOKEINTERFACE && meth != NULL) { - stackFix = gen_stack_to_args(true, cs, 0); - vpark(); - gen_gc_stack(-1, true); - } - // - // Check for null here - we just spilled all the args and - // parked all the registers, so we have a chance to use HW NPE - // For INVOKEINTERFACE we did not spill args, but we'll call VM first, - // which is pretty expensive by itself, so the HW check does not give - // much. - // - if (!is_static) { - // For invokeSPECIAL, we're using indirect address provided by - // the VM. This means we do not read vtable, which means no - // memory access, so we can't use HW checks - have to use - // explicit one. Not a big loss, as the INVOKESPECIAL mostly - // comes right after NEW which guarantees non-null. - // in lazy resolution mode we must do manual check and provide helper with - // non-null results. - gen_check_null(thiz, opcod != OPCODE_INVOKESPECIAL && meth!=NULL); - } if (meth == NULL) { //lazy resolution mode: get method addr and call it. assert(m_lazy_resolution); @@ -807,19 +777,41 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short //1. get method address if (opcod == OPCODE_INVOKESTATIC || opcod == OPCODE_INVOKESPECIAL) { SYNC_FIRST(static const CallSig cs_get_is_addr(CCONV_HELPERS, iplatf, iplatf, i32)); + rlock(cs_get_is_addr); + + if (!is_static) + { + Val &thiz = vstack(thiz_depth, false); + // For invokeSPECIAL, we're using indirect address provided by + // the VM. This means we do not read vtable, which means no + // memory access, so we can't use HW checks - have to use + // explicit one. Not a big loss, as the INVOKESPECIAL mostly + // comes right after NEW which guarantees non-null. + // in lazy resolution mode we must do manual check and provide helper with + // non-null results. + gen_check_null(thiz, false); + } + char* helper = opcod == OPCODE_INVOKESTATIC ? rt_helper_get_invokestatic_addr_withresolve : rt_helper_get_invokespecial_addr_withresolve; + vpark(); gen_call_vm(cs_get_is_addr, helper, 0, m_klass, cpIndex); runlock(cs_get_is_addr); gr_ret = cs_get_is_addr.ret_reg(0); } else { assert(opcod == OPCODE_INVOKEVIRTUAL || opcod == OPCODE_INVOKEINTERFACE); SYNC_FIRST(static const CallSig cs_get_iv_addr(CCONV_HELPERS, iplatf, iplatf, i32, jobj)); + rlock(cs_get_iv_addr); + + Val &thiz = vstack(thiz_depth, false); + gen_check_null(thiz, false); + char * helper = opcod == OPCODE_INVOKEVIRTUAL ? rt_helper_get_invokevirtual_addr_withresolve : rt_helper_get_invokeinterface_addr_withresolve; // setup constant parameters first, Val vclass(iplatf, m_klass); Val vcpIdx(cpIndex); + vpark(); gen_args(cs_get_iv_addr, 0, &vclass, &vcpIdx, &thiz); gen_call_vm(cs_get_iv_addr, helper, 3); runlock(cs_get_iv_addr); @@ -841,6 +833,12 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short // if it's INVOKEINTERFACE, then first resolve it Class_Handle klass = method_get_class(meth); const CallSig cs_vtbl(CCONV_HELPERS, iplatf, jobj, jobj); + rlock(cs_vtbl); + + Val &thiz = vstack(thiz_depth, true); + rlock(thiz); + gen_check_null(thiz, true); + // Prepare args for ldInterface helper if (cs_vtbl.reg(0) == gr_x) { assert(cs_vtbl.size() != 0); @@ -854,8 +852,10 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short } mov(cs_vtbl.get(0), thiz.as_opnd()); } + runlock(thiz); gen_call_vm(cs_vtbl, rt_helper_get_vtable, 1, klass); AR gr_ret = cs_vtbl.ret_reg(0); + runlock(cs_vtbl); // // Method's vtable is in gr_ret now, prepare stack // @@ -871,9 +871,20 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short call(gr_ret, cs, is_set(DBG_CHECK_STACK)); } else if (opcod == OPCODE_INVOKEVIRTUAL) { + Val &thiz = vstack(thiz_depth, true); + rlock(thiz); + + stackFix = gen_stack_to_args(true, cs, 0); + vpark(); + gen_gc_stack(-1, true); + // Check for null here - we just spilled all the args and + // parked all the registers, so we have a chance to use HW NPE + gen_check_null(thiz, true); + AR gr = valloc(jobj); unsigned offset = method_get_vtable_offset(meth); Opnd ptr; + if (g_vtbl_squeeze) { ld4(gr, thiz.reg(), rt_vtable_offset); AR gr_vtbase = valloc(jobj); @@ -886,8 +897,30 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short ptr = Opnd(jobj, gr, offset); } call(ptr, cs, is_set(DBG_CHECK_STACK)); + runlock(thiz); } else { + Val *thiz = NULL; + + if (!is_static) + thiz = &vstack(thiz_depth, true); + + stackFix = gen_stack_to_args(true, cs, 0); + vpark(); + gen_gc_stack(-1, true); + + if (!is_static) + // Check for null here - we just spilled all the args and + // parked all the registers, so we have a chance to use HW NPE + // For invokeSPECIAL, we're using indirect address provided by + // the VM. This means we do not read vtable, which means no + // memory access, so we can't use HW checks - have to use + // explicit one. Not a big loss, as the INVOKESPECIAL mostly + // comes right after NEW which guarantees non-null. + // in lazy resolution mode we must do manual check and provide helper with + // non-null results. + gen_check_null(*thiz, false); + void * paddr = method_get_indirect_address(meth); #ifdef _IA32_ Opnd ptr(jobj, ar_x, paddr); @@ -900,9 +933,6 @@ void CodeGen::gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short call(ptr, cs, is_set(DBG_CHECK_STACK)); } - if (!is_static) { - runlock(thiz); - } // to unlock after gen_stack_to_args() runlock(cs); // to unlock after explicit lock at the top of this method @@ -931,7 +961,7 @@ void CodeGen::gen_args(const CallSig& cs, unsigned idx, const Val * parg0, if (args[i] == 0) { break; } - rlock(*args[0]); + rlock(*args[i]); } // 2nd, generate moves for (unsigned i=0; ijava_lang_ThreadDeath_Class = preload_class(vm_env, "java/lang/ThreadDeath"); + // String must be initialized before strings from intern pool are + // used. But for initializing l.j.String we need to have exception + // classes loaded, because the first call to compilations + // initializes all of the JIT helpers that hardcode class handles + // inside of the helpers. + hythread_suspend_disable(); + class_initialize(vm_env->JavaLangString_Class); + hythread_suspend_enable(); + vm_env->java_lang_Cloneable_Class = preload_class(vm_env, vm_env->Clonable_String); vm_env->java_lang_Thread_Class = @@ -904,10 +913,9 @@ int vm_init1(JavaVM_Internal * java_vm, JavaVMInitArgs * vm_arguments) { // Precompile InternalError. class_alloc_new_object_and_run_default_constructor(vm_env->java_lang_InternalError_Class); - //String must be initialized before strings from intern pool are used - class_initialize(vm_env->JavaLangString_Class); - - + // j.l.Class needs to be initialized for loading magics helper + // class + class_initialize(vm_env->JavaLangClass_Class); hythread_suspend_enable(); Method * m; diff --git a/vm/vmcore/src/jit/jit_runtime_support.cpp b/vm/vmcore/src/jit/jit_runtime_support.cpp index e101e58..f2ce626 100644 --- a/vm/vmcore/src/jit/jit_runtime_support.cpp +++ b/vm/vmcore/src/jit/jit_runtime_support.cpp @@ -1391,9 +1391,9 @@ static void *rth_invokevirtual_addr_withresolve(Class_Handle klass, unsigned cp_ ASSERT_THROW_AREA; Global_Env* env = VM_Global_State::loader_env; - if (obj == NULL) { + if (obj == (ManagedObject*)VM_Global_State::loader_env->managed_null) { exn_throw_by_class(env->java_lang_NullPointerException_Class); - return NULL; + return obj; } Method* m = NULL; @@ -1443,17 +1443,17 @@ static void *rth_invokeinterface_addr_withresolve(Class_Handle klass, unsigned c ASSERT_THROW_AREA; Global_Env* env = VM_Global_State::loader_env; - if (obj == NULL) { - - //Returning zero address and not generating NPE if the object is null - //is safe in terms of preserving program semantics(the exception will be generated - //on the first method invocation) and - //allows profitable code transformations such as hoisting vm helper outside the loop body - //This is our current convention which touches all variants of this helper, - //If it changes, all variants must be fixed. - + if (obj == (ManagedObject*)VM_Global_State::loader_env->managed_null) { + + //Returning zero address and not generating NPE if the object is null + //is safe in terms of preserving program semantics(the exception will be generated + //on the first method invocation) and + //allows profitable code transformations such as hoisting vm helper outside the loop body + //This is our current convention which touches all variants of this helper, + //If it changes, all variants must be fixed. + //exn_throw_by_class(env->java_lang_NullPointerException_Class); - return NULL; + return obj; } Method* m = NULL; @@ -1585,7 +1585,7 @@ static NativeCodePtr rth_get_lil_static_field_addr_withresolve(int * dyn_count) ///OPCODE_CHECKCAST static void *rth_checkcast_withresolve(Class_Handle klass, unsigned cp_idx, ManagedObject* obj) { - if (obj==NULL) { + if (obj == (ManagedObject*)VM_Global_State::loader_env->managed_null) { return obj; } diff --git a/vm/vmcore/src/jni/jni.cpp b/vm/vmcore/src/jni/jni.cpp index 99e1357..5a694cd 100644 --- a/vm/vmcore/src/jni/jni.cpp +++ b/vm/vmcore/src/jni/jni.cpp @@ -1041,6 +1041,9 @@ jobject JNICALL AllocObject(JNIEnv * jni_env, ThrowNew_Quick(jni_env, "java/lang/InstantiationException", clss->get_name()->bytes); return NULL; } + if (!ensure_initialised(jni_env, clss)) { + return NULL; + } tmn_suspend_disable(); //---------------------------------v ManagedObject *new_obj = (ManagedObject *)class_alloc_new_object(clss);