Mercurial > hg > graal-compiler
diff src/cpu/x86/vm/sharedRuntime_x86_32.cpp @ 6275:957c266d8bc5
Merge with http://hg.openjdk.java.net/hsx/hsx24/hotspot/
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Tue, 21 Aug 2012 10:39:19 +0200 |
parents | 1d7922586cf6 |
children | e4ae9932c292 da91efe96a93 |
line wrap: on
line diff
--- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Mon Aug 20 15:21:31 2012 +0200 +++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Tue Aug 21 10:39:19 2012 +0200 @@ -643,6 +643,19 @@ __ movdbl(r, Address(saved_sp, next_val_off)); } +static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg, + address code_start, address code_end, + Label& L_ok) { + Label L_fail; + __ lea(temp_reg, ExternalAddress(code_start)); + __ cmpptr(pc_reg, temp_reg); + __ jcc(Assembler::belowEqual, L_fail); + __ lea(temp_reg, ExternalAddress(code_end)); + __ cmpptr(pc_reg, temp_reg); + __ jcc(Assembler::below, L_ok); + __ bind(L_fail); +} + static void gen_i2c_adapter(MacroAssembler *masm, int total_args_passed, int comp_args_on_stack, @@ -653,9 +666,53 @@ // we may do a i2c -> c2i transition if we lose a race where compiled // code goes non-entrant while we get args ready. + // Adapters can be frameless because they do not require the caller + // to perform additional cleanup work, such as correcting the stack pointer. + // An i2c adapter is frameless because the *caller* frame, which is interpreted, + // routinely repairs its own stack pointer (from interpreter_frame_last_sp), + // even if a callee has modified the stack pointer. + // A c2i adapter is frameless because the *callee* frame, which is interpreted, + // routinely repairs its caller's stack pointer (from sender_sp, which is set + // up via the senderSP register). + // In other words, if *either* the caller or callee is interpreted, we can + // get the stack pointer repaired after a call. + // This is why c2i and i2c adapters cannot be indefinitely composed. + // In particular, if a c2i adapter were to somehow call an i2c adapter, + // both caller and callee would be compiled methods, and neither would + // clean up the stack pointer changes performed by the two adapters. + // If this happens, control eventually transfers back to the compiled + // caller, but with an uncorrected stack, causing delayed havoc. + // Pick up the return address __ movptr(rax, Address(rsp, 0)); + if (VerifyAdapterCalls && + (Interpreter::code() != NULL || StubRoutines::code1() != NULL)) { + // So, let's test for cascading c2i/i2c adapters right now. + // assert(Interpreter::contains($return_addr) || + // StubRoutines::contains($return_addr), + // "i2c adapter must return to an interpreter frame"); + __ block_comment("verify_i2c { "); + Label L_ok; + if (Interpreter::code() != NULL) + range_check(masm, rax, rdi, + Interpreter::code()->code_start(), Interpreter::code()->code_end(), + L_ok); + if (StubRoutines::code1() != NULL) + range_check(masm, rax, rdi, + StubRoutines::code1()->code_begin(), StubRoutines::code1()->code_end(), + L_ok); + if (StubRoutines::code2() != NULL) + range_check(masm, rax, rdi, + StubRoutines::code2()->code_begin(), StubRoutines::code2()->code_end(), + L_ok); + const char* msg = "i2c adapter must return to an interpreter frame"; + __ block_comment(msg); + __ stop(msg); + __ bind(L_ok); + __ block_comment("} verify_i2ce "); + } + // Must preserve original SP for loading incoming arguments because // we need to align the outgoing SP for compiled code. __ movptr(rdi, rsp); @@ -1293,6 +1350,89 @@ __ bind(done); } +static void verify_oop_args(MacroAssembler* masm, + int total_args_passed, + const BasicType* sig_bt, + const VMRegPair* regs) { + Register temp_reg = rbx; // not part of any compiled calling seq + if (VerifyOops) { + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_OBJECT || + sig_bt[i] == T_ARRAY) { + VMReg r = regs[i].first(); + assert(r->is_valid(), "bad oop arg"); + if (r->is_stack()) { + __ movptr(temp_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize)); + __ verify_oop(temp_reg); + } else { + __ verify_oop(r->as_Register()); + } + } + } + } +} + +static void gen_special_dispatch(MacroAssembler* masm, + int total_args_passed, + int comp_args_on_stack, + vmIntrinsics::ID special_dispatch, + const BasicType* sig_bt, + const VMRegPair* regs) { + verify_oop_args(masm, total_args_passed, sig_bt, regs); + + // Now write the args into the outgoing interpreter space + bool has_receiver = false; + Register receiver_reg = noreg; + int member_arg_pos = -1; + Register member_reg = noreg; + int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(special_dispatch); + if (ref_kind != 0) { + member_arg_pos = total_args_passed - 1; // trailing MemberName argument + member_reg = rbx; // known to be free at this point + has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind); + } else if (special_dispatch == vmIntrinsics::_invokeBasic) { + has_receiver = true; + } else { + guarantee(false, err_msg("special_dispatch=%d", special_dispatch)); + } + + if (member_reg != noreg) { + // Load the member_arg into register, if necessary. + assert(member_arg_pos >= 0 && member_arg_pos < total_args_passed, "oob"); + assert(sig_bt[member_arg_pos] == T_OBJECT, "dispatch argument must be an object"); + VMReg r = regs[member_arg_pos].first(); + assert(r->is_valid(), "bad member arg"); + if (r->is_stack()) { + __ movptr(member_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize)); + } else { + // no data motion is needed + member_reg = r->as_Register(); + } + } + + if (has_receiver) { + // Make sure the receiver is loaded into a register. + assert(total_args_passed > 0, "oob"); + assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object"); + VMReg r = regs[0].first(); + assert(r->is_valid(), "bad receiver arg"); + if (r->is_stack()) { + // Porting note: This assumes that compiled calling conventions always + // pass the receiver oop in a register. If this is not true on some + // platform, pick a temp and load the receiver from stack. + assert(false, "receiver always in a register"); + receiver_reg = rcx; // known to be free at this point + __ movptr(receiver_reg, Address(rsp, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize)); + } else { + // no data motion is needed + receiver_reg = r->as_Register(); + } + } + + // Figure out which address we are really jumping to: + MethodHandles::generate_method_handle_dispatch(masm, special_dispatch, + receiver_reg, member_reg, /*for_compiler_entry:*/ true); +} // --------------------------------------------------------------------------- // Generate a native wrapper for a given method. The method takes arguments @@ -1323,14 +1463,37 @@ // transition back to thread_in_Java // return to caller // -nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, +nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, methodHandle method, int compile_id, int total_in_args, int comp_args_on_stack, - BasicType *in_sig_bt, - VMRegPair *in_regs, + BasicType* in_sig_bt, + VMRegPair* in_regs, BasicType ret_type) { + if (method->is_method_handle_intrinsic()) { + vmIntrinsics::ID iid = method->intrinsic_id(); + intptr_t start = (intptr_t)__ pc(); + int vep_offset = ((intptr_t)__ pc()) - start; + gen_special_dispatch(masm, + total_in_args, + comp_args_on_stack, + method->intrinsic_id(), + in_sig_bt, + in_regs); + int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period + __ flush(); + int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually + return nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_offset, + frame_complete, + stack_slots / VMRegImpl::slots_per_word, + in_ByteSize(-1), + in_ByteSize(-1), + (OopMapSet*)NULL); + } bool is_critical_native = true; address native_func = method->critical_native_function(); if (native_func == NULL) { @@ -1436,7 +1599,7 @@ if (in_regs[i].first()->is_Register()) { const Register reg = in_regs[i].first()->as_Register(); switch (in_sig_bt[i]) { - case T_ARRAY: + case T_ARRAY: // critical array (uses 2 slots on LP64) case T_BOOLEAN: case T_BYTE: case T_SHORT: