jrose@1145: /* twisti@2436: * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. jrose@1145: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jrose@1145: * jrose@1145: * This code is free software; you can redistribute it and/or modify it jrose@1145: * under the terms of the GNU General Public License version 2 only, as jrose@1145: * published by the Free Software Foundation. jrose@1145: * jrose@1145: * This code is distributed in the hope that it will be useful, but WITHOUT jrose@1145: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jrose@1145: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jrose@1145: * version 2 for more details (a copy is included in the LICENSE file that jrose@1145: * accompanied this code). jrose@1145: * jrose@1145: * You should have received a copy of the GNU General Public License version jrose@1145: * 2 along with this work; if not, write to the Free Software Foundation, jrose@1145: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jrose@1145: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. jrose@1145: * jrose@1145: */ jrose@1145: stefank@2314: #include "precompiled.hpp" stefank@2314: #include "interpreter/interpreter.hpp" stefank@2314: #include "memory/allocation.inline.hpp" stefank@2314: #include "prims/methodHandles.hpp" jrose@1145: jrose@1145: #define __ _masm-> jrose@1145: twisti@2204: #ifdef PRODUCT twisti@2204: #define BLOCK_COMMENT(str) /* nothing */ twisti@2204: #else twisti@2204: #define BLOCK_COMMENT(str) __ block_comment(str) twisti@2204: #endif twisti@2204: twisti@2204: #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") twisti@2204: jrose@1145: address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm, jrose@1145: address interpreted_entry) { twisti@1858: // Just before the actual machine code entry point, allocate space twisti@1858: // for a MethodHandleEntry::Data record, so that we can manage everything twisti@1858: // from one base pointer. jrose@1145: __ align(wordSize); jrose@1145: address target = __ pc() + sizeof(Data); jrose@1145: while (__ pc() < target) { jrose@1145: __ nop(); jrose@1145: __ align(wordSize); jrose@1145: } jrose@1145: jrose@1145: MethodHandleEntry* me = (MethodHandleEntry*) __ pc(); jrose@1145: me->set_end_address(__ pc()); // set a temporary end_address jrose@1145: me->set_from_interpreted_entry(interpreted_entry); jrose@1145: me->set_type_checking_entry(NULL); jrose@1145: jrose@1145: return (address) me; jrose@1145: } jrose@1145: jrose@1145: MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm, jrose@1145: address start_addr) { jrose@1145: MethodHandleEntry* me = (MethodHandleEntry*) start_addr; jrose@1145: assert(me->end_address() == start_addr, "valid ME"); jrose@1145: jrose@1145: // Fill in the real end_address: jrose@1145: __ align(wordSize); jrose@1145: me->set_end_address(__ pc()); jrose@1145: jrose@1145: return me; jrose@1145: } jrose@1145: never@2950: // stack walking support never@2950: never@2950: frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) { never@2950: //RicochetFrame* f = RicochetFrame::from_frame(fr); never@2950: // Cf. is_interpreted_frame path of frame::sender never@2950: intptr_t* younger_sp = fr.sp(); never@2950: intptr_t* sp = fr.sender_sp(); never@2950: map->make_integer_regs_unsaved(); never@2950: map->shift_window(sp, younger_sp); never@2950: bool this_frame_adjusted_stack = true; // I5_savedSP is live in this RF never@2950: return frame(sp, younger_sp, this_frame_adjusted_stack); never@2950: } never@2950: never@2950: void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map) { never@2950: ResourceMark rm; never@2950: RicochetFrame* f = RicochetFrame::from_frame(fr); never@2950: never@2950: // pick up the argument type descriptor: never@2950: Thread* thread = Thread::current(); never@2950: Handle cookie(thread, f->compute_saved_args_layout(true, true)); never@2950: never@2950: // process fixed part never@2950: blk->do_oop((oop*)f->saved_target_addr()); never@2950: blk->do_oop((oop*)f->saved_args_layout_addr()); never@2950: never@2950: // process variable arguments: never@2950: if (cookie.is_null()) return; // no arguments to describe never@2950: never@2950: // the cookie is actually the invokeExact method for my target never@2950: // his argument signature is what I'm interested in never@2950: assert(cookie->is_method(), ""); never@2950: methodHandle invoker(thread, methodOop(cookie())); never@2950: assert(invoker->name() == vmSymbols::invokeExact_name(), "must be this kind of method"); never@2950: assert(!invoker->is_static(), "must have MH argument"); never@2950: int slot_count = invoker->size_of_parameters(); never@2950: assert(slot_count >= 1, "must include 'this'"); never@2950: intptr_t* base = f->saved_args_base(); never@2950: intptr_t* retval = NULL; never@2950: if (f->has_return_value_slot()) never@2950: retval = f->return_value_slot_addr(); never@2950: int slot_num = slot_count - 1; never@2950: intptr_t* loc = &base[slot_num]; never@2950: //blk->do_oop((oop*) loc); // original target, which is irrelevant never@2950: int arg_num = 0; never@2950: for (SignatureStream ss(invoker->signature()); !ss.is_done(); ss.next()) { never@2950: if (ss.at_return_type()) continue; never@2950: BasicType ptype = ss.type(); never@2950: if (ptype == T_ARRAY) ptype = T_OBJECT; // fold all refs to T_OBJECT never@2950: assert(ptype >= T_BOOLEAN && ptype <= T_OBJECT, "not array or void"); never@2950: slot_num -= type2size[ptype]; never@2950: loc = &base[slot_num]; never@2950: bool is_oop = (ptype == T_OBJECT && loc != retval); never@2950: if (is_oop) blk->do_oop((oop*)loc); never@2950: arg_num += 1; never@2950: } never@2950: assert(slot_num == 0, "must have processed all the arguments"); never@2950: } never@2950: never@2950: // Ricochet Frames never@2950: const Register MethodHandles::RicochetFrame::L1_continuation = L1; never@2950: const Register MethodHandles::RicochetFrame::L2_saved_target = L2; never@2950: const Register MethodHandles::RicochetFrame::L3_saved_args_layout = L3; never@2950: const Register MethodHandles::RicochetFrame::L4_saved_args_base = L4; // cf. Gargs = G4 never@2950: const Register MethodHandles::RicochetFrame::L5_conversion = L5; never@2950: #ifdef ASSERT never@2950: const Register MethodHandles::RicochetFrame::L0_magic_number_1 = L0; never@2950: #endif //ASSERT never@2950: never@2950: oop MethodHandles::RicochetFrame::compute_saved_args_layout(bool read_cache, bool write_cache) { never@2950: if (read_cache) { never@2950: oop cookie = saved_args_layout(); never@2950: if (cookie != NULL) return cookie; never@2950: } never@2950: oop target = saved_target(); never@2950: oop mtype = java_lang_invoke_MethodHandle::type(target); never@2950: oop mtform = java_lang_invoke_MethodType::form(mtype); never@2950: oop cookie = java_lang_invoke_MethodTypeForm::vmlayout(mtform); never@2950: if (write_cache) { never@2950: (*saved_args_layout_addr()) = cookie; never@2950: } never@2950: return cookie; never@2950: } never@2950: never@2950: void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm, never@2950: // output params: never@2950: int* bounce_offset, never@2950: int* exception_offset, never@2950: int* frame_size_in_words) { never@2950: (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize; never@2950: never@2950: address start = __ pc(); never@2950: never@2950: #ifdef ASSERT never@2950: __ illtrap(0); __ illtrap(0); __ illtrap(0); never@2950: // here's a hint of something special: never@2950: __ set(MAGIC_NUMBER_1, G0); never@2950: __ set(MAGIC_NUMBER_2, G0); never@2950: #endif //ASSERT never@2950: __ illtrap(0); // not reached never@2950: never@2950: // Return values are in registers. never@2950: // L1_continuation contains a cleanup continuation we must return never@2950: // to. never@2950: never@2950: (*bounce_offset) = __ pc() - start; never@2950: BLOCK_COMMENT("ricochet_blob.bounce"); never@2950: never@2950: if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); never@2950: trace_method_handle(_masm, "ricochet_blob.bounce"); never@2950: never@2950: __ JMP(L1_continuation, 0); never@2950: __ delayed()->nop(); never@2950: __ illtrap(0); never@2950: never@2950: DEBUG_ONLY(__ set(MAGIC_NUMBER_2, G0)); never@2950: never@2950: (*exception_offset) = __ pc() - start; never@2950: BLOCK_COMMENT("ricochet_blob.exception"); never@2950: never@2950: // compare this to Interpreter::rethrow_exception_entry, which is parallel code never@2950: // for example, see TemplateInterpreterGenerator::generate_throw_exception never@2950: // Live registers in: never@2950: // Oexception (O0): exception never@2950: // Oissuing_pc (O1): return address/pc that threw exception (ignored, always equal to bounce addr) never@2950: __ verify_oop(Oexception); never@2950: never@2950: // Take down the frame. never@2950: never@2950: // Cf. InterpreterMacroAssembler::remove_activation. never@2950: leave_ricochet_frame(_masm, /*recv_reg=*/ noreg, I5_savedSP, I7); never@2950: never@2950: // We are done with this activation frame; find out where to go next. never@2950: // The continuation point will be an exception handler, which expects never@2950: // the following registers set up: never@2950: // never@2950: // Oexception: exception never@2950: // Oissuing_pc: the local call that threw exception never@2950: // Other On: garbage never@2950: // In/Ln: the contents of the caller's register window never@2950: // never@2950: // We do the required restore at the last possible moment, because we never@2950: // need to preserve some state across a runtime call. never@2950: // (Remember that the caller activation is unknown--it might not be never@2950: // interpreted, so things like Lscratch are useless in the caller.) never@2950: __ mov(Oexception, Oexception ->after_save()); // get exception in I0 so it will be on O0 after restore never@2950: __ add(I7, frame::pc_return_offset, Oissuing_pc->after_save()); // likewise set I1 to a value local to the caller never@2950: __ call_VM_leaf(L7_thread_cache, never@2950: CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), never@2950: G2_thread, Oissuing_pc->after_save()); never@2950: never@2950: // The caller's SP was adjusted upon method entry to accomodate never@2950: // the callee's non-argument locals. Undo that adjustment. never@2950: __ JMP(O0, 0); // return exception handler in caller never@2950: __ delayed()->restore(I5_savedSP, G0, SP); never@2950: never@2950: // (same old exception object is already in Oexception; see above) never@2950: // Note that an "issuing PC" is actually the next PC after the call never@2950: } never@2950: never@2950: void MethodHandles::RicochetFrame::enter_ricochet_frame(MacroAssembler* _masm, never@2950: Register recv_reg, never@2950: Register argv_reg, never@2950: address return_handler) { never@2950: // does not include the __ save() never@2950: assert(argv_reg == Gargs, ""); never@2950: Address G3_mh_vmtarget( recv_reg, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes()); never@2950: Address G3_amh_conversion(recv_reg, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes()); never@2950: never@2950: // Create the RicochetFrame. never@2950: // Unlike on x86 we can store all required information in local never@2950: // registers. never@2950: BLOCK_COMMENT("push RicochetFrame {"); never@2950: __ set(ExternalAddress(return_handler), L1_continuation); never@2950: __ load_heap_oop(G3_mh_vmtarget, L2_saved_target); never@2950: __ mov(G0, L3_saved_args_layout); never@2950: __ mov(Gargs, L4_saved_args_base); never@2950: __ lduw(G3_amh_conversion, L5_conversion); // 32-bit field never@2950: // I5, I6, I7 are already set up never@2950: DEBUG_ONLY(__ set((int32_t) MAGIC_NUMBER_1, L0_magic_number_1)); never@2950: BLOCK_COMMENT("} RicochetFrame"); never@2950: } never@2950: never@2950: void MethodHandles::RicochetFrame::leave_ricochet_frame(MacroAssembler* _masm, never@2950: Register recv_reg, never@2950: Register new_sp_reg, never@2950: Register sender_pc_reg) { never@2950: assert(new_sp_reg == I5_savedSP, "exact_sender_sp already in place"); never@2950: assert(sender_pc_reg == I7, "in a fixed place"); never@2950: // does not include the __ ret() & __ restore() never@2950: assert_different_registers(recv_reg, new_sp_reg, sender_pc_reg); never@2950: // Take down the frame. never@2950: // Cf. InterpreterMacroAssembler::remove_activation. never@2950: BLOCK_COMMENT("end_ricochet_frame {"); never@2950: if (recv_reg->is_valid()) never@2950: __ mov(L2_saved_target, recv_reg); never@2950: BLOCK_COMMENT("} end_ricochet_frame"); never@2950: } never@2950: never@2950: // Emit code to verify that FP is pointing at a valid ricochet frame. never@2950: #ifdef ASSERT never@2950: enum { never@2950: ARG_LIMIT = 255, SLOP = 35, never@2950: // use this parameter for checking for garbage stack movements: never@2950: UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP) never@2950: // the slop defends against false alarms due to fencepost errors never@2950: }; never@2950: never@2950: void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) { never@2950: // The stack should look like this: never@2950: // ... keep1 | dest=42 | keep2 | magic | handler | magic | recursive args | [RF] never@2950: // Check various invariants. never@2950: never@2950: Register O7_temp = O7, O5_temp = O5; never@2950: never@2950: Label L_ok_1, L_ok_2, L_ok_3, L_ok_4; never@2950: BLOCK_COMMENT("verify_clean {"); never@2950: // Magic numbers must check out: never@2950: __ set((int32_t) MAGIC_NUMBER_1, O7_temp); never@2950: __ cmp(O7_temp, L0_magic_number_1); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_ok_1); never@2950: __ delayed()->nop(); never@2950: __ stop("damaged ricochet frame: MAGIC_NUMBER_1 not found"); never@2950: never@2950: __ BIND(L_ok_1); never@2950: never@2950: // Arguments pointer must look reasonable: never@2950: #ifdef _LP64 never@2950: Register FP_temp = O5_temp; never@2950: __ add(FP, STACK_BIAS, FP_temp); never@2950: #else never@2950: Register FP_temp = FP; never@2950: #endif never@2950: __ cmp(L4_saved_args_base, FP_temp); never@2950: __ br(Assembler::greaterEqualUnsigned, false, Assembler::pt, L_ok_2); never@2950: __ delayed()->nop(); never@2950: __ stop("damaged ricochet frame: L4 < FP"); never@2950: never@2950: __ BIND(L_ok_2); never@2950: __ sub(L4_saved_args_base, UNREASONABLE_STACK_MOVE * Interpreter::stackElementSize, O7_temp); never@2950: __ cmp(O7_temp, FP_temp); never@2950: __ br(Assembler::lessEqualUnsigned, false, Assembler::pt, L_ok_3); never@2950: __ delayed()->nop(); never@2950: __ stop("damaged ricochet frame: (L4 - UNREASONABLE_STACK_MOVE) > FP"); never@2950: never@2950: __ BIND(L_ok_3); never@2950: extract_conversion_dest_type(_masm, L5_conversion, O7_temp); never@2950: __ cmp(O7_temp, T_VOID); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_ok_4); never@2950: __ delayed()->nop(); never@2950: extract_conversion_vminfo(_masm, L5_conversion, O5_temp); never@2950: __ ld_ptr(L4_saved_args_base, __ argument_offset(O5_temp, O5_temp), O7_temp); never@2950: assert(__ is_simm13(RETURN_VALUE_PLACEHOLDER), "must be simm13"); never@2950: __ cmp(O7_temp, (int32_t) RETURN_VALUE_PLACEHOLDER); never@2950: __ brx(Assembler::equal, false, Assembler::pt, L_ok_4); never@2950: __ delayed()->nop(); never@2950: __ stop("damaged ricochet frame: RETURN_VALUE_PLACEHOLDER not found"); never@2950: __ BIND(L_ok_4); never@2950: BLOCK_COMMENT("} verify_clean"); never@2950: } never@2950: #endif //ASSERT never@2950: never@2950: void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg, Register temp_reg, Register temp2_reg) { never@2950: if (VerifyMethodHandles) never@2950: verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(), temp_reg, temp2_reg, never@2950: "AMH argument is a Class"); never@2950: __ load_heap_oop(Address(klass_reg, java_lang_Class::klass_offset_in_bytes()), klass_reg); never@2950: } never@2950: never@2950: void MethodHandles::load_conversion_vminfo(MacroAssembler* _masm, Address conversion_field_addr, Register reg) { never@2950: assert(CONV_VMINFO_SHIFT == 0, "preshifted"); never@2950: assert(CONV_VMINFO_MASK == right_n_bits(BitsPerByte), "else change type of following load"); never@2950: __ ldub(conversion_field_addr.plus_disp(BytesPerInt - 1), reg); never@2950: } never@2950: never@2950: void MethodHandles::extract_conversion_vminfo(MacroAssembler* _masm, Register conversion_field_reg, Register reg) { never@2950: assert(CONV_VMINFO_SHIFT == 0, "preshifted"); never@2950: __ and3(conversion_field_reg, CONV_VMINFO_MASK, reg); never@2950: } never@2950: never@2950: void MethodHandles::extract_conversion_dest_type(MacroAssembler* _masm, Register conversion_field_reg, Register reg) { never@2950: __ srl(conversion_field_reg, CONV_DEST_TYPE_SHIFT, reg); never@2950: __ and3(reg, 0x0F, reg); never@2950: } never@2950: never@2950: void MethodHandles::load_stack_move(MacroAssembler* _masm, never@2950: Address G3_amh_conversion, never@2950: Register stack_move_reg) { never@2950: BLOCK_COMMENT("load_stack_move {"); never@2950: __ ldsw(G3_amh_conversion, stack_move_reg); never@2950: __ sra(stack_move_reg, CONV_STACK_MOVE_SHIFT, stack_move_reg); never@2950: if (VerifyMethodHandles) { never@2950: Label L_ok, L_bad; never@2950: int32_t stack_move_limit = 0x0800; // extra-large never@2950: __ cmp(stack_move_reg, stack_move_limit); never@2950: __ br(Assembler::greaterEqual, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: __ cmp(stack_move_reg, -stack_move_limit); never@2950: __ br(Assembler::greater, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop("load_stack_move of garbage value"); never@2950: __ BIND(L_ok); never@2950: } never@2950: BLOCK_COMMENT("} load_stack_move"); never@2950: } never@2950: never@2950: #ifdef ASSERT never@2950: void MethodHandles::RicochetFrame::verify() const { never@2950: assert(magic_number_1() == MAGIC_NUMBER_1, ""); never@2950: if (!Universe::heap()->is_gc_active()) { never@2950: if (saved_args_layout() != NULL) { never@2950: assert(saved_args_layout()->is_method(), "must be valid oop"); never@2950: } never@2950: if (saved_target() != NULL) { never@2950: assert(java_lang_invoke_MethodHandle::is_instance(saved_target()), "checking frame value"); never@2950: } never@2950: } never@2950: int conv_op = adapter_conversion_op(conversion()); never@2950: assert(conv_op == java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS || never@2950: conv_op == java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS || never@2950: conv_op == java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF, never@2950: "must be a sane conversion"); never@2950: if (has_return_value_slot()) { never@2950: assert(*return_value_slot_addr() == RETURN_VALUE_PLACEHOLDER, ""); never@2950: } never@2950: } never@2950: never@2950: void MethodHandles::verify_argslot(MacroAssembler* _masm, Register argslot_reg, Register temp_reg, const char* error_message) { never@2950: // Verify that argslot lies within (Gargs, FP]. never@2950: Label L_ok, L_bad; never@2950: BLOCK_COMMENT("verify_argslot {"); never@2950: __ add(FP, STACK_BIAS, temp_reg); // STACK_BIAS is zero on !_LP64 never@2950: __ cmp(argslot_reg, temp_reg); never@2950: __ brx(Assembler::greaterUnsigned, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: __ cmp(Gargs, argslot_reg); never@2950: __ brx(Assembler::lessEqualUnsigned, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop(error_message); never@2950: __ BIND(L_ok); never@2950: BLOCK_COMMENT("} verify_argslot"); never@2950: } never@2950: never@2950: void MethodHandles::verify_argslots(MacroAssembler* _masm, never@2950: RegisterOrConstant arg_slots, never@2950: Register arg_slot_base_reg, never@2950: Register temp_reg, never@2950: Register temp2_reg, never@2950: bool negate_argslots, never@2950: const char* error_message) { never@2950: // Verify that [argslot..argslot+size) lies within (Gargs, FP). never@2950: Label L_ok, L_bad; never@2950: BLOCK_COMMENT("verify_argslots {"); never@2950: if (negate_argslots) { never@2950: if (arg_slots.is_constant()) { never@2950: arg_slots = -1 * arg_slots.as_constant(); never@2950: } else { never@2950: __ neg(arg_slots.as_register(), temp_reg); never@2950: arg_slots = temp_reg; never@2950: } never@2950: } never@2950: __ add(arg_slot_base_reg, __ argument_offset(arg_slots, temp_reg), temp_reg); never@2950: __ add(FP, STACK_BIAS, temp2_reg); // STACK_BIAS is zero on !_LP64 never@2950: __ cmp(temp_reg, temp2_reg); never@2950: __ brx(Assembler::greaterUnsigned, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: // Gargs points to the first word so adjust by BytesPerWord never@2950: __ add(arg_slot_base_reg, BytesPerWord, temp_reg); never@2950: __ cmp(Gargs, temp_reg); never@2950: __ brx(Assembler::lessEqualUnsigned, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop(error_message); never@2950: __ BIND(L_ok); never@2950: BLOCK_COMMENT("} verify_argslots"); never@2950: } never@2950: never@2950: // Make sure that arg_slots has the same sign as the given direction. never@2950: // If (and only if) arg_slots is a assembly-time constant, also allow it to be zero. never@2950: void MethodHandles::verify_stack_move(MacroAssembler* _masm, never@2950: RegisterOrConstant arg_slots, int direction) { never@2950: enum { UNREASONABLE_STACK_MOVE = 256 * 4 }; // limit of 255 arguments never@2950: bool allow_zero = arg_slots.is_constant(); never@2950: if (direction == 0) { direction = +1; allow_zero = true; } never@2950: assert(stack_move_unit() == -1, "else add extra checks here"); never@2950: if (arg_slots.is_register()) { never@2950: Label L_ok, L_bad; never@2950: BLOCK_COMMENT("verify_stack_move {"); never@2950: // __ btst(-stack_move_unit() - 1, arg_slots.as_register()); // no need never@2950: // __ br(Assembler::notZero, false, Assembler::pn, L_bad); never@2950: // __ delayed()->nop(); never@2950: __ cmp(arg_slots.as_register(), (int32_t) NULL_WORD); never@2950: if (direction > 0) { never@2950: __ br(allow_zero ? Assembler::less : Assembler::lessEqual, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: __ cmp(arg_slots.as_register(), (int32_t) UNREASONABLE_STACK_MOVE); never@2950: __ br(Assembler::less, false, Assembler::pn, L_ok); never@2950: __ delayed()->nop(); never@2950: } else { never@2950: __ br(allow_zero ? Assembler::greater : Assembler::greaterEqual, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: __ cmp(arg_slots.as_register(), (int32_t) -UNREASONABLE_STACK_MOVE); never@2950: __ br(Assembler::greater, false, Assembler::pn, L_ok); never@2950: __ delayed()->nop(); never@2950: } never@2950: __ BIND(L_bad); never@2950: if (direction > 0) never@2950: __ stop("assert arg_slots > 0"); never@2950: else never@2950: __ stop("assert arg_slots < 0"); never@2950: __ BIND(L_ok); never@2950: BLOCK_COMMENT("} verify_stack_move"); never@2950: } else { never@2950: intptr_t size = arg_slots.as_constant(); never@2950: if (direction < 0) size = -size; never@2950: assert(size >= 0, "correct direction of constant move"); never@2950: assert(size < UNREASONABLE_STACK_MOVE, "reasonable size of constant move"); never@2950: } never@2950: } never@2950: never@2950: void MethodHandles::verify_klass(MacroAssembler* _masm, never@2950: Register obj_reg, KlassHandle klass, never@2950: Register temp_reg, Register temp2_reg, never@2950: const char* error_message) { never@2950: oop* klass_addr = klass.raw_value(); never@2950: assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() && never@2950: klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(), never@2950: "must be one of the SystemDictionaryHandles"); never@2950: Label L_ok, L_bad; never@2950: BLOCK_COMMENT("verify_klass {"); never@2950: __ verify_oop(obj_reg); never@2950: __ br_null(obj_reg, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: __ load_klass(obj_reg, temp_reg); never@2950: __ set(ExternalAddress(klass_addr), temp2_reg); never@2950: __ ld_ptr(Address(temp2_reg, 0), temp2_reg); never@2950: __ cmp(temp_reg, temp2_reg); never@2950: __ brx(Assembler::equal, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: intptr_t super_check_offset = klass->super_check_offset(); never@2950: __ ld_ptr(Address(temp_reg, super_check_offset), temp_reg); never@2950: __ set(ExternalAddress(klass_addr), temp2_reg); never@2950: __ ld_ptr(Address(temp2_reg, 0), temp2_reg); never@2950: __ cmp(temp_reg, temp2_reg); never@2950: __ brx(Assembler::equal, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop(error_message); never@2950: __ BIND(L_ok); never@2950: BLOCK_COMMENT("} verify_klass"); never@2950: } never@2950: #endif // ASSERT jrose@1145: jrose@1145: // Code generation jrose@1145: address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) { twisti@2271: // I5_savedSP/O5_savedSP: sender SP (must preserve) twisti@1858: // G4 (Gargs): incoming argument list (must preserve) twisti@2271: // G5_method: invoke methodOop twisti@1858: // G3_method_handle: receiver method handle (must load from sp[MethodTypeForm.vmslots]) twisti@2271: // O0, O1, O2, O3, O4: garbage temps, blown away twisti@2271: Register O0_mtype = O0; twisti@1858: Register O1_scratch = O1; jrose@2266: Register O2_scratch = O2; jrose@2266: Register O3_scratch = O3; twisti@2271: Register O4_argslot = O4; jrose@2266: Register O4_argbase = O4; twisti@1858: twisti@1858: // emit WrongMethodType path first, to enable back-branch from main path twisti@1858: Label wrong_method_type; twisti@1858: __ bind(wrong_method_type); jrose@2266: Label invoke_generic_slow_path; jrose@2266: assert(methodOopDesc::intrinsic_id_size_in_bytes() == sizeof(u1), "");; jrose@2266: __ ldub(Address(G5_method, methodOopDesc::intrinsic_id_offset_in_bytes()), O1_scratch); jrose@2266: __ cmp(O1_scratch, (int) vmIntrinsics::_invokeExact); jrose@2266: __ brx(Assembler::notEqual, false, Assembler::pt, invoke_generic_slow_path); jrose@2266: __ delayed()->nop(); twisti@2271: __ mov(O0_mtype, G5_method_type); // required by throw_WrongMethodType jrose@2266: // mov(G3_method_handle, G3_method_handle); // already in this register twisti@1858: __ jump_to(AddressLiteral(Interpreter::throw_WrongMethodType_entry()), O1_scratch); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: // here's where control starts out: twisti@1858: __ align(CodeEntryAlignment); twisti@1858: address entry_point = __ pc(); twisti@1858: jrose@2266: // fetch the MethodType from the method handle never@2950: // FIXME: Interpreter should transmit pre-popped stack pointer, to locate base of arg list. never@2950: // This would simplify several touchy bits of code. never@2950: // See 6984712: JSR 292 method handle calls need a clean argument base pointer twisti@1858: { twisti@1858: Register tem = G5_method; twisti@1858: for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) { twisti@2271: __ ld_ptr(Address(tem, *pchase), O0_mtype); twisti@2271: tem = O0_mtype; // in case there is another indirection twisti@1858: } twisti@1858: } twisti@1858: twisti@1858: // given the MethodType, find out where the MH argument is buried jrose@2639: __ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O4_argslot); jrose@2639: __ ldsw( Address(O4_argslot, __ delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, O1_scratch)), O4_argslot); never@2950: __ add(__ argument_address(O4_argslot, O4_argslot, 1), O4_argbase); jrose@2266: // Note: argument_address uses its input as a scratch register! never@2950: Address mh_receiver_slot_addr(O4_argbase, -Interpreter::stackElementSize); never@2950: __ ld_ptr(mh_receiver_slot_addr, G3_method_handle); twisti@1858: jrose@2266: trace_method_handle(_masm, "invokeExact"); jrose@2266: twisti@2271: __ check_method_handle_type(O0_mtype, G3_method_handle, O1_scratch, wrong_method_type); never@2950: never@2950: // Nobody uses the MH receiver slot after this. Make sure. never@2950: DEBUG_ONLY(__ set((int32_t) 0x999999, O1_scratch); __ st_ptr(O1_scratch, mh_receiver_slot_addr)); never@2950: twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: jrose@2266: // for invokeGeneric (only), apply argument and result conversions on the fly jrose@2266: __ bind(invoke_generic_slow_path); jrose@2266: #ifdef ASSERT never@2950: if (VerifyMethodHandles) { never@2950: Label L; jrose@2266: __ ldub(Address(G5_method, methodOopDesc::intrinsic_id_offset_in_bytes()), O1_scratch); jrose@2266: __ cmp(O1_scratch, (int) vmIntrinsics::_invokeGeneric); jrose@2266: __ brx(Assembler::equal, false, Assembler::pt, L); jrose@2266: __ delayed()->nop(); jrose@2266: __ stop("bad methodOop::intrinsic_id"); jrose@2266: __ bind(L); jrose@2266: } jrose@2266: #endif //ASSERT jrose@2266: jrose@2266: // make room on the stack for another pointer: never@2950: insert_arg_slots(_masm, 2 * stack_move_unit(), O4_argbase, O1_scratch, O2_scratch, O3_scratch); jrose@2266: // load up an adapter from the calling type (Java weaves this) jrose@2266: Register O2_form = O2_scratch; jrose@2266: Register O3_adapter = O3_scratch; jrose@2639: __ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O2_form); twisti@2903: __ load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter); twisti@2903: __ verify_oop(O3_adapter); jrose@2266: __ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize)); jrose@2266: // As a trusted first argument, pass the type being called, so the adapter knows jrose@2266: // the actual types of the arguments and return values. jrose@2266: // (Generic invokers are shared among form-families of method-type.) twisti@2271: __ st_ptr(O0_mtype, Address(O4_argbase, 0 * Interpreter::stackElementSize)); jrose@2266: // FIXME: assert that O3_adapter is of the right method-type. jrose@2266: __ mov(O3_adapter, G3_method_handle); jrose@2266: trace_method_handle(_masm, "invokeGeneric"); jrose@2266: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); jrose@2266: twisti@1858: return entry_point; jrose@1145: } jrose@1145: never@2950: // Workaround for C++ overloading nastiness on '0' for RegisterOrConstant. never@2950: static RegisterOrConstant constant(int value) { never@2950: return RegisterOrConstant(value); never@2950: } twisti@1858: never@2950: static void load_vmargslot(MacroAssembler* _masm, Address vmargslot_addr, Register result) { never@2950: __ ldsw(vmargslot_addr, result); never@2950: } never@2950: never@2950: static RegisterOrConstant adjust_SP_and_Gargs_down_by_slots(MacroAssembler* _masm, never@2950: RegisterOrConstant arg_slots, never@2950: Register temp_reg, Register temp2_reg) { never@2950: // Keep the stack pointer 2*wordSize aligned. never@2950: const int TwoWordAlignmentMask = right_n_bits(LogBytesPerWord + 1); never@2950: if (arg_slots.is_constant()) { never@2950: const int offset = arg_slots.as_constant() << LogBytesPerWord; never@2950: const int masked_offset = round_to(offset, 2 * BytesPerWord); never@2950: const int masked_offset2 = (offset + 1*BytesPerWord) & ~TwoWordAlignmentMask; never@2950: assert(masked_offset == masked_offset2, "must agree"); never@2950: __ sub(Gargs, offset, Gargs); never@2950: __ sub(SP, masked_offset, SP ); never@2950: return offset; never@2950: } else { twisti@1858: #ifdef ASSERT never@2950: { never@2950: Label L_ok; never@2950: __ cmp(arg_slots.as_register(), 0); never@2950: __ br(Assembler::greaterEqual, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ stop("negative arg_slots"); never@2950: __ bind(L_ok); never@2950: } twisti@1858: #endif never@2950: __ sll_ptr(arg_slots.as_register(), LogBytesPerWord, temp_reg); never@2950: __ add( temp_reg, 1*BytesPerWord, temp2_reg); never@2950: __ andn(temp2_reg, TwoWordAlignmentMask, temp2_reg); never@2950: __ sub(Gargs, temp_reg, Gargs); never@2950: __ sub(SP, temp2_reg, SP ); never@2950: return temp_reg; never@2950: } twisti@1858: } twisti@1858: never@2950: static RegisterOrConstant adjust_SP_and_Gargs_up_by_slots(MacroAssembler* _masm, never@2950: RegisterOrConstant arg_slots, never@2950: Register temp_reg, Register temp2_reg) { never@2950: // Keep the stack pointer 2*wordSize aligned. never@2950: const int TwoWordAlignmentMask = right_n_bits(LogBytesPerWord + 1); never@2950: if (arg_slots.is_constant()) { never@2950: const int offset = arg_slots.as_constant() << LogBytesPerWord; never@2950: const int masked_offset = offset & ~TwoWordAlignmentMask; never@2950: __ add(Gargs, offset, Gargs); never@2950: __ add(SP, masked_offset, SP ); never@2950: return offset; never@2950: } else { never@2950: __ sll_ptr(arg_slots.as_register(), LogBytesPerWord, temp_reg); never@2950: __ andn(temp_reg, TwoWordAlignmentMask, temp2_reg); never@2950: __ add(Gargs, temp_reg, Gargs); never@2950: __ add(SP, temp2_reg, SP ); never@2950: return temp_reg; never@2950: } never@2950: } twisti@1858: twisti@1858: // Helper to insert argument slots into the stack. never@2950: // arg_slots must be a multiple of stack_move_unit() and < 0 never@2950: // argslot_reg is decremented to point to the new (shifted) location of the argslot never@2950: // But, temp_reg ends up holding the original value of argslot_reg. twisti@1858: void MethodHandles::insert_arg_slots(MacroAssembler* _masm, twisti@1858: RegisterOrConstant arg_slots, twisti@1858: Register argslot_reg, twisti@1858: Register temp_reg, Register temp2_reg, Register temp3_reg) { never@2950: // allow constant zero never@2950: if (arg_slots.is_constant() && arg_slots.as_constant() == 0) never@2950: return; never@2950: twisti@1858: assert_different_registers(argslot_reg, temp_reg, temp2_reg, temp3_reg, twisti@1858: (!arg_slots.is_register() ? Gargs : arg_slots.as_register())); twisti@1858: never@2950: BLOCK_COMMENT("insert_arg_slots {"); never@2950: if (VerifyMethodHandles) never@2950: verify_argslot(_masm, argslot_reg, temp_reg, "insertion point must fall within current frame"); never@2950: if (VerifyMethodHandles) never@2950: verify_stack_move(_masm, arg_slots, -1); twisti@1858: twisti@1858: // Make space on the stack for the inserted argument(s). twisti@1858: // Then pull down everything shallower than argslot_reg. twisti@1858: // The stacked return address gets pulled down with everything else. twisti@1858: // That is, copy [sp, argslot) downward by -size words. In pseudo-code: twisti@1858: // sp -= size; twisti@1858: // for (temp = sp + size; temp < argslot; temp++) twisti@1858: // temp[-size] = temp[0] twisti@1858: // argslot -= size; twisti@1858: never@2950: // offset is temp3_reg in case of arg_slots being a register. never@2950: RegisterOrConstant offset = adjust_SP_and_Gargs_up_by_slots(_masm, arg_slots, temp3_reg, temp_reg); never@2950: __ sub(Gargs, offset, temp_reg); // source pointer for copy twisti@1858: twisti@1858: { twisti@1858: Label loop; twisti@2204: __ BIND(loop); twisti@1858: // pull one word down each time through the loop never@2950: __ ld_ptr( Address(temp_reg, 0 ), temp2_reg); never@2950: __ st_ptr(temp2_reg, Address(temp_reg, offset) ); twisti@1858: __ add(temp_reg, wordSize, temp_reg); twisti@1858: __ cmp(temp_reg, argslot_reg); never@2950: __ brx(Assembler::lessUnsigned, false, Assembler::pt, loop); twisti@1858: __ delayed()->nop(); // FILLME twisti@1858: } twisti@1858: twisti@1858: // Now move the argslot down, to point to the opened-up space. twisti@1858: __ add(argslot_reg, offset, argslot_reg); twisti@2204: BLOCK_COMMENT("} insert_arg_slots"); twisti@1858: } twisti@1858: twisti@1858: twisti@1858: // Helper to remove argument slots from the stack. never@2950: // arg_slots must be a multiple of stack_move_unit() and > 0 twisti@1858: void MethodHandles::remove_arg_slots(MacroAssembler* _masm, twisti@1858: RegisterOrConstant arg_slots, twisti@1858: Register argslot_reg, twisti@1858: Register temp_reg, Register temp2_reg, Register temp3_reg) { never@2950: // allow constant zero never@2950: if (arg_slots.is_constant() && arg_slots.as_constant() == 0) never@2950: return; twisti@1858: assert_different_registers(argslot_reg, temp_reg, temp2_reg, temp3_reg, twisti@1858: (!arg_slots.is_register() ? Gargs : arg_slots.as_register())); twisti@1858: never@2950: BLOCK_COMMENT("remove_arg_slots {"); never@2950: if (VerifyMethodHandles) never@2950: verify_argslots(_masm, arg_slots, argslot_reg, temp_reg, temp2_reg, false, never@2950: "deleted argument(s) must fall within current frame"); never@2950: if (VerifyMethodHandles) never@2950: verify_stack_move(_masm, arg_slots, +1); twisti@1858: twisti@1858: // Pull up everything shallower than argslot. twisti@1858: // Then remove the excess space on the stack. twisti@1858: // The stacked return address gets pulled up with everything else. twisti@1858: // That is, copy [sp, argslot) upward by size words. In pseudo-code: twisti@1858: // for (temp = argslot-1; temp >= sp; --temp) twisti@1858: // temp[size] = temp[0] twisti@1858: // argslot += size; twisti@1858: // sp += size; never@2950: never@2950: RegisterOrConstant offset = __ regcon_sll_ptr(arg_slots, LogBytesPerWord, temp3_reg); twisti@1858: __ sub(argslot_reg, wordSize, temp_reg); // source pointer for copy never@2950: twisti@1858: { never@2950: Label L_loop; never@2950: __ BIND(L_loop); twisti@1858: // pull one word up each time through the loop never@2950: __ ld_ptr( Address(temp_reg, 0 ), temp2_reg); never@2950: __ st_ptr(temp2_reg, Address(temp_reg, offset) ); twisti@1858: __ sub(temp_reg, wordSize, temp_reg); twisti@1858: __ cmp(temp_reg, Gargs); never@2950: __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pt, L_loop); twisti@1858: __ delayed()->nop(); // FILLME twisti@1858: } twisti@1858: twisti@1858: // And adjust the argslot address to point at the deletion point. twisti@1858: __ add(argslot_reg, offset, argslot_reg); twisti@1858: never@2950: // We don't need the offset at this point anymore, just adjust SP and Gargs. never@2950: (void) adjust_SP_and_Gargs_up_by_slots(_masm, arg_slots, temp3_reg, temp_reg); never@2950: twisti@2204: BLOCK_COMMENT("} remove_arg_slots"); twisti@1858: } twisti@1858: never@2950: // Helper to copy argument slots to the top of the stack. never@2950: // The sequence starts with argslot_reg and is counted by slot_count never@2950: // slot_count must be a multiple of stack_move_unit() and >= 0 never@2950: // This function blows the temps but does not change argslot_reg. never@2950: void MethodHandles::push_arg_slots(MacroAssembler* _masm, never@2950: Register argslot_reg, never@2950: RegisterOrConstant slot_count, never@2950: Register temp_reg, Register temp2_reg) { never@2950: // allow constant zero never@2950: if (slot_count.is_constant() && slot_count.as_constant() == 0) never@2950: return; never@2950: assert_different_registers(argslot_reg, temp_reg, temp2_reg, never@2950: (!slot_count.is_register() ? Gargs : slot_count.as_register()), never@2950: SP); never@2950: assert(Interpreter::stackElementSize == wordSize, "else change this code"); never@2950: never@2950: BLOCK_COMMENT("push_arg_slots {"); never@2950: if (VerifyMethodHandles) never@2950: verify_stack_move(_masm, slot_count, 0); never@2950: never@2950: RegisterOrConstant offset = adjust_SP_and_Gargs_down_by_slots(_masm, slot_count, temp2_reg, temp_reg); never@2950: never@2950: if (slot_count.is_constant()) { never@2950: for (int i = slot_count.as_constant() - 1; i >= 0; i--) { never@2950: __ ld_ptr( Address(argslot_reg, i * wordSize), temp_reg); never@2950: __ st_ptr(temp_reg, Address(Gargs, i * wordSize)); never@2950: } never@2950: } else { never@2950: Label L_plural, L_loop, L_break; never@2950: // Emit code to dynamically check for the common cases, zero and one slot. never@2950: __ cmp(slot_count.as_register(), (int32_t) 1); never@2950: __ br(Assembler::greater, false, Assembler::pn, L_plural); never@2950: __ delayed()->nop(); never@2950: __ br(Assembler::less, false, Assembler::pn, L_break); never@2950: __ delayed()->nop(); never@2950: __ ld_ptr( Address(argslot_reg, 0), temp_reg); never@2950: __ st_ptr(temp_reg, Address(Gargs, 0)); never@2950: __ ba(false, L_break); never@2950: __ delayed()->nop(); // FILLME never@2950: __ BIND(L_plural); never@2950: never@2950: // Loop for 2 or more: never@2950: // top = &argslot[slot_count] never@2950: // while (top > argslot) *(--Gargs) = *(--top) never@2950: Register top_reg = temp_reg; never@2950: __ add(argslot_reg, offset, top_reg); never@2950: __ add(Gargs, offset, Gargs ); // move back up again so we can go down never@2950: __ BIND(L_loop); never@2950: __ sub(top_reg, wordSize, top_reg); never@2950: __ sub(Gargs, wordSize, Gargs ); never@2950: __ ld_ptr( Address(top_reg, 0), temp2_reg); never@2950: __ st_ptr(temp2_reg, Address(Gargs, 0)); never@2950: __ cmp(top_reg, argslot_reg); never@2950: __ brx(Assembler::greaterUnsigned, false, Assembler::pt, L_loop); never@2950: __ delayed()->nop(); // FILLME never@2950: __ BIND(L_break); never@2950: } never@2950: BLOCK_COMMENT("} push_arg_slots"); never@2950: } never@2950: never@2950: // in-place movement; no change to Gargs never@2950: // blows temp_reg, temp2_reg never@2950: void MethodHandles::move_arg_slots_up(MacroAssembler* _masm, never@2950: Register bottom_reg, // invariant never@2950: Address top_addr, // can use temp_reg never@2950: RegisterOrConstant positive_distance_in_slots, // destroyed if register never@2950: Register temp_reg, Register temp2_reg) { never@2950: assert_different_registers(bottom_reg, never@2950: temp_reg, temp2_reg, never@2950: positive_distance_in_slots.register_or_noreg()); never@2950: BLOCK_COMMENT("move_arg_slots_up {"); never@2950: Label L_loop, L_break; never@2950: Register top_reg = temp_reg; never@2950: if (!top_addr.is_same_address(Address(top_reg, 0))) { never@2950: __ add(top_addr, top_reg); never@2950: } never@2950: // Detect empty (or broken) loop: never@2950: #ifdef ASSERT never@2950: if (VerifyMethodHandles) { never@2950: // Verify that &bottom < &top (non-empty interval) never@2950: Label L_ok, L_bad; never@2950: if (positive_distance_in_slots.is_register()) { never@2950: __ cmp(positive_distance_in_slots.as_register(), (int32_t) 0); never@2950: __ br(Assembler::lessEqual, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: } never@2950: __ cmp(bottom_reg, top_reg); never@2950: __ brx(Assembler::lessUnsigned, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop("valid bounds (copy up)"); never@2950: __ BIND(L_ok); never@2950: } never@2950: #endif never@2950: __ cmp(bottom_reg, top_reg); never@2950: __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pn, L_break); never@2950: __ delayed()->nop(); never@2950: // work top down to bottom, copying contiguous data upwards never@2950: // In pseudo-code: never@2950: // while (--top >= bottom) *(top + distance) = *(top + 0); never@2950: RegisterOrConstant offset = __ argument_offset(positive_distance_in_slots, positive_distance_in_slots.register_or_noreg()); never@2950: __ BIND(L_loop); never@2950: __ sub(top_reg, wordSize, top_reg); never@2950: __ ld_ptr( Address(top_reg, 0 ), temp2_reg); never@2950: __ st_ptr(temp2_reg, Address(top_reg, offset) ); never@2950: __ cmp(top_reg, bottom_reg); never@2950: __ brx(Assembler::greaterUnsigned, false, Assembler::pt, L_loop); never@2950: __ delayed()->nop(); // FILLME never@2950: assert(Interpreter::stackElementSize == wordSize, "else change loop"); never@2950: __ BIND(L_break); never@2950: BLOCK_COMMENT("} move_arg_slots_up"); never@2950: } never@2950: never@2950: // in-place movement; no change to rsp never@2950: // blows temp_reg, temp2_reg never@2950: void MethodHandles::move_arg_slots_down(MacroAssembler* _masm, never@2950: Address bottom_addr, // can use temp_reg never@2950: Register top_reg, // invariant never@2950: RegisterOrConstant negative_distance_in_slots, // destroyed if register never@2950: Register temp_reg, Register temp2_reg) { never@2950: assert_different_registers(top_reg, never@2950: negative_distance_in_slots.register_or_noreg(), never@2950: temp_reg, temp2_reg); never@2950: BLOCK_COMMENT("move_arg_slots_down {"); never@2950: Label L_loop, L_break; never@2950: Register bottom_reg = temp_reg; never@2950: if (!bottom_addr.is_same_address(Address(bottom_reg, 0))) { never@2950: __ add(bottom_addr, bottom_reg); never@2950: } never@2950: // Detect empty (or broken) loop: never@2950: #ifdef ASSERT never@2950: assert(!negative_distance_in_slots.is_constant() || negative_distance_in_slots.as_constant() < 0, ""); never@2950: if (VerifyMethodHandles) { never@2950: // Verify that &bottom < &top (non-empty interval) never@2950: Label L_ok, L_bad; never@2950: if (negative_distance_in_slots.is_register()) { never@2950: __ cmp(negative_distance_in_slots.as_register(), (int32_t) 0); never@2950: __ br(Assembler::greaterEqual, false, Assembler::pn, L_bad); never@2950: __ delayed()->nop(); never@2950: } never@2950: __ cmp(bottom_reg, top_reg); never@2950: __ brx(Assembler::lessUnsigned, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_bad); never@2950: __ stop("valid bounds (copy down)"); never@2950: __ BIND(L_ok); never@2950: } never@2950: #endif never@2950: __ cmp(bottom_reg, top_reg); never@2950: __ brx(Assembler::greaterEqualUnsigned, false, Assembler::pn, L_break); never@2950: __ delayed()->nop(); never@2950: // work bottom up to top, copying contiguous data downwards never@2950: // In pseudo-code: never@2950: // while (bottom < top) *(bottom - distance) = *(bottom + 0), bottom++; never@2950: RegisterOrConstant offset = __ argument_offset(negative_distance_in_slots, negative_distance_in_slots.register_or_noreg()); never@2950: __ BIND(L_loop); never@2950: __ ld_ptr( Address(bottom_reg, 0 ), temp2_reg); never@2950: __ st_ptr(temp2_reg, Address(bottom_reg, offset) ); never@2950: __ add(bottom_reg, wordSize, bottom_reg); never@2950: __ cmp(bottom_reg, top_reg); never@2950: __ brx(Assembler::lessUnsigned, false, Assembler::pt, L_loop); never@2950: __ delayed()->nop(); // FILLME never@2950: assert(Interpreter::stackElementSize == wordSize, "else change loop"); never@2950: __ BIND(L_break); never@2950: BLOCK_COMMENT("} move_arg_slots_down"); never@2950: } never@2950: never@2950: // Copy from a field or array element to a stacked argument slot. never@2950: // is_element (ignored) says whether caller is loading an array element instead of an instance field. never@2950: void MethodHandles::move_typed_arg(MacroAssembler* _masm, never@2950: BasicType type, bool is_element, never@2950: Address value_src, Address slot_dest, never@2950: Register temp_reg) { never@2950: assert(!slot_dest.uses(temp_reg), "must be different register"); never@2950: BLOCK_COMMENT(!is_element ? "move_typed_arg {" : "move_typed_arg { (array element)"); never@2950: if (type == T_OBJECT || type == T_ARRAY) { never@2950: __ load_heap_oop(value_src, temp_reg); never@2950: __ verify_oop(temp_reg); never@2950: __ st_ptr(temp_reg, slot_dest); never@2950: } else if (type != T_VOID) { never@2950: int arg_size = type2aelembytes(type); never@2950: bool arg_is_signed = is_signed_subword_type(type); never@2950: int slot_size = is_subword_type(type) ? type2aelembytes(T_INT) : arg_size; // store int sub-words as int never@2950: __ load_sized_value( value_src, temp_reg, arg_size, arg_is_signed); never@2950: __ store_sized_value(temp_reg, slot_dest, slot_size ); never@2950: } never@2950: BLOCK_COMMENT("} move_typed_arg"); never@2950: } never@2950: never@2950: // Cf. TemplateInterpreterGenerator::generate_return_entry_for and never@2950: // InterpreterMacroAssembler::save_return_value never@2950: void MethodHandles::move_return_value(MacroAssembler* _masm, BasicType type, never@2950: Address return_slot) { never@2950: BLOCK_COMMENT("move_return_value {"); never@2950: // Look at the type and pull the value out of the corresponding register. never@2950: if (type == T_VOID) { never@2950: // nothing to do never@2950: } else if (type == T_OBJECT) { never@2950: __ verify_oop(O0); never@2950: __ st_ptr(O0, return_slot); never@2950: } else if (type == T_INT || is_subword_type(type)) { never@2950: int type_size = type2aelembytes(T_INT); never@2950: __ store_sized_value(O0, return_slot, type_size); never@2950: } else if (type == T_LONG) { never@2950: // store the value by parts never@2950: // Note: We assume longs are continguous (if misaligned) on the interpreter stack. never@2950: #if !defined(_LP64) && defined(COMPILER2) never@2950: __ stx(G1, return_slot); never@2950: #else never@2950: #ifdef _LP64 never@2950: __ stx(O0, return_slot); never@2950: #else never@2950: if (return_slot.has_disp()) { never@2950: // The displacement is a constant never@2950: __ st(O0, return_slot); never@2950: __ st(O1, return_slot.plus_disp(Interpreter::stackElementSize)); never@2950: } else { never@2950: __ std(O0, return_slot); never@2950: } never@2950: #endif never@2950: #endif never@2950: } else if (type == T_FLOAT) { never@2950: __ stf(FloatRegisterImpl::S, Ftos_f, return_slot); never@2950: } else if (type == T_DOUBLE) { never@2950: __ stf(FloatRegisterImpl::D, Ftos_f, return_slot); never@2950: } else { never@2950: ShouldNotReachHere(); never@2950: } never@2950: BLOCK_COMMENT("} move_return_value"); never@2950: } twisti@1858: twisti@1858: #ifndef PRODUCT twisti@1858: extern "C" void print_method_handle(oop mh); twisti@1858: void trace_method_handle_stub(const char* adaptername, never@2868: oopDesc* mh, never@2868: intptr_t* saved_sp) { never@2950: bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have mh never@2868: tty->print_cr("MH %s mh="INTPTR_FORMAT " saved_sp=" INTPTR_FORMAT, adaptername, (intptr_t) mh, saved_sp); never@2950: if (has_mh) never@2950: print_method_handle(mh); twisti@1858: } twisti@2204: void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { twisti@2204: if (!TraceMethodHandles) return; twisti@2204: BLOCK_COMMENT("trace_method_handle {"); twisti@2204: // save: Gargs, O5_savedSP twisti@2204: __ save_frame(16); twisti@2204: __ set((intptr_t) adaptername, O0); twisti@2204: __ mov(G3_method_handle, O1); never@2868: __ mov(I5_savedSP, O2); twisti@2204: __ mov(G3_method_handle, L3); twisti@2204: __ mov(Gargs, L4); twisti@2204: __ mov(G5_method_type, L5); twisti@2204: __ call_VM_leaf(L7, CAST_FROM_FN_PTR(address, trace_method_handle_stub)); twisti@2204: twisti@2204: __ mov(L3, G3_method_handle); twisti@2204: __ mov(L4, Gargs); twisti@2204: __ mov(L5, G5_method_type); twisti@2204: __ restore(); twisti@2204: BLOCK_COMMENT("} trace_method_handle"); twisti@2204: } twisti@1858: #endif // PRODUCT twisti@1858: jrose@1862: // which conversion op types are implemented here? jrose@1862: int MethodHandles::adapter_conversion_ops_supported_mask() { jrose@2639: return ((1<from_compiled_entry(), "method must be linked"); twisti@1858: twisti@1858: __ mov(O5_savedSP, SP); // Cut the stack back to where the caller started. twisti@1858: twisti@2411: Label L_no_method; jrose@2639: // FIXME: fill in _raise_exception_method with a suitable java.lang.invoke method twisti@1858: __ set(AddressLiteral((address) &_raise_exception_method), G5_method); twisti@1858: __ ld_ptr(Address(G5_method, 0), G5_method); twisti@1858: __ tst(G5_method); twisti@2411: __ brx(Assembler::zero, false, Assembler::pn, L_no_method); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@2411: const int jobject_oop_offset = 0; twisti@1858: __ ld_ptr(Address(G5_method, jobject_oop_offset), G5_method); twisti@1858: __ tst(G5_method); twisti@2411: __ brx(Assembler::zero, false, Assembler::pn, L_no_method); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: __ verify_oop(G5_method); twisti@2603: __ jump_indirect_to(G5_method_fce, O3_scratch); // jump to compiled entry twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: // Do something that is at least causes a valid throw from the interpreter. twisti@2411: __ bind(L_no_method); twisti@2411: __ unimplemented("call throw_WrongMethodType_entry"); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _invokestatic_mh: twisti@1858: case _invokespecial_mh: twisti@1858: { twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop twisti@1858: __ verify_oop(G5_method); twisti@1858: // Same as TemplateTable::invokestatic or invokespecial, twisti@1858: // minus the CP setup and profiling: twisti@1858: if (ek == _invokespecial_mh) { twisti@1858: // Must load & check the first argument before entering the target method. twisti@1858: __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch); never@2950: __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle); twisti@1858: __ null_check(G3_method_handle); twisti@1858: __ verify_oop(G3_method_handle); twisti@1858: } twisti@1858: __ jump_indirect_to(G5_method_fie, O1_scratch); twisti@1858: __ delayed()->nop(); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _invokevirtual_mh: twisti@1858: { twisti@1858: // Same as TemplateTable::invokevirtual, twisti@1858: // minus the CP setup and profiling: twisti@1858: twisti@1858: // Pick out the vtable index and receiver offset from the MH, twisti@1858: // and then we can discard it: never@2950: Register O2_index = O2_scratch; twisti@1858: __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch); never@2950: __ ldsw(G3_dmh_vmindex, O2_index); twisti@1858: // Note: The verifier allows us to ignore G3_mh_vmtarget. never@2950: __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle); twisti@1858: __ null_check(G3_method_handle, oopDesc::klass_offset_in_bytes()); twisti@1858: twisti@1858: // Get receiver klass: twisti@1858: Register O0_klass = O0_argslot; twisti@1858: __ load_klass(G3_method_handle, O0_klass); twisti@1858: __ verify_oop(O0_klass); twisti@1858: twisti@1858: // Get target methodOop & entry point: twisti@1858: const int base = instanceKlass::vtable_start_offset() * wordSize; twisti@1858: assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); twisti@1858: never@2950: __ sll_ptr(O2_index, LogBytesPerWord, O2_index); never@2950: __ add(O0_klass, O2_index, O0_klass); twisti@1858: Address vtable_entry_addr(O0_klass, base + vtableEntry::method_offset_in_bytes()); twisti@1858: __ ld_ptr(vtable_entry_addr, G5_method); twisti@1858: twisti@1858: __ verify_oop(G5_method); twisti@1858: __ jump_indirect_to(G5_method_fie, O1_scratch); twisti@1858: __ delayed()->nop(); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _invokeinterface_mh: twisti@1858: { twisti@1858: // Same as TemplateTable::invokeinterface, twisti@1858: // minus the CP setup and profiling: twisti@1858: __ load_method_handle_vmslots(O0_argslot, G3_method_handle, O1_scratch); twisti@1858: Register O1_intf = O1_scratch; never@2950: Register G5_index = G5_scratch; twisti@2201: __ load_heap_oop(G3_mh_vmtarget, O1_intf); twisti@1858: __ ldsw(G3_dmh_vmindex, G5_index); never@2950: __ ld_ptr(__ argument_address(O0_argslot, O0_argslot, -1), G3_method_handle); twisti@1858: __ null_check(G3_method_handle, oopDesc::klass_offset_in_bytes()); twisti@1858: twisti@1858: // Get receiver klass: twisti@1858: Register O0_klass = O0_argslot; twisti@1858: __ load_klass(G3_method_handle, O0_klass); twisti@1858: __ verify_oop(O0_klass); twisti@1858: twisti@1858: // Get interface: twisti@1858: Label no_such_interface; twisti@1858: __ verify_oop(O1_intf); twisti@1858: __ lookup_interface_method(O0_klass, O1_intf, twisti@1858: // Note: next two args must be the same: twisti@1858: G5_index, G5_method, twisti@1858: O2_scratch, twisti@1858: O3_scratch, twisti@1858: no_such_interface); twisti@1858: twisti@1858: __ verify_oop(G5_method); twisti@1858: __ jump_indirect_to(G5_method_fie, O1_scratch); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: __ bind(no_such_interface); twisti@1858: // Throw an exception. twisti@1858: // For historical reasons, it will be IncompatibleClassChangeError. twisti@1858: __ unimplemented("not tested yet"); twisti@2411: __ ld_ptr(Address(O1_intf, java_mirror_offset), O2_required); // required interface twisti@2411: __ mov( O0_klass, O1_actual); // bad receiver twisti@2411: __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch); twisti@2411: __ delayed()->mov(Bytecodes::_invokeinterface, O0_code); // who is complaining? twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _bound_ref_mh: twisti@1858: case _bound_int_mh: twisti@1858: case _bound_long_mh: twisti@1858: case _bound_ref_direct_mh: twisti@1858: case _bound_int_direct_mh: twisti@1858: case _bound_long_direct_mh: twisti@1858: { twisti@1858: const bool direct_to_method = (ek >= _bound_ref_direct_mh); never@2950: BasicType arg_type = ek_bound_mh_arg_type(ek); never@2950: int arg_slots = type2size[arg_type]; twisti@1858: twisti@1858: // Make room for the new argument: never@2950: load_vmargslot(_masm, G3_bmh_vmargslot, O0_argslot); never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); twisti@1858: never@2950: insert_arg_slots(_masm, arg_slots * stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch); twisti@1858: twisti@1858: // Store bound argument into the new stack slot: twisti@2201: __ load_heap_oop(G3_bmh_argument, O1_scratch); twisti@1858: if (arg_type == T_OBJECT) { twisti@1858: __ st_ptr(O1_scratch, Address(O0_argslot, 0)); twisti@1858: } else { twisti@1858: Address prim_value_addr(O1_scratch, java_lang_boxing_object::value_offset_in_bytes(arg_type)); never@2950: move_typed_arg(_masm, arg_type, false, never@2950: prim_value_addr, never@2950: Address(O0_argslot, 0), never@2950: O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3) twisti@1858: } twisti@1858: twisti@1858: if (direct_to_method) { twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop twisti@1858: __ verify_oop(G5_method); twisti@1858: __ jump_indirect_to(G5_method_fie, O1_scratch); twisti@1858: __ delayed()->nop(); twisti@1858: } else { twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); // target is a methodOop twisti@1858: __ verify_oop(G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_retype_only: twisti@1858: case _adapter_retype_raw: twisti@1858: // Immediately jump to the next MH layer: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); never@2950: __ verify_oop(G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: // This is OK when all parameter types widen. twisti@1858: // It is also OK when a return type narrows. twisti@1858: break; twisti@1858: twisti@1858: case _adapter_check_cast: twisti@1858: { twisti@1858: // Check a reference argument before jumping to the next layer of MH: never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); never@2950: Address vmarg = __ argument_address(O0_argslot, O0_argslot); twisti@1858: twisti@1858: // What class are we casting to? never@2950: Register O1_klass = O1_scratch; // Interesting AMH data. never@2950: __ load_heap_oop(G3_amh_argument, O1_klass); // This is a Class object! never@2950: load_klass_from_Class(_masm, O1_klass, O2_scratch, O3_scratch); twisti@1858: never@2950: Label L_done; never@2950: __ ld_ptr(vmarg, O2_scratch); never@2950: __ tst(O2_scratch); never@2950: __ brx(Assembler::zero, false, Assembler::pn, L_done); // No cast if null. twisti@1858: __ delayed()->nop(); never@2950: __ load_klass(O2_scratch, O2_scratch); twisti@1858: twisti@1858: // Live at this point: twisti@2874: // - O0_argslot : argslot index in vmarg; may be required in the failing path never@2950: // - O1_klass : klass required by the target method never@2950: // - O2_scratch : argument klass to test twisti@1858: // - G3_method_handle: adapter method handle never@2950: __ check_klass_subtype(O2_scratch, O1_klass, O3_scratch, O4_scratch, L_done); twisti@1858: twisti@1858: // If we get here, the type check failed! twisti@2411: __ load_heap_oop(G3_amh_argument, O2_required); // required class twisti@2411: __ ld_ptr( vmarg, O1_actual); // bad object twisti@2411: __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch); twisti@2411: __ delayed()->mov(Bytecodes::_checkcast, O0_code); // who is complaining? twisti@1858: never@2950: __ BIND(L_done); twisti@1858: // Get the new MH: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_prim_to_prim: twisti@1858: case _adapter_ref_to_prim: twisti@1858: // Handled completely by optimized cases. twisti@1858: __ stop("init_AdapterMethodHandle should not issue this"); twisti@1858: break; twisti@1858: twisti@1858: case _adapter_opt_i2i: // optimized subcase of adapt_prim_to_prim twisti@1858: //case _adapter_opt_f2i: // optimized subcase of adapt_prim_to_prim twisti@1858: case _adapter_opt_l2i: // optimized subcase of adapt_prim_to_prim twisti@1858: case _adapter_opt_unboxi: // optimized subcase of adapt_ref_to_prim twisti@1858: { twisti@1858: // Perform an in-place conversion to int or an int subword. never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); twisti@2351: Address value; never@2950: Address vmarg; twisti@1858: bool value_left_justified = false; twisti@1858: twisti@1858: switch (ek) { twisti@1858: case _adapter_opt_i2i: never@2950: value = vmarg = __ argument_address(O0_argslot, O0_argslot); twisti@2256: break; twisti@1858: case _adapter_opt_l2i: twisti@2256: { twisti@2256: // just delete the extra slot twisti@2351: #ifdef _LP64 twisti@2351: // In V9, longs are given 2 64-bit slots in the interpreter, but the twisti@2351: // data is passed in only 1 slot. twisti@2351: // Keep the second slot. never@2950: __ add(__ argument_address(O0_argslot, O0_argslot, -1), O0_argslot); twisti@2351: remove_arg_slots(_masm, -stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch); twisti@2351: value = Address(O0_argslot, 4); // Get least-significant 32-bit of 64-bit value. twisti@2351: vmarg = Address(O0_argslot, Interpreter::stackElementSize); twisti@2351: #else twisti@2351: // Keep the first slot. never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); twisti@2256: remove_arg_slots(_masm, -stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch); twisti@2351: value = Address(O0_argslot, 0); twisti@2351: vmarg = value; twisti@2351: #endif twisti@2256: } twisti@1858: break; twisti@1858: case _adapter_opt_unboxi: twisti@1858: { never@2950: vmarg = __ argument_address(O0_argslot, O0_argslot); twisti@1858: // Load the value up from the heap. twisti@1858: __ ld_ptr(vmarg, O1_scratch); twisti@1858: int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT); twisti@1858: #ifdef ASSERT twisti@1858: for (int bt = T_BOOLEAN; bt < T_INT; bt++) { twisti@1858: if (is_subword_type(BasicType(bt))) twisti@1858: assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(BasicType(bt)), ""); twisti@1858: } twisti@1858: #endif twisti@1858: __ null_check(O1_scratch, value_offset); twisti@1858: value = Address(O1_scratch, value_offset); twisti@1858: #ifdef _BIG_ENDIAN twisti@1858: // Values stored in objects are packed. twisti@1858: value_left_justified = true; twisti@1858: #endif twisti@1858: } twisti@1858: break; twisti@1858: default: twisti@1858: ShouldNotReachHere(); twisti@1858: } twisti@1858: twisti@1858: // This check is required on _BIG_ENDIAN never@2950: Register G5_vminfo = G5_scratch; twisti@1858: __ ldsw(G3_amh_conversion, G5_vminfo); twisti@1858: assert(CONV_VMINFO_SHIFT == 0, "preshifted"); twisti@1858: twisti@1858: // Original 32-bit vmdata word must be of this form: twisti@1858: // | MBZ:6 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 | twisti@1858: __ lduw(value, O1_scratch); twisti@1858: if (!value_left_justified) twisti@1858: __ sll(O1_scratch, G5_vminfo, O1_scratch); twisti@1858: Label zero_extend, done; twisti@1858: __ btst(CONV_VMINFO_SIGN_FLAG, G5_vminfo); twisti@1858: __ br(Assembler::zero, false, Assembler::pn, zero_extend); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: // this path is taken for int->byte, int->short twisti@1858: __ sra(O1_scratch, G5_vminfo, O1_scratch); twisti@1858: __ ba(false, done); twisti@1858: __ delayed()->nop(); twisti@1858: twisti@1858: __ bind(zero_extend); twisti@1858: // this is taken for int->char twisti@1858: __ srl(O1_scratch, G5_vminfo, O1_scratch); twisti@1858: twisti@1858: __ bind(done); twisti@1858: __ st(O1_scratch, vmarg); twisti@1858: twisti@1858: // Get the new MH: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_opt_i2l: // optimized subcase of adapt_prim_to_prim twisti@1858: case _adapter_opt_unboxl: // optimized subcase of adapt_ref_to_prim twisti@1858: { twisti@1858: // Perform an in-place int-to-long or ref-to-long conversion. never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); twisti@1858: twisti@1858: // On big-endian machine we duplicate the slot and store the MSW twisti@1858: // in the first slot. never@2950: __ add(__ argument_address(O0_argslot, O0_argslot, 1), O0_argslot); twisti@1858: never@2950: insert_arg_slots(_masm, stack_move_unit(), O0_argslot, O1_scratch, O2_scratch, O3_scratch); twisti@1858: twisti@1858: Address arg_lsw(O0_argslot, 0); twisti@1861: Address arg_msw(O0_argslot, -Interpreter::stackElementSize); twisti@1858: twisti@1858: switch (ek) { twisti@1858: case _adapter_opt_i2l: twisti@1858: { twisti@2664: #ifdef _LP64 twisti@2664: __ ldsw(arg_lsw, O2_scratch); // Load LSW sign-extended twisti@2664: #else twisti@2664: __ ldsw(arg_lsw, O3_scratch); // Load LSW sign-extended twisti@2664: __ srlx(O3_scratch, BitsPerInt, O2_scratch); // Move MSW value to lower 32-bits for std twisti@2664: #endif twisti@2664: __ st_long(O2_scratch, arg_msw); // Uses O2/O3 on !_LP64 twisti@1858: } twisti@1858: break; twisti@1858: case _adapter_opt_unboxl: twisti@1858: { twisti@1858: // Load the value up from the heap. twisti@1858: __ ld_ptr(arg_lsw, O1_scratch); twisti@1858: int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_LONG); twisti@1858: assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(T_DOUBLE), ""); twisti@1858: __ null_check(O1_scratch, value_offset); twisti@1858: __ ld_long(Address(O1_scratch, value_offset), O2_scratch); // Uses O2/O3 on !_LP64 twisti@1858: __ st_long(O2_scratch, arg_msw); twisti@1858: } twisti@1858: break; twisti@1858: default: twisti@1858: ShouldNotReachHere(); twisti@1858: } twisti@1858: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_opt_f2d: // optimized subcase of adapt_prim_to_prim twisti@1858: case _adapter_opt_d2f: // optimized subcase of adapt_prim_to_prim twisti@1858: { twisti@1858: // perform an in-place floating primitive conversion twisti@1858: __ unimplemented(entry_name(ek)); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_prim_to_ref: twisti@1858: __ unimplemented(entry_name(ek)); // %%% FIXME: NYI twisti@1858: break; twisti@1858: twisti@1858: case _adapter_swap_args: twisti@1858: case _adapter_rot_args: twisti@1858: // handled completely by optimized cases twisti@1858: __ stop("init_AdapterMethodHandle should not issue this"); twisti@1858: break; twisti@1858: twisti@1858: case _adapter_opt_swap_1: twisti@1858: case _adapter_opt_swap_2: twisti@1858: case _adapter_opt_rot_1_up: twisti@1858: case _adapter_opt_rot_1_down: twisti@1858: case _adapter_opt_rot_2_up: twisti@1858: case _adapter_opt_rot_2_down: twisti@1858: { never@2950: int swap_slots = ek_adapter_opt_swap_slots(ek); never@2950: int rotate = ek_adapter_opt_swap_mode(ek); twisti@1858: twisti@1858: // 'argslot' is the position of the first argument to swap. never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); never@2950: if (VerifyMethodHandles) never@2950: verify_argslot(_masm, O0_argslot, O2_scratch, "swap point must fall within current frame"); twisti@1858: twisti@1858: // 'vminfo' is the second. twisti@1858: Register O1_destslot = O1_scratch; never@2950: load_conversion_vminfo(_masm, G3_amh_conversion, O1_destslot); never@2950: __ add(__ argument_address(O1_destslot, O1_destslot), O1_destslot); never@2950: if (VerifyMethodHandles) never@2950: verify_argslot(_masm, O1_destslot, O2_scratch, "swap point must fall within current frame"); twisti@1858: never@2950: assert(Interpreter::stackElementSize == wordSize, "else rethink use of wordSize here"); twisti@1858: if (!rotate) { never@2950: // simple swap never@2950: for (int i = 0; i < swap_slots; i++) { never@2950: __ ld_ptr( Address(O0_argslot, i * wordSize), O2_scratch); never@2950: __ ld_ptr( Address(O1_destslot, i * wordSize), O3_scratch); never@2950: __ st_ptr(O3_scratch, Address(O0_argslot, i * wordSize)); never@2950: __ st_ptr(O2_scratch, Address(O1_destslot, i * wordSize)); twisti@1858: } twisti@1858: } else { never@2950: // A rotate is actually pair of moves, with an "odd slot" (or pair) never@2950: // changing place with a series of other slots. never@2950: // First, push the "odd slot", which is going to get overwritten never@2950: switch (swap_slots) { never@2950: case 2 : __ ld_ptr(Address(O0_argslot, 1 * wordSize), O4_scratch); // fall-thru never@2950: case 1 : __ ld_ptr(Address(O0_argslot, 0 * wordSize), O3_scratch); break; never@2950: default: ShouldNotReachHere(); twisti@1858: } twisti@1858: if (rotate > 0) { never@2950: // Here is rotate > 0: never@2950: // (low mem) (high mem) never@2950: // | dest: more_slots... | arg: odd_slot :arg+1 | never@2950: // => never@2950: // | dest: odd_slot | dest+1: more_slots... :arg+1 | never@2950: // work argslot down to destslot, copying contiguous data upwards never@2950: // pseudo-code: twisti@1858: // argslot = src_addr - swap_bytes twisti@1858: // destslot = dest_addr never@2950: // while (argslot >= destslot) *(argslot + swap_bytes) = *(argslot + 0), argslot--; never@2950: move_arg_slots_up(_masm, never@2950: O1_destslot, never@2950: Address(O0_argslot, 0), never@2950: swap_slots, never@2950: O0_argslot, O2_scratch); twisti@1858: } else { never@2950: // Here is the other direction, rotate < 0: never@2950: // (low mem) (high mem) never@2950: // | arg: odd_slot | arg+1: more_slots... :dest+1 | never@2950: // => never@2950: // | arg: more_slots... | dest: odd_slot :dest+1 | never@2950: // work argslot up to destslot, copying contiguous data downwards never@2950: // pseudo-code: twisti@1858: // argslot = src_addr + swap_bytes twisti@1858: // destslot = dest_addr never@2950: // while (argslot <= destslot) *(argslot - swap_bytes) = *(argslot + 0), argslot++; never@2950: __ add(O1_destslot, wordSize, O1_destslot); never@2950: move_arg_slots_down(_masm, never@2950: Address(O0_argslot, swap_slots * wordSize), never@2950: O1_destslot, never@2950: -swap_slots, never@2950: O0_argslot, O2_scratch); never@2950: never@2950: __ sub(O1_destslot, wordSize, O1_destslot); twisti@1858: } never@2950: // pop the original first chunk into the destination slot, now free never@2950: switch (swap_slots) { never@2950: case 2 : __ st_ptr(O4_scratch, Address(O1_destslot, 1 * wordSize)); // fall-thru never@2950: case 1 : __ st_ptr(O3_scratch, Address(O1_destslot, 0 * wordSize)); break; never@2950: default: ShouldNotReachHere(); twisti@1858: } twisti@1858: } twisti@1858: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_dup_args: twisti@1858: { twisti@1858: // 'argslot' is the position of the first argument to duplicate. never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); twisti@1858: twisti@1858: // 'stack_move' is negative number of words to duplicate. never@2950: Register O1_stack_move = O1_scratch; never@2950: load_stack_move(_masm, G3_amh_conversion, O1_stack_move); twisti@1858: never@2950: if (VerifyMethodHandles) { never@2950: verify_argslots(_masm, O1_stack_move, O0_argslot, O2_scratch, O3_scratch, true, never@2950: "copied argument(s) must fall within current frame"); never@2950: } twisti@1858: never@2950: // insert location is always the bottom of the argument list: never@2950: __ neg(O1_stack_move); never@2950: push_arg_slots(_masm, O0_argslot, O1_stack_move, O2_scratch, O3_scratch); twisti@1858: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_drop_args: twisti@1858: { twisti@1858: // 'argslot' is the position of the first argument to nuke. never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); twisti@1858: twisti@1858: // 'stack_move' is number of words to drop. never@2950: Register O1_stack_move = O1_scratch; never@2950: load_stack_move(_masm, G3_amh_conversion, O1_stack_move); twisti@1858: never@2950: remove_arg_slots(_masm, O1_stack_move, O0_argslot, O2_scratch, O3_scratch, O4_scratch); twisti@1858: twisti@2201: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); twisti@1858: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: twisti@1858: case _adapter_collect_args: never@2950: case _adapter_fold_args: twisti@1858: case _adapter_spread_args: twisti@1858: // Handled completely by optimized cases. twisti@1858: __ stop("init_AdapterMethodHandle should not issue this"); twisti@1858: break; twisti@1858: never@2950: case _adapter_opt_collect_ref: never@2950: case _adapter_opt_collect_int: never@2950: case _adapter_opt_collect_long: never@2950: case _adapter_opt_collect_float: never@2950: case _adapter_opt_collect_double: never@2950: case _adapter_opt_collect_void: never@2950: case _adapter_opt_collect_0_ref: never@2950: case _adapter_opt_collect_1_ref: never@2950: case _adapter_opt_collect_2_ref: never@2950: case _adapter_opt_collect_3_ref: never@2950: case _adapter_opt_collect_4_ref: never@2950: case _adapter_opt_collect_5_ref: never@2950: case _adapter_opt_filter_S0_ref: never@2950: case _adapter_opt_filter_S1_ref: never@2950: case _adapter_opt_filter_S2_ref: never@2950: case _adapter_opt_filter_S3_ref: never@2950: case _adapter_opt_filter_S4_ref: never@2950: case _adapter_opt_filter_S5_ref: never@2950: case _adapter_opt_collect_2_S0_ref: never@2950: case _adapter_opt_collect_2_S1_ref: never@2950: case _adapter_opt_collect_2_S2_ref: never@2950: case _adapter_opt_collect_2_S3_ref: never@2950: case _adapter_opt_collect_2_S4_ref: never@2950: case _adapter_opt_collect_2_S5_ref: never@2950: case _adapter_opt_fold_ref: never@2950: case _adapter_opt_fold_int: never@2950: case _adapter_opt_fold_long: never@2950: case _adapter_opt_fold_float: never@2950: case _adapter_opt_fold_double: never@2950: case _adapter_opt_fold_void: never@2950: case _adapter_opt_fold_1_ref: never@2950: case _adapter_opt_fold_2_ref: never@2950: case _adapter_opt_fold_3_ref: never@2950: case _adapter_opt_fold_4_ref: never@2950: case _adapter_opt_fold_5_ref: twisti@1858: { never@2950: // Given a fresh incoming stack frame, build a new ricochet frame. never@2950: // On entry, TOS points at a return PC, and FP is the callers frame ptr. never@2950: // RSI/R13 has the caller's exact stack pointer, which we must also preserve. never@2950: // RCX contains an AdapterMethodHandle of the indicated kind. never@2950: never@2950: // Relevant AMH fields: never@2950: // amh.vmargslot: never@2950: // points to the trailing edge of the arguments never@2950: // to filter, collect, or fold. For a boxing operation, never@2950: // it points just after the single primitive value. never@2950: // amh.argument: never@2950: // recursively called MH, on |collect| arguments never@2950: // amh.vmtarget: never@2950: // final destination MH, on return value, etc. never@2950: // amh.conversion.dest: never@2950: // tells what is the type of the return value never@2950: // (not needed here, since dest is also derived from ek) never@2950: // amh.conversion.vminfo: never@2950: // points to the trailing edge of the return value never@2950: // when the vmtarget is to be called; this is never@2950: // equal to vmargslot + (retained ? |collect| : 0) never@2950: never@2950: // Pass 0 or more argument slots to the recursive target. never@2950: int collect_count_constant = ek_adapter_opt_collect_count(ek); never@2950: never@2950: // The collected arguments are copied from the saved argument list: never@2950: int collect_slot_constant = ek_adapter_opt_collect_slot(ek); never@2950: never@2950: assert(ek_orig == _adapter_collect_args || never@2950: ek_orig == _adapter_fold_args, ""); never@2950: bool retain_original_args = (ek_orig == _adapter_fold_args); never@2950: never@2950: // The return value is replaced (or inserted) at the 'vminfo' argslot. never@2950: // Sometimes we can compute this statically. never@2950: int dest_slot_constant = -1; never@2950: if (!retain_original_args) never@2950: dest_slot_constant = collect_slot_constant; never@2950: else if (collect_slot_constant >= 0 && collect_count_constant >= 0) never@2950: // We are preserving all the arguments, and the return value is prepended, never@2950: // so the return slot is to the left (above) the |collect| sequence. never@2950: dest_slot_constant = collect_slot_constant + collect_count_constant; never@2950: never@2950: // Replace all those slots by the result of the recursive call. never@2950: // The result type can be one of ref, int, long, float, double, void. never@2950: // In the case of void, nothing is pushed on the stack after return. never@2950: BasicType dest = ek_adapter_opt_collect_type(ek); never@2950: assert(dest == type2wfield[dest], "dest is a stack slot type"); never@2950: int dest_count = type2size[dest]; never@2950: assert(dest_count == 1 || dest_count == 2 || (dest_count == 0 && dest == T_VOID), "dest has a size"); never@2950: never@2950: // Choose a return continuation. never@2950: EntryKind ek_ret = _adapter_opt_return_any; never@2950: if (dest != T_CONFLICT && OptimizeMethodHandles) { never@2950: switch (dest) { never@2950: case T_INT : ek_ret = _adapter_opt_return_int; break; never@2950: case T_LONG : ek_ret = _adapter_opt_return_long; break; never@2950: case T_FLOAT : ek_ret = _adapter_opt_return_float; break; never@2950: case T_DOUBLE : ek_ret = _adapter_opt_return_double; break; never@2950: case T_OBJECT : ek_ret = _adapter_opt_return_ref; break; never@2950: case T_VOID : ek_ret = _adapter_opt_return_void; break; never@2950: default : ShouldNotReachHere(); never@2950: } never@2950: if (dest == T_OBJECT && dest_slot_constant >= 0) { never@2950: EntryKind ek_try = EntryKind(_adapter_opt_return_S0_ref + dest_slot_constant); never@2950: if (ek_try <= _adapter_opt_return_LAST && never@2950: ek_adapter_opt_return_slot(ek_try) == dest_slot_constant) { never@2950: ek_ret = ek_try; never@2950: } never@2950: } never@2950: assert(ek_adapter_opt_return_type(ek_ret) == dest, ""); never@2950: } never@2950: never@2950: // Already pushed: ... keep1 | collect | keep2 | never@2950: never@2950: // Push a few extra argument words, if we need them to store the return value. never@2950: { never@2950: int extra_slots = 0; never@2950: if (retain_original_args) { never@2950: extra_slots = dest_count; never@2950: } else if (collect_count_constant == -1) { never@2950: extra_slots = dest_count; // collect_count might be zero; be generous never@2950: } else if (dest_count > collect_count_constant) { never@2950: extra_slots = (dest_count - collect_count_constant); never@2950: } else { never@2950: // else we know we have enough dead space in |collect| to repurpose for return values never@2950: } never@2950: if (extra_slots != 0) { never@2950: __ sub(SP, round_to(extra_slots, 2) * Interpreter::stackElementSize, SP); never@2950: } never@2950: } never@2950: never@2950: // Set up Ricochet Frame. never@2950: __ mov(SP, O5_savedSP); // record SP for the callee never@2950: never@2950: // One extra (empty) slot for outgoing target MH (see Gargs computation below). never@2950: __ save_frame(2); // Note: we need to add 2 slots since frame::memory_parameter_word_sp_offset is 23. never@2950: never@2950: // Note: Gargs is live throughout the following, until we make our recursive call. never@2950: // And the RF saves a copy in L4_saved_args_base. never@2950: never@2950: RicochetFrame::enter_ricochet_frame(_masm, G3_method_handle, Gargs, never@2950: entry(ek_ret)->from_interpreted_entry()); never@2950: never@2950: // Compute argument base: never@2950: // Set up Gargs for current frame, extra (empty) slot is for outgoing target MH (space reserved by save_frame above). never@2950: __ add(FP, STACK_BIAS - (1 * Interpreter::stackElementSize), Gargs); never@2950: never@2950: // Now pushed: ... keep1 | collect | keep2 | extra | [RF] never@2950: never@2950: #ifdef ASSERT never@2950: if (VerifyMethodHandles && dest != T_CONFLICT) { never@2950: BLOCK_COMMENT("verify AMH.conv.dest {"); never@2950: extract_conversion_dest_type(_masm, RicochetFrame::L5_conversion, O1_scratch); never@2950: Label L_dest_ok; never@2950: __ cmp(O1_scratch, (int) dest); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_dest_ok); never@2950: __ delayed()->nop(); never@2950: if (dest == T_INT) { never@2950: for (int bt = T_BOOLEAN; bt < T_INT; bt++) { never@2950: if (is_subword_type(BasicType(bt))) { never@2950: __ cmp(O1_scratch, (int) bt); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_dest_ok); never@2950: __ delayed()->nop(); never@2950: } never@2950: } never@2950: } never@2950: __ stop("bad dest in AMH.conv"); never@2950: __ BIND(L_dest_ok); never@2950: BLOCK_COMMENT("} verify AMH.conv.dest"); never@2950: } never@2950: #endif //ASSERT never@2950: never@2950: // Find out where the original copy of the recursive argument sequence begins. never@2950: Register O0_coll = O0_scratch; never@2950: { never@2950: RegisterOrConstant collect_slot = collect_slot_constant; never@2950: if (collect_slot_constant == -1) { never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O1_scratch); never@2950: collect_slot = O1_scratch; never@2950: } never@2950: // collect_slot might be 0, but we need the move anyway. never@2950: __ add(RicochetFrame::L4_saved_args_base, __ argument_offset(collect_slot, collect_slot.register_or_noreg()), O0_coll); never@2950: // O0_coll now points at the trailing edge of |collect| and leading edge of |keep2| never@2950: } never@2950: never@2950: // Replace the old AMH with the recursive MH. (No going back now.) never@2950: // In the case of a boxing call, the recursive call is to a 'boxer' method, never@2950: // such as Integer.valueOf or Long.valueOf. In the case of a filter never@2950: // or collect call, it will take one or more arguments, transform them, never@2950: // and return some result, to store back into argument_base[vminfo]. never@2950: __ load_heap_oop(G3_amh_argument, G3_method_handle); never@2950: if (VerifyMethodHandles) verify_method_handle(_masm, G3_method_handle, O1_scratch, O2_scratch); never@2950: never@2950: // Calculate |collect|, the number of arguments we are collecting. never@2950: Register O1_collect_count = O1_scratch; never@2950: RegisterOrConstant collect_count; never@2950: if (collect_count_constant < 0) { never@2950: __ load_method_handle_vmslots(O1_collect_count, G3_method_handle, O2_scratch); never@2950: collect_count = O1_collect_count; never@2950: } else { never@2950: collect_count = collect_count_constant; never@2950: #ifdef ASSERT never@2950: if (VerifyMethodHandles) { never@2950: BLOCK_COMMENT("verify collect_count_constant {"); never@2950: __ load_method_handle_vmslots(O3_scratch, G3_method_handle, O2_scratch); never@2950: Label L_count_ok; never@2950: __ cmp(O3_scratch, collect_count_constant); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_count_ok); never@2950: __ delayed()->nop(); never@2950: __ stop("bad vminfo in AMH.conv"); never@2950: __ BIND(L_count_ok); never@2950: BLOCK_COMMENT("} verify collect_count_constant"); never@2950: } never@2950: #endif //ASSERT never@2950: } never@2950: never@2950: // copy |collect| slots directly to TOS: never@2950: push_arg_slots(_masm, O0_coll, collect_count, O2_scratch, O3_scratch); never@2950: // Now pushed: ... keep1 | collect | keep2 | RF... | collect | never@2950: // O0_coll still points at the trailing edge of |collect| and leading edge of |keep2| never@2950: never@2950: // If necessary, adjust the saved arguments to make room for the eventual return value. never@2950: // Normal adjustment: ... keep1 | +dest+ | -collect- | keep2 | RF... | collect | never@2950: // If retaining args: ... keep1 | +dest+ | collect | keep2 | RF... | collect | never@2950: // In the non-retaining case, this might move keep2 either up or down. never@2950: // We don't have to copy the whole | RF... collect | complex, never@2950: // but we must adjust RF.saved_args_base. never@2950: // Also, from now on, we will forget about the original copy of |collect|. never@2950: // If we are retaining it, we will treat it as part of |keep2|. never@2950: // For clarity we will define |keep3| = |collect|keep2| or |keep2|. never@2950: never@2950: BLOCK_COMMENT("adjust trailing arguments {"); never@2950: // Compare the sizes of |+dest+| and |-collect-|, which are opposed opening and closing movements. never@2950: int open_count = dest_count; never@2950: RegisterOrConstant close_count = collect_count_constant; never@2950: Register O1_close_count = O1_collect_count; never@2950: if (retain_original_args) { never@2950: close_count = constant(0); never@2950: } else if (collect_count_constant == -1) { never@2950: close_count = O1_collect_count; never@2950: } never@2950: never@2950: // How many slots need moving? This is simply dest_slot (0 => no |keep3|). never@2950: RegisterOrConstant keep3_count; never@2950: Register O2_keep3_count = O2_scratch; never@2950: if (dest_slot_constant < 0) { never@2950: extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O2_keep3_count); never@2950: keep3_count = O2_keep3_count; never@2950: } else { never@2950: keep3_count = dest_slot_constant; never@2950: #ifdef ASSERT never@2950: if (VerifyMethodHandles && dest_slot_constant < 0) { never@2950: BLOCK_COMMENT("verify dest_slot_constant {"); never@2950: extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O3_scratch); never@2950: Label L_vminfo_ok; never@2950: __ cmp(O3_scratch, dest_slot_constant); never@2950: __ br(Assembler::equal, false, Assembler::pt, L_vminfo_ok); never@2950: __ delayed()->nop(); never@2950: __ stop("bad vminfo in AMH.conv"); never@2950: __ BIND(L_vminfo_ok); never@2950: BLOCK_COMMENT("} verify dest_slot_constant"); never@2950: } never@2950: #endif //ASSERT never@2950: } never@2950: never@2950: // tasks remaining: never@2950: bool move_keep3 = (!keep3_count.is_constant() || keep3_count.as_constant() != 0); never@2950: bool stomp_dest = (NOT_DEBUG(dest == T_OBJECT) DEBUG_ONLY(dest_count != 0)); never@2950: bool fix_arg_base = (!close_count.is_constant() || open_count != close_count.as_constant()); never@2950: never@2950: // Old and new argument locations (based at slot 0). never@2950: // Net shift (&new_argv - &old_argv) is (close_count - open_count). never@2950: bool zero_open_count = (open_count == 0); // remember this bit of info never@2950: if (move_keep3 && fix_arg_base) { never@2950: // It will be easier to have everything in one register: never@2950: if (close_count.is_register()) { never@2950: // Deduct open_count from close_count register to get a clean +/- value. never@2950: __ sub(close_count.as_register(), open_count, close_count.as_register()); never@2950: } else { never@2950: close_count = close_count.as_constant() - open_count; never@2950: } never@2950: open_count = 0; never@2950: } never@2950: Register L4_old_argv = RicochetFrame::L4_saved_args_base; never@2950: Register O3_new_argv = O3_scratch; never@2950: if (fix_arg_base) { never@2950: __ add(L4_old_argv, __ argument_offset(close_count, O4_scratch), O3_new_argv, never@2950: -(open_count * Interpreter::stackElementSize)); never@2950: } never@2950: never@2950: // First decide if any actual data are to be moved. never@2950: // We can skip if (a) |keep3| is empty, or (b) the argument list size didn't change. never@2950: // (As it happens, all movements involve an argument list size change.) never@2950: never@2950: // If there are variable parameters, use dynamic checks to skip around the whole mess. never@2950: Label L_done; never@2950: if (keep3_count.is_register()) { never@2950: __ tst(keep3_count.as_register()); never@2950: __ br(Assembler::zero, false, Assembler::pn, L_done); never@2950: __ delayed()->nop(); never@2950: } never@2950: if (close_count.is_register()) { never@2950: __ cmp(close_count.as_register(), open_count); never@2950: __ br(Assembler::equal, false, Assembler::pn, L_done); never@2950: __ delayed()->nop(); never@2950: } never@2950: never@2950: if (move_keep3 && fix_arg_base) { never@2950: bool emit_move_down = false, emit_move_up = false, emit_guard = false; never@2950: if (!close_count.is_constant()) { never@2950: emit_move_down = emit_guard = !zero_open_count; never@2950: emit_move_up = true; never@2950: } else if (open_count != close_count.as_constant()) { never@2950: emit_move_down = (open_count > close_count.as_constant()); never@2950: emit_move_up = !emit_move_down; never@2950: } never@2950: Label L_move_up; never@2950: if (emit_guard) { never@2950: __ cmp(close_count.as_register(), open_count); never@2950: __ br(Assembler::greater, false, Assembler::pn, L_move_up); never@2950: __ delayed()->nop(); never@2950: } never@2950: never@2950: if (emit_move_down) { never@2950: // Move arguments down if |+dest+| > |-collect-| never@2950: // (This is rare, except when arguments are retained.) never@2950: // This opens space for the return value. never@2950: if (keep3_count.is_constant()) { never@2950: for (int i = 0; i < keep3_count.as_constant(); i++) { never@2950: __ ld_ptr( Address(L4_old_argv, i * Interpreter::stackElementSize), O4_scratch); never@2950: __ st_ptr(O4_scratch, Address(O3_new_argv, i * Interpreter::stackElementSize) ); never@2950: } never@2950: } else { never@2950: // Live: O1_close_count, O2_keep3_count, O3_new_argv never@2950: Register argv_top = O0_scratch; never@2950: __ add(L4_old_argv, __ argument_offset(keep3_count, O4_scratch), argv_top); never@2950: move_arg_slots_down(_masm, never@2950: Address(L4_old_argv, 0), // beginning of old argv never@2950: argv_top, // end of old argv never@2950: close_count, // distance to move down (must be negative) never@2950: O4_scratch, G5_scratch); never@2950: } never@2950: } never@2950: never@2950: if (emit_guard) { never@2950: __ ba(false, L_done); // assumes emit_move_up is true also never@2950: __ delayed()->nop(); never@2950: __ BIND(L_move_up); never@2950: } never@2950: never@2950: if (emit_move_up) { never@2950: // Move arguments up if |+dest+| < |-collect-| never@2950: // (This is usual, except when |keep3| is empty.) never@2950: // This closes up the space occupied by the now-deleted collect values. never@2950: if (keep3_count.is_constant()) { never@2950: for (int i = keep3_count.as_constant() - 1; i >= 0; i--) { never@2950: __ ld_ptr( Address(L4_old_argv, i * Interpreter::stackElementSize), O4_scratch); never@2950: __ st_ptr(O4_scratch, Address(O3_new_argv, i * Interpreter::stackElementSize) ); never@2950: } never@2950: } else { never@2950: Address argv_top(L4_old_argv, __ argument_offset(keep3_count, O4_scratch)); never@2950: // Live: O1_close_count, O2_keep3_count, O3_new_argv never@2950: move_arg_slots_up(_masm, never@2950: L4_old_argv, // beginning of old argv never@2950: argv_top, // end of old argv never@2950: close_count, // distance to move up (must be positive) never@2950: O4_scratch, G5_scratch); never@2950: } never@2950: } never@2950: } never@2950: __ BIND(L_done); never@2950: never@2950: if (fix_arg_base) { never@2950: // adjust RF.saved_args_base never@2950: __ mov(O3_new_argv, RicochetFrame::L4_saved_args_base); never@2950: } never@2950: never@2950: if (stomp_dest) { never@2950: // Stomp the return slot, so it doesn't hold garbage. never@2950: // This isn't strictly necessary, but it may help detect bugs. never@2950: __ set(RicochetFrame::RETURN_VALUE_PLACEHOLDER, O4_scratch); never@2950: __ st_ptr(O4_scratch, Address(RicochetFrame::L4_saved_args_base, never@2950: __ argument_offset(keep3_count, keep3_count.register_or_noreg()))); // uses O2_keep3_count never@2950: } never@2950: BLOCK_COMMENT("} adjust trailing arguments"); never@2950: never@2950: BLOCK_COMMENT("do_recursive_call"); never@2950: __ mov(SP, O5_savedSP); // record SP for the callee never@2950: __ set(ExternalAddress(SharedRuntime::ricochet_blob()->bounce_addr() - frame::pc_return_offset), O7); never@2950: // The globally unique bounce address has two purposes: never@2950: // 1. It helps the JVM recognize this frame (frame::is_ricochet_frame). never@2950: // 2. When returned to, it cuts back the stack and redirects control flow never@2950: // to the return handler. never@2950: // The return handler will further cut back the stack when it takes never@2950: // down the RF. Perhaps there is a way to streamline this further. never@2950: never@2950: // State during recursive call: never@2950: // ... keep1 | dest | dest=42 | keep3 | RF... | collect | bounce_pc | never@2950: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); twisti@1858: } twisti@1858: break; twisti@1858: never@2950: case _adapter_opt_return_ref: never@2950: case _adapter_opt_return_int: never@2950: case _adapter_opt_return_long: never@2950: case _adapter_opt_return_float: never@2950: case _adapter_opt_return_double: never@2950: case _adapter_opt_return_void: never@2950: case _adapter_opt_return_S0_ref: never@2950: case _adapter_opt_return_S1_ref: never@2950: case _adapter_opt_return_S2_ref: never@2950: case _adapter_opt_return_S3_ref: never@2950: case _adapter_opt_return_S4_ref: never@2950: case _adapter_opt_return_S5_ref: never@2950: { never@2950: BasicType dest_type_constant = ek_adapter_opt_return_type(ek); never@2950: int dest_slot_constant = ek_adapter_opt_return_slot(ek); never@2950: never@2950: if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); never@2950: never@2950: if (dest_slot_constant == -1) { never@2950: // The current stub is a general handler for this dest_type. never@2950: // It can be called from _adapter_opt_return_any below. never@2950: // Stash the address in a little table. never@2950: assert((dest_type_constant & CONV_TYPE_MASK) == dest_type_constant, "oob"); never@2950: address return_handler = __ pc(); never@2950: _adapter_return_handlers[dest_type_constant] = return_handler; never@2950: if (dest_type_constant == T_INT) { never@2950: // do the subword types too never@2950: for (int bt = T_BOOLEAN; bt < T_INT; bt++) { never@2950: if (is_subword_type(BasicType(bt)) && never@2950: _adapter_return_handlers[bt] == NULL) { never@2950: _adapter_return_handlers[bt] = return_handler; never@2950: } never@2950: } never@2950: } never@2950: } never@2950: never@2950: // On entry to this continuation handler, make Gargs live again. never@2950: __ mov(RicochetFrame::L4_saved_args_base, Gargs); never@2950: never@2950: Register O7_temp = O7; never@2950: Register O5_vminfo = O5; never@2950: never@2950: RegisterOrConstant dest_slot = dest_slot_constant; never@2950: if (dest_slot_constant == -1) { never@2950: extract_conversion_vminfo(_masm, RicochetFrame::L5_conversion, O5_vminfo); never@2950: dest_slot = O5_vminfo; never@2950: } never@2950: // Store the result back into the argslot. never@2950: // This code uses the interpreter calling sequence, in which the return value never@2950: // is usually left in the TOS register, as defined by InterpreterMacroAssembler::pop. never@2950: // There are certain irregularities with floating point values, which can be seen never@2950: // in TemplateInterpreterGenerator::generate_return_entry_for. never@2950: move_return_value(_masm, dest_type_constant, __ argument_address(dest_slot, O7_temp)); never@2950: never@2950: RicochetFrame::leave_ricochet_frame(_masm, G3_method_handle, I5_savedSP, I7); never@2950: never@2950: // Load the final target and go. never@2950: if (VerifyMethodHandles) verify_method_handle(_masm, G3_method_handle, O0_scratch, O1_scratch); never@2950: __ restore(I5_savedSP, G0, SP); never@2950: __ jump_to_method_handle_entry(G3_method_handle, O0_scratch); never@2950: __ illtrap(0); never@2950: } never@2950: break; never@2950: never@2950: case _adapter_opt_return_any: never@2950: { never@2950: Register O7_temp = O7; never@2950: Register O5_dest_type = O5; never@2950: never@2950: if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); never@2950: extract_conversion_dest_type(_masm, RicochetFrame::L5_conversion, O5_dest_type); never@2950: __ set(ExternalAddress((address) &_adapter_return_handlers[0]), O7_temp); never@2950: __ sll_ptr(O5_dest_type, LogBytesPerWord, O5_dest_type); never@2950: __ ld_ptr(O7_temp, O5_dest_type, O7_temp); never@2950: never@2950: #ifdef ASSERT never@2950: { Label L_ok; never@2950: __ br_notnull(O7_temp, false, Assembler::pt, L_ok); never@2950: __ delayed()->nop(); never@2950: __ stop("bad method handle return"); never@2950: __ BIND(L_ok); never@2950: } never@2950: #endif //ASSERT never@2950: __ JMP(O7_temp, 0); never@2950: __ delayed()->nop(); never@2950: } never@2950: break; never@2950: never@2950: case _adapter_opt_spread_0: never@2950: case _adapter_opt_spread_1_ref: never@2950: case _adapter_opt_spread_2_ref: never@2950: case _adapter_opt_spread_3_ref: never@2950: case _adapter_opt_spread_4_ref: never@2950: case _adapter_opt_spread_5_ref: never@2950: case _adapter_opt_spread_ref: never@2950: case _adapter_opt_spread_byte: never@2950: case _adapter_opt_spread_char: never@2950: case _adapter_opt_spread_short: never@2950: case _adapter_opt_spread_int: never@2950: case _adapter_opt_spread_long: never@2950: case _adapter_opt_spread_float: never@2950: case _adapter_opt_spread_double: never@2950: { never@2950: // spread an array out into a group of arguments never@2950: int length_constant = ek_adapter_opt_spread_count(ek); never@2950: bool length_can_be_zero = (length_constant == 0); never@2950: if (length_constant < 0) { never@2950: // some adapters with variable length must handle the zero case never@2950: if (!OptimizeMethodHandles || never@2950: ek_adapter_opt_spread_type(ek) != T_OBJECT) never@2950: length_can_be_zero = true; never@2950: } never@2950: never@2950: // find the address of the array argument never@2950: load_vmargslot(_masm, G3_amh_vmargslot, O0_argslot); never@2950: __ add(__ argument_address(O0_argslot, O0_argslot), O0_argslot); never@2950: never@2950: // O0_argslot points both to the array and to the first output arg never@2950: Address vmarg = Address(O0_argslot, 0); never@2950: never@2950: // Get the array value. never@2950: Register O1_array = O1_scratch; never@2950: Register O2_array_klass = O2_scratch; never@2950: BasicType elem_type = ek_adapter_opt_spread_type(ek); never@2950: int elem_slots = type2size[elem_type]; // 1 or 2 never@2950: int array_slots = 1; // array is always a T_OBJECT never@2950: int length_offset = arrayOopDesc::length_offset_in_bytes(); never@2950: int elem0_offset = arrayOopDesc::base_offset_in_bytes(elem_type); never@2950: __ ld_ptr(vmarg, O1_array); never@2950: never@2950: Label L_array_is_empty, L_insert_arg_space, L_copy_args, L_args_done; never@2950: if (length_can_be_zero) { never@2950: // handle the null pointer case, if zero is allowed never@2950: Label L_skip; never@2950: if (length_constant < 0) { never@2950: load_conversion_vminfo(_masm, G3_amh_conversion, O3_scratch); never@2950: __ br_zero(Assembler::notZero, false, Assembler::pn, O3_scratch, L_skip); never@2950: __ delayed()->nop(); never@2950: } never@2950: __ br_null(O1_array, false, Assembler::pn, L_array_is_empty); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_skip); never@2950: } never@2950: __ null_check(O1_array, oopDesc::klass_offset_in_bytes()); never@2950: __ load_klass(O1_array, O2_array_klass); never@2950: never@2950: // Check the array type. never@2950: Register O3_klass = O3_scratch; never@2950: __ load_heap_oop(G3_amh_argument, O3_klass); // this is a Class object! never@2950: load_klass_from_Class(_masm, O3_klass, O4_scratch, G5_scratch); never@2950: never@2950: Label L_ok_array_klass, L_bad_array_klass, L_bad_array_length; never@2950: __ check_klass_subtype(O2_array_klass, O3_klass, O4_scratch, G5_scratch, L_ok_array_klass); never@2950: // If we get here, the type check failed! never@2950: __ ba(false, L_bad_array_klass); never@2950: __ delayed()->nop(); never@2950: __ BIND(L_ok_array_klass); never@2950: never@2950: // Check length. never@2950: if (length_constant >= 0) { never@2950: __ ldsw(Address(O1_array, length_offset), O4_scratch); never@2950: __ cmp(O4_scratch, length_constant); never@2950: } else { never@2950: Register O3_vminfo = O3_scratch; never@2950: load_conversion_vminfo(_masm, G3_amh_conversion, O3_vminfo); never@2950: __ ldsw(Address(O1_array, length_offset), O4_scratch); never@2950: __ cmp(O3_vminfo, O4_scratch); never@2950: } never@2950: __ br(Assembler::notEqual, false, Assembler::pn, L_bad_array_length); never@2950: __ delayed()->nop(); never@2950: never@2950: Register O2_argslot_limit = O2_scratch; never@2950: never@2950: // Array length checks out. Now insert any required stack slots. never@2950: if (length_constant == -1) { never@2950: // Form a pointer to the end of the affected region. never@2950: __ add(O0_argslot, Interpreter::stackElementSize, O2_argslot_limit); never@2950: // 'stack_move' is negative number of words to insert never@2950: // This number already accounts for elem_slots. never@2950: Register O3_stack_move = O3_scratch; never@2950: load_stack_move(_masm, G3_amh_conversion, O3_stack_move); never@2950: __ cmp(O3_stack_move, 0); never@2950: assert(stack_move_unit() < 0, "else change this comparison"); never@2950: __ br(Assembler::less, false, Assembler::pn, L_insert_arg_space); never@2950: __ delayed()->nop(); never@2950: __ br(Assembler::equal, false, Assembler::pn, L_copy_args); never@2950: __ delayed()->nop(); never@2950: // single argument case, with no array movement never@2950: __ BIND(L_array_is_empty); never@2950: remove_arg_slots(_masm, -stack_move_unit() * array_slots, never@2950: O0_argslot, O1_scratch, O2_scratch, O3_scratch); never@2950: __ ba(false, L_args_done); // no spreading to do never@2950: __ delayed()->nop(); never@2950: __ BIND(L_insert_arg_space); never@2950: // come here in the usual case, stack_move < 0 (2 or more spread arguments) never@2950: // Live: O1_array, O2_argslot_limit, O3_stack_move never@2950: insert_arg_slots(_masm, O3_stack_move, never@2950: O0_argslot, O4_scratch, G5_scratch, O1_scratch); never@2950: // reload from rdx_argslot_limit since rax_argslot is now decremented never@2950: __ ld_ptr(Address(O2_argslot_limit, -Interpreter::stackElementSize), O1_array); never@2950: } else if (length_constant >= 1) { never@2950: int new_slots = (length_constant * elem_slots) - array_slots; never@2950: insert_arg_slots(_masm, new_slots * stack_move_unit(), never@2950: O0_argslot, O2_scratch, O3_scratch, O4_scratch); never@2950: } else if (length_constant == 0) { never@2950: __ BIND(L_array_is_empty); never@2950: remove_arg_slots(_masm, -stack_move_unit() * array_slots, never@2950: O0_argslot, O1_scratch, O2_scratch, O3_scratch); never@2950: } else { never@2950: ShouldNotReachHere(); never@2950: } never@2950: never@2950: // Copy from the array to the new slots. never@2950: // Note: Stack change code preserves integrity of O0_argslot pointer. never@2950: // So even after slot insertions, O0_argslot still points to first argument. never@2950: // Beware: Arguments that are shallow on the stack are deep in the array, never@2950: // and vice versa. So a downward-growing stack (the usual) has to be copied never@2950: // elementwise in reverse order from the source array. never@2950: __ BIND(L_copy_args); never@2950: if (length_constant == -1) { never@2950: // [O0_argslot, O2_argslot_limit) is the area we are inserting into. never@2950: // Array element [0] goes at O0_argslot_limit[-wordSize]. never@2950: Register O1_source = O1_array; never@2950: __ add(Address(O1_array, elem0_offset), O1_source); never@2950: Register O4_fill_ptr = O4_scratch; never@2950: __ mov(O2_argslot_limit, O4_fill_ptr); never@2950: Label L_loop; never@2950: __ BIND(L_loop); never@2950: __ add(O4_fill_ptr, -Interpreter::stackElementSize * elem_slots, O4_fill_ptr); never@2950: move_typed_arg(_masm, elem_type, true, never@2950: Address(O1_source, 0), Address(O4_fill_ptr, 0), never@2950: O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3) never@2950: __ add(O1_source, type2aelembytes(elem_type), O1_source); never@2950: __ cmp(O4_fill_ptr, O0_argslot); never@2950: __ brx(Assembler::greaterUnsigned, false, Assembler::pt, L_loop); never@2950: __ delayed()->nop(); // FILLME never@2950: } else if (length_constant == 0) { never@2950: // nothing to copy never@2950: } else { never@2950: int elem_offset = elem0_offset; never@2950: int slot_offset = length_constant * Interpreter::stackElementSize; never@2950: for (int index = 0; index < length_constant; index++) { never@2950: slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward never@2950: move_typed_arg(_masm, elem_type, true, never@2950: Address(O1_array, elem_offset), Address(O0_argslot, slot_offset), never@2950: O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3) never@2950: elem_offset += type2aelembytes(elem_type); never@2950: } never@2950: } never@2950: __ BIND(L_args_done); never@2950: never@2950: // Arguments are spread. Move to next method handle. never@2950: __ load_heap_oop(G3_mh_vmtarget, G3_method_handle); never@2950: __ jump_to_method_handle_entry(G3_method_handle, O1_scratch); never@2950: never@2950: __ BIND(L_bad_array_klass); never@2950: assert(!vmarg.uses(O2_required), "must be different registers"); never@2950: __ load_heap_oop(Address(O2_array_klass, java_mirror_offset), O2_required); // required class never@2950: __ ld_ptr( vmarg, O1_actual); // bad object never@2950: __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch); never@2950: __ delayed()->mov(Bytecodes::_aaload, O0_code); // who is complaining? never@2950: never@2950: __ bind(L_bad_array_length); never@2950: assert(!vmarg.uses(O2_required), "must be different registers"); never@2950: __ mov( G3_method_handle, O2_required); // required class never@2950: __ ld_ptr(vmarg, O1_actual); // bad object never@2950: __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch); never@2950: __ delayed()->mov(Bytecodes::_arraylength, O0_code); // who is complaining? never@2950: } twisti@1858: break; twisti@1858: twisti@1858: default: never@2950: DEBUG_ONLY(tty->print_cr("bad ek=%d (%s)", (int)ek, entry_name(ek))); twisti@1858: ShouldNotReachHere(); twisti@1858: } never@2950: BLOCK_COMMENT(err_msg("} Entry %s", entry_name(ek))); twisti@1858: twisti@1858: address me_cookie = MethodHandleEntry::start_compiled_entry(_masm, interp_entry); twisti@1858: __ unimplemented(entry_name(ek)); // %%% FIXME: NYI twisti@1858: twisti@1858: init_entry(ek, MethodHandleEntry::finish_compiled_entry(_masm, me_cookie)); jrose@1145: }