--- old/src/cpu/sparc/vm/interp_masm_sparc.cpp 2008-11-08 23:39:18.000000000 -0800 +++ new/src/cpu/sparc/vm/interp_masm_sparc.cpp 2008-11-08 23:39:18.000000000 -0800 @@ -831,10 +831,26 @@ } -void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register tmp, int bcp_offset) { +void InterpreterMacroAssembler::get_index_at_bcp(Register Rtmp, Register Rdst, + int bcp_offset, bool giant_index) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + if (!giant_index) { + get_2_byte_integer_at_bcp(bcp_offset, Rtmp, Rdst, Unsigned); + } else { + assert(InvokeDynamic, "giant index used only for InvokeDynamic"); + get_4_byte_integer_at_bcp(bcp_offset, Rtmp, Rdst); + assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); + xor3(Rdst, -1, Rdst); // convert to plain index + } +} + + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register tmp, + int bcp_offset, bool giant_index) { assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); assert_different_registers(cache, tmp); assert_not_delayed(); + assert(!giant_index,"NYI"); get_2_byte_integer_at_bcp(bcp_offset, cache, tmp, Unsigned); // convert from field index to ConstantPoolCacheEntry index // and from word index to byte offset @@ -843,10 +859,12 @@ } -void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset) { +void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, + int bcp_offset, bool giant_index) { assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); assert_different_registers(cache, tmp); assert_not_delayed(); + assert(!giant_index,"NYI"); get_2_byte_integer_at_bcp(bcp_offset, cache, tmp, Unsigned); // convert from field index to ConstantPoolCacheEntry index // and from word index to byte offset @@ -1739,7 +1757,8 @@ // Count a virtual call in the bytecodes. void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register scratch) { + Register scratch, + bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; --- old/src/cpu/sparc/vm/interp_masm_sparc.hpp 2008-11-08 23:39:20.000000000 -0800 +++ new/src/cpu/sparc/vm/interp_masm_sparc.hpp 2008-11-08 23:39:19.000000000 -0800 @@ -191,8 +191,9 @@ Register Rdst, setCCOrNot should_set_CC = dont_set_CC ); - void get_cache_and_index_at_bcp(Register cache, Register tmp, int bcp_offset); - void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset); + void get_cache_and_index_at_bcp(Register cache, Register tmp, int bcp_offset, bool giant_index = false); + void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset, bool giant_index = false); + void get_index_at_bcp(Register Rtmp, Register Rdst, int bcp_offset, bool giant_index = false); // common code @@ -304,7 +305,7 @@ void profile_not_taken_branch(Register scratch); void profile_call(Register scratch); void profile_final_call(Register scratch); - void profile_virtual_call(Register receiver, Register scratch); + void profile_virtual_call(Register receiver, Register scratch, bool receiver_can_be_null = false); void profile_ret(TosState state, Register return_bci, Register scratch); void profile_null_seen(Register scratch); void profile_typecheck(Register klass, Register scratch); --- old/src/cpu/sparc/vm/templateInterpreter_sparc.cpp 2008-11-08 23:39:21.000000000 -0800 +++ new/src/cpu/sparc/vm/templateInterpreter_sparc.cpp 2008-11-08 23:39:21.000000000 -0800 @@ -160,7 +160,16 @@ } -address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, bool unbox) { + TosState incoming_state = state; + if (InvokeDynamic) { + if (unbox) { + incoming_state = atos; + } + } else { + assert(!unbox, "old behavior"); + } + address compiled_entry = __ pc(); Label cont; @@ -175,7 +184,7 @@ // do this here. Unfortunately if we did a rethrow we'd see an machepilog node // first which would move g1 -> O0/O1 and destroy the exception we were throwing. - if( state == ltos ) { + if( incoming_state == ltos ) { __ srl (G1, 0,O1); __ srlx(G1,32,O0); } @@ -192,10 +201,27 @@ __ mov(Llast_SP, SP); // Remove any adapter added stack space. + if (unbox && state != atos) { + // cast and unbox + __ unimplemented(); + } const Register cache = G3_scratch; const Register size = G1_scratch; + Label L_got_cache, L_giant_index; + if (InvokeDynamic) { + __ ldub(Lbcp, 0, size); + __ cmp(size, Bytecodes::_invokedynamic); + __ br(Assembler::equal, false, Assembler::pn, L_giant_index); + __ delayed()->nop(); + } __ get_cache_and_index_at_bcp(cache, G1_scratch, 1); + ////__ get_cache_and_index_at_bcp(cache, G1_scratch, 1, false); + __ bind(L_got_cache); + if (unbox && state == atos) { + // insert a casting conversion, to keep verifier sane + __ unimplemented(); + } __ ld_ptr(Address(cache, 0, in_bytes(constantPoolCacheOopDesc::base_offset()) + in_bytes(ConstantPoolCacheEntry::flags_offset())), size); __ and3(size, 0xFF, size); // argument size in words @@ -203,6 +229,15 @@ __ add(Lesp, size, Lesp); // pop arguments __ dispatch_next(state, step); + // out of the main line of code... + if (InvokeDynamic) { + __ bind(L_giant_index); + __ unimplemented(); + ////__ get_cache_and_index_at_bcp(cache, G1_scratch, 1, true); + __ ba(false, L_got_cache); + __ delayed()->nop(); + } + return entry; } --- old/src/cpu/sparc/vm/templateTable_sparc.cpp 2008-11-08 23:39:23.000000000 -0800 +++ new/src/cpu/sparc/vm/templateTable_sparc.cpp 2008-11-08 23:39:23.000000000 -0800 @@ -3125,6 +3125,20 @@ } +void TemplateTable::invokedynamic(int byte_no) { + transition(vtos, vtos); + + if (!InvokeDynamic) { + // We do not encounter this bytecode if !InvokeDynamic. + // See Rewriter::rewrite_invokedynamic. + __ stop("invokedynamic not enabled"); + return; + } + + __ stop("invokedynamic NYI"); +} + + //---------------------------------------------------------------------------------------------------- // Allocation --- old/src/cpu/x86/vm/interp_masm_x86_32.cpp 2008-11-08 23:39:25.000000000 -0800 +++ new/src/cpu/x86/vm/interp_masm_x86_32.cpp 2008-11-08 23:39:25.000000000 -0800 @@ -189,20 +189,33 @@ } -void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset) { +void InterpreterMacroAssembler::get_cache_index_at_bcp(Register reg, int bcp_offset, bool giant_index) { assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + if (!giant_index) { + load_unsigned_word(reg, Address(rsi, bcp_offset)); + } else { + assert(InvokeDynamic, "giant index used only for InvokeDynamic"); + movl(reg, Address(rsi, bcp_offset)); + assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); + notl(reg); // convert to plain index + } +} + + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, + int bcp_offset, bool giant_index) { assert(cache != index, "must use different registers"); - load_unsigned_word(index, Address(rsi, bcp_offset)); + get_cache_index_at_bcp(index, bcp_offset, giant_index); movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); assert(sizeof(ConstantPoolCacheEntry) == 4*wordSize, "adjust code below"); shlptr(index, 2); // convert from field index to ConstantPoolCacheEntry index } -void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset) { - assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); +void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, + int bcp_offset, bool giant_index) { assert(cache != tmp, "must use different register"); - load_unsigned_word(tmp, Address(rsi, bcp_offset)); + get_cache_index_at_bcp(tmp, bcp_offset, giant_index); assert(sizeof(ConstantPoolCacheEntry) == 4*wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index // and from word offset to byte offset @@ -1245,7 +1258,9 @@ } -void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, Register reg2) { +void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, + Register reg2, + bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; @@ -1255,8 +1270,15 @@ // We are making a call. Increment the count. increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + Label skip_receiver_profile; + if (receiver_can_be_null) { + testptr(receiver, receiver); + jcc(Assembler::zero, skip_receiver_profile); + } + // Record the receiver type. record_klass_in_profile(receiver, mdp, reg2); + bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, --- old/src/cpu/x86/vm/interp_masm_x86_32.hpp 2008-11-08 23:39:28.000000000 -0800 +++ new/src/cpu/x86/vm/interp_masm_x86_32.hpp 2008-11-08 23:39:27.000000000 -0800 @@ -76,8 +76,9 @@ void get_cpool_and_tags(Register cpool, Register tags) { get_constant_pool(cpool); movptr(tags, Address(cpool, constantPoolOopDesc::tags_offset_in_bytes())); } void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); - void get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset); - void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset); + void get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset, bool giant_index = false); + void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset, bool giant_index = false); + void get_cache_index_at_bcp(Register index, int bcp_offset, bool giant_index = false); // Expression stack void f2ieee(); // truncate ftos to 32bits @@ -226,7 +227,8 @@ void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, Register scratch2); + void profile_virtual_call(Register receiver, Register mdp, Register scratch2, + bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass, Register scratch); --- old/src/cpu/x86/vm/interp_masm_x86_64.cpp 2008-11-08 23:39:29.000000000 -0800 +++ new/src/cpu/x86/vm/interp_masm_x86_64.cpp 2008-11-08 23:39:29.000000000 -0800 @@ -185,12 +185,27 @@ } +void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, + int bcp_offset, + bool giant_index) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + if (!giant_index) { + load_unsigned_word(index, Address(r13, bcp_offset)); + } else { + assert(InvokeDynamic, "giant index used only for InvokeDynamic"); + movl(index, Address(r13, bcp_offset)); + assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); + notl(index); // convert to plain index + } +} + + void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, - int bcp_offset) { - assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + int bcp_offset, + bool giant_index) { assert(cache != index, "must use different registers"); - load_unsigned_word(index, Address(r13, bcp_offset)); + get_cache_index_at_bcp(index, bcp_offset, giant_index); movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index @@ -200,10 +215,10 @@ void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, - int bcp_offset) { - assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + int bcp_offset, + bool giant_index) { assert(cache != tmp, "must use different register"); - load_unsigned_word(tmp, Address(r13, bcp_offset)); + get_cache_index_at_bcp(tmp, bcp_offset, giant_index); assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index // and from word offset to byte offset @@ -1288,7 +1303,8 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2) { + Register reg2, + bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; @@ -1298,8 +1314,15 @@ // We are making a call. Increment the count. increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + Label skip_receiver_profile; + if (receiver_can_be_null) { + testptr(receiver, receiver); + jcc(Assembler::zero, skip_receiver_profile); + } + // Record the receiver type. record_klass_in_profile(receiver, mdp, reg2); + bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, --- old/src/cpu/x86/vm/interp_masm_x86_64.hpp 2008-11-08 23:39:31.000000000 -0800 +++ new/src/cpu/x86/vm/interp_masm_x86_64.hpp 2008-11-08 23:39:31.000000000 -0800 @@ -95,9 +95,10 @@ void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); void get_cache_and_index_at_bcp(Register cache, Register index, - int bcp_offset); + int bcp_offset, bool giant_index = false); void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, - int bcp_offset); + int bcp_offset, bool giant_index = false); + void get_cache_index_at_bcp(Register index, int bcp_offset, bool giant_index = false); void pop_ptr(Register r = rax); @@ -236,7 +237,8 @@ void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2); + Register scratch2, + bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass, Register scratch); --- old/src/cpu/x86/vm/templateInterpreter_x86_32.cpp 2008-11-08 23:39:33.000000000 -0800 +++ new/src/cpu/x86/vm/templateInterpreter_x86_32.cpp 2008-11-08 23:39:33.000000000 -0800 @@ -166,13 +166,22 @@ } -address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, bool unbox) { + TosState incoming_state = state; + if (InvokeDynamic) { + if (unbox) { + incoming_state = atos; + } + } else { + assert(!unbox, "old behavior"); + } + Label interpreter_entry; address compiled_entry = __ pc(); #ifdef COMPILER2 // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases - if ((state == ftos && UseSSE < 1) || (state == dtos && UseSSE < 2)) { + if ((incoming_state == ftos && UseSSE < 1) || (incoming_state == dtos && UseSSE < 2)) { for (int i = 1; i < 8; i++) { __ ffree(i); } @@ -180,7 +189,7 @@ __ empty_FPU_stack(); } #endif - if ((state == ftos && UseSSE < 1) || (state == dtos && UseSSE < 2)) { + if ((incoming_state == ftos && UseSSE < 1) || (incoming_state == dtos && UseSSE < 2)) { __ MacroAssembler::verify_FPU(1, "generate_return_entry_for compiled"); } else { __ MacroAssembler::verify_FPU(0, "generate_return_entry_for compiled"); @@ -196,12 +205,12 @@ // In SSE mode, interpreter returns FP results in xmm0 but they need // to end up back on the FPU so it can operate on them. - if (state == ftos && UseSSE >= 1) { + if (incoming_state == ftos && UseSSE >= 1) { __ subptr(rsp, wordSize); __ movflt(Address(rsp, 0), xmm0); __ fld_s(Address(rsp, 0)); __ addptr(rsp, wordSize); - } else if (state == dtos && UseSSE >= 2) { + } else if (incoming_state == dtos && UseSSE >= 2) { __ subptr(rsp, 2*wordSize); __ movdbl(Address(rsp, 0), xmm0); __ fld_d(Address(rsp, 0)); @@ -217,13 +226,116 @@ __ restore_bcp(); __ restore_locals(); - __ get_cache_and_index_at_bcp(rbx, rcx, 1); + + Label L_fail; + + if (unbox && state != atos) { + // cast and unbox + BasicType type = as_BasicType(state); + if (type == T_BYTE) type = T_BOOLEAN; // FIXME + KlassHandle boxk = KlassHandles::box_klass(type); + __ mov32(rbx, ExternalAddress((address) boxk.raw_value())); + __ testl(rax, rax); + Label L_got_value, L_get_value; + // convert nulls to zeroes (avoid NPEs here) + if (!(type == T_FLOAT || type == T_DOUBLE)) { + // if rax already contains zero bits, forge ahead + __ jcc(Assembler::zero, L_got_value); + } else { + __ jcc(Assembler::notZero, L_get_value); + __ fldz(); + __ jmp(L_got_value); + } + __ bind(L_get_value); + __ cmp32(rbx, Address(rax, oopDesc::klass_offset_in_bytes())); + __ jcc(Assembler::notEqual, L_fail); + int offset = java_lang_boxing_object::value_offset_in_bytes(type); + // Cf. TemplateTable::getfield_or_static + switch (type) { + case T_BYTE: // fall through: + case T_BOOLEAN: __ load_signed_byte(rax, Address(rax, offset)); break; + case T_CHAR: __ load_unsigned_word(rax, Address(rax, offset)); break; + case T_SHORT: __ load_signed_word(rax, Address(rax, offset)); break; + case T_INT: __ movl(rax, Address(rax, offset)); break; + case T_FLOAT: __ fld_s(Address(rax, offset)); break; + case T_DOUBLE: __ fld_d(Address(rax, offset)); break; + // Access to java.lang.Double.value does not need to be atomic: + case T_LONG: { __ movl(rdx, Address(rax, offset + 4)); + __ movl(rax, Address(rax, offset + 0)); } break; + default: ShouldNotReachHere(); + } + __ bind(L_got_value); + + // put FPU results into xmm0, as above: + if (state == ftos && UseSSE >= 1) { + __ subl(rsp, wordSize); + __ movflt(Address(rsp, 0), xmm0); + __ fld_s(Address(rsp, 0)); + __ addl(rsp, wordSize); + } else if (state == dtos && UseSSE >= 2) { + __ subl(rsp, 2*wordSize); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ addl(rsp, 2*wordSize); + } + } + + Label L_got_cache, L_giant_index; + if (InvokeDynamic) { + __ cmpb(Address(rsi, 0), Bytecodes::_invokedynamic); + __ jcc(Assembler::equal, L_giant_index); + } + __ get_cache_and_index_at_bcp(rbx, rcx, 1, false); + __ bind(L_got_cache); + if (unbox && state == atos) { + // insert a casting conversion, to keep verifier sane + Label L_ok, L_ok_pops; + __ testl(rax, rax); + __ jcc(Assembler::zero, L_ok); + __ push(rax); // save the object to check + __ movl(rax, Address(rax, oopDesc::klass_offset_in_bytes())); + __ push(rbx); // save CP cache reference + __ push(rcx); // save CP cache reference + __ movl(rbx, Address(rbx, rcx, + Address::times_4, constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f1_offset())); + __ movl(rbx, Address(rbx, __ delayed_value(java_dyn_impl_DynCallSite::type_offset_in_bytes, rcx))); + __ movl(rbx, Address(rbx, __ delayed_value(java_dyn_MethodType::rtype_offset_in_bytes, rcx))); + __ movl(rbx, Address(rbx, __ delayed_value(java_lang_Class::klass_offset_in_bytes, rcx))); + __ check_klass_subtype(rax, rbx, rcx, L_ok_pops); + __ addptr(rsp, 2*wordSize); // toss rcx, keep rbx as failed klass + __ pop(rax); + __ jmp(L_fail); + + __ bind(L_ok_pops); + // restore pushed temp regs: + __ pop(rcx); + __ pop(rbx); + __ pop(rax); + __ bind(L_ok); + } __ movl(rbx, Address(rbx, rcx, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset())); __ andptr(rbx, 0xFF); __ lea(rsp, Address(rsp, rbx, Interpreter::stackElementScale())); __ dispatch_next(state, step); + + // out of the main line of code... + if (InvokeDynamic) { + __ bind(L_giant_index); + __ get_cache_and_index_at_bcp(rbx, rcx, 1, true); + __ jmp(L_got_cache); + + if (unbox) { + __ bind(L_fail); + __ push(rbx); // missed klass (required) + __ push(rax); // bad object (actual) + __ movptr(rdx, ExternalAddress((address) &Interpreter::_throw_WrongMethodType_entry)); + __ call(rdx); + } + } + return entry; } --- old/src/cpu/x86/vm/templateInterpreter_x86_64.cpp 2008-11-08 23:39:35.000000000 -0800 +++ new/src/cpu/x86/vm/templateInterpreter_x86_64.cpp 2008-11-08 23:39:35.000000000 -0800 @@ -176,7 +176,17 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, - int step) { + int step, + bool unbox) { + TosState incoming_state = state; + if (InvokeDynamic) { + if (unbox) { + incoming_state = atos; + } + Unimplemented(); + } else { + assert(!unbox, "old behavior"); + } // amd64 doesn't need to do anything special about compiled returns // to the interpreter so the code that exists on x86 to place a sentinel @@ -191,7 +201,14 @@ __ restore_bcp(); __ restore_locals(); - __ get_cache_and_index_at_bcp(rbx, rcx, 1); + + Label L_got_cache, L_giant_index; + if (InvokeDynamic) { + __ cmpb(Address(r13, 0), Bytecodes::_invokedynamic); + __ jcc(Assembler::equal, L_giant_index); + } + __ get_cache_and_index_at_bcp(rbx, rcx, 1, false); + __ bind(L_got_cache); __ movl(rbx, Address(rbx, rcx, Address::times_8, in_bytes(constantPoolCacheOopDesc::base_offset()) + @@ -200,6 +217,12 @@ if (TaggedStackInterpreter) __ shll(rbx, 1); // 2 slots per parameter. __ lea(rsp, Address(rsp, rbx, Address::times_8)); __ dispatch_next(state, step); + + if (InvokeDynamic) { + __ get_cache_and_index_at_bcp(rbx, rcx, 1, true); + __ jmp(L_got_cache); + } + return entry; } --- old/src/cpu/x86/vm/templateTable_x86_32.cpp 2008-11-08 23:39:38.000000000 -0800 +++ new/src/cpu/x86/vm/templateTable_x86_32.cpp 2008-11-08 23:39:38.000000000 -0800 @@ -206,12 +206,12 @@ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), scratch, rsi, bc); #ifndef ASSERT __ jmpb(patch_done); - __ bind(fast_patch); - } #else __ jmp(patch_done); +#endif __ bind(fast_patch); } +#ifdef ASSERT Label okay; __ load_unsigned_byte(scratch, at_bcp(0)); __ cmpl(scratch, (int)Bytecodes::java_code(bytecode)); @@ -2105,6 +2105,7 @@ void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) { assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + bool is_invokedynamic = (bytecode() == Bytecodes::_invokedynamic); Register temp = rbx; @@ -2112,13 +2113,19 @@ const int shift_count = (1 + byte_no)*BitsPerByte; Label resolved; - __ get_cache_and_index_at_bcp(Rcache, index, 1); - __ movl(temp, Address(Rcache, index, Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); - __ shrl(temp, shift_count); - // have we resolved this bytecode? - __ andl(temp, 0xFF); - __ cmpl(temp, (int)bytecode()); - __ jcc(Assembler::equal, resolved); + __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic); + if (is_invokedynamic) { + // we are resolved if the f1 field contains a non-null CallSite object + __ cmpptr(Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), (int32_t) NULL_WORD); + __ jcc(Assembler::notEqual, resolved); + } else { + __ movl(temp, Address(Rcache, index, Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); + __ shrl(temp, shift_count); + // have we resolved this bytecode? + __ andl(temp, 0xFF); + __ cmpl(temp, (int)bytecode()); + __ jcc(Assembler::equal, resolved); + } // resolve first time through address entry; @@ -2131,12 +2138,13 @@ case Bytecodes::_invokespecial : // fall through case Bytecodes::_invokestatic : // fall through case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break; default : ShouldNotReachHere(); break; } __ movl(temp, (int)bytecode()); __ call_VM(noreg, entry, temp); // Update registers with resolved info - __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic); __ bind(resolved); } @@ -2881,9 +2889,14 @@ } -void TemplateTable::prepare_invoke(Register method, Register index, int byte_no, Bytecodes::Code code) { +void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) { + bool neg_byte_no = (byte_no < 0); + if (neg_byte_no) byte_no = -byte_no; + // determine flags + Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokedynamic = code == Bytecodes::_invokedynamic; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; const bool load_receiver = code != Bytecodes::_invokestatic; @@ -2894,6 +2907,9 @@ const Register flags = rdx; assert_different_registers(method, index, recv, flags); + assert(!neg_byte_no || is_invokedynamic, "byte_no<0 hack only for invdyn"); + const bool is_invdyn_bootstrap = neg_byte_no; + // save 'interpreter return address' __ save_bcp(); @@ -2904,8 +2920,13 @@ __ movl(recv, flags); __ andl(recv, 0xFF); // recv count is 0 based? - __ movl(recv, Address(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1))); - __ verify_oop(recv); + Address recv_addr(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1)); + if (is_invokedynamic) { + __ lea(recv, recv_addr); + } else { + __ movptr(recv, recv_addr); + __ verify_oop(recv); + } } // do null check if needed @@ -2922,11 +2943,15 @@ // Make sure we don't need to mask flags for tosBits after the above shift ConstantPoolCacheEntry::verify_tosBits(); // load return address - { const int table = - is_invokeinterface - ? (int)Interpreter::return_5_addrs_by_index_table() - : (int)Interpreter::return_3_addrs_by_index_table(); - __ movl(flags, Address(noreg, flags, Address::times_4, table)); + { + address table_addr = + (is_invdyn_bootstrap) + ? (address)Interpreter::return_5_unbox_addrs_by_index_table() + : (is_invokeinterface || is_invokedynamic) + ? (address)Interpreter::return_5_addrs_by_index_table() + : (address)Interpreter::return_3_addrs_by_index_table(); + ExternalAddress table(table_addr); + __ movptr(flags, ArrayAddress(table, Address(noreg, flags, Address::times_ptr))); } // push return address @@ -2988,7 +3013,7 @@ void TemplateTable::invokevirtual(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // rbx,: index // rcx: receiver @@ -3000,7 +3025,7 @@ void TemplateTable::invokespecial(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -3010,7 +3035,7 @@ void TemplateTable::invokestatic(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -3026,7 +3051,7 @@ void TemplateTable::invokeinterface(int byte_no) { transition(vtos, vtos); - prepare_invoke(rax, rbx, byte_no, bytecode()); + prepare_invoke(rax, rbx, byte_no); // rax,: Interface // rbx,: index @@ -3178,6 +3203,79 @@ __ jump_from_interpreted(rbx, rdx); } +void TemplateTable::invokedynamic(int byte_no) { + transition(vtos, vtos); + + if (!InvokeDynamic) { + // We do not encounter this bytecode if !InvokeDynamic. + // See Rewriter::rewrite_invokedynamic. + __ stop("invokedynamic not enabled"); + return; + } + + prepare_invoke(rax, rbx, byte_no); + + // rax: CallSite object (f1) + // rbx: unused (f2) + // rcx: receiver address + // rdx: flags (unused) + + if (ProfileInterpreter) { + Label L; + __ movl(rdx, Address(rcx, 0)); + __ testl(rdx, rdx); + __ jcc(Assembler::zero, L); + + // Get receiver klass into rdx + __ movl(rdx, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ verify_oop(rdx); + __ bind(L); + + // profile this call + __ profile_virtual_call(rdx, rsi, rdi, true); + } + + Label handle_unlinked_site; + __ movptr(rcx, Address(rax, __ delayed_value(java_dyn_impl_DynCallSite::target_offset_in_bytes, rcx))); + __ testptr(rcx, rcx); + __ jcc(Assembler::zero, handle_unlinked_site); + + __ prepare_to_jump_from_interpreted(); + __ jump_to_method_handle_entry(rcx, rdx); + + // Initial calls come here... + __ bind(handle_unlinked_site); + __ pop(rcx); // remove return address pushed by prepare_invoke + + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::bootstrap_invokedynamic); + // squish stacked arguments together on stack, preceded by call site + // return bootstrap method as a handle in rcx + __ restore_bcp(); // rsi must be correct for call_VM + __ call_VM(rax, entry, rax); + __ movl(rdi, rax); // protect MH from prepare_invoke + + // recompute return address + __ restore_bcp(); // rsi must be correct for prepare_invoke + prepare_invoke(rax, rbx, -byte_no); + // rax: CallSite object (f1) + // rbx: unused (f2) + // rcx: receiver address (now holds arglist) + // rdx: flags + + // save SP now, before we add the bootstrap call to the stack + __ prepare_to_jump_from_interpreted(); + + // let's play adapter + __ pop(rbx); // return value + __ push(rdi); // boot MH + __ push(rax); // call site + __ movptr(rcx, Address(rcx, 0)); + __ push(rcx); // arglist + __ push(rbx); // return value + __ mov(rcx, rdi); + __ jump_to_method_handle_entry(rcx, rdx); +} + //---------------------------------------------------------------------------------------------------- // Allocation --- old/src/cpu/x86/vm/templateTable_x86_32.hpp 2008-11-08 23:39:41.000000000 -0800 +++ new/src/cpu/x86/vm/templateTable_x86_32.hpp 2008-11-08 23:39:40.000000000 -0800 @@ -22,8 +22,7 @@ * */ - static void prepare_invoke(Register method, Register index, int byte_no, - Bytecodes::Code code); + static void prepare_invoke(Register method, Register index, int byte_no); static void invokevirtual_helper(Register index, Register recv, Register flags); static void volatile_barrier(Assembler::Membar_mask_bits order_constraint ); --- old/src/cpu/x86/vm/templateTable_x86_64.cpp 2008-11-08 23:39:43.000000000 -0800 +++ new/src/cpu/x86/vm/templateTable_x86_64.cpp 2008-11-08 23:39:42.000000000 -0800 @@ -209,12 +209,12 @@ scratch, r13, bc); #ifndef ASSERT __ jmpb(patch_done); - __ bind(fast_patch); - } #else __ jmp(patch_done); +#endif __ bind(fast_patch); } +#ifdef ASSERT Label okay; __ load_unsigned_byte(scratch, at_bcp(0)); __ cmpl(scratch, (int) Bytecodes::java_code(bytecode)); @@ -2058,6 +2058,7 @@ Register Rcache, Register index) { assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + bool is_invokedynamic = (bytecode() == Bytecodes::_invokedynamic); const Register temp = rbx; assert_different_registers(Rcache, index, temp); @@ -2065,15 +2066,24 @@ const int shift_count = (1 + byte_no) * BitsPerByte; Label resolved; __ get_cache_and_index_at_bcp(Rcache, index, 1); - __ movl(temp, Address(Rcache, - index, Address::times_8, - constantPoolCacheOopDesc::base_offset() + - ConstantPoolCacheEntry::indices_offset())); - __ shrl(temp, shift_count); - // have we resolved this bytecode? - __ andl(temp, 0xFF); - __ cmpl(temp, (int) bytecode()); - __ jcc(Assembler::equal, resolved); + if (is_invokedynamic) { + // we are resolved if the f1 field contains a non-null CallSite object + __ cmpptr(Address(Rcache, + index, Address::times_ptr, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::f1_offset()), (int32_t) NULL_WORD); + __ jcc(Assembler::notEqual, resolved); + } else { + __ movl(temp, Address(Rcache, + index, Address::times_ptr, + constantPoolCacheOopDesc::base_offset() + + ConstantPoolCacheEntry::indices_offset())); + __ shrl(temp, shift_count); + // have we resolved this bytecode? + __ andl(temp, 0xFF); + __ cmpl(temp, (int) bytecode()); + __ jcc(Assembler::equal, resolved); + } // resolve first time through address entry; @@ -2090,6 +2100,9 @@ case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + case Bytecodes::_invokedynamic: + entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); + break; default: ShouldNotReachHere(); break; @@ -2098,7 +2111,7 @@ __ call_VM(noreg, entry, temp); // Update registers with resolved info - __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic); __ bind(resolved); } @@ -2834,10 +2847,14 @@ void TemplateTable::prepare_invoke(Register method, Register index, - int byte_no, - Bytecodes::Code code) { + int byte_no) { + bool neg_byte_no = (byte_no < 0); + if (neg_byte_no) byte_no = -byte_no; + // determine flags + Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokedynamic = code == Bytecodes::_invokedynamic; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; const bool load_receiver = code != Bytecodes::_invokestatic; @@ -2848,6 +2865,9 @@ const Register flags = rdx; assert_different_registers(method, index, recv, flags); + assert(!neg_byte_no || is_invokedynamic, "byte_no<0 hack only for invdyn"); + const bool is_invdyn_bootstrap = neg_byte_no; + // save 'interpreter return address' __ save_bcp(); @@ -2858,9 +2878,14 @@ __ movl(recv, flags); __ andl(recv, 0xFF); if (TaggedStackInterpreter) __ shll(recv, 1); // index*2 - __ movq(recv, Address(rsp, recv, Address::times_8, - -Interpreter::expr_offset_in_bytes(1))); - __ verify_oop(recv); + Address recv_addr(rsp, recv, Address::times_8, + -Interpreter::expr_offset_in_bytes(1)); + if (is_invokedynamic) { + __ lea(recv, recv_addr); + } else { + __ movptr(recv, recv_addr); + __ verify_oop(recv); + } } // do null check if needed @@ -2878,10 +2903,15 @@ ConstantPoolCacheEntry::verify_tosBits(); // load return address { - ExternalAddress return_5((address)Interpreter::return_5_addrs_by_index_table()); - ExternalAddress return_3((address)Interpreter::return_3_addrs_by_index_table()); - __ lea(rscratch1, (is_invokeinterface ? return_5 : return_3)); - __ movq(flags, Address(rscratch1, flags, Address::times_8)); + address table_addr = + (is_invdyn_bootstrap) + ? (address)Interpreter::return_5_unbox_addrs_by_index_table() + : (is_invokeinterface || is_invokedynamic) + ? (address)Interpreter::return_5_addrs_by_index_table() + : (address)Interpreter::return_3_addrs_by_index_table(); + ExternalAddress table(table_addr); + __ lea(rscratch1, table); + __ movptr(flags, Address(rscratch1, flags, Address::times_ptr)); } // push return address @@ -2947,7 +2977,7 @@ void TemplateTable::invokevirtual(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // rbx: index // rcx: receiver @@ -2959,7 +2989,7 @@ void TemplateTable::invokespecial(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -2969,7 +2999,7 @@ void TemplateTable::invokestatic(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -2983,7 +3013,7 @@ void TemplateTable::invokeinterface(int byte_no) { transition(vtos, vtos); - prepare_invoke(rax, rbx, byte_no, bytecode()); + prepare_invoke(rax, rbx, byte_no); // rax: Interface // rbx: index @@ -3152,6 +3182,79 @@ __ jump_from_interpreted(rbx, rdx); } +void TemplateTable::invokedynamic(int byte_no) { + transition(vtos, vtos); + + if (!InvokeDynamic) { + // We do not encounter this bytecode if !InvokeDynamic. + // See Rewriter::rewrite_invokedynamic. + __ stop("invokedynamic not enabled"); + return; + } + + prepare_invoke(rax, rbx, byte_no); + + // rax: CallSite object (f1) + // rbx: unused (f2) + // rcx: receiver address + // rdx: flags (unused) + + if (ProfileInterpreter) { + Label L; + __ movl(rdx, Address(rcx, 0)); + __ testl(rdx, rdx); + __ jcc(Assembler::zero, L); + + // Get receiver klass into rdx + __ movl(rdx, Address(rdx, oopDesc::klass_offset_in_bytes())); + __ verify_oop(rdx); + __ bind(L); + + // profile this call + __ profile_virtual_call(rdx, r13, r14, true); + } + + Label handle_unlinked_site; + __ movptr(rcx, Address(rax, __ delayed_value(java_dyn_impl_DynCallSite::target_offset_in_bytes, rcx))); + __ testptr(rcx, rcx); + __ jcc(Assembler::zero, handle_unlinked_site); + + __ prepare_to_jump_from_interpreted(); + __ jump_to_method_handle_entry(rcx, rdx); + + // Initial calls come here... + __ bind(handle_unlinked_site); + __ pop(rcx); // remove return address pushed by prepare_invoke + + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::bootstrap_invokedynamic); + // squish stacked arguments together on stack, preceded by call site + // return bootstrap method as a handle in rcx + __ restore_bcp(); // rsi must be correct for call_VM + __ call_VM(rax, entry, rax); + __ movl(rdi, rax); // protect MH from prepare_invoke + + // recompute return address + __ restore_bcp(); // rsi must be correct for prepare_invoke + prepare_invoke(rax, rbx, -byte_no); + // rax: CallSite object (f1) + // rbx: unused (f2) + // rcx: receiver address (now holds arglist) + // rdx: flags + + // save SP now, before we add the bootstrap call to the stack + __ prepare_to_jump_from_interpreted(); + + // let's play adapter + __ pop(rbx); // return value + __ push(rdi); // boot MH + __ push(rax); // call site + __ movptr(rcx, Address(rcx, 0)); + __ push(rcx); // arglist + __ push(rbx); // return value + __ mov(rcx, rdi); + __ jump_to_method_handle_entry(rcx, rdx); +} + //----------------------------------------------------------------------------- // Allocation --- old/src/cpu/x86/vm/templateTable_x86_64.hpp 2008-11-08 23:39:45.000000000 -0800 +++ new/src/cpu/x86/vm/templateTable_x86_64.hpp 2008-11-08 23:39:45.000000000 -0800 @@ -22,8 +22,7 @@ * */ - static void prepare_invoke(Register method, Register index, int byte_no, - Bytecodes::Code code); + static void prepare_invoke(Register method, Register index, int byte_no); static void invokevirtual_helper(Register index, Register recv, Register flags); static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); --- old/src/share/vm/c1/c1_GraphBuilder.cpp 2008-11-08 23:39:47.000000000 -0800 +++ new/src/share/vm/c1/c1_GraphBuilder.cpp 2008-11-08 23:39:47.000000000 -0800 @@ -1543,6 +1543,14 @@ code = Bytecodes::_invokespecial; } + if (InvokeDynamic && target->is_method_handle_invoke()) { + if (target->holder()->name() == ciSymbol::java_dyn_Dynamic()) { + BAILOUT("invokedynamic NYI"); // FIXME + return; + } + // normal method handle invokes should work fine + } + // NEEDS_CLEANUP // I've added the target-is_loaded() test below but I don't really understand // how klass->is_loaded() can be true and yet target->is_loaded() is false. @@ -2455,7 +2463,6 @@ case Bytecodes::_invokespecial : // fall through case Bytecodes::_invokestatic : // fall through case Bytecodes::_invokeinterface: invoke(code); break; - case Bytecodes::_xxxunusedxxx : ShouldNotReachHere(); break; case Bytecodes::_new : new_instance(s.get_index_big()); break; case Bytecodes::_newarray : new_type_array(); break; case Bytecodes::_anewarray : new_object_array(); break; --- old/src/share/vm/ci/bcEscapeAnalyzer.cpp 2008-11-08 23:39:49.000000000 -0800 +++ new/src/share/vm/ci/bcEscapeAnalyzer.cpp 2008-11-08 23:39:49.000000000 -0800 @@ -848,9 +848,6 @@ } } break; - case Bytecodes::_xxxunusedxxx: - ShouldNotReachHere(); - break; case Bytecodes::_new: state.apush(allocated_obj); break; --- old/src/share/vm/ci/ciMethod.cpp 2008-11-08 23:39:52.000000000 -0800 +++ new/src/share/vm/ci/ciMethod.cpp 2008-11-08 23:39:52.000000000 -0800 @@ -697,6 +697,13 @@ return CURRENT_THREAD_ENV->get_object(mtype)->as_instance(); } +ciMethod* ciMethod::extended_invoke_method() { + check_is_loaded(); + VM_ENTRY_MARK; + methodOop extinv = get_methodOop()->extended_invoke_method(); + return CURRENT_THREAD_ENV->get_object(extinv)->as_method(); +} + // ------------------------------------------------------------------ // ciMethod::build_method_data --- old/src/share/vm/ci/ciMethod.hpp 2008-11-08 23:39:54.000000000 -0800 +++ new/src/share/vm/ci/ciMethod.hpp 2008-11-08 23:39:53.000000000 -0800 @@ -209,6 +209,7 @@ int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC bool is_method_handle_invoke(); ciInstance* method_handle_type(); + ciMethod* extended_invoke_method(); // What kind of ciObject is this? bool is_method() { return true; } --- old/src/share/vm/ci/ciStreams.cpp 2008-11-08 23:39:56.000000000 -0800 +++ new/src/share/vm/ci/ciStreams.cpp 2008-11-08 23:39:56.000000000 -0800 @@ -303,7 +303,7 @@ int ciBytecodeStream::get_method_index() { switch (cur_bc()) { case Bytecodes::_invokeinterface: - return Bytes::get_Java_u2(_pc-4); + return get_index_int(); case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: --- old/src/share/vm/ci/ciStreams.hpp 2008-11-08 23:39:58.000000000 -0800 +++ new/src/share/vm/ci/ciStreams.hpp 2008-11-08 23:39:58.000000000 -0800 @@ -91,11 +91,13 @@ _end = _start + max; } - address cur_bcp() { return _bc_start; } // Returns bcp to current instruction + address cur_bcp() const { return _bc_start; } // Returns bcp to current instruction int next_bci() const { return _pc -_start; } int cur_bci() const { return _bc_start - _start; } + int instruction_size() const { return _pc - _bc_start; } Bytecodes::Code cur_bc() const{ return check_java(_bc); } + Bytecodes::Code raw_cur_bc() const { return Bytecodes::raw_code_at(cur_bcp()); } Bytecodes::Code next_bc() { return Bytecodes::java_code((Bytecodes::Code)* _pc); } // Return current ByteCode and increment PC to next bytecode, skipping all @@ -121,34 +123,39 @@ return check_java(_bc); } - bool is_wide() { return ( _pc == _was_wide ); } + bool is_wide() const { return ( _pc == _was_wide ); } // Get a byte index following this bytecode. // If prefixed with a wide bytecode, get a wide index. int get_index() const { + assert_index_size(is_wide() ? 2 : 1); return (_pc == _was_wide) // was widened? ? Bytes::get_Java_u2(_bc_start+2) // yes, return wide index : _bc_start[1]; // no, return narrow index } - // Set a byte index following this bytecode. - // If prefixed with a wide bytecode, get a wide index. - void put_index(int idx) { - if (_pc == _was_wide) // was widened? - Bytes::put_Java_u2(_bc_start+2,idx); // yes, set wide index - else - _bc_start[1]=idx; // no, set narrow index + // Get 2-byte index (getfield/putstatic/etc) + int get_index_big() const { + assert_index_size(2); + return Bytes::get_Java_u2(_bc_start+1); } - // Get 2-byte index (getfield/putstatic/etc) - int get_index_big() const { return Bytes::get_Java_u2(_bc_start+1); } + // Get 2-byte index (or 4-byte, for invokedynamic) + int get_index_int() const { + return has_giant_index() ? get_index_giant() : get_index_big(); + } + + // Get 4-byte index, for invokedynamic. + int get_index_giant() const { + assert_index_size(4); + return Bytes::get_native_u4(_bc_start+1); + } + + bool has_giant_index() const { return (raw_cur_bc() == Bytecodes::_invokedynamic); } // Get dimensions byte (multinewarray) int get_dimensions() const { return *(unsigned char*)(_pc-1); } - // Get unsigned index fast - int get_index_fast() const { return Bytes::get_native_u2(_pc-2); } - // Sign-extended index byte/short, no widening int get_byte() const { return (int8_t)(_pc[-1]); } int get_short() const { return (int16_t)Bytes::get_Java_u2(_pc-2); } @@ -225,6 +232,22 @@ ciKlass* get_declared_method_holder(); int get_method_holder_index(); int get_method_signature_index(); + + private: + void assert_index_size(int required_size) const { +#ifdef ASSERT + int isize = instruction_size() - (is_wide() ? 1 : 0) - 1; + if (isize == 2 && raw_cur_bc() == Bytecodes::_iinc) + isize = 1; + else if (isize <= 2) + ; // no change + else if (has_giant_index()) + isize = 4; + else + isize = 2; + assert(isize = required_size, "wrong index size"); +#endif + } }; --- old/src/share/vm/classfile/javaClasses.cpp 2008-11-08 23:40:01.000000000 -0800 +++ new/src/share/vm/classfile/javaClasses.cpp 2008-11-08 23:40:00.000000000 -0800 @@ -2200,6 +2200,52 @@ } +// Support for java_dyn_impl_DynCallSite + +int java_dyn_impl_DynCallSite::_type_offset; +int java_dyn_impl_DynCallSite::_target_offset; +int java_dyn_impl_DynCallSite::_vmref_offset; +int java_dyn_impl_DynCallSite::_vmdata_offset; + +void java_dyn_impl_DynCallSite::compute_offsets() { + if (!InvokeDynamic) return; + klassOop k = SystemDictionary::dyn_impl_DynCallSite_klass(); + if (k != NULL) { + compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::java_dyn_MethodType_signature(), true); + compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_dyn_MethodHandle_signature(), true); + compute_offset(_vmref_offset, k, vmSymbols::vmref_name(), vmSymbols::object_signature(), true); + compute_offset(_vmdata_offset, k, vmSymbols::vmdata_name(), vmSymbols::long_signature(), true); + if (_vmdata_offset != 0) _vmdata_offset += oopDesc::address_padding_in_bytes(); + } +} + +oop java_dyn_impl_DynCallSite::type(oop site) { + return site->obj_field(_type_offset); +} + +oop java_dyn_impl_DynCallSite::target(oop site) { + return site->obj_field(_target_offset); +} + +void java_dyn_impl_DynCallSite::set_target(oop site, oop target) { + site->obj_field_put(_target_offset, target); +} + +oop java_dyn_impl_DynCallSite::vmref(oop site) { + return site->obj_field(_vmref_offset); +} + +void java_dyn_impl_DynCallSite::set_vmref(oop site, oop ref) { + site->obj_field_put(_vmref_offset, ref); +} + +address java_dyn_impl_DynCallSite::vmdata(oop site) { + return site->address_field(_vmdata_offset); +} + +void java_dyn_impl_DynCallSite::set_vmdata(oop site, address data) { + site->address_field_put(_vmdata_offset, data); +} // Support for java_security_AccessControlContext @@ -2541,6 +2587,9 @@ java_dyn_MethodHandle::compute_offsets(); java_dyn_MethodType::compute_offsets(); } + if (InvokeDynamic) { + java_dyn_impl_DynCallSite::compute_offsets(); + } java_security_AccessControlContext::compute_offsets(); // Initialize reflection classes. The layouts of these classes // changed with the new reflection implementation in JDK 1.4, and --- old/src/share/vm/classfile/javaClasses.hpp 2008-11-08 23:40:03.000000000 -0800 +++ new/src/share/vm/classfile/javaClasses.hpp 2008-11-08 23:40:03.000000000 -0800 @@ -872,6 +872,40 @@ }; +// Interface to java.dyn.impl.DynCallSite objects + +class java_dyn_impl_DynCallSite: AllStatic { + friend class JavaClasses; + +private: + static int _type_offset; + static int _target_offset; + static int _vmref_offset; + static int _vmdata_offset; + + static void compute_offsets(); + +public: + // Accessors + static oop type(oop site); + + static oop target(oop site); + static void set_target(oop site, oop target); + + static oop vmref(oop site); + static void set_vmref(oop site, oop ref); + + static address vmdata(oop site); + static void set_vmdata(oop site, address data); + + // Accessors for code generation: + static int target_offset_in_bytes() { return _target_offset; } + static int type_offset_in_bytes() { return _type_offset; } + static int vmref_offset_in_bytes() { return _vmref_offset; } + static int vmdata_offset_in_bytes() { return _vmdata_offset; } +}; + + // Interface to java.security.AccessControlContext objects class java_security_AccessControlContext: AllStatic { --- old/src/share/vm/classfile/systemDictionary.cpp 2008-11-08 23:40:05.000000000 -0800 +++ new/src/share/vm/classfile/systemDictionary.cpp 2008-11-08 23:40:05.000000000 -0800 @@ -1909,6 +1909,16 @@ // Skip the rest of the method handle classes, if MethodHandle is not loaded. scan = WKID(meth_group_end+1); } + WKID indy_group_start = WK_KLASS_ENUM_NAME(java_dyn_Linkage_klass); + WKID indy_group_end = WK_KLASS_ENUM_NAME(java_dyn_Dynamic_klass); + initialize_wk_klasses_until(indy_group_start, scan, CHECK); + if (InvokeDynamic) { + initialize_wk_klasses_through(indy_group_start, scan, CHECK); + } + if (_well_known_klasses[indy_group_start] == NULL) { + // Skip the rest of the dynamic typing classes, if Linkage is not loaded. + scan = WKID(indy_group_end+1); + } initialize_wk_klasses_until(WKID_LIMIT, scan, CHECK); @@ -2324,6 +2334,138 @@ return Handle(THREAD, (oop) result.get_jobject()); } +static symbolHandle prepend_argument_type(symbolHandle prepend, + symbolHandle signature, + TRAPS) { + ResourceMark rm; + char fixed_buf[512 DEBUG_ONLY(* 0 + 20)]; + int buf_len = prepend->utf8_length() + signature->utf8_length() + 1; + char* buf = (buf_len <= (int) sizeof(fixed_buf) + ? fixed_buf + : NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buf_len)); + assert(prepend->byte_at(0) != '(', "argument sig"); + assert(signature->byte_at(0) == '(', "method sig"); + int fillp = buf_len, len; + buf[--fillp] = 0; + fillp -= (len = signature->utf8_length()); + memcpy(&buf[fillp], signature->bytes(), len); + assert((int) strlen(&buf[fillp]) == len, ""); + assert(buf[fillp] == '(', "skip this"); + fillp++; // kill the paren + fillp -= (len = prepend->utf8_length()); + memcpy(&buf[fillp], prepend->bytes(), len); + buf[--fillp] = '('; + assert(fillp == 0, "dead reckoning"); + assert((int) strlen(buf) == buf_len - 1, ""); + return oopFactory::new_symbol_handle(buf, THREAD); +} + +methodOop SystemDictionary::find_invokedynamic_invoke(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS) { + if (!InvokeDynamic) return NULL; + + methodOop mhinvoke_oop = find_method_handle_invoke(signature, + class_loader, + protection_domain, + CHECK_NULL); + + methodOop idm_oop = mhinvoke_oop->invokedynamic_method(); + + if (idm_oop != NULL) return idm_oop; + + // First time through, so compute it up. + methodHandle mhinvoke(THREAD, mhinvoke_oop); DEBUG_ONLY(mhinvoke_oop = NULL); + KlassHandle dyn_klass = KlassHandles::java_dyn_Dynamic_klass(); + Handle mt(THREAD, mhinvoke->method_handle_type()); + methodHandle idm = methodOopDesc::make_invoke_method(dyn_klass, signature, + mt, CHECK_NULL); + + // Now link in a third method, which is the target type of the invoke: + // MH.invoke(abc)d --> Dyn.invoke(abc)d --> MH.invoke(Object;abc)d + symbolHandle extsig = prepend_argument_type(vmSymbolHandles::object_signature(), signature, CHECK_NULL); + { + methodOop extm = find_method_handle_invoke(extsig, + class_loader, protection_domain, + CHECK_NULL); + idm->set_extended_invoke_method(extm); + } + + { + MutexLocker ml(SystemDictionary_lock, Thread::current()); + if (mhinvoke->invokedynamic_method() != NULL) + // another thread got here first; go with the winner: + return mhinvoke->invokedynamic_method(); + + mhinvoke->set_invokedynamic_method(idm()); + return idm(); + } +} + + +// Ask Java code to find or construct a java.dyn.CallSite for the given +// name and signature, as interpreted relative to the given class loader. +Handle SystemDictionary::make_dynamic_call_site(KlassHandle caller, + symbolHandle name, + methodHandle mh_invdyn, + jlong vmdata, + TRAPS) { + Handle empty; + // call java.dyn.impl.DynCallSite::makeSite(caller, name, mtype, vmdata) + oop name_str_oop = StringTable::intern(name(), CHECK_(empty)); // not a handle! + JavaCallArguments args(Handle(THREAD, caller->java_mirror())); + args.push_oop(name_str_oop); + args.push_oop(mh_invdyn->method_handle_type()); + args.push_long(vmdata); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + SystemDictionary::dyn_impl_DynCallSite_klass(), + vmSymbols::makeSite_name(), vmSymbols::makeSite_signature(), + &args, CHECK_(empty)); + oop call_site_oop = (oop) result.get_jobject(); + java_dyn_impl_DynCallSite::set_vmref(call_site_oop, mh_invdyn()); + return call_site_oop; +} + +Handle SystemDictionary::find_bootstrap_method(KlassHandle caller, + KlassHandle search_bootstrap_klass, + TRAPS) { + Handle empty; + if (!caller->oop_is_instance()) return empty; + + instanceKlassHandle ik(THREAD, caller()); + + if (ik->bootstrap_method() != NULL) { + return Handle(THREAD, ik->bootstrap_method()); + } + + // call java.dyn.Linkage::findBootstrapMethod(caller, sbk) + JavaCallArguments args(Handle(THREAD, ik->java_mirror())); + if (search_bootstrap_klass.is_null()) + args.push_oop(Handle()); + else + args.push_oop(search_bootstrap_klass->java_mirror()); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + SystemDictionary::java_dyn_Linkage_klass(), + vmSymbols::findBootstrapMethod_name(), + vmSymbols::findBootstrapMethod_signature(), + &args, CHECK_(empty)); + oop boot_method_oop = (oop) result.get_jobject(); + + if (boot_method_oop != NULL) { + // probably no race conditions, but let's be careful: + if (Atomic::cmpxchg_ptr(boot_method_oop, ik->adr_bootstrap_method(), NULL) == NULL) + ik->set_bootstrap_method(boot_method_oop); + else + boot_method_oop = ik->bootstrap_method(); + } else { + boot_method_oop = ik->bootstrap_method(); + } + + return Handle(THREAD, boot_method_oop); +} // Since the identity hash code for symbols changes when the symbols are // moved from the regular perm gen (hash in the mark word) to the shared --- old/src/share/vm/classfile/systemDictionary.hpp 2008-11-08 23:40:07.000000000 -0800 +++ new/src/share/vm/classfile/systemDictionary.hpp 2008-11-08 23:40:07.000000000 -0800 @@ -141,6 +141,12 @@ template(java_dyn_MethodType_klass, java_dyn_MethodType, Opt) \ template(java_dyn_MethodTypeForm_klass, java_dyn_MethodTypeForm, Opt) \ template(java_dyn_WrongMethodTypeException_klass, java_dyn_WrongMethodTypeException, Opt) \ + template(java_dyn_Linkage_klass, java_dyn_Linkage, Opt) \ + template(java_dyn_CallSite_klass, java_dyn_CallSite, Opt) \ + template(dyn_impl_DynCallSite_klass, java_dyn_impl_DynCallSite, Opt) \ + template(java_dyn_Dynamic_klass, java_dyn_Dynamic, Opt) \ + /* Note: MethodHandle must be first, and Dynamic last in group */ \ + \ template(vector_klass, java_util_Vector, Pre) \ template(hashtable_klass, java_util_Hashtable, Pre) \ template(stringBuffer_klass, java_lang_StringBuffer, Pre) \ @@ -469,6 +475,25 @@ Handle class_loader, Handle protection_domain, TRAPS); + // find the MH::invoke method for an invokedynamic instruction + static methodOop find_invokedynamic_invoke(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS); + // ask Java to create a dynamic call site, while linking an invokedynamic op + static Handle make_dynamic_call_site(KlassHandle caller, + symbolHandle name, + methodHandle mh_invoke, + jlong vmdata, + TRAPS); + + // coordinate with Java about bootstrap methods + static Handle find_bootstrap_method(KlassHandle caller, + // This argument is non-null only when a + // classfile attribute has been found: + KlassHandle search_bootstrap_klass, + TRAPS); + // Utility for printing loader "name" as part of tracing constraints static const char* loader_name(oop loader) { return ((loader) == NULL ? "" : --- old/src/share/vm/classfile/vmSymbols.hpp 2008-11-08 23:40:09.000000000 -0800 +++ new/src/share/vm/classfile/vmSymbols.hpp 2008-11-08 23:40:09.000000000 -0800 @@ -123,6 +123,7 @@ template(tag_runtime_invisible_parameter_annotations,"RuntimeInvisibleParameterAnnotations") \ template(tag_annotation_default, "AnnotationDefault") \ template(tag_enclosing_method, "EnclosingMethod") \ + template(tag_bootstrap_invoke_dynamic, "BootstrapInvokeDynamic") \ \ /* exception klasses: at least all exceptions thrown by the VM have entries here */ \ template(java_lang_ArithmeticException, "java/lang/ArithmeticException") \ @@ -215,6 +216,9 @@ template(base_name, "base") \ \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ + template(java_dyn_Dynamic, "java/dyn/Dynamic") \ + template(java_dyn_Linkage, "java/dyn/Linkage") \ + template(java_dyn_CallSite, "java/dyn/CallSite") \ template(java_dyn_MethodHandle, "java/dyn/MethodHandle") \ template(java_dyn_MethodType, "java/dyn/MethodType") \ template(java_dyn_MethodTypeForm, "java/dyn/MethodTypeForm") \ @@ -224,12 +228,17 @@ template(java_dyn_MethodTypeForm_signature, "Ljava/dyn/MethodTypeForm;") \ /* internal classes known only to the JVM: */ \ template(java_dyn_impl_MTForm, "java/dyn/impl/MTForm") \ + template(java_dyn_impl_DynCallSite, "java/dyn/impl/DynCallSite") \ template(java_dyn_impl_MH, "java/dyn/impl/MH") \ template(java_dyn_impl_AMH, "java/dyn/impl/AMH") \ template(java_dyn_impl_BMH, "java/dyn/impl/BMH") \ template(java_dyn_impl_DMH, "java/dyn/impl/DMH") \ template(makeImpl_name, "makeImpl") /*MethodType::makeImpl*/ \ template(makeImpl_signature, "(Ljava/lang/Class;[Ljava/lang/Class;ZZ)Ljava/dyn/MethodType;") \ + template(makeSite_name, "makeSite") /*DynCallSite::makeImpl*/ \ + template(makeSite_signature, "(Ljava/lang/Class;Ljava/lang/String;Ljava/dyn/MethodType;J)Ljava/dyn/impl/DynCallSite;") \ + template(findBootstrapMethod_name, "findBootstrapMethod") \ + template(findBootstrapMethod_signature, "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/dyn/MethodHandle;") \ \ /* common method and field names */ \ template(object_initializer_name, "") \ --- old/src/share/vm/includeDB_core 2008-11-08 23:40:11.000000000 -0800 +++ new/src/share/vm/includeDB_core 2008-11-08 23:40:10.000000000 -0800 @@ -4088,6 +4088,7 @@ templateTable_.cpp interpreterRuntime.hpp templateTable_.cpp interpreter.hpp templateTable_.cpp methodDataOop.hpp +templateTable_.cpp methodHandles.hpp templateTable_.cpp objArrayKlass.hpp templateTable_.cpp oop.inline.hpp templateTable_.cpp sharedRuntime.hpp --- old/src/share/vm/includeDB_gc_parallel 2008-11-08 23:40:13.000000000 -0800 +++ new/src/share/vm/includeDB_gc_parallel 2008-11-08 23:40:12.000000000 -0800 @@ -42,6 +42,12 @@ constantPoolKlass.cpp psScavenge.inline.hpp constantPoolKlass.cpp parOopClosures.inline.hpp +cpCacheKlass.cpp cardTableRS.hpp +cpCacheKlass.cpp oop.pcgc.inline.hpp +cpCacheKlass.cpp psPromotionManager.inline.hpp +cpCacheKlass.cpp psScavenge.inline.hpp +cpCacheKlass.cpp parOopClosures.inline.hpp + genCollectedHeap.cpp concurrentMarkSweepThread.hpp genCollectedHeap.cpp vmCMSOperations.hpp --- old/src/share/vm/includeDB_jvmti 2008-11-08 23:40:14.000000000 -0800 +++ new/src/share/vm/includeDB_jvmti 2008-11-08 23:40:14.000000000 -0800 @@ -28,6 +28,7 @@ jvmtiClassFileReconstituter.cpp bytes_.hpp jvmtiClassFileReconstituter.cpp jvmtiClassFileReconstituter.hpp jvmtiClassFileReconstituter.cpp symbolTable.hpp +jvmtiClassFileReconstituter.cpp signature.hpp jvmtiClassFileReconstituter.hpp jvmtiEnv.hpp --- old/src/share/vm/interpreter/abstractInterpreter.hpp 2008-11-08 23:40:16.000000000 -0800 +++ new/src/share/vm/interpreter/abstractInterpreter.hpp 2008-11-08 23:40:15.000000000 -0800 @@ -217,6 +217,77 @@ stackElementSize()) + tag_offset_in_bytes(); } + // access to stacked values according to type: + static oop* oop_addr_in_slot(intptr_t* slot_addr) { + return (oop*) slot_addr; + } + template + static T* subword_addr_in_slot(intptr_t* slot_addr) { + if ((int) sizeof(T) < wordSize && !Bytes::is_Java_byte_ordering_different()) + // big-endian LP64 + return (T*)(slot_addr + 1) - 1; + else + return (T*) slot_addr; + } + static jint* int_addr_in_slot(intptr_t* slot_addr) { + return subword_addr_in_slot(slot_addr); + } + static jlong long_in_slot(intptr_t* slot_addr) { + if (sizeof(intptr_t) >= sizeof(jlong)) { + return *(jlong*) slot_addr; + } else if (!TaggedStackInterpreter) { + return Bytes::get_native_u8((address)slot_addr); + } else { + assert(sizeof(intptr_t) * 2 == sizeof(jlong), "ILP32"); + // assemble the long in memory order (not arithmetic order) + union { jlong j; jint i[2]; } u; + u.i[0] = (jint) slot_addr[0*stackElementSize()]; + u.i[1] = (jint) slot_addr[1*stackElementSize()]; + return u.j; + } + } + static void set_long_in_slot(intptr_t* slot_addr, jlong value) { + if (sizeof(intptr_t) >= sizeof(jlong)) { + *(jlong*) slot_addr = value; + } else if (!TaggedStackInterpreter) { + Bytes::put_native_u8((address)slot_addr, value); + } else { + assert(sizeof(intptr_t) * 2 == sizeof(jlong), "ILP32"); + // assemble the long in memory order (not arithmetic order) + union { jlong j; jint i[2]; } u; + u.j = value; + slot_addr[0*stackElementSize()] = (intptr_t) u.i[0]; + slot_addr[1*stackElementSize()] = (intptr_t) u.i[1]; + } + } + static void get_jvalue_in_slot(intptr_t* slot_addr, BasicType type, jvalue* value) { + switch (type) { + case T_BOOLEAN: value->z = *subword_addr_in_slot(slot_addr); break; + case T_CHAR: value->c = *subword_addr_in_slot(slot_addr); break; + case T_BYTE: value->c = *subword_addr_in_slot(slot_addr); break; + case T_SHORT: value->c = *subword_addr_in_slot(slot_addr); break; + case T_INT: value->i = *subword_addr_in_slot(slot_addr); break; + case T_LONG: value->j = long_in_slot(slot_addr); break; + case T_FLOAT: value->f = *subword_addr_in_slot(slot_addr); break; + case T_DOUBLE: value->d = jdouble_cast(long_in_slot(slot_addr)); break; + case T_OBJECT: value->l = (jobject) *oop_addr_in_slot(slot_addr); break; + default: ShouldNotReachHere(); + } + } + static void set_jvalue_in_slot(intptr_t* slot_addr, BasicType type, jvalue* value) { + switch (type) { + case T_BOOLEAN: *subword_addr_in_slot(slot_addr) = (value->z != 0); break; + case T_CHAR: *subword_addr_in_slot(slot_addr) = value->c; break; + case T_BYTE: *subword_addr_in_slot(slot_addr) = value->c; break; + case T_SHORT: *subword_addr_in_slot(slot_addr) = value->c; break; + case T_INT: *subword_addr_in_slot(slot_addr) = value->i; break; + case T_LONG: set_long_in_slot(slot_addr, value->j); break; + case T_FLOAT: *subword_addr_in_slot(slot_addr) = value->f; break; + case T_DOUBLE: set_long_in_slot(slot_addr, jlong_cast(value->d)); break; + case T_OBJECT: *oop_addr_in_slot(slot_addr) = (oop) value->l; break; + default: ShouldNotReachHere(); + } + } }; //------------------------------------------------------------------------------------------------------------------------ --- old/src/share/vm/interpreter/bytecode.cpp 2008-11-08 23:40:17.000000000 -0800 +++ new/src/share/vm/interpreter/bytecode.cpp 2008-11-08 23:40:17.000000000 -0800 @@ -34,12 +34,6 @@ } -void Bytecode::set_fast_index(int i) { - assert(0 <= i && i < 0x10000, "illegal index value"); - Bytes::put_native_u2(addr_at(1), (jushort)i); -} - - bool Bytecode::check_must_rewrite() const { assert(Bytecodes::can_rewrite(code()), "post-check only"); @@ -118,7 +112,10 @@ int Bytecode_invoke::index() const { - return Bytes::get_Java_u2(bcp() + 1); + if (has_giant_index()) + return Bytes::get_native_u4(bcp() + 1); + else + return Bytes::get_Java_u2(bcp() + 1); } --- old/src/share/vm/interpreter/bytecode.hpp 2008-11-08 23:40:19.000000000 -0800 +++ new/src/share/vm/interpreter/bytecode.hpp 2008-11-08 23:40:18.000000000 -0800 @@ -65,14 +65,6 @@ // The base class for different kinds of bytecode abstractions. // Provides the primitive operations to manipulate code relative // to an objects 'this' pointer. -// -// Note: Even though it seems that the fast_index & set_fast_index -// functions are machine specific, they're not. They only use -// the natural way to store a 16bit index on a given machine, -// independent of the particular byte ordering. Since all other -// places in the system that refer to these indices use the -// same method (the natural byte ordering on the platform) -// this will always work and be machine-independent). class Bytecode: public ThisRelativeObj { protected: @@ -83,24 +75,41 @@ // Attributes address bcp() const { return addr_at(0); } address next_bcp() const { return addr_at(0) + Bytecodes::length_at(bcp()); } + int instruction_size() const { return Bytecodes::length_at(bcp()); } Bytecodes::Code code() const { return Bytecodes::code_at(addr_at(0)); } + Bytecodes::Code raw_code() const { return Bytecodes::raw_code_at(addr_at(0)); } Bytecodes::Code java_code() const { return Bytecodes::java_code(code()); } bool must_rewrite() const { return Bytecodes::can_rewrite(code()) && check_must_rewrite(); } bool is_active_breakpoint() const { return Bytecodes::is_active_breakpoint_at(bcp()); } - int one_byte_index() const { return byte_at(1); } - int two_byte_index() const { return (byte_at(1) << 8) + byte_at(2); } + int one_byte_index() const { assert_index_size(1); return byte_at(1); } + int two_byte_index() const { assert_index_size(2); return (byte_at(1) << 8) + byte_at(2); } + int offset() const { return (two_byte_index() << 16) >> 16; } address destination() const { return bcp() + offset(); } - int fast_index() const { return Bytes::get_native_u2(addr_at(1)); } // Attribute modification void set_code(Bytecodes::Code code); - void set_fast_index(int i); // Creation inline friend Bytecode* Bytecode_at(address bcp); + + private: + void assert_index_size(int required_size) const { +#ifdef ASSERT + int isize = instruction_size() - 1; + if (isize == 2 && raw_code() == Bytecodes::_iinc) + isize = 1; + else if (isize <= 2) + ; // no change + else if (raw_code() == Bytecodes::_invokedynamic) + isize = 4; + else + isize = 2; + assert(isize = required_size, "wrong index size"); +#endif + } }; inline Bytecode* Bytecode_at(address bcp) { @@ -185,6 +194,7 @@ symbolOop signature() const; // returns the signature of the invoked method BasicType result_type(Thread *thread) const; // returns the result type of the invoke + Bytecodes::Code raw_code() const { return Bytecodes::raw_code_at(bcp(), _method()); } Bytecodes::Code code() const { return Bytecodes::code_at(bcp(), _method()); } Bytecodes::Code adjusted_invoke_code() const { return Bytecodes::java_code(code()); } @@ -195,6 +205,7 @@ bool is_invokevirtual() const { return adjusted_invoke_code() == Bytecodes::_invokevirtual; } bool is_invokestatic() const { return adjusted_invoke_code() == Bytecodes::_invokestatic; } bool is_invokespecial() const { return adjusted_invoke_code() == Bytecodes::_invokespecial; } + bool has_giant_index() const { return raw_code() == Bytecodes::_invokedynamic; } bool is_valid() const { return is_invokeinterface() || is_invokevirtual() || --- old/src/share/vm/interpreter/bytecodeStream.hpp 2008-11-08 23:40:20.000000000 -0800 +++ new/src/share/vm/interpreter/bytecodeStream.hpp 2008-11-08 23:40:20.000000000 -0800 @@ -108,7 +108,9 @@ int end_bci() const { return _end_bci; } Bytecodes::Code code() const { return _code; } + Bytecodes::Code raw_code() const { return Bytecodes::raw_code_at(bcp()); } bool is_wide() const { return _is_wide; } + int instruction_size() const { return (_next_bci - _bci); } bool is_last_bytecode() const { return _next_bci >= _end_bci; } address bcp() const { return method()->code_base() + _bci; } @@ -122,8 +124,29 @@ int dest_w() const { return bci() + (int )Bytes::get_Java_u4(bcp() + 1); } // Unsigned indices, widening - int get_index() const { return (is_wide()) ? Bytes::get_Java_u2(bcp() + 2) : bcp()[1]; } - int get_index_big() const { return (int)Bytes::get_Java_u2(bcp() + 1); } + int get_index() const { assert_index_size(is_wide() ? 2 : 1); + return (is_wide()) ? Bytes::get_Java_u2(bcp() + 2) : bcp()[1]; } + int get_index_big() const { assert_index_size(2); + return (int)Bytes::get_Java_u2(bcp() + 1); } + int get_index_int() const { return has_giant_index() ? get_index_giant() : get_index_big(); } + int get_index_giant() const { assert_index_size(4); return Bytes::get_native_u4(bcp() + 1); } + int has_giant_index() const { return (raw_code() == Bytecodes::_invokedynamic); } + + private: + void assert_index_size(int required_size) const { +#ifdef ASSERT + int isize = instruction_size() - (int)_is_wide - 1; + if (isize == 2 && raw_code() == Bytecodes::_iinc) + isize = 1; + else if (isize <= 2) + ; // no change + else if (has_giant_index()) + isize = 4; + else + isize = 2; + assert(isize = required_size, "wrong index size"); +#endif + } }; // In BytecodeStream, non-java bytecodes will be translated into the --- old/src/share/vm/interpreter/bytecodeTracer.cpp 2008-11-08 23:40:22.000000000 -0800 +++ new/src/share/vm/interpreter/bytecodeTracer.cpp 2008-11-08 23:40:21.000000000 -0800 @@ -48,12 +48,15 @@ int get_index() { return *(address)_next_pc++; } int get_big_index() { int i=Bytes::get_Java_u2(_next_pc); _next_pc+=2; return i; } + int get_giant_index() { int i=Bytes::get_native_u4(_next_pc); _next_pc+=4; return i; } int get_index_special() { return (is_wide()) ? get_big_index() : get_index(); } methodOop method() { return _current_method; } bool is_wide() { return _is_wide; } + bool check_index(int i, bool in_cp_cache, int& cp_index, outputStream* st = tty); void print_constant(int i, outputStream* st = tty); + void print_field_or_method(int i, outputStream* st = tty); void print_attributes(Bytecodes::Code code, int bci, outputStream* st = tty); void bytecode_epilog(int bci, outputStream* st = tty); @@ -182,7 +185,71 @@ } } +bool BytecodePrinter::check_index(int i, bool in_cp_cache, int& cp_index, outputStream* st) { + constantPoolOop constants = method()->constants(); + int ilimit = constants->length(), climit = 0; + + constantPoolCacheOop cache = NULL; + if (in_cp_cache) { + cache = constants->cache(); + if (cache != NULL) { + //climit = cache->length(); // %%% private! + size_t size = cache->size() * HeapWordSize; + size -= sizeof(constantPoolCacheOopDesc); + size /= sizeof(ConstantPoolCacheEntry); + climit = (int) size; + } + } + + if (in_cp_cache && constantPoolCacheOopDesc::is_secondary_index(i)) { + i = constantPoolCacheOopDesc::decode_secondary_index(i); + st->print(" secondary cache[%d] of", i); + if (i >= 0 && i < climit) { + if (!cache->entry_at(i)->is_secondary_entry()) { + st->print_cr(" not secondary entry?", i); + return false; + } + i = cache->entry_at(i)->main_entry_index(); + goto check_cache_index; + } else { + st->print_cr(" not in cache[*]?", i); + return false; + } + } + + if (cache != NULL) { + i = Bytes::swap_u2(i); + if (WizardMode) st->print(" (swap=%d)", i); + goto check_cache_index; + } + + check_cp_index: + if (i >= 0 && i < ilimit) { + if (WizardMode) st->print(" cp[%d]", i); + cp_index = i; + return true; + } + + st->print_cr(" CP[%d] not in CP", i); + return false; + + check_cache_index: + if (i >= 0 && i < climit) { + if (cache->entry_at(i)->is_secondary_entry()) { + st->print_cr(" secondary entry?"); + return false; + } + i = cache->entry_at(i)->constant_pool_index(); + goto check_cp_index; + } + st->print_cr(" not in CP[*]?", i); + return false; +} + void BytecodePrinter::print_constant(int i, outputStream* st) { + int orig_i = i; + if (!check_index(orig_i, false, i, st)) return; + constantPoolOop constants = method()->constants(); constantTag tag = constants->tag_at(i); @@ -203,13 +270,37 @@ st->print_cr(" %s", constants->resolved_klass_at(i)->klass_part()->external_name()); } else if (tag.is_unresolved_klass()) { st->print_cr(" ", i); - } else ShouldNotReachHere(); + } else { + st->print_cr(" bad tag=%d at %d", tag.value(), i); + } +} + +void BytecodePrinter::print_field_or_method(int i, outputStream* st) { + int orig_i = i; + if (!check_index(orig_i, true, i, st)) return; + + constantPoolOop constants = method()->constants(); + constantTag tag = constants->tag_at(i); + + switch (tag.value()) { + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_Fieldref: + break; + default: + st->print_cr(" bad tag=%d at %d", tag.value(), i); + return; + } + + symbolOop name = constants->name_ref_at(orig_i); + symbolOop signature = constants->signature_ref_at(orig_i); + st->print_cr(" %d <%s> <%s> ", i, name->as_C_string(), signature->as_C_string()); } -void BytecodePrinter::print_attributes(Bytecodes::Code code, int bci, outputStream* st) { +void BytecodePrinter::print_attributes(Bytecodes::Code raw_code, int bci, outputStream* st) { // Show attributes of pre-rewritten codes - code = Bytecodes::java_code(code); + Bytecodes::Code code = Bytecodes::java_code(raw_code); // If the code doesn't have any fields there's nothing to print. // note this is ==1 because the tableswitch and lookupswitch are // zero size (for some reason) and we want to print stuff out for them. @@ -354,33 +445,25 @@ case Bytecodes::_putstatic: case Bytecodes::_getstatic: case Bytecodes::_putfield: - case Bytecodes::_getfield: { - int i = get_big_index(); - constantPoolOop constants = method()->constants(); - symbolOop field = constants->name_ref_at(i); - st->print_cr(" %d <%s>", i, field->as_C_string()); - } + case Bytecodes::_getfield: + print_field_or_method(get_big_index(), st); break; case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: - { int i = get_big_index(); - constantPoolOop constants = method()->constants(); - symbolOop name = constants->name_ref_at(i); - symbolOop signature = constants->signature_ref_at(i); - st->print_cr(" %d <%s> <%s> ", i, name->as_C_string(), signature->as_C_string()); - } + print_field_or_method(get_big_index(), st); break; case Bytecodes::_invokeinterface: - { int i = get_big_index(); + if (raw_code == Bytecodes::_invokedynamic) { + int i = get_giant_index(); + print_field_or_method(i, st); + } else { + int i = get_big_index(); int n = get_index(); - get_index(); - constantPoolOop constants = method()->constants(); - symbolOop name = constants->name_ref_at(i); - symbolOop signature = constants->signature_ref_at(i); - st->print_cr(" %d <%s> <%s> %d", i, name->as_C_string(), signature->as_C_string(), n); + get_index(); // ignore zero byte + print_field_or_method(i, st); } break; --- old/src/share/vm/interpreter/bytecodes.cpp 2008-11-08 23:40:23.000000000 -0800 +++ new/src/share/vm/interpreter/bytecodes.cpp 2008-11-08 23:40:23.000000000 -0800 @@ -357,7 +357,7 @@ def(_invokespecial , "invokespecial" , "bjj" , NULL , T_ILLEGAL, -1, true); def(_invokestatic , "invokestatic" , "bjj" , NULL , T_ILLEGAL, 0, true); def(_invokeinterface , "invokeinterface" , "bjj__", NULL , T_ILLEGAL, -1, true); - def(_xxxunusedxxx , "xxxunusedxxx" , NULL , NULL , T_VOID , 0, false); + //_invokedynamic is below def(_new , "new" , "bii" , NULL , T_OBJECT , 1, true ); def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true ); def(_anewarray , "anewarray" , "bii" , NULL , T_OBJECT , 0, true ); @@ -408,6 +408,9 @@ // Faster method invocation. def(_fast_invokevfinal , "fast_invokevfinal" , "bjj" , NULL , T_ILLEGAL, -1, true, _invokevirtual ); + // JSR 292 (this is not an externally visible bytecode) + def(_invokedynamic , "invokedynamic" , "bjjjj", NULL , T_ILLEGAL, -1, true, _invokeinterface ); + def(_fast_linearswitch , "fast_linearswitch" , "" , NULL , T_VOID , -1, false, _lookupswitch ); def(_fast_binaryswitch , "fast_binaryswitch" , "" , NULL , T_VOID , -1, false, _lookupswitch ); --- old/src/share/vm/interpreter/bytecodes.hpp 2008-11-08 23:40:25.000000000 -0800 +++ new/src/share/vm/interpreter/bytecodes.hpp 2008-11-08 23:40:24.000000000 -0800 @@ -218,7 +218,7 @@ _invokespecial = 183, // 0xb7 _invokestatic = 184, // 0xb8 _invokeinterface = 185, // 0xb9 - _xxxunusedxxx = 186, // 0xba + _invokedynamic = 186, // 0xba // if InvokeDynamic _new = 187, // 0xbb _newarray = 188, // 0xbc _anewarray = 189, // 0xbd @@ -305,8 +305,12 @@ // Fetch a bytecode, hiding breakpoints as necessary: + static Code raw_code_at(address bcp, methodOop method = NULL) { + return cast(*bcp); + } static Code code_at(address bcp, methodOop method = NULL) { - Code code = cast(*bcp); return (code != _breakpoint) ? code : non_breakpoint_code_at(bcp, method); + Code code = raw_code_at(bcp); + return (code != _breakpoint) ? code : non_breakpoint_code_at(bcp, method); } static Code java_code_at(address bcp, methodOop method = NULL) { return java_code(code_at(bcp, method)); --- old/src/share/vm/interpreter/interpreterRuntime.cpp 2008-11-08 23:40:26.000000000 -0800 +++ new/src/share/vm/interpreter/interpreterRuntime.cpp 2008-11-08 23:40:26.000000000 -0800 @@ -686,6 +686,131 @@ IRT_END +// First time execution: Resolve symbols, create a permanent DynCallSite object. +IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) { + ResourceMark rm(thread); + + assert(InvokeDynamic, ""); + const Bytecodes::Code bytecode = Bytecodes::_invokedynamic; + + methodHandle caller_method(thread, method(thread)); + + // first determine if there is a bootstrap method + { + KlassHandle caller_klass(thread, caller_method->method_holder()); + Handle bootm = SystemDictionary::find_bootstrap_method(caller_klass, KlassHandle(), CHECK); + if (bootm.is_null()) { + // if there is no bootstrap method, the Dynamic call was really + // an old-fashioned invokeinterface, so we must throw an ICCE + THROW(vmSymbols::java_lang_IncompatibleClassChangeError()); + } + } + + constantPoolHandle pool(thread, caller_method->constants()); + pool->set_invokedynamic(); // mark header to flag active call sites + + int raw_index = four_byte_index(thread); + assert(constantPoolCacheOopDesc::is_secondary_index(raw_index), "invokedynamic indexes marked specially"); + + // there are two CPC entries that are of interest: + int site_index = constantPoolCacheOopDesc::decode_secondary_index(raw_index); + int main_index = pool->cache()->entry_at(site_index)->main_entry_index(); + // here is the CP index of the call site linkage info: + int call_index = pool->cache()->entry_at(main_index)->constant_pool_index(); + + // first resolve the signature to a MH.invoke methodOop + if (!pool->cache()->entry_at(main_index)->is_resolved(bytecode)) { + JvmtiHideSingleStepping jhss(thread); + CallInfo info; + LinkResolver::resolve_invoke(info, Handle(), pool, + raw_index, bytecode, CHECK); + pool->cache()->entry_at(main_index)->set_method( + bytecode, + info.resolved_method(), + info.vtable_index()); + } + + // The method (f2 entry) of the main entry is the MH.invoke for the + // invokedynamic target call signature. The extended invoke method + // has the explicit signature plus a prepended Object argument + // to represent the "Dynamic" receiver. + intptr_t f2_value = pool->cache()->entry_at(main_index)->f2(); + methodHandle mh_invdyn(THREAD, (methodOop) f2_value); + assert(mh_invdyn.not_null() && mh_invdyn->is_method() && mh_invdyn->is_method_handle_invoke(), + "correct result from LinkResolver::resolve_invokedynamic"); + + // Find out more information about the call site. + jlong vmdata = 0; + { + int method_idnum = caller_method->method_idnum(); + int bci = caller_method->bci_from(bcp(thread)); + vmdata = jlong_from(method_idnum, bci); + } + + methodHandle mh_extinv(THREAD, mh_invdyn->extended_invoke_method()); + assert(mh_extinv.not_null(), ""); + + Handle call_site + = SystemDictionary::make_dynamic_call_site(caller_method->method_holder(), + pool->uncached_name_ref_at(call_index), + mh_extinv, vmdata, CHECK); + pool->cache()->entry_at(site_index)->set_dynamic_call(call_site(), 0); +} +IRT_END + + +// Called on first time execution, and also whenever the CallSite.target is null. +// FIXME: Do more of this in Java code. +IRT_ENTRY(void, InterpreterRuntime::bootstrap_invokedynamic(JavaThread* thread, oopDesc* call_site)) { + methodHandle mh_invdyn(thread, (methodOop) java_dyn_impl_DynCallSite::vmref(call_site)); + Handle mh_type(thread, mh_invdyn->method_handle_type()); + objArrayHandle mh_ptypes(thread, java_dyn_MethodType::ptypes(mh_type())); + + // squish the arguments down to a single array + int nargs = mh_ptypes->length(); + objArrayHandle arg_array; + { + objArrayOop aaoop = oopFactory::new_objArray(SystemDictionary::object_klass(), nargs, CHECK); + arg_array = objArrayHandle(thread, aaoop); + } + frame fr = thread->last_frame(); + assert(fr.interpreter_frame_bcp() != NULL, "sanity"); + int tos_offset = 0; + for (int i = nargs; --i >= 0; ) { + intptr_t* slot_addr = fr.interpreter_frame_tos_at(tos_offset++); + oop ptype = mh_ptypes->obj_at(i); + oop arg = NULL; + if (!java_lang_Class::is_primitive(ptype)) { + arg = *(oop*) slot_addr; + } else { + assert(i > 0, "first argument is always an oop"); + BasicType bt = java_lang_Class::primitive_type(ptype); + assert(frame::interpreter_frame_expression_stack_direction() < 0, "else reconsider this code"); + jvalue value; + Interpreter::get_jvalue_in_slot(slot_addr, bt, &value); + tos_offset += type2size[bt]-1; + arg = java_lang_boxing_object::create(bt, &value, CHECK); + // FIXME: These boxing objects are not canonicalized under + // the Java autoboxing rules. They should be... + // The best approach would be to push the arglist creation into Java. + // The JVM should use a lower-level interface to communicate argument lists. + } + arg_array->obj_at_put(i, arg); + } + + // return the argument array by storing it down over the first argument: + assert(nargs > 0, "always a first argument"); + *fr.interpreter_frame_tos_at(tos_offset - 1) = (intptr_t) arg_array(); + + // now find the bootstrap method + oop bootstrap_mh_oop = instanceKlass::cast(fr.interpreter_frame_method()->method_holder())->bootstrap_method(); + assert(bootstrap_mh_oop != NULL, "must already be determined"); + thread->set_vm_result(bootstrap_mh_oop); +} +IRT_END + + + //------------------------------------------------------------------------------------------------------------------------ // Miscellaneous --- old/src/share/vm/interpreter/interpreterRuntime.hpp 2008-11-08 23:40:28.000000000 -0800 +++ new/src/share/vm/interpreter/interpreterRuntime.hpp 2008-11-08 23:40:27.000000000 -0800 @@ -42,8 +42,11 @@ static bool already_resolved(JavaThread *thread) { return cache_entry(thread)->is_resolved(code(thread)); } static int one_byte_index(JavaThread *thread) { return bcp(thread)[1]; } static int two_byte_index(JavaThread *thread) { return Bytes::get_Java_u2(bcp(thread) + 1); } + static int four_byte_index(JavaThread *thread) { return Bytes::get_native_u4(bcp(thread) + 1); } static int number_of_dimensions(JavaThread *thread) { return bcp(thread)[3]; } - static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return method(thread)->constants()->cache()->entry_at(Bytes::get_native_u2(bcp(thread) + 1)); } + + static ConstantPoolCacheEntry* cache_entry_at(JavaThread *thread, int i) { return method(thread)->constants()->cache()->entry_at(i); } + static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return cache_entry_at(thread, Bytes::get_native_u2(bcp(thread) + 1)); } static void note_trap(JavaThread *thread, int reason, TRAPS); public: @@ -83,7 +86,9 @@ static void new_illegal_monitor_state_exception(JavaThread* thread); // Calls - static void resolve_invoke (JavaThread* thread, Bytecodes::Code bytecode); + static void resolve_invoke (JavaThread* thread, Bytecodes::Code bytecode); + static void resolve_invokedynamic(JavaThread* thread); + static void bootstrap_invokedynamic(JavaThread* thread, oopDesc* call_site); // Breakpoints static void _breakpoint(JavaThread* thread, methodOopDesc* method, address bcp); --- old/src/share/vm/interpreter/linkResolver.cpp 2008-11-08 23:40:29.000000000 -0800 +++ new/src/share/vm/interpreter/linkResolver.cpp 2008-11-08 23:40:29.000000000 -0800 @@ -163,6 +163,19 @@ result = methodHandle(THREAD, result_oop); } } + if (InvokeDynamic && + klass() == SystemDictionary::java_dyn_Dynamic_klass()) { + // same as find_method_handle_invoke, except the receiver + // is explicitly prepended as an Object argument + methodOop result_oop = SystemDictionary::find_invokedynamic_invoke(signature, + Handle(), + Handle(), + CHECK); + if (result_oop != NULL) { + assert(result_oop->is_method_handle_invoke(), "consistent"); + result = methodHandle(THREAD, result_oop); + } + } } void LinkResolver::check_method_accessability(KlassHandle ref_klass, @@ -336,6 +349,11 @@ lookup_instance_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK); if (resolved_method.is_null()) { + // JSR 292: see if this is an implicitly generated method Dynamic.*(*...) + lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, CHECK); + } + + if (resolved_method.is_null()) { // lookup method in all the super-interfaces lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); if (resolved_method.is_null()) { @@ -765,6 +783,10 @@ bool check_access, bool check_null_and_abstract, TRAPS) { methodHandle resolved_method; linktime_resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); + if (InvokeDynamic && resolved_method->is_method_handle_invoke()) { + result.set_static(resolved_klass, resolved_method, CHECK); + return; // do not perform runtime checks on implicit method + } runtime_resolve_interface_method(result, resolved_method, resolved_klass, recv, recv_klass, check_null_and_abstract, CHECK); } @@ -947,6 +969,7 @@ case Bytecodes::_invokestatic : resolve_invokestatic (result, pool, index, CHECK); break; case Bytecodes::_invokespecial : resolve_invokespecial (result, pool, index, CHECK); break; case Bytecodes::_invokevirtual : resolve_invokevirtual (result, recv, pool, index, CHECK); break; + case Bytecodes::_invokedynamic : resolve_invokedynamic (result, pool, index, CHECK); break; case Bytecodes::_invokeinterface: resolve_invokeinterface(result, recv, pool, index, CHECK); break; } return; @@ -1008,6 +1031,18 @@ resolve_interface_call(result, recv, recvrKlass, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); } + +void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { + assert(constantPoolCacheOopDesc::is_secondary_index(index), "must be secondary index"); + KlassHandle resolved_klass; + symbolHandle method_name; + symbolHandle method_signature; + KlassHandle current_klass; + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); + assert(resolved_klass() == SystemDictionary::java_dyn_Dynamic_klass(), "else can't get here"); + resolve_interface_call(result, Handle(), KlassHandle(), resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); +} + //------------------------------------------------------------------------------------------------------------------------ #ifndef PRODUCT --- old/src/share/vm/interpreter/linkResolver.hpp 2008-11-08 23:40:31.000000000 -0800 +++ new/src/share/vm/interpreter/linkResolver.hpp 2008-11-08 23:40:31.000000000 -0800 @@ -167,6 +167,7 @@ static void resolve_invokespecial (CallInfo& result, constantPoolHandle pool, int index, TRAPS); static void resolve_invokevirtual (CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS); static void resolve_invokeinterface(CallInfo& result, Handle recv, constantPoolHandle pool, int index, TRAPS); + static void resolve_invokedynamic (CallInfo& result, constantPoolHandle pool, int index, TRAPS); static void resolve_invoke (CallInfo& result, Handle recv, constantPoolHandle pool, int index, Bytecodes::Code byte, TRAPS); }; --- old/src/share/vm/interpreter/rewriter.cpp 2008-11-08 23:40:32.000000000 -0800 +++ new/src/share/vm/interpreter/rewriter.cpp 2008-11-08 23:40:32.000000000 -0800 @@ -25,34 +25,57 @@ # include "incls/_precompiled.incl" # include "incls/_rewriter.cpp.incl" - -// Computes an index_map (new_index -> original_index) for contant pool entries +// Computes a CPC map (new_index -> original_index) for constant pool entries // that are referred to by the interpreter at runtime via the constant pool cache. -void Rewriter::compute_index_maps(constantPoolHandle pool, intArray*& index_map, intStack*& inverse_index_map) { - const int length = pool->length(); - index_map = new intArray(length, -1); - // Choose an initial value large enough that we don't get frequent - // calls to grow(). - inverse_index_map = new intStack(length / 2); +// Also computes a CP map (original_index -> new_index). +// Marks entries in CP which require additional processing. +void Rewriter::compute_index_maps() { + const int length = _pool->length(); + init_cp_map(length); for (int i = 0; i < length; i++) { - switch (pool->tag_at(i).value()) { + int tag = _pool->tag_at(i).value(); + switch (tag) { + case JVM_CONSTANT_InterfaceMethodref: + if (InvokeDynamic) { + int k_index = _pool->klass_ref_index_at(i); + symbolOop k_name = _pool->klass_name_at(k_index); + if (k_name == vmSymbols::java_dyn_Dynamic()) { + // all calls of the form Dynamic.*(...) + add_cp_cache_entry(i, true); + // more cpc entries added later, one per call site + break; + } + } + // else fall through... case JVM_CONSTANT_Fieldref : // fall through case JVM_CONSTANT_Methodref : // fall through - case JVM_CONSTANT_InterfaceMethodref: { - index_map->at_put(i, inverse_index_map->length()); - inverse_index_map->append(i); - } + add_cp_cache_entry(i); + break; } } + + guarantee((int)_cp_cache_map.length()-1 <= (int)((u2)-1), + "all cp cache indexes fit in a u2"); } -// Creates a constant pool cache given an inverse_index_map -constantPoolCacheHandle Rewriter::new_constant_pool_cache(intArray& inverse_index_map, TRAPS) { - const int length = inverse_index_map.length(); - constantPoolCacheOop cache = oopFactory::new_constantPoolCache(length, CHECK_(constantPoolCacheHandle())); - cache->initialize(inverse_index_map); - return constantPoolCacheHandle(THREAD, cache); +int Rewriter::add_extra_cp_cache_entry(int main_entry) { + // Hack: We put it on the map as an encoded value. + // The only place that consumes this is ConstantPoolCacheEntry::set_initial_state + int encoded = constantPoolCacheOopDesc::encode_secondary_index(main_entry); + int plain_secondary_index = _cp_cache_map.append(encoded); + return constantPoolCacheOopDesc::encode_secondary_index(plain_secondary_index); +} + + + +// Creates a constant pool cache given a CPC map +void Rewriter::make_constant_pool_cache(TRAPS) { + const int length = _cp_cache_map.length(); + constantPoolCacheOop cache = oopFactory::new_constantPoolCache(length, CHECK); + cache->initialize(_cp_cache_map); + _pool->set_cache(cache); + cache->set_constant_pool(_pool()); } @@ -96,8 +119,40 @@ } +// Rewrite a classfile-order CP index into a native-order CPC index. +int Rewriter::rewrite_member_reference(address bcp, int offset) { + address p = bcp + offset; + int cp_index = Bytes::get_Java_u2(p); + int cache_index = cp_entry_to_cp_cache(cp_index); + Bytes::put_native_u2(p, cache_index); + return cp_index; +} + + +void Rewriter::rewrite_invokedynamic(address bcp, int offset, int cp_index) { + address p = bcp + offset; + assert(InvokeDynamic, ""); + assert(p[-1] == Bytecodes::_invokeinterface, ""); + p[-1] = Bytecodes::_invokedynamic; + int cpc = cp_entry_to_cp_cache(cp_index); + int cpc2 = add_extra_cp_cache_entry(cpc); + assert(Bytes::get_native_u2(p) == cpc, "already patched"); + + // Replace the trailing four bytes with a CPC index for the dynamic + // call site. Unlike other CPC entries, there is one per bytecode, + // not just one per distinct CP entry. In other words, the + // CPC-to-CP relation is many-to-one for invokedynamic entries. + // This means we must use a larger index size than u2 to address + // all these entries. That is the main reason invokedynamic + // must have a five-byte instruction format. (Of course, other JVM + // implementations can use the bytes for other purposes.) + Bytes::put_native_u4(p, cpc2); + // Note: We use native_u4 format exclusively for 4-byte indexes. +} + + // Rewrites a method given the index_map information -methodHandle Rewriter::rewrite_method(methodHandle method, intArray& index_map, TRAPS) { +void Rewriter::scan_method(methodOop method) { int nof_jsrs = 0; bool has_monitor_bytecodes = false; @@ -114,8 +169,10 @@ const int code_length = method->code_size(); int bc_length; + int cp_index; for (int bci = 0; bci < code_length; bci += bc_length) { address bcp = code_base + bci; + int prefix_length = 0; c = (Bytecodes::Code)(*bcp); // Since we have the code, see if we can get the length @@ -130,6 +187,7 @@ // by 'wide'. We don't currently examine any of the bytecodes // modified by wide, but in case we do in the future... if (c == Bytecodes::_wide) { + prefix_length = 1; c = (Bytecodes::Code)bcp[1]; } } @@ -152,14 +210,20 @@ case Bytecodes::_putstatic : // fall through case Bytecodes::_getfield : // fall through case Bytecodes::_putfield : // fall through - case Bytecodes::_invokevirtual : // fall through case Bytecodes::_invokespecial : // fall through - case Bytecodes::_invokestatic : // fall through - case Bytecodes::_invokeinterface: { - address p = bcp + 1; - Bytes::put_native_u2(p, index_map[Bytes::get_Java_u2(p)]); + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokestatic : + rewrite_member_reference(bcp, prefix_length+1); + // Note: cp_entry_is_extended() may be true for invokespecial. + // The interpreter must watch for this corner case, + // and support it as usual. + break; + + case Bytecodes::_invokeinterface: + cp_index = rewrite_member_reference(bcp, prefix_length+1); + if (cp_entry_is_extended(cp_index)) + rewrite_invokedynamic(bcp, prefix_length+1, cp_index); break; - } case Bytecodes::_jsr : // fall through case Bytecodes::_jsr_w : nof_jsrs++; break; case Bytecodes::_monitorenter : // fall through @@ -177,53 +241,56 @@ // have to be rewritten, so we run the oopMapGenerator on the method if (nof_jsrs > 0) { method->set_has_jsrs(); - ResolveOopMapConflicts romc(method); - methodHandle original_method = method; - method = romc.do_potential_rewrite(CHECK_(methodHandle())); - if (method() != original_method()) { - // Insert invalid bytecode into original methodOop and set - // interpreter entrypoint, so that a executing this method - // will manifest itself in an easy recognizable form. - address bcp = original_method->bcp_from(0); - *bcp = (u1)Bytecodes::_shouldnotreachhere; - int kind = Interpreter::method_kind(original_method); - original_method->set_interpreter_kind(kind); - } + // Second pass will revisit this method. + assert(method->has_jsrs(), ""); + } +} - // Update monitor matching info. - if (romc.monitor_safe()) { - method->set_guaranteed_monitor_matching(); - } +// After constant pool is created, revisit methods containing jsrs. +methodHandle Rewriter::rewrite_jsrs(methodHandle method, TRAPS) { + ResolveOopMapConflicts romc(method); + methodHandle original_method = method; + method = romc.do_potential_rewrite(CHECK_(methodHandle())); + if (method() != original_method()) { + // Insert invalid bytecode into original methodOop and set + // interpreter entrypoint, so that a executing this method + // will manifest itself in an easy recognizable form. + address bcp = original_method->bcp_from(0); + *bcp = (u1)Bytecodes::_shouldnotreachhere; + int kind = Interpreter::method_kind(original_method); + original_method->set_interpreter_kind(kind); } - // Setup method entrypoints for compiler and interpreter - method->link_method(method, CHECK_(methodHandle())); + // Update monitor matching info. + if (romc.monitor_safe()) { + method->set_guaranteed_monitor_matching(); + } return method; } void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) { - // gather starting points ResourceMark rm(THREAD); - constantPoolHandle pool (THREAD, klass->constants()); - objArrayHandle methods (THREAD, klass->methods()); - assert(pool->cache() == NULL, "constant pool cache must not be set yet"); + Rewriter rw(klass, CHECK); + // (That's all, folks.) +} + +Rewriter::Rewriter(instanceKlassHandle klass, TRAPS) + : _klass(klass), + // gather starting points + _pool( THREAD, klass->constants()), + _methods(THREAD, klass->methods()) +{ + assert(_pool->cache() == NULL, "constant pool cache must not be set yet"); // determine index maps for methodOop rewriting - intArray* index_map = NULL; - intStack* inverse_index_map = NULL; - compute_index_maps(pool, index_map, inverse_index_map); - - // allocate constant pool cache - constantPoolCacheHandle cache = new_constant_pool_cache(*inverse_index_map, CHECK); - pool->set_cache(cache()); - cache->set_constant_pool(pool()); + compute_index_maps(); - if (RegisterFinalizersAtInit && klass->name() == vmSymbols::java_lang_Object()) { - int i = methods->length(); + if (RegisterFinalizersAtInit && _klass->name() == vmSymbols::java_lang_Object()) { + int i = _methods->length(); while (i-- > 0) { - methodOop method = (methodOop)methods->obj_at(i); + methodOop method = (methodOop)_methods->obj_at(i); if (method->intrinsic_id() == vmIntrinsics::_Object_init) { // rewrite the return bytecodes of Object. to register the // object for finalization if needed. @@ -234,13 +301,27 @@ } } - // rewrite methods - { int i = methods->length(); - while (i-- > 0) { - methodHandle m(THREAD, (methodOop)methods->obj_at(i)); - m = rewrite_method(m, *index_map, CHECK); + // rewrite methods, in two passes + int i, len = _methods->length(); + + for (i = len; --i >= 0; ) { + methodOop method = (methodOop)_methods->obj_at(i); + scan_method(method); + } + + // allocate constant pool cache, now that we've seen all the bytecodes + make_constant_pool_cache(CHECK); + + for (i = len; --i >= 0; ) { + methodHandle m(THREAD, (methodOop)_methods->obj_at(i)); + + if (m->has_jsrs()) { + m = rewrite_jsrs(m, CHECK); // Method might have gotten rewritten. - methods->obj_at_put(i, m()); + _methods->obj_at_put(i, m()); } + + // Set up method entry points for compiler and interpreter. + m->link_method(m, CHECK); } } --- old/src/share/vm/interpreter/rewriter.hpp 2008-11-08 23:40:34.000000000 -0800 +++ new/src/share/vm/interpreter/rewriter.hpp 2008-11-08 23:40:34.000000000 -0800 @@ -25,13 +25,44 @@ // The Rewriter adds caches to the constant pool and rewrites bytecode indices // pointing into the constant pool for better interpreter performance. -class Rewriter: public AllStatic { +class Rewriter: public StackObj { private: - static void compute_index_maps(constantPoolHandle pool, intArray*& index_map, intStack*& inverse_index_map); - static constantPoolCacheHandle new_constant_pool_cache(intArray& inverse_index_map, TRAPS); - static methodHandle rewrite_method(methodHandle method, intArray& index_map, TRAPS); - static void rewrite_Object_init(methodHandle method, TRAPS); + instanceKlassHandle _klass; + constantPoolHandle _pool; + objArrayHandle _methods; + intArray _cp_map; + intStack _cp_cache_map; + + void init_cp_map(int length) { + _cp_map.initialize(length, -1); + // Choose an initial value large enough that we don't get frequent + // calls to grow(). + _cp_cache_map.initialize(length / 2); + } + int cp_entry_to_cp_cache(int i) { return _cp_map[i] >> 1; } + bool cp_entry_is_extended(int i) { return (_cp_map[i] & 1) != 0; } + int add_cp_cache_entry(int cp_index, bool is_extended = false) { + assert(_cp_map[cp_index] == -1, "not twice on same cp_index"); + int cache_index = _cp_cache_map.append(cp_index); + _cp_map.at_put(cp_index, (cache_index << 1) | (is_extended ? 1 : 0)); + assert(cp_entry_to_cp_cache(cp_index) == cache_index, ""); + assert(cp_entry_is_extended(cp_index) == is_extended, ""); + return cache_index; + } + int add_extra_cp_cache_entry(int main_entry); + + // All the work goes in here: + Rewriter(instanceKlassHandle klass, TRAPS); + + void compute_index_maps(); + void make_constant_pool_cache(TRAPS); + void scan_method(methodOop m); + methodHandle rewrite_jsrs(methodHandle m, TRAPS); + void rewrite_Object_init(methodHandle m, TRAPS); + int rewrite_member_reference(address bcp, int offset); + void rewrite_invokedynamic(address bcp, int offset, int cp_index); public: + // Driver routine: static void rewrite(instanceKlassHandle klass, TRAPS); }; --- old/src/share/vm/interpreter/templateInterpreter.cpp 2008-11-08 23:40:35.000000000 -0800 +++ new/src/share/vm/interpreter/templateInterpreter.cpp 2008-11-08 23:40:35.000000000 -0800 @@ -178,12 +178,14 @@ #endif // !PRODUCT EntryPoint TemplateInterpreter::_return_entry[TemplateInterpreter::number_of_return_entries]; EntryPoint TemplateInterpreter::_earlyret_entry; +EntryPoint TemplateInterpreter::_return_unbox_entry; EntryPoint TemplateInterpreter::_deopt_entry [TemplateInterpreter::number_of_deopt_entries ]; EntryPoint TemplateInterpreter::_continuation_entry; EntryPoint TemplateInterpreter::_safept_entry; address TemplateInterpreter::_return_3_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; address TemplateInterpreter::_return_5_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; +address TemplateInterpreter::_return_5_unbox_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; DispatchTable TemplateInterpreter::_active_table; DispatchTable TemplateInterpreter::_normal_table; @@ -251,6 +253,22 @@ } } + if (InvokeDynamic) { + CodeletMark cm(_masm, "unboxing return entry points"); + Interpreter::_return_unbox_entry = + EntryPoint( + generate_return_unbox_entry_for(btos, 5), + generate_return_unbox_entry_for(ctos, 5), + generate_return_unbox_entry_for(stos, 5), + generate_return_unbox_entry_for(atos, 5), // cast conversion + generate_return_unbox_entry_for(itos, 5), + generate_return_unbox_entry_for(ltos, 5), + generate_return_unbox_entry_for(ftos, 5), + generate_return_unbox_entry_for(dtos, 5), + Interpreter::_return_entry[5].entry(vtos) // no unboxing for void + ); + } + { CodeletMark cm(_masm, "earlyret entry points"); Interpreter::_earlyret_entry = EntryPoint( @@ -298,8 +316,11 @@ for (int j = 0; j < number_of_states; j++) { const TosState states[] = {btos, ctos, stos, itos, ltos, ftos, dtos, atos, vtos}; - Interpreter::_return_3_addrs_by_index[Interpreter::TosState_as_index(states[j])] = Interpreter::return_entry(states[j], 3); - Interpreter::_return_5_addrs_by_index[Interpreter::TosState_as_index(states[j])] = Interpreter::return_entry(states[j], 5); + int index = Interpreter::TosState_as_index(states[j]); + Interpreter::_return_3_addrs_by_index[index] = Interpreter::return_entry(states[j], 3); + Interpreter::_return_5_addrs_by_index[index] = Interpreter::return_entry(states[j], 5); + if (InvokeDynamic) + Interpreter::_return_5_unbox_addrs_by_index[index] = Interpreter::return_unbox_entry(states[j], 5); } { CodeletMark cm(_masm, "continuation entry points"); @@ -526,6 +547,18 @@ } +address TemplateInterpreter::return_unbox_entry(TosState state, int length) { + assert(InvokeDynamic, ""); + if (state == vtos) { + // no unboxing to do, actually + return return_entry(state, length); + } else { + assert(length == 5, "unboxing entries generated for invokedynamic only"); + return _return_unbox_entry.entry(state); + } +} + + address TemplateInterpreter::deopt_entry(TosState state, int length) { guarantee(0 <= length && length < Interpreter::number_of_deopt_entries, "illegal length"); return _deopt_entry[length].entry(state); --- old/src/share/vm/interpreter/templateInterpreter.hpp 2008-11-08 23:40:37.000000000 -0800 +++ new/src/share/vm/interpreter/templateInterpreter.hpp 2008-11-08 23:40:37.000000000 -0800 @@ -83,9 +83,9 @@ public: enum MoreConstants { - number_of_return_entries = 9, // number of return entry points - number_of_deopt_entries = 9, // number of deoptimization entry points - number_of_return_addrs = 9 // number of return addresses + number_of_return_entries = number_of_states, // number of return entry points + number_of_deopt_entries = number_of_states, // number of deoptimization entry points + number_of_return_addrs = number_of_states // number of return addresses }; protected: @@ -110,12 +110,14 @@ #endif // !PRODUCT static EntryPoint _return_entry[number_of_return_entries]; // entry points to return to from a call static EntryPoint _earlyret_entry; // entry point to return early from a call + static EntryPoint _return_unbox_entry; // entry point to unbox a return value from a call static EntryPoint _deopt_entry[number_of_deopt_entries]; // entry points to return to from a deoptimization static EntryPoint _continuation_entry; static EntryPoint _safept_entry; static address _return_3_addrs_by_index[number_of_return_addrs]; // for invokevirtual return entries static address _return_5_addrs_by_index[number_of_return_addrs]; // for invokeinterface return entries + static address _return_5_unbox_addrs_by_index[number_of_return_addrs]; // for invokedynamic bootstrap methods static DispatchTable _active_table; // the active dispatch table (used by the interpreter for dispatch) static DispatchTable _normal_table; // the normal dispatch table (used to set the active table in normal mode) @@ -156,10 +158,12 @@ // Support for invokes static address* return_3_addrs_by_index_table() { return _return_3_addrs_by_index; } static address* return_5_addrs_by_index_table() { return _return_5_addrs_by_index; } + static address* return_5_unbox_addrs_by_index_table() { return _return_5_unbox_addrs_by_index; } static int TosState_as_index(TosState state); // computes index into return_3_entry_by_index table static address return_entry (TosState state, int length); static address deopt_entry (TosState state, int length); + static address return_unbox_entry(TosState state, int length); // Safepoint support static void notice_safepoints(); // stops the thread when reaching a safepoint --- old/src/share/vm/interpreter/templateInterpreterGenerator.hpp 2008-11-08 23:40:38.000000000 -0800 +++ new/src/share/vm/interpreter/templateInterpreterGenerator.hpp 2008-11-08 23:40:38.000000000 -0800 @@ -51,7 +51,10 @@ address generate_WrongMethodType_handler(); address generate_ArrayIndexOutOfBounds_handler(const char* name); address generate_continuation_for(TosState state); - address generate_return_entry_for(TosState state, int step); + address generate_return_entry_for(TosState state, int step, bool unbox = false); + address generate_return_unbox_entry_for(TosState state, int step) { + return generate_return_entry_for(state, step, true); + } address generate_earlyret_entry_for(TosState state); address generate_deopt_entry_for(TosState state, int step); address generate_safept_entry_for(TosState state, address runtime_entry); --- old/src/share/vm/interpreter/templateTable.cpp 2008-11-08 23:40:40.000000000 -0800 +++ new/src/share/vm/interpreter/templateTable.cpp 2008-11-08 23:40:40.000000000 -0800 @@ -503,6 +503,8 @@ def(Bytecodes::_fast_invokevfinal , ubcp|disp|clvm|____, vtos, vtos, fast_invokevfinal , 2 ); + // JSR 292 (this is not an externally visible bytecode) + def(Bytecodes::_invokedynamic , ubcp|disp|clvm|____, vtos, vtos, invokedynamic , 1 ); def(Bytecodes::_fast_linearswitch , ubcp|disp|____|____, itos, vtos, fast_linearswitch , _ ); def(Bytecodes::_fast_binaryswitch , ubcp|disp|____|____, itos, vtos, fast_binaryswitch , _ ); --- old/src/share/vm/interpreter/templateTable.hpp 2008-11-08 23:40:41.000000000 -0800 +++ new/src/share/vm/interpreter/templateTable.hpp 2008-11-08 23:40:41.000000000 -0800 @@ -261,6 +261,7 @@ static void invokespecial(int byte_no); static void invokestatic(int byte_no); static void invokeinterface(int byte_no); + static void invokedynamic(int byte_no); static void fast_invokevfinal(int byte_no); static void getfield_or_static(int byte_no, bool is_static); --- old/src/share/vm/oops/constantPoolKlass.cpp 2008-11-08 23:40:43.000000000 -0800 +++ new/src/share/vm/oops/constantPoolKlass.cpp 2008-11-08 23:40:43.000000000 -0800 @@ -304,6 +304,7 @@ if (cp->flags() != 0) { st->print(" - flags : 0x%x", cp->flags()); if (cp->has_pseudo_string()) st->print(" has_pseudo_string"); + if (cp->has_invokedynamic()) st->print(" has_invokedynamic"); st->cr(); } --- old/src/share/vm/oops/constantPoolOop.cpp 2008-11-08 23:40:45.000000000 -0800 +++ new/src/share/vm/oops/constantPoolOop.cpp 2008-11-08 23:40:44.000000000 -0800 @@ -275,6 +275,18 @@ } + +int constantPoolOopDesc::map_instruction_operand_to_index(int operand) { + if (constantPoolCacheOopDesc::is_secondary_index(operand)) { + assert(InvokeDynamic, "rewriter only produces four-byte indexes for invokedynamic"); + return cache()->main_entry_at(operand)->constant_pool_index(); + } + assert((int)(u2)operand == operand, "clean u2"); + int index = Bytes::swap_u2(operand); + return cache()->entry_at(index)->constant_pool_index(); +} + + void constantPoolOopDesc::verify_constant_pool_resolve(constantPoolHandle this_oop, KlassHandle k, TRAPS) { if (k->oop_is_instance() || k->oop_is_objArray()) { instanceKlassHandle holder (THREAD, this_oop->pool_holder()); --- old/src/share/vm/oops/constantPoolOop.hpp 2008-11-08 23:40:46.000000000 -0800 +++ new/src/share/vm/oops/constantPoolOop.hpp 2008-11-08 23:40:46.000000000 -0800 @@ -51,6 +51,7 @@ void release_tag_at_put(int which, jbyte t) { tags()->release_byte_at_put(which, t); } enum FlagBit { + FB_has_invokedynamic = 1, FB_has_pseudo_string = 2 }; @@ -94,7 +95,9 @@ typeArrayOop tags() const { return _tags; } bool has_pseudo_string() const { return flag_at(FB_has_pseudo_string); } + bool has_invokedynamic() const { return flag_at(FB_has_invokedynamic); } void set_pseudo_string() { set_flag_at(FB_has_pseudo_string); } + void set_invokedynamic() { set_flag_at(FB_has_invokedynamic); } // Klass holding pool klassOop pool_holder() const { return _pool_holder; } @@ -408,17 +411,17 @@ // byte order (which comes from the bytecodes after rewriting) or, // if "uncached" is true, a vanilla constant pool index jint field_or_method_at(int which, bool uncached) { - int i = -1; - if (uncached || cache() == NULL) { - i = which; - } else { + int i = which; + if (!uncached && cache() != NULL) { // change byte-ordering and go via cache - i = cache()->entry_at(Bytes::swap_u2(which))->constant_pool_index(); + i = map_instruction_operand_to_index(which); } assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); return *int_at_addr(i); } + int map_instruction_operand_to_index(int operand); + // Used while constructing constant pool (only by ClassFileParser) jint klass_index_at(int which) { assert(tag_at(which).is_klass_index(), "Corrupted constant pool"); --- old/src/share/vm/oops/cpCacheKlass.cpp 2008-11-08 23:40:48.000000000 -0800 +++ new/src/share/vm/oops/cpCacheKlass.cpp 2008-11-08 23:40:47.000000000 -0800 @@ -135,11 +135,47 @@ void constantPoolCacheKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constantPoolCache(), "should be constant pool"); + if (InvokeDynamic) { + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // during a scavenge, it is safe to inspect my pool, since it is perm + constantPoolOop pool = cache->constant_pool(); + assert(pool->is_constantPool(), "should be constant pool"); + if (pool->has_invokedynamic()) { + for (int i = 0; i < cache->length(); i++) { + ConstantPoolCacheEntry* e = cache->entry_at(i); + oop* p = (oop*)&e->_f1; + if (e->is_secondary_entry()) { + if (PSScavenge::should_scavenge(p)) + pm->claim_or_forward_breadth(p); + assert(!(e->is_vfinal() && PSScavenge::should_scavenge((oop*)&e->_f2)), + "no live oops here"); + } + } + } + } } void constantPoolCacheKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constantPoolCache(), "should be constant pool"); + if (InvokeDynamic) { + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + // during a scavenge, it is safe to inspect my pool, since it is perm + constantPoolOop pool = cache->constant_pool(); + assert(pool->is_constantPool(), "should be constant pool"); + if (pool->has_invokedynamic()) { + for (int i = 0; i < cache->length(); i++) { + ConstantPoolCacheEntry* e = cache->entry_at(i); + oop* p = (oop*)&e->_f1; + if (e->is_secondary_entry()) { + if (PSScavenge::should_scavenge(p)) + pm->claim_or_forward_depth(p); + assert(!(e->is_vfinal() && PSScavenge::should_scavenge((oop*)&e->_f2)), + "no live oops here"); + } + } + } + } } int --- old/src/share/vm/oops/cpCacheOop.cpp 2008-11-08 23:40:49.000000000 -0800 +++ new/src/share/vm/oops/cpCacheOop.cpp 2008-11-08 23:40:49.000000000 -0800 @@ -29,8 +29,18 @@ // Implememtation of ConstantPoolCacheEntry void ConstantPoolCacheEntry::set_initial_state(int index) { - assert(0 <= index && index < 0x10000, "sanity check"); + if (constantPoolCacheOopDesc::is_secondary_index(index)) { + // Hack: The rewriter is trying to say that this entry itself + // will be a secondary entry. + int main_index = constantPoolCacheOopDesc::decode_secondary_index(index); + assert(0 <= main_index && main_index < 0x10000, "sanity check"); + _indices = (main_index << 16); + assert(main_entry_index() == main_index, ""); + return; + } + assert(0 < index && index < 0x10000, "sanity check"); _indices = index; + assert(constant_pool_index() == index, ""); } @@ -136,6 +146,7 @@ int byte_no = -1; bool needs_vfinal_flag = false; switch (invoke_code) { + case Bytecodes::_invokedynamic: case Bytecodes::_invokevirtual: case Bytecodes::_invokeinterface: { if (method->can_be_statically_bound()) { @@ -211,6 +222,23 @@ } +void ConstantPoolCacheEntry::set_dynamic_call(Handle call_site, int index) { + methodOop method = (methodOop) java_dyn_impl_DynCallSite::vmref(call_site()); + assert(method->is_method(), "must be initialized properly"); + int param_size = method->size_of_parameters(); + assert(param_size > 1, "method argument size must include MH.this & initial dynamic receiver"); + param_size -= 1; // do not count MH.this; it is not stacked for invokedynamic + if (Atomic::cmpxchg_ptr(call_site(), &_f1, NULL) == NULL) { + // racing threads might be trying to install their own favorites + set_f1(call_site()); + } + set_f2(index); + set_flags(as_flags(as_TosState(method->result_type()), method->is_final_method(), false, false, false, true) | param_size); + // do not do set_bytecode on a secondary CP cache entry + //set_bytecode_1(Bytecodes::_invokedynamic); +} + + class LocalOopClosure: public OopClosure { private: void (*_f)(oop*); @@ -392,7 +420,11 @@ // print separator if (index == 0) tty->print_cr(" -------------"); // print entry - tty->print_cr("%3d (%08x) [%02x|%02x|%5d]", index, this, bytecode_2(), bytecode_1(), constant_pool_index()); + tty->print_cr("%3d (%08x) ", index, this); + if (is_secondary_entry()) + tty->print_cr("[%5d|secondary]", main_entry_index()); + else + tty->print_cr("[%02x|%02x|%5d]", bytecode_2(), bytecode_1(), constant_pool_index()); tty->print_cr(" [ %08x]", (address)(oop)_f1); tty->print_cr(" [ %08x]", _f2); tty->print_cr(" [ %08x]", _flags); --- old/src/share/vm/oops/cpCacheOop.hpp 2008-11-08 23:40:51.000000000 -0800 +++ new/src/share/vm/oops/cpCacheOop.hpp 2008-11-08 23:40:51.000000000 -0800 @@ -89,6 +89,8 @@ // _f1 = method for all but virtual calls, unused by virtual calls // (note: for interface calls, which are essentially virtual, // contains klassOop for the corresponding interface. +// for invokedynamic (a variant of invokeiterface), f1 contains +// the CallSite object for the invocation // _f2 = method/vtable index for virtual calls only, unused by all other // calls. The vf flag indicates this is a method pointer not an // index. @@ -108,6 +110,8 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { friend class VMStructs; + friend class constantPoolCacheKlass; + private: volatile intx _indices; // constant pool index & rewrite bytecodes volatile oop _f1; // entry specific oop field @@ -175,6 +179,11 @@ int index // Method index into interface ); + void set_dynamic_call( + Handle call_site, // Resolved java.dyn.CallSite (f1) + int index // index (unused f2) + ); + void set_parameter_size(int value) { assert(parameter_size() == 0 || parameter_size() == value, "size must not change"); @@ -216,7 +225,11 @@ } // Accessors - int constant_pool_index() const { return _indices & 0xFFFF; } + bool is_secondary_entry() const { return (_indices & 0xFFFF) == 0; } + int constant_pool_index() const { assert((_indices & 0xFFFF) != 0, "must be main entry"); + return (_indices & 0xFFFF); } + int main_entry_index() const { assert((_indices & 0xFFFF) == 0, "must be secondary entry"); + return ((uintx)_indices >> 16); } Bytecodes::Code bytecode_1() const { return Bytecodes::cast((_indices >> 16) & 0xFF); } Bytecodes::Code bytecode_2() const { return Bytecodes::cast((_indices >> 24) & 0xFF); } volatile oop f1() const { return _f1; } @@ -311,10 +324,30 @@ // Initialization void initialize(intArray& inverse_index_map); + // Secondary indexes. + // They must look completely different from normal indexes. + // The main reason is that byte swapping is sometimes done on normal indexes. + // Also, it is helpful for debugging to tell the two apart. + static bool is_secondary_index(int i) { return (i < 0); } + static int decode_secondary_index(int i) { assert(is_secondary_index(i), ""); return ~i; } + static int encode_secondary_index(int i) { assert(!is_secondary_index(i), ""); return ~i; } + // Accessors void set_constant_pool(constantPoolOop pool) { oop_store_without_check((oop*)&_constant_pool, (oop)pool); } constantPoolOop constant_pool() const { return _constant_pool; } ConstantPoolCacheEntry* entry_at(int i) const { assert(0 <= i && i < length(), "index out of bounds"); return base() + i; } + ConstantPoolCacheEntry* main_entry_at(int i) const { + ConstantPoolCacheEntry* e; + if (is_secondary_index(i)) { + // run through an extra level of indirection: + i = decode_secondary_index(i); + e = entry_at(i); + i = e->main_entry_index(); + } + e = entry_at(i); + assert(!e->is_secondary_entry(), "only one level of indirection"); + return e; + } // Code generation static ByteSize base_offset() { return in_ByteSize(sizeof(constantPoolCacheOopDesc)); } --- old/src/share/vm/oops/generateOopMap.cpp 2008-11-08 23:40:52.000000000 -0800 +++ new/src/share/vm/oops/generateOopMap.cpp 2008-11-08 23:40:52.000000000 -0800 @@ -1253,7 +1253,7 @@ case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: case Bytecodes::_invokeinterface: - int idx = currentBC->get_index_big(); + int idx = currentBC->get_index_int(); constantPoolOop cp = method()->constants(); int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); @@ -1284,7 +1284,7 @@ case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: case Bytecodes::_invokeinterface: - int idx = currentBC->get_index_big(); + int idx = currentBC->get_index_int(); constantPoolOop cp = method()->constants(); int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); @@ -1556,7 +1556,7 @@ case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: do_method(false, false, itr->get_index_big(), itr->bci()); break; case Bytecodes::_invokestatic: do_method(true, false, itr->get_index_big(), itr->bci()); break; - case Bytecodes::_invokeinterface: do_method(false, true, itr->get_index_big(), itr->bci()); break; + case Bytecodes::_invokeinterface: do_method(false, true, itr->get_index_int(), itr->bci()); break; case Bytecodes::_newarray: case Bytecodes::_anewarray: pp_new_ref(vCTS, itr->bci()); break; case Bytecodes::_checkcast: do_checkcast(); break; --- old/src/share/vm/oops/instanceKlass.cpp 2008-11-08 23:40:54.000000000 -0800 +++ new/src/share/vm/oops/instanceKlass.cpp 2008-11-08 23:40:54.000000000 -0800 @@ -1996,9 +1996,11 @@ // Printing +#define BULLET " - " + void FieldPrinter::do_field(fieldDescriptor* fd) { - if (fd->is_static() == (_obj == NULL)) { - _st->print(" - "); + _st->print(BULLET); + if (fd->is_static() || (_obj == NULL)) { fd->print_on(_st); _st->cr(); } else { @@ -2019,7 +2021,7 @@ value->is_typeArray() && offset <= (juint) value->length() && offset + length <= (juint) value->length()) { - st->print("string: "); + st->print(BULLET"string: "); Handle h_obj(obj); java_lang_String::print(h_obj, st); st->cr(); @@ -2027,22 +2029,22 @@ } } - st->print_cr("fields:"); + st->print_cr(BULLET"---- fields (total size %d words):", oop_size(obj)); FieldPrinter print_nonstatic_field(st, obj); do_nonstatic_fields(&print_nonstatic_field); if (as_klassOop() == SystemDictionary::class_klass()) { klassOop mirrored_klass = java_lang_Class::as_klassOop(obj); - st->print(" - fake entry for mirror: "); + st->print(BULLET"fake entry for mirror: "); mirrored_klass->print_value_on(st); st->cr(); - st->print(" - fake entry resolved_constructor: "); + st->print(BULLET"fake entry resolved_constructor: "); methodOop ctor = java_lang_Class::resolved_constructor(obj); ctor->print_value_on(st); klassOop array_klass = java_lang_Class::array_klass(obj); - st->print(" - fake entry for array: "); - array_klass->print_value_on(st); st->cr(); + st->print(BULLET"fake entry for array: "); + array_klass->print_value_on(st); st->cr(); } } @@ -2051,6 +2053,28 @@ st->print("a "); name()->print_value_on(st); obj->print_address_on(st); + if (as_klassOop() == SystemDictionary::string_klass() + && java_lang_String::value(obj) != NULL) { + ResourceMark rm; + int len = java_lang_String::length(obj); + int plen = (len < 24 ? len : 12); + char* str = java_lang_String::as_utf8_string(obj, 0, plen); + st->print(" = \"%s\"", str); + if (len > plen) + st->print("...[%d]", len); + } else if (as_klassOop() == SystemDictionary::class_klass()) { + klassOop k = java_lang_Class::as_klassOop(obj); + st->print(" = "); + if (k != NULL) { + k->print_value_on(st); + } else { + const char* tname = type2name(java_lang_Class::primitive_type(obj)); + st->print("%s", tname ? tname : "type?"); + } + } else if (java_lang_boxing_object::is_instance(obj)) { + st->print(" = "); + java_lang_boxing_object::print(obj, st); + } } #endif // ndef PRODUCT --- old/src/share/vm/oops/instanceKlass.hpp 2008-11-08 23:40:56.000000000 -0800 +++ new/src/share/vm/oops/instanceKlass.hpp 2008-11-08 23:40:56.000000000 -0800 @@ -161,6 +161,8 @@ klassOop _implementors[implementors_limit]; // Generic signature, or null if none. symbolOop _generic_signature; + // invokedynamic bootstrap method (a java.dyn.MethodHandle) + oop _bootstrap_method; // Annotations for this class, or null if none. typeArrayOop _class_annotations; // Annotation objects (byte arrays) for fields, or null if no annotations. @@ -443,6 +445,10 @@ u2 method_index) { _enclosing_method_class_index = class_index; _enclosing_method_method_index = method_index; } + // JSR 292 support + oop bootstrap_method() const { return _bootstrap_method; } + void set_bootstrap_method(oop mh) { oop_store(&_bootstrap_method, mh); } + // jmethodID support static jmethodID get_jmethod_id(instanceKlassHandle ik_h, size_t idnum, jmethodID new_id, jmethodID* new_jmeths); @@ -723,6 +729,7 @@ oop* adr_inner_classes() const { return (oop*)&this->_inner_classes;} oop* adr_implementors() const { return (oop*)&this->_implementors[0];} oop* adr_generic_signature() const { return (oop*)&this->_generic_signature;} + oop* adr_bootstrap_method() const { return (oop*)&this->_bootstrap_method;} oop* adr_methods_jmethod_ids() const { return (oop*)&this->_methods_jmethod_ids;} oop* adr_methods_cached_itable_indices() const { return (oop*)&this->_methods_cached_itable_indices;} oop* adr_class_annotations() const { return (oop*)&this->_class_annotations;} --- old/src/share/vm/oops/instanceKlassKlass.cpp 2008-11-08 23:40:58.000000000 -0800 +++ new/src/share/vm/oops/instanceKlassKlass.cpp 2008-11-08 23:40:57.000000000 -0800 @@ -84,6 +84,7 @@ MarkSweep::mark_and_push(ik->adr_host_klass()); MarkSweep::mark_and_push(ik->adr_signers()); MarkSweep::mark_and_push(ik->adr_generic_signature()); + MarkSweep::mark_and_push(ik->adr_bootstrap_method()); MarkSweep::mark_and_push(ik->adr_class_annotations()); MarkSweep::mark_and_push(ik->adr_fields_annotations()); MarkSweep::mark_and_push(ik->adr_methods_annotations()); @@ -124,6 +125,7 @@ PSParallelCompact::mark_and_push(cm, ik->adr_host_klass()); PSParallelCompact::mark_and_push(cm, ik->adr_signers()); PSParallelCompact::mark_and_push(cm, ik->adr_generic_signature()); + PSParallelCompact::mark_and_push(cm, ik->adr_bootstrap_method()); PSParallelCompact::mark_and_push(cm, ik->adr_class_annotations()); PSParallelCompact::mark_and_push(cm, ik->adr_fields_annotations()); PSParallelCompact::mark_and_push(cm, ik->adr_methods_annotations()); @@ -170,6 +172,7 @@ blk->do_oop(&ik->adr_implementors()[i]); } blk->do_oop(ik->adr_generic_signature()); + blk->do_oop(ik->adr_bootstrap_method()); blk->do_oop(ik->adr_class_annotations()); blk->do_oop(ik->adr_fields_annotations()); blk->do_oop(ik->adr_methods_annotations()); @@ -230,6 +233,8 @@ } adr = ik->adr_generic_signature(); if (mr.contains(adr)) blk->do_oop(adr); + adr = ik->adr_bootstrap_method(); + if (mr.contains(adr)) blk->do_oop(adr); adr = ik->adr_class_annotations(); if (mr.contains(adr)) blk->do_oop(adr); adr = ik->adr_fields_annotations(); @@ -274,6 +279,7 @@ MarkSweep::adjust_pointer(&ik->adr_implementors()[i]); } MarkSweep::adjust_pointer(ik->adr_generic_signature()); + MarkSweep::adjust_pointer(ik->adr_bootstrap_method()); MarkSweep::adjust_pointer(ik->adr_class_annotations()); MarkSweep::adjust_pointer(ik->adr_fields_annotations()); MarkSweep::adjust_pointer(ik->adr_methods_annotations()); @@ -454,6 +460,7 @@ ik->set_breakpoints(NULL); ik->init_previous_versions(); ik->set_generic_signature(NULL); + ik->set_bootstrap_method(NULL); ik->release_set_methods_jmethod_ids(NULL); ik->release_set_methods_cached_itable_indices(NULL); ik->set_class_annotations(NULL); @@ -487,6 +494,8 @@ // Printing +#define BULLET " - " + static const char* state_names[] = { "unparseable_by_gc", "allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error" }; @@ -497,13 +506,13 @@ instanceKlass* ik = instanceKlass::cast(klassOop(obj)); klassKlass::oop_print_on(obj, st); - st->print(" - instance size: %d", ik->size_helper()); st->cr(); - st->print(" - klass size: %d", ik->object_size()); st->cr(); - st->print(" - access: "); ik->access_flags().print_on(st); st->cr(); - st->print(" - state: "); st->print_cr(state_names[ik->_init_state]); - st->print(" - name: "); ik->name()->print_value_on(st); st->cr(); - st->print(" - super: "); ik->super()->print_value_on(st); st->cr(); - st->print(" - sub: "); + st->print(BULLET"instance size: %d", ik->size_helper()); st->cr(); + st->print(BULLET"klass size: %d", ik->object_size()); st->cr(); + st->print(BULLET"access: "); ik->access_flags().print_on(st); st->cr(); + st->print(BULLET"state: "); st->print_cr(state_names[ik->_init_state]); + st->print(BULLET"name: "); ik->name()->print_value_on(st); st->cr(); + st->print(BULLET"super: "); ik->super()->print_value_on(st); st->cr(); + st->print(BULLET"sub: "); Klass* sub = ik->subklass(); int n; for (n = 0; sub != NULL; n++, sub = sub->next_sibling()) { @@ -516,12 +525,12 @@ st->cr(); if (ik->is_interface()) { - st->print_cr(" - nof implementors: %d", ik->nof_implementors()); + st->print_cr(BULLET"nof implementors: %d", ik->nof_implementors()); int print_impl = 0; for (int i = 0; i < instanceKlass::implementors_limit; i++) { if (ik->implementor(i) != NULL) { if (++print_impl == 1) - st->print_cr(" - implementor: "); + st->print_cr(BULLET"implementor: "); st->print(" "); ik->implementor(i)->print_value_on(st); } @@ -529,34 +538,33 @@ if (print_impl > 0) st->cr(); } - st->print(" - arrays: "); ik->array_klasses()->print_value_on(st); st->cr(); - st->print(" - methods: "); ik->methods()->print_value_on(st); st->cr(); + st->print(BULLET"arrays: "); ik->array_klasses()->print_value_on(st); st->cr(); + st->print(BULLET"methods: "); ik->methods()->print_value_on(st); st->cr(); if (Verbose) { objArrayOop methods = ik->methods(); for(int i = 0; i < methods->length(); i++) { tty->print("%d : ", i); methods->obj_at(i)->print_value(); tty->cr(); } } - st->print(" - method ordering: "); ik->method_ordering()->print_value_on(st); st->cr(); - st->print(" - local interfaces: "); ik->local_interfaces()->print_value_on(st); st->cr(); - st->print(" - trans. interfaces: "); ik->transitive_interfaces()->print_value_on(st); st->cr(); - st->print(" - constants: "); ik->constants()->print_value_on(st); st->cr(); - st->print(" - class loader: "); ik->class_loader()->print_value_on(st); st->cr(); - st->print(" - protection domain: "); ik->protection_domain()->print_value_on(st); st->cr(); - st->print(" - host class: "); ik->host_klass()->print_value_on(st); st->cr(); - st->print(" - signers: "); ik->signers()->print_value_on(st); st->cr(); + st->print(BULLET"method ordering: "); ik->method_ordering()->print_value_on(st); st->cr(); + st->print(BULLET"local interfaces: "); ik->local_interfaces()->print_value_on(st); st->cr(); + st->print(BULLET"trans. interfaces: "); ik->transitive_interfaces()->print_value_on(st); st->cr(); + st->print(BULLET"constants: "); ik->constants()->print_value_on(st); st->cr(); + st->print(BULLET"class loader: "); ik->class_loader()->print_value_on(st); st->cr(); + st->print(BULLET"protection domain: "); ik->protection_domain()->print_value_on(st); st->cr(); + st->print(BULLET"host class: "); ik->host_klass()->print_value_on(st); st->cr(); + st->print(BULLET"signers: "); ik->signers()->print_value_on(st); st->cr(); if (ik->source_file_name() != NULL) { - st->print(" - source file: "); + st->print(BULLET"source file: "); ik->source_file_name()->print_value_on(st); st->cr(); } if (ik->source_debug_extension() != NULL) { - st->print(" - source debug extension: "); + st->print(BULLET"source debug extension: "); ik->source_debug_extension()->print_value_on(st); st->cr(); } - st->print_cr(" - previous version: "); { ResourceMark rm; // PreviousVersionInfo objects returned via PreviousVersionWalker @@ -564,38 +572,48 @@ // GrowableArray _after_ the PreviousVersionWalker destructor // has destroyed the handles. { + bool have_pv = false; PreviousVersionWalker pvw(ik); for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); pv_info != NULL; pv_info = pvw.next_previous_version()) { + if (!have_pv) + st->print(BULLET"previous version: "); + have_pv = true; pv_info->prev_constant_pool_handle()()->print_value_on(st); } - st->cr(); + if (have_pv) st->cr(); } // pvw is cleaned up } // rm is cleaned up if (ik->generic_signature() != NULL) { - st->print(" - generic signature: "); + st->print(BULLET"generic signature: "); ik->generic_signature()->print_value_on(st); + st->cr(); + } + if (ik->bootstrap_method() != NULL) { + st->print(BULLET"bootstrap method: "); + ik->bootstrap_method()->print_value_on(st); + st->cr(); } - st->print(" - inner classes: "); ik->inner_classes()->print_value_on(st); st->cr(); - st->print(" - java mirror: "); ik->java_mirror()->print_value_on(st); st->cr(); - st->print(" - vtable length %d (start addr: " INTPTR_FORMAT ")", ik->vtable_length(), ik->start_of_vtable()); st->cr(); - st->print(" - itable length %d (start addr: " INTPTR_FORMAT ")", ik->itable_length(), ik->start_of_itable()); st->cr(); - st->print_cr(" - static fields:"); + st->print(BULLET"inner classes: "); ik->inner_classes()->print_value_on(st); st->cr(); + st->print(BULLET"java mirror: "); ik->java_mirror()->print_value_on(st); st->cr(); + st->print(BULLET"vtable length %d (start addr: " INTPTR_FORMAT ")", ik->vtable_length(), ik->start_of_vtable()); st->cr(); + st->print(BULLET"itable length %d (start addr: " INTPTR_FORMAT ")", ik->itable_length(), ik->start_of_itable()); st->cr(); + st->print_cr(BULLET"---- static fields (%d words):", ik->static_field_size()); FieldPrinter print_static_field(st); ik->do_local_static_fields(&print_static_field); - st->print_cr(" - non-static fields:"); - FieldPrinter print_nonstatic_field(st, obj); + st->print_cr(BULLET"---- non-static fields (%d words):", ik->nonstatic_field_size()); + FieldPrinter print_nonstatic_field(st); ik->do_nonstatic_fields(&print_nonstatic_field); - st->print(" - static oop maps: "); + st->print(BULLET"static oop maps: "); if (ik->static_oop_field_size() > 0) { int first_offset = ik->offset_of_static_fields(); st->print("%d-%d", first_offset, first_offset + ik->static_oop_field_size() - 1); } st->cr(); - st->print(" - non-static oop maps: "); + st->print(BULLET"non-static oop maps: "); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); OopMapBlock* end_map = map + ik->nonstatic_oop_map_size(); while (map < end_map) { --- old/src/share/vm/oops/methodOop.cpp 2008-11-08 23:40:59.000000000 -0800 +++ new/src/share/vm/oops/methodOop.cpp 2008-11-08 23:40:59.000000000 -0800 @@ -792,6 +792,7 @@ _imcp_invoke_name = 1, // utf8: 'invoke' _imcp_invoke_signature, // utf8: (variable symbolOop) _imcp_method_type_value, // string: (variable java/dyn/MethodType, sic) + _imcp_related_invoke_method, // string: (variable methodOop, sic) _imcp_limit }; @@ -819,7 +820,8 @@ Handle method_type, TRAPS) { methodHandle empty; - assert(holder() == SystemDictionary::java_dyn_MethodHandle_klass(), + assert(holder() == SystemDictionary::java_dyn_MethodHandle_klass() || + holder() == SystemDictionary::java_dyn_Dynamic_klass(), "must be a JSR 292 magic type"); if (TraceMethodHandles) { @@ -836,6 +838,7 @@ cp->symbol_at_put(_imcp_invoke_name, vmSymbols::invoke_name()); cp->symbol_at_put(_imcp_invoke_signature, signature()); cp->string_at_put(_imcp_method_type_value, vmSymbols::void_signature()); + cp->string_at_put(_imcp_related_invoke_method, vmSymbols::void_signature()); cp->set_pool_holder(holder()); // set up the fancy stuff: @@ -847,6 +850,7 @@ 0, 0, 0, CHECK_(empty)); m = methodHandle(THREAD, m_oop); } + cp->pseudo_string_at_put(_imcp_related_invoke_method, m()); m->set_constants(cp()); m->set_name_index(_imcp_invoke_name); m->set_signature_index(_imcp_invoke_signature); @@ -880,6 +884,42 @@ return m; } +methodOop methodOopDesc::related_invoke_method(int relation) const { + if (!is_method_handle_invoke()) { assert(false, "caller resp."); return NULL; } + methodOop idm = (methodOop) constants()->pseudo_string_at(_imcp_related_invoke_method); + if (this == idm) return NULL; +#ifdef ASSERT + assert(idm->is_method() && methodOop(idm)->is_method_handle_invoke(), ""); + klassOop expect = ((relation == RM_invdyn) + ? SystemDictionary::java_dyn_Dynamic_klass() + : SystemDictionary::java_dyn_MethodHandle_klass()); + assert(idm->method_holder() == expect, "expected type of related method"); +#endif //ASSERT + return idm; +} + +void methodOopDesc::set_related_invoke_method(methodOop idm, int relation) { + if (!is_method_handle_invoke()) { assert(false, "caller resp."); return; } + +#ifdef ASSERT + assert(idm != NULL && this != idm && idm->is_method() && methodOop(idm)->is_method_handle_invoke(), ""); + if (method_holder() == SystemDictionary::java_dyn_MethodHandle_klass()) { + assert(relation == RM_invdyn, "expected relation"); + assert(idm->method_holder() == SystemDictionary::java_dyn_Dynamic_klass(), "MH -> Dyn"); + assert(idm->method_handle_type() == this->method_handle_type(), "same types"); + } else if (method_holder() == SystemDictionary::java_dyn_Dynamic_klass()) { + assert(relation == RM_extinv, "expected relation"); + assert(idm->method_holder() == SystemDictionary::java_dyn_MethodHandle_klass(), "Dyn -> MH"); + assert(idm->method_handle_type() != this->method_handle_type(), "MH type is extension"); + } else { + ShouldNotReachHere(); + } + methodOop oldidm = (methodOop) constants()->pseudo_string_at(_imcp_related_invoke_method); + assert(this == oldidm || idm == oldidm, "one change allowed"); +#endif //ASSERT + + constants()->pseudo_string_at_put(_imcp_related_invoke_method, idm); +} methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length, --- old/src/share/vm/oops/methodOop.hpp 2008-11-08 23:41:01.000000000 -0800 +++ new/src/share/vm/oops/methodOop.hpp 2008-11-08 23:41:01.000000000 -0800 @@ -529,7 +529,17 @@ oop method_handle_type() const; static jint* method_type_pointer_chase(); // presize interpreter frames for extra stack slots, if needed - static int extra_stack() { return MethodHandles ? 2 : 0; } + static int extra_stack() { return InvokeDynamic ? 2 : MethodHandles ? 1 : 0; } + private: + methodOop related_invoke_method(int relation) const; + void set_related_invoke_method(methodOop idm, int relation); + enum { RM_invdyn, RM_extinv }; + public: + methodOop invokedynamic_method() const { return related_invoke_method(RM_invdyn); } + void set_invokedynamic_method(methodOop idm) { set_related_invoke_method(idm, RM_invdyn); } + methodOop extended_invoke_method() const { return related_invoke_method(RM_extinv); } + void set_extended_invoke_method(methodOop ext) { set_related_invoke_method(ext, RM_extinv); } + // RedefineClasses() support: bool is_old() const { return access_flags().is_old(); } void set_is_old() { _access_flags.set_is_old(); } --- old/src/share/vm/opto/bytecodeInfo.cpp 2008-11-08 23:41:03.000000000 -0800 +++ new/src/share/vm/opto/bytecodeInfo.cpp 2008-11-08 23:41:02.000000000 -0800 @@ -313,7 +313,7 @@ // stricter than callee_holder->is_initialized() ciBytecodeStream iter(caller_method); iter.force_bci(caller_bci); - int index = iter.get_index_big(); + int index = iter.get_index_int(); if( !caller_method->is_klass_loaded(index, true) ) { return false; } --- old/src/share/vm/opto/doCall.cpp 2008-11-08 23:41:04.000000000 -0800 +++ new/src/share/vm/opto/doCall.cpp 2008-11-08 23:41:04.000000000 -0800 @@ -248,6 +248,14 @@ holder_klass); return true; } + if (dest_method->is_method_handle_invoke() + && holder_klass->name() == ciSymbol::java_dyn_Dynamic()) { + // FIXME: NYI + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none, + holder_klass); + return true; + } assert(dest_method->will_link(method()->holder(), klass, bc()), "dest_method: typeflow responsibility"); return false; --- old/src/share/vm/prims/jvmtiClassFileReconstituter.cpp 2008-11-08 23:41:06.000000000 -0800 +++ new/src/share/vm/prims/jvmtiClassFileReconstituter.cpp 2008-11-08 23:41:05.000000000 -0800 @@ -662,12 +662,21 @@ case Bytecodes::_invokeinterface : assert(len == 3 || (code == Bytecodes::_invokeinterface && len ==5), "sanity check"); + int cpci = Bytes::get_native_u2(bcp+1); + bool is_invokedynamic = (InvokeDynamic && bs.raw_code() == Bytecodes::_invokedynamic); + if (is_invokedynamic) + cpci = Bytes::get_native_u4(bcp+1); // cache cannot be pre-fetched since some classes won't have it yet ConstantPoolCacheEntry* entry = - mh->constants()->cache()->entry_at(Bytes::get_native_u2(bcp+1)); + mh->constants()->cache()->main_entry_at(cpci); int i = entry->constant_pool_index(); assert(i < mh->constants()->length(), "sanity check"); Bytes::put_Java_u2((address)(p+1), (u2)i); // java byte ordering + if (is_invokedynamic) { + ArgumentSizeComputer size_it(mh->constants()->uncached_signature_ref_at(i)); + *(p+3) = (unsigned char)(size_it.size() + 1); + *(p+4) = 0; + } break; } } --- old/src/share/vm/prims/methodComparator.cpp 2008-11-08 23:41:07.000000000 -0800 +++ new/src/share/vm/prims/methodComparator.cpp 2008-11-08 23:41:07.000000000 -0800 @@ -148,8 +148,8 @@ case Bytecodes::_invokespecial : // fall through case Bytecodes::_invokestatic : // fall through case Bytecodes::_invokeinterface : { - u2 cpci_old = _s_old->get_index_big(); - u2 cpci_new = _s_new->get_index_big(); + u2 cpci_old = _s_old->get_index_int(); + u2 cpci_new = _s_new->get_index_int(); // Check if the names of classes, field/method names and signatures at these indexes // are the same. Indices which are really into constantpool cache (rather than constant // pool itself) are accepted by the constantpool query routines below. --- old/src/share/vm/prims/methodHandles.cpp 2008-11-08 23:41:09.000000000 -0800 +++ new/src/share/vm/prims/methodHandles.cpp 2008-11-08 23:41:09.000000000 -0800 @@ -613,6 +613,16 @@ JVM_END +JVM_ENTRY(void, MH_linkCallSite(JNIEnv *env, jobject igcls, jobject site_jh, jobject target_jh)) { + // No special action required, yet. + oop site_oop = JNIHandles::resolve(site_jh); + if (site_oop == NULL || site_oop->klass() != SystemDictionary::dyn_impl_DynCallSite_klass()) + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "call site"); + java_dyn_impl_DynCallSite::set_target(site_oop, JNIHandles::resolve(target_jh)); +} +JVM_END + + /// JVM_RegisterMethodHandleMethods #define ADR "J" @@ -630,6 +640,7 @@ #define DMH DYNI"DMH;" #define MT DYNP"MethodType;" #define MTFM DYNI"MTForm;" +#define DCST DYNI"DynCallSite;" #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) @@ -641,7 +652,8 @@ {CC"init", CC"("DMH OBJ"Z)V", FN_PTR(MH_init_DMH)}, {CC"init", CC"("MTFM MT")V", FN_PTR(MH_init_MTForm)}, {CC"getMethodName", CC"("MH")"STRG, FN_PTR(MH_methodName)}, - {CC"getVMRef", CC"("MH")"OBJ, FN_PTR(MH_vmref)} + {CC"getVMRef", CC"("MH")"OBJ, FN_PTR(MH_vmref)}, + {CC"linkCallSite", CC"("DCST MH")V", FN_PTR(MH_linkCallSite)} }; --- old/src/share/vm/runtime/arguments.cpp 2008-11-08 23:41:11.000000000 -0800 +++ new/src/share/vm/runtime/arguments.cpp 2008-11-08 23:41:10.000000000 -0800 @@ -2573,6 +2573,12 @@ } #endif // PRODUCT + if (InvokeDynamic && !MethodHandles) { + if (!FLAG_IS_DEFAULT(MethodHandles)) { + warning("forcing MethodHandles true to support InvokeDynamic"); + } + MethodHandles = true; + } if (MethodHandles && !AnonymousClasses) { if (!FLAG_IS_DEFAULT(AnonymousClasses)) { warning("forcing AnonymousClasses true to support MethodHandles"); --- old/src/share/vm/runtime/globals.hpp 2008-11-08 23:41:12.000000000 -0800 +++ new/src/share/vm/runtime/globals.hpp 2008-11-08 23:41:12.000000000 -0800 @@ -3239,6 +3239,12 @@ develop(bool, TraceMethodHandles, false, \ "trace internal method handle operations") \ \ + product(bool, InvokeDynamic, false, \ + "recognize the invokedynamic instruction") \ + \ + develop(bool, TraceInvokeDynamic, false, \ + "trace internal invoke dynamic operations") \ + \ product(bool, TaggedStackInterpreter, false, \ "Insert tags in interpreter execution stack for oopmap generaion")\ \ --- old/src/share/vm/runtime/sharedRuntime.cpp 2008-11-08 23:41:14.000000000 -0800 +++ new/src/share/vm/runtime/sharedRuntime.cpp 2008-11-08 23:41:14.000000000 -0800 @@ -793,6 +793,7 @@ KlassHandle receiver_klass (THREAD, receiver->klass()); klassOop rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle)); // klass is already loaded + assert(rk != SystemDictionary::java_dyn_Dynamic_klass(), "compiler should have caught this"); KlassHandle static_receiver_klass (THREAD, rk); assert(receiver_klass->is_subtype_of(static_receiver_klass()), "actual receiver must be subclass of static receiver klass"); if (receiver_klass->oop_is_instance()) { --- old/src/share/vm/utilities/globalDefinitions.hpp 2008-11-08 23:41:16.000000000 -0800 +++ new/src/share/vm/utilities/globalDefinitions.hpp 2008-11-08 23:41:16.000000000 -0800 @@ -578,7 +578,7 @@ inline TosState as_TosState(BasicType type) { switch (type) { case T_BYTE : return btos; - case T_BOOLEAN: return btos; + case T_BOOLEAN: return btos; // FIXME: Add ztos case T_CHAR : return ctos; case T_SHORT : return stos; case T_INT : return itos; @@ -592,6 +592,22 @@ return ilgl; } +inline BasicType as_BasicType(TosState state) { + switch (state) { + //case ztos: return T_BOOLEAN;//FIXME + case btos : return T_BYTE; + case ctos : return T_CHAR; + case stos : return T_SHORT; + case itos : return T_INT; + case ltos : return T_LONG; + case ftos : return T_FLOAT; + case dtos : return T_DOUBLE; + case atos : return T_ARRAY; + case vtos : return T_VOID; + } + return T_ILLEGAL; +} + // Helper function to convert BasicType info into TosState // Note: Cannot define here as it uses global constant at the time being.