diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index 61d27e3224248..09bb370f210f7 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -36,6 +36,349 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_aarch64.inline.hpp" + +class SimpleRuntimeFrame { + + public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + // we don't expect any arg reg save area so aarch64 asserts that + // frame::arg_reg_save_area_bytes == 0 + rfp_off = 0, + rfp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address in LR + // and sp should be 16 byte aligned + // push rfp and retaddr by hand + __ protect_return_address(); + __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); + // we don't expect an arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + if (c_rarg1 != j_rarg0) { + __ movw(c_rarg1, j_rarg0); + } + + // we need to set the past SP to the stack pointer of the stub frame + // and the pc to the address where this runtime call will return + // although actually any pc in this code blob will do). + Label retaddr; + __ set_last_Java_frame(sp, noreg, retaddr, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + // + // n.b. 2 gp args, 0 fp args, integral return type + + __ mov(c_rarg0, rthread); + __ movw(c_rarg2, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ lea(rscratch1, + RuntimeAddress(CAST_FROM_FN_PTR(address, + Deoptimization::uncommon_trap))); + __ blr(rscratch1); + __ bind(retaddr); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + + // location of rfp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // move UnrollBlock* into r4 + __ mov(r4, r0); + +#ifdef ASSERT + { Label L; + __ ldrw(rscratch1, Address(r4, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ cmpw(rscratch1, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ br(Assembler::EQ, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on r0 and sp. + __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ ldrw(r2, Address(r4, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ sub(r2, r2, 2 * wordSize); + __ add(sp, sp, r2); + __ ldp(rfp, zr, __ post(sp, 2 * wordSize)); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ ldrw(r1, Address(r4, + Deoptimization::UnrollBlock:: + total_frame_sizes_offset())); + __ bang_stack_size(r1, r2); +#endif + + // Load address of array of frame pcs into r2 (address*) + __ ldr(r2, Address(r4, + Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Load address of array of frame sizes into r5 (intptr_t*) + __ ldr(r5, Address(r4, + Deoptimization::UnrollBlock:: + frame_sizes_offset())); + + // Counter + __ ldrw(r3, Address(r4, + Deoptimization::UnrollBlock:: + number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = r8; + + __ mov(sender_sp, sp); + __ ldrw(r1, Address(r4, + Deoptimization::UnrollBlock:: + caller_adjustment_offset())); // (int) + __ sub(sp, sp, r1); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ldr(r1, Address(r5, 0)); // Load frame size + __ sub(r1, r1, 2 * wordSize); // We'll push pc and rfp by hand + __ ldr(lr, Address(r2, 0)); // Save return address + __ enter(); // and old rfp & set new rfp + __ sub(sp, sp, r1); // Prolog + __ str(sender_sp, Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable + // This value is corrected by layout_activation_impl + __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ mov(sender_sp, sp); // Pass sender_sp to next frame + __ add(r5, r5, wordSize); // Bump array pointer (sizes) + __ add(r2, r2, wordSize); // Bump array pointer (pcs) + __ subsw(r3, r3, 1); // Decrement counter + __ br(Assembler::GT, loop); + __ ldr(lr, Address(r2, 0)); // save final return address + // Re-push self-frame + __ enter(); // & old rfp & set new rfp + + // Use rfp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(sp, rfp, the_pc, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // Thread is in rdi already. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode); + // + // n.b. 2 gp args, 0 fp args, integral return type + + // sp should already be aligned + __ mov(c_rarg0, rthread); + __ movw(c_rarg1, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + __ blr(rscratch1); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(lr); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in aarch64.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// r0: exception oop +// r3: exception pc +// +// Results: +// r0: exception oop +// r3: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers r0, r3, r2, r4, r5, r8-r11 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(R3_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R0_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R2_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + // TODO check various assumptions made here + // + // make sure we do so before running this + + address start = __ pc(); + + // push rfp and retaddr by hand + // Exception pc is 'return address' for stack walker + __ protect_return_address(); + __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); + // there are no callee save registers and we don't expect an + // arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + __ str(r0, Address(rthread, JavaThread::exception_oop_offset())); + __ str(r3, Address(rthread, JavaThread::exception_pc_offset())); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + // + // n.b. 1 gp arg, 0 fp args, integral return type + + // the stack should always be aligned + address the_pc = __ pc(); + __ set_last_Java_frame(sp, noreg, the_pc, rscratch1); + __ mov(c_rarg0, rthread); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + __ blr(rscratch1); + // handle_exception_C is a special VM call which does not require an explicit + // instruction sync afterwards. + + // May jump to SVE compiled code + __ reinitialize_ptrue(); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // rfp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + // and we dont' expect an arg reg save area + __ ldp(rfp, r3, Address(__ post(sp, 2 * wordSize))); + __ authenticate_return_address(r3); + + // r0: exception handler + + // We have a handler in r0 (could be deopt blob). + __ mov(r8, r0); + + // Get the exception oop + __ ldr(r0, Address(rthread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ ldr(r4, Address(rthread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ str(zr, Address(rthread, JavaThread::exception_handler_pc_offset())); + __ str(zr, Address(rthread, JavaThread::exception_pc_offset())); #endif + // Clear the exception oop so GC no longer processes it as a root. + __ str(zr, Address(rthread, JavaThread::exception_oop_offset())); + + // r0: exception oop + // r8: exception handler + // r4: exception pc + // Jump to handler + + __ br(r8); + + // Make sure all code is generated + masm->flush(); + + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index bb2554e65ce83..65c026b95abbd 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -68,26 +68,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { - - public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - // we don't expect any arg reg save area so aarch64 asserts that - // frame::arg_reg_save_area_bytes == 0 - rfp_off = 0, - rfp_off2, - return_off, return_off2, - framesize - }; -}; - // FIXME -- this is used by C1 class RegisterSaver { const bool _save_vectors; @@ -2581,197 +2561,6 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address in LR - // and sp should be 16 byte aligned - // push rfp and retaddr by hand - __ protect_return_address(); - __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); - // we don't expect an arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - if (c_rarg1 != j_rarg0) { - __ movw(c_rarg1, j_rarg0); - } - - // we need to set the past SP to the stack pointer of the stub frame - // and the pc to the address where this runtime call will return - // although actually any pc in this code blob will do). - Label retaddr; - __ set_last_Java_frame(sp, noreg, retaddr, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // Thread is in rdi already. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); - // - // n.b. 2 gp args, 0 fp args, integral return type - - __ mov(c_rarg0, rthread); - __ movw(c_rarg2, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ lea(rscratch1, - RuntimeAddress(CAST_FROM_FN_PTR(address, - Deoptimization::uncommon_trap))); - __ blr(rscratch1); - __ bind(retaddr); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - - // location of rfp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // move UnrollBlock* into r4 - __ mov(r4, r0); - -#ifdef ASSERT - { Label L; - __ ldrw(rscratch1, Address(r4, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ cmpw(rscratch1, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ br(Assembler::EQ, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on r0 and sp. - __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ ldrw(r2, Address(r4, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ sub(r2, r2, 2 * wordSize); - __ add(sp, sp, r2); - __ ldp(rfp, zr, __ post(sp, 2 * wordSize)); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ ldrw(r1, Address(r4, - Deoptimization::UnrollBlock:: - total_frame_sizes_offset())); - __ bang_stack_size(r1, r2); -#endif - - // Load address of array of frame pcs into r2 (address*) - __ ldr(r2, Address(r4, - Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Load address of array of frame sizes into r5 (intptr_t*) - __ ldr(r5, Address(r4, - Deoptimization::UnrollBlock:: - frame_sizes_offset())); - - // Counter - __ ldrw(r3, Address(r4, - Deoptimization::UnrollBlock:: - number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = r8; - - __ mov(sender_sp, sp); - __ ldrw(r1, Address(r4, - Deoptimization::UnrollBlock:: - caller_adjustment_offset())); // (int) - __ sub(sp, sp, r1); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ldr(r1, Address(r5, 0)); // Load frame size - __ sub(r1, r1, 2 * wordSize); // We'll push pc and rfp by hand - __ ldr(lr, Address(r2, 0)); // Save return address - __ enter(); // and old rfp & set new rfp - __ sub(sp, sp, r1); // Prolog - __ str(sender_sp, Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable - // This value is corrected by layout_activation_impl - __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); - __ mov(sender_sp, sp); // Pass sender_sp to next frame - __ add(r5, r5, wordSize); // Bump array pointer (sizes) - __ add(r2, r2, wordSize); // Bump array pointer (pcs) - __ subsw(r3, r3, 1); // Decrement counter - __ br(Assembler::GT, loop); - __ ldr(lr, Address(r2, 0)); // save final return address - // Re-push self-frame - __ enter(); // & old rfp & set new rfp - - // Use rfp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(sp, rfp, the_pc, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // Thread is in rdi already. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode); - // - // n.b. 2 gp args, 0 fp args, integral return type - - // sp should already be aligned - __ mov(c_rarg0, rthread); - __ movw(c_rarg1, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - __ blr(rscratch1); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(lr); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // @@ -2983,141 +2772,3 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // frame_size_words or bytes?? return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); } - -#ifdef COMPILER2 -// This is here instead of runtime_aarch64_64.cpp because it uses SimpleRuntimeFrame -// -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in x86_64.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// r0: exception oop -// r3: exception pc -// -// Results: -// r0: exception oop -// r3: exception pc in caller or ??? -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers r0, r3, r2, r4, r5, r8-r11 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(R3_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R0_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R2_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - // TODO check various assumptions made here - // - // make sure we do so before running this - - address start = __ pc(); - - // push rfp and retaddr by hand - // Exception pc is 'return address' for stack walker - __ protect_return_address(); - __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); - // there are no callee save registers and we don't expect an - // arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - __ str(r0, Address(rthread, JavaThread::exception_oop_offset())); - __ str(r3, Address(rthread, JavaThread::exception_pc_offset())); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - // - // n.b. 1 gp arg, 0 fp args, integral return type - - // the stack should always be aligned - address the_pc = __ pc(); - __ set_last_Java_frame(sp, noreg, the_pc, rscratch1); - __ mov(c_rarg0, rthread); - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); - __ blr(rscratch1); - // handle_exception_C is a special VM call which does not require an explicit - // instruction sync afterwards. - - // May jump to SVE compiled code - __ reinitialize_ptrue(); - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // rfp is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - // and we dont' expect an arg reg save area - __ ldp(rfp, r3, Address(__ post(sp, 2 * wordSize))); - __ authenticate_return_address(r3); - - // r0: exception handler - - // We have a handler in r0 (could be deopt blob). - __ mov(r8, r0); - - // Get the exception oop - __ ldr(r0, Address(rthread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ ldr(r4, Address(rthread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ str(zr, Address(rthread, JavaThread::exception_handler_pc_offset())); - __ str(zr, Address(rthread, JavaThread::exception_pc_offset())); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ str(zr, Address(rthread, JavaThread::exception_oop_offset())); - - // r0: exception oop - // r8: exception handler - // r4: exception pc - // Jump to handler - - __ br(r8); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} - -#endif // COMPILER2 diff --git a/src/hotspot/cpu/arm/runtime_arm.cpp b/src/hotspot/cpu/arm/runtime_arm.cpp index 94a9ef553c75e..6f6c0c17e000d 100644 --- a/src/hotspot/cpu/arm/runtime_arm.cpp +++ b/src/hotspot/cpu/arm/runtime_arm.cpp @@ -37,10 +37,146 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_arm.inline.hpp" -#endif #define __ masm-> +//------------------------------generate_uncommon_trap_blob-------------------- +// Ought to generate an ideal graph & compile, but here's some ASM +// instead. +void OptoRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + + // setup code generation tools +#ifdef _LP64 + CodeBuffer buffer("uncommon_trap_blob", 2700, 512); +#else + // Measured 8/7/03 at 660 in 32bit debug build + CodeBuffer buffer("uncommon_trap_blob", 2000, 512); +#endif + // bypassed when code generation useless + MacroAssembler* masm = new MacroAssembler(&buffer); + const Register Rublock = R6; + const Register Rsender = altFP_7_11; + assert_different_registers(Rublock, Rsender, Rexception_obj, R0, R1, R2, R3, R8, Rtemp); + + // + // This is the entry point for all traps the compiler takes when it thinks + // it cannot handle further execution of compilation code. The frame is + // deoptimized in these cases and converted into interpreter frames for + // execution + // The steps taken by this frame are as follows: + // - push a fake "unpack_frame" + // - call the C routine Deoptimization::uncommon_trap (this function + // packs the current compiled frame into vframe arrays and returns + // information about the number and size of interpreter frames which + // are equivalent to the frame which is being deoptimized) + // - deallocate the "unpack_frame" + // - deallocate the deoptimization frame + // - in a loop using the information returned in the previous step + // push interpreter frames; + // - create a dummy "unpack_frame" + // - call the C routine: Deoptimization::unpack_frames (this function + // lays out values on the interpreter frame which was just created) + // - deallocate the dummy unpack_frame + // - return to the interpreter entry point + // + // Refer to the following methods for more information: + // - Deoptimization::uncommon_trap + // - Deoptimization::unpack_frame + + // the unloaded class index is in R0 (first parameter to this blob) + + __ raw_push(FP, LR); + __ set_last_Java_frame(SP, FP, false, Rtemp); + __ mov(R2, Deoptimization::Unpack_uncommon_trap); + __ mov(R1, R0); + __ mov(R0, Rthread); + __ call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); + __ mov(Rublock, R0); + __ reset_last_Java_frame(Rtemp); + __ raw_pop(FP, LR); + +#ifdef ASSERT + { Label L; + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ cmp_32(Rtemp, Deoptimization::Unpack_uncommon_trap); + __ b(L, eq); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + + // Set initial stack state before pushing interpreter frames + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); + __ ldr(R2, Address(Rublock, Deoptimization::UnrollBlock::frame_pcs_offset())); + __ ldr(R3, Address(Rublock, Deoptimization::UnrollBlock::frame_sizes_offset())); + + __ add(SP, SP, Rtemp); + + // See if it is enough stack to push deoptimized frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + // + // The compiled method that we are deoptimizing was popped from the stack. + // If the stack bang results in a stack overflow, we don't return to the + // method that is being deoptimized. The stack overflow exception is + // propagated to the caller of the deoptimized method. Need to get the pc + // from the caller in LR and restore FP. + __ ldr(LR, Address(R2, 0)); + __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); + __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ arm_stack_overflow_check(R8, Rtemp); +#endif + __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::number_of_frames_offset())); + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::caller_adjustment_offset())); + __ mov(Rsender, SP); + __ sub(SP, SP, Rtemp); + // __ ldr(FP, Address(FP)); + __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ldr(LR, Address(R2, wordSize, post_indexed)); // load frame pc + __ ldr(Rtemp, Address(R3, wordSize, post_indexed)); // load frame size + + __ raw_push(FP, LR); // create new frame + __ mov(FP, SP); + __ sub(Rtemp, Rtemp, 2*wordSize); + + __ sub(SP, SP, Rtemp); + + __ str(Rsender, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); + __ mov(LR, 0); + __ str(LR, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); + __ subs(R8, R8, 1); // decrement counter + __ mov(Rsender, SP); + __ b(loop, ne); + + // Re-push self-frame + __ ldr(LR, Address(R2)); + __ raw_push(FP, LR); + __ mov(FP, SP); + + // Call unpack_frames with proper arguments + __ mov(R0, Rthread); + __ mov(R1, Deoptimization::Unpack_uncommon_trap); + __ set_last_Java_frame(SP, FP, true, Rtemp); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); + // oop_maps->add_gc_map(__ pc() - start, new OopMap(frame_size_in_words, 0)); + __ reset_last_Java_frame(Rtemp); + + __ mov(SP, FP); + __ pop(RegisterSet(FP) | RegisterSet(PC)); + + masm->flush(); + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, nullptr, 2 /* LR+FP */); +} + //------------------------------ generate_exception_blob --------------------------- // creates exception blob at the end // Using exception blob, this code is jumped from a compiled method. @@ -148,3 +284,6 @@ void OptoRuntime::generate_exception_blob() { _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize_in_words); } + +#endif // COMPILER2 + diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 3792fab082ba6..1305283aeaeba 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -1595,147 +1595,6 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); } -#ifdef COMPILER2 - -//------------------------------generate_uncommon_trap_blob-------------------- -// Ought to generate an ideal graph & compile, but here's some ASM -// instead. -void SharedRuntime::generate_uncommon_trap_blob() { - // allocate space for the code - ResourceMark rm; - - // setup code generation tools -#ifdef _LP64 - CodeBuffer buffer("uncommon_trap_blob", 2700, 512); -#else - // Measured 8/7/03 at 660 in 32bit debug build - CodeBuffer buffer("uncommon_trap_blob", 2000, 512); -#endif - // bypassed when code generation useless - MacroAssembler* masm = new MacroAssembler(&buffer); - const Register Rublock = R6; - const Register Rsender = altFP_7_11; - assert_different_registers(Rublock, Rsender, Rexception_obj, R0, R1, R2, R3, R8, Rtemp); - - // - // This is the entry point for all traps the compiler takes when it thinks - // it cannot handle further execution of compilation code. The frame is - // deoptimized in these cases and converted into interpreter frames for - // execution - // The steps taken by this frame are as follows: - // - push a fake "unpack_frame" - // - call the C routine Deoptimization::uncommon_trap (this function - // packs the current compiled frame into vframe arrays and returns - // information about the number and size of interpreter frames which - // are equivalent to the frame which is being deoptimized) - // - deallocate the "unpack_frame" - // - deallocate the deoptimization frame - // - in a loop using the information returned in the previous step - // push interpreter frames; - // - create a dummy "unpack_frame" - // - call the C routine: Deoptimization::unpack_frames (this function - // lays out values on the interpreter frame which was just created) - // - deallocate the dummy unpack_frame - // - return to the interpreter entry point - // - // Refer to the following methods for more information: - // - Deoptimization::uncommon_trap - // - Deoptimization::unpack_frame - - // the unloaded class index is in R0 (first parameter to this blob) - - __ raw_push(FP, LR); - __ set_last_Java_frame(SP, FP, false, Rtemp); - __ mov(R2, Deoptimization::Unpack_uncommon_trap); - __ mov(R1, R0); - __ mov(R0, Rthread); - __ call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); - __ mov(Rublock, R0); - __ reset_last_Java_frame(Rtemp); - __ raw_pop(FP, LR); - -#ifdef ASSERT - { Label L; - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ cmp_32(Rtemp, Deoptimization::Unpack_uncommon_trap); - __ b(L, eq); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - - // Set initial stack state before pushing interpreter frames - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); - __ ldr(R2, Address(Rublock, Deoptimization::UnrollBlock::frame_pcs_offset())); - __ ldr(R3, Address(Rublock, Deoptimization::UnrollBlock::frame_sizes_offset())); - - __ add(SP, SP, Rtemp); - - // See if it is enough stack to push deoptimized frames. -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - // - // The compiled method that we are deoptimizing was popped from the stack. - // If the stack bang results in a stack overflow, we don't return to the - // method that is being deoptimized. The stack overflow exception is - // propagated to the caller of the deoptimized method. Need to get the pc - // from the caller in LR and restore FP. - __ ldr(LR, Address(R2, 0)); - __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); - __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ arm_stack_overflow_check(R8, Rtemp); -#endif - __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::number_of_frames_offset())); - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::caller_adjustment_offset())); - __ mov(Rsender, SP); - __ sub(SP, SP, Rtemp); - // __ ldr(FP, Address(FP)); - __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ldr(LR, Address(R2, wordSize, post_indexed)); // load frame pc - __ ldr(Rtemp, Address(R3, wordSize, post_indexed)); // load frame size - - __ raw_push(FP, LR); // create new frame - __ mov(FP, SP); - __ sub(Rtemp, Rtemp, 2*wordSize); - - __ sub(SP, SP, Rtemp); - - __ str(Rsender, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); - __ mov(LR, 0); - __ str(LR, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); - __ subs(R8, R8, 1); // decrement counter - __ mov(Rsender, SP); - __ b(loop, ne); - - // Re-push self-frame - __ ldr(LR, Address(R2)); - __ raw_push(FP, LR); - __ mov(FP, SP); - - // Call unpack_frames with proper arguments - __ mov(R0, Rthread); - __ mov(R1, Deoptimization::Unpack_uncommon_trap); - __ set_last_Java_frame(SP, FP, true, Rtemp); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); - // oop_maps->add_gc_map(__ pc() - start, new OopMap(frame_size_in_words, 0)); - __ reset_last_Java_frame(Rtemp); - - __ mov(SP, FP); - __ pop(RegisterSet(FP) | RegisterSet(PC)); - - masm->flush(); - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, nullptr, 2 /* LR+FP */); -} - -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 9b5a86bc45bfd..505f67ab6f952 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -3078,7 +3078,7 @@ void SharedRuntime::generate_deopt_blob() { } #ifdef COMPILER2 -void SharedRuntime::generate_uncommon_trap_blob() { +void OptoRuntime::generate_uncommon_trap_blob() { // Allocate space for the code. ResourceMark rm; // Setup code generation tools. @@ -3144,7 +3144,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { #ifdef ASSERT __ lwz(R22_tmp2, in_bytes(Deoptimization::UnrollBlock::unpack_kind_offset()), unroll_block_reg); __ cmpdi(CCR0, R22_tmp2, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ asm_assert_eq("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap"); + __ asm_assert_eq("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); #endif // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp new file mode 100644 index 0000000000000..9e16278c3b547 --- /dev/null +++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#ifdef COMPILER2 +#include "asm/macroAssembler.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "code/vmreg.hpp" +#include "interpreter/interpreter.hpp" +#include "opto/runtime.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/globalDefinitions.hpp" +#include "vmreg_riscv.inline.hpp" + +class SimpleRuntimeFrame { +public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that fp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + // we don't expect any arg reg save area so riscv asserts that + // frame::arg_reg_save_area_bytes == 0 + fp_off = 0, fp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + assert_cond(masm != nullptr); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address in RA + // and sp should be 16 byte aligned + // push fp and retaddr by hand + __ addi(sp, sp, -2 * wordSize); + __ sd(ra, Address(sp, wordSize)); + __ sd(fp, Address(sp, 0)); + // we don't expect an arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + __ sign_extend(c_rarg1, j_rarg0, 32); + + // we need to set the past SP to the stack pointer of the stub frame + // and the pc to the address where this runtime call will return + // although actually any pc in this code blob will do). + Label retaddr; + __ set_last_Java_frame(sp, noreg, retaddr, t0); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index, jint exec_mode) + // + // n.b. 3 gp args, 0 fp args, integral return type + + __ mv(c_rarg0, xthread); + __ mv(c_rarg2, Deoptimization::Unpack_uncommon_trap); + __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); + __ bind(retaddr); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + assert_cond(oop_maps != nullptr && map != nullptr); + + // location of fp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // move UnrollBlock* into x14 + __ mv(x14, x10); + +#ifdef ASSERT + { Label L; + __ lwu(t0, Address(x14, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ mv(t1, Deoptimization::Unpack_uncommon_trap); + __ beq(t0, t1, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ lwu(x12, Address(x14, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ sub(x12, x12, 2 * wordSize); + __ add(sp, sp, x12); + __ ld(fp, Address(sp, 0)); + __ ld(ra, Address(sp, wordSize)); + __ addi(sp, sp, 2 * wordSize); + // RA should now be the return address to the caller (3) frame + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ lwu(x11, Address(x14, + Deoptimization::UnrollBlock:: + total_frame_sizes_offset())); + __ bang_stack_size(x11, x12); +#endif + + // Load address of array of frame pcs into x12 (address*) + __ ld(x12, Address(x14, + Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Load address of array of frame sizes into x15 (intptr_t*) + __ ld(x15, Address(x14, + Deoptimization::UnrollBlock:: + frame_sizes_offset())); + + // Counter + __ lwu(x13, Address(x14, + Deoptimization::UnrollBlock:: + number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = t1; // temporary register + + __ lwu(x11, Address(x14, + Deoptimization::UnrollBlock:: + caller_adjustment_offset())); // (int) + __ mv(sender_sp, sp); + __ sub(sp, sp, x11); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ld(x11, Address(x15, 0)); // Load frame size + __ sub(x11, x11, 2 * wordSize); // We'll push pc and fp by hand + __ ld(ra, Address(x12, 0)); // Save return address + __ enter(); // and old fp & set new fp + __ sub(sp, sp, x11); // Prolog + __ sd(sender_sp, Address(fp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable + // This value is corrected by layout_activation_impl + __ sd(zr, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ mv(sender_sp, sp); // Pass sender_sp to next frame + __ add(x15, x15, wordSize); // Bump array pointer (sizes) + __ add(x12, x12, wordSize); // Bump array pointer (pcs) + __ subw(x13, x13, 1); // Decrement counter + __ bgtz(x13, loop); + __ ld(ra, Address(x12, 0)); // save final return address + // Re-push self-frame + __ enter(); // & old fp & set new fp + + // Use fp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(sp, fp, the_pc, t0); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode) + // + + // n.b. 2 gp args, 0 fp args, integral return type + + // sp should already be aligned + __ mv(c_rarg0, xthread); + __ mv(c_rarg1, Deoptimization::Unpack_uncommon_trap); + __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in riscv.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// x10: exception oop +// x13: exception pc +// +// Results: +// x10: exception oop +// x13: exception pc in caller +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers x10, x13, x12, x14, x15, t0 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(R13_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R10_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R12_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + assert_cond(masm != nullptr); + + // TODO check various assumptions made here + // + // make sure we do so before running this + + address start = __ pc(); + + // push fp and retaddr by hand + // Exception pc is 'return address' for stack walker + __ addi(sp, sp, -2 * wordSize); + __ sd(ra, Address(sp, wordSize)); + __ sd(fp, Address(sp)); + // there are no callee save registers and we don't expect an + // arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + __ sd(x10, Address(xthread, JavaThread::exception_oop_offset())); + __ sd(x13, Address(xthread, JavaThread::exception_pc_offset())); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + // + // n.b. 1 gp arg, 0 fp args, integral return type + + // the stack should always be aligned + address the_pc = __ pc(); + __ set_last_Java_frame(sp, noreg, the_pc, t0); + __ mv(c_rarg0, xthread); + __ rt_call(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C)); + + // handle_exception_C is a special VM call which does not require an explicit + // instruction sync afterwards. + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + assert_cond(oop_maps != nullptr); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // fp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + // and we dont' expect an arg reg save area + __ ld(fp, Address(sp)); + __ ld(x13, Address(sp, wordSize)); + __ addi(sp, sp , 2 * wordSize); + + // x10: exception handler + + // We have a handler in x10 (could be deopt blob). + __ mv(t0, x10); + + // Get the exception oop + __ ld(x10, Address(xthread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ ld(x14, Address(xthread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ sd(zr, Address(xthread, JavaThread::exception_handler_pc_offset())); + __ sd(zr, Address(xthread, JavaThread::exception_pc_offset())); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ sd(zr, Address(xthread, JavaThread::exception_oop_offset())); + + // x10: exception oop + // t0: exception handler + // x14: exception pc + // Jump to handler + + __ jr(t0); + + // Make sure all code is generated + masm->flush(); + + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 + + diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 01ab3d5c27403..ad06f688d6a36 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -67,24 +67,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { -public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that fp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - // we don't expect any arg reg save area so riscv asserts that - // frame::arg_reg_save_area_bytes == 0 - fp_off = 0, fp_off2, - return_off, return_off2, - framesize - }; -}; - class RegisterSaver { const bool _save_vectors; public: @@ -2441,195 +2423,6 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - assert_cond(masm != nullptr); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address in RA - // and sp should be 16 byte aligned - // push fp and retaddr by hand - __ addi(sp, sp, -2 * wordSize); - __ sd(ra, Address(sp, wordSize)); - __ sd(fp, Address(sp, 0)); - // we don't expect an arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - __ sign_extend(c_rarg1, j_rarg0, 32); - - // we need to set the past SP to the stack pointer of the stub frame - // and the pc to the address where this runtime call will return - // although actually any pc in this code blob will do). - Label retaddr; - __ set_last_Java_frame(sp, noreg, retaddr, t0); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index, jint exec_mode) - // - // n.b. 3 gp args, 0 fp args, integral return type - - __ mv(c_rarg0, xthread); - __ mv(c_rarg2, Deoptimization::Unpack_uncommon_trap); - __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); - __ bind(retaddr); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - assert_cond(oop_maps != nullptr && map != nullptr); - - // location of fp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // move UnrollBlock* into x14 - __ mv(x14, x10); - -#ifdef ASSERT - { Label L; - __ lwu(t0, Address(x14, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ mv(t1, Deoptimization::Unpack_uncommon_trap); - __ beq(t0, t1, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ lwu(x12, Address(x14, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ sub(x12, x12, 2 * wordSize); - __ add(sp, sp, x12); - __ ld(fp, Address(sp, 0)); - __ ld(ra, Address(sp, wordSize)); - __ addi(sp, sp, 2 * wordSize); - // RA should now be the return address to the caller (3) frame - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ lwu(x11, Address(x14, - Deoptimization::UnrollBlock:: - total_frame_sizes_offset())); - __ bang_stack_size(x11, x12); -#endif - - // Load address of array of frame pcs into x12 (address*) - __ ld(x12, Address(x14, - Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Load address of array of frame sizes into x15 (intptr_t*) - __ ld(x15, Address(x14, - Deoptimization::UnrollBlock:: - frame_sizes_offset())); - - // Counter - __ lwu(x13, Address(x14, - Deoptimization::UnrollBlock:: - number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = t1; // temporary register - - __ lwu(x11, Address(x14, - Deoptimization::UnrollBlock:: - caller_adjustment_offset())); // (int) - __ mv(sender_sp, sp); - __ sub(sp, sp, x11); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ld(x11, Address(x15, 0)); // Load frame size - __ sub(x11, x11, 2 * wordSize); // We'll push pc and fp by hand - __ ld(ra, Address(x12, 0)); // Save return address - __ enter(); // and old fp & set new fp - __ sub(sp, sp, x11); // Prolog - __ sd(sender_sp, Address(fp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable - // This value is corrected by layout_activation_impl - __ sd(zr, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); - __ mv(sender_sp, sp); // Pass sender_sp to next frame - __ add(x15, x15, wordSize); // Bump array pointer (sizes) - __ add(x12, x12, wordSize); // Bump array pointer (pcs) - __ subw(x13, x13, 1); // Decrement counter - __ bgtz(x13, loop); - __ ld(ra, Address(x12, 0)); // save final return address - // Re-push self-frame - __ enter(); // & old fp & set new fp - - // Use fp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(sp, fp, the_pc, t0); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode) - // - - // n.b. 2 gp args, 0 fp args, integral return type - - // sp should already be aligned - __ mv(c_rarg0, xthread); - __ mv(c_rarg1, Deoptimization::Unpack_uncommon_trap); - __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, @@ -2835,139 +2628,3 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // return the blob return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); } - -#ifdef COMPILER2 -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in riscv.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// x10: exception oop -// x13: exception pc -// -// Results: -// x10: exception oop -// x13: exception pc in caller -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers x10, x13, x12, x14, x15, t0 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(R13_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R10_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R12_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - assert_cond(masm != nullptr); - - // TODO check various assumptions made here - // - // make sure we do so before running this - - address start = __ pc(); - - // push fp and retaddr by hand - // Exception pc is 'return address' for stack walker - __ addi(sp, sp, -2 * wordSize); - __ sd(ra, Address(sp, wordSize)); - __ sd(fp, Address(sp)); - // there are no callee save registers and we don't expect an - // arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - __ sd(x10, Address(xthread, JavaThread::exception_oop_offset())); - __ sd(x13, Address(xthread, JavaThread::exception_pc_offset())); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - // - // n.b. 1 gp arg, 0 fp args, integral return type - - // the stack should always be aligned - address the_pc = __ pc(); - __ set_last_Java_frame(sp, noreg, the_pc, t0); - __ mv(c_rarg0, xthread); - __ rt_call(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C)); - - // handle_exception_C is a special VM call which does not require an explicit - // instruction sync afterwards. - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - assert_cond(oop_maps != nullptr); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // fp is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - // and we dont' expect an arg reg save area - __ ld(fp, Address(sp)); - __ ld(x13, Address(sp, wordSize)); - __ addi(sp, sp , 2 * wordSize); - - // x10: exception handler - - // We have a handler in x10 (could be deopt blob). - __ mv(t0, x10); - - // Get the exception oop - __ ld(x10, Address(xthread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ ld(x14, Address(xthread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ sd(zr, Address(xthread, JavaThread::exception_handler_pc_offset())); - __ sd(zr, Address(xthread, JavaThread::exception_pc_offset())); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ sd(zr, Address(xthread, JavaThread::exception_oop_offset())); - - // x10: exception oop - // t0: exception handler - // x14: exception pc - // Jump to handler - - __ jr(t0); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 0ee88345282b7..641b3712a175b 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2706,7 +2706,7 @@ void SharedRuntime::generate_deopt_blob() { #ifdef COMPILER2 //------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { +void OptoRuntime::generate_uncommon_trap_blob() { // Allocate space for the code ResourceMark rm; // Setup code generation tools @@ -2769,7 +2769,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { } else { __ z_cliy(unpack_kind_byte_offset, unroll_block_reg, Deoptimization::Unpack_uncommon_trap); } - __ asm_assert(Assembler::bcondEqual, "SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap", 0); + __ asm_assert(Assembler::bcondEqual, "OptoRuntime::generate_deopt_blob: expected Unpack_uncommon_trap", 0); #endif __ zap_from_to(Z_SP, Z_SP, Z_R0_scratch, Z_R1, 500, -1); diff --git a/src/hotspot/cpu/x86/runtime_x86_32.cpp b/src/hotspot/cpu/x86/runtime_x86_32.cpp index d38fa3c60bdd1..2a21c42a5e606 100644 --- a/src/hotspot/cpu/x86/runtime_x86_32.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_32.cpp @@ -41,6 +41,180 @@ #define __ masm-> +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 512, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + enum frame_layout { + arg0_off, // thread sp + 0 // Arg location for + arg1_off, // unloaded_class_index sp + 1 // calling C + arg2_off, // exec_mode sp + 2 + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, // callee saved register sp + 3 + return_off, // slot for return address sp + 4 + framesize + }; + + address start = __ pc(); + + // Push self-frame. + __ subptr(rsp, return_off*wordSize); // Epilog! + + // rbp, is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers no that adapter frames are gone. + __ movptr(Address(rsp, rbp_off*wordSize), rbp); + + // Clear the floating point exception stack + __ empty_FPU_stack(); + + // set last_Java_sp + __ get_thread(rdx); + __ set_last_Java_frame(rdx, noreg, noreg, nullptr, noreg); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + __ movptr(Address(rsp, arg0_off*wordSize), rdx); + // argument already in ECX + __ movl(Address(rsp, arg1_off*wordSize),rcx); + __ movl(Address(rsp, arg2_off*wordSize), Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = new OopMap( framesize, 0 ); + // No oopMap for rbp, it is known implicitly + + oop_maps->add_gc_map( __ pc()-start, map); + + __ get_thread(rcx); + + __ reset_last_Java_frame(rcx, false); + + // Load UnrollBlock into EDI + __ movptr(rdi, rax); + +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), + (int32_t)Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on EAX and ESP. + __ addptr(rsp,(framesize-1)*wordSize); // Epilog! + + // Pop deoptimized frame + __ movl2ptr(rcx, Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); + __ addptr(rsp, rcx); + + // sp should be pointing at the return address to the caller (3) + + // Pick up the initial fp we should save + // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) + __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ bang_stack_size(rbx, rcx); +#endif + + // Load array of frame pcs into ECX + __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset())); + + __ pop(rsi); // trash the pc + + // Load array of frame sizes into ESI + __ movptr(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset())); + + Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset()); + + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset())); + __ movl(counter, rbx); + + // Now adjust the caller's stack to make up for the extra locals + // but record the original sp so that we can save it in the skeletal interpreter + // frame and the stack walking of interpreter_sender will get the unextended sp + // value and not the "real" sp value. + + Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset()); + __ movptr(sp_temp, rsp); + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset())); + __ subptr(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movptr(rbx, Address(rsi, 0)); // Load frame size + __ subptr(rbx, 2*wordSize); // we'll push pc and rbp, by hand + __ pushptr(Address(rcx, 0)); // save return address + __ enter(); // save old & set new rbp, + __ subptr(rsp, rbx); // Prolog! + __ movptr(rbx, sp_temp); // sender's sp + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); + __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable + __ movptr(sp_temp, rsp); // pass to next frame + __ addptr(rsi, wordSize); // Bump array pointer (sizes) + __ addptr(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(counter); // decrement counter + __ jcc(Assembler::notZero, loop); + __ pushptr(Address(rcx, 0)); // save final return address + + // Re-push self-frame + __ enter(); // save old & set new rbp, + __ subptr(rsp, (framesize-2) * wordSize); // Prolog! + + + // set last_Java_sp, last_Java_fp + __ get_thread(rdi); + __ set_last_Java_frame(rdi, noreg, rbp, nullptr, noreg); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + __ movptr(Address(rsp,arg0_off*wordSize),rdi); + __ movl(Address(rsp,arg1_off*wordSize), Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + // Set an oopmap for the call site + oop_maps->add_gc_map( __ pc()-start, new OopMap( framesize, 0 ) ); + + __ get_thread(rdi); + __ reset_last_Java_frame(rdi, true); + + // Pop self-frame. + __ leave(); // Epilog! + + // Jump to interpreter + __ ret(0); + + // ------------- + // make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize); +} + //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end // Using exception blob, this code is jumped from a compiled method. diff --git a/src/hotspot/cpu/x86/runtime_x86_64.cpp b/src/hotspot/cpu/x86/runtime_x86_64.cpp index e08550715b495..eb3bab36b88cb 100644 --- a/src/hotspot/cpu/x86/runtime_x86_64.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_64.cpp @@ -34,11 +34,328 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_x86.inline.hpp" + +class SimpleRuntimeFrame { + + public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, + rbp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address on the + // stack, so rsp is 8-byte aligned until we allocate our frame. + __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! + + // No callee saved registers. rbp is assumed implicitly saved + __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + __ movl(c_rarg1, j_rarg0); + + __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // Thread is in rdi already. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + + __ mov(c_rarg0, r15_thread); + __ movl(c_rarg2, Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + + // location of rbp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // Load UnrollBlock* into rdi + __ mov(rdi, rax); + +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), + Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } #endif + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on rax and rsp. + __ addptr(rsp, (SimpleRuntimeFrame::framesize - 2) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ movl(rcx, Address(rdi, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ addptr(rsp, rcx); + + // rsp should be pointing at the return address to the caller (3) + + // Pick up the initial fp we should save + // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) + __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ bang_stack_size(rbx, rcx); +#endif + + // Load address of array of frame pcs into rcx (address*) + __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Trash the return pc + __ addptr(rsp, wordSize); + + // Load address of array of frame sizes into rsi (intptr_t*) + __ movptr(rsi, Address(rdi, Deoptimization::UnrollBlock:: frame_sizes_offset())); + + // Counter + __ movl(rdx, Address(rdi, Deoptimization::UnrollBlock:: number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = r8; + + __ mov(sender_sp, rsp); + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock:: caller_adjustment_offset())); // (int) + __ subptr(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movptr(rbx, Address(rsi, 0)); // Load frame size + __ subptr(rbx, 2 * wordSize); // We'll push pc and rbp by hand + __ pushptr(Address(rcx, 0)); // Save return address + __ enter(); // Save old & set new rbp + __ subptr(rsp, rbx); // Prolog + __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), + sender_sp); // Make it walkable + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ mov(sender_sp, rsp); // Pass sender_sp to next frame + __ addptr(rsi, wordSize); // Bump array pointer (sizes) + __ addptr(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(rdx); // Decrement counter + __ jcc(Assembler::notZero, loop); + __ pushptr(Address(rcx, 0)); // Save final return address + + // Re-push self-frame + __ enter(); // Save old & set new rbp + __ subptr(rsp, (SimpleRuntimeFrame::framesize - 4) << LogBytesPerInt); + // Prolog + + // Use rbp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(noreg, rbp, the_pc, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // Thread is in rdi already. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode); + + __ andptr(rsp, -(StackAlignmentInBytes)); // Align SP as required by ABI + __ mov(c_rarg0, r15_thread); + __ movl(c_rarg1, Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(0); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in x86_64.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// rax: exception oop +// rdx: exception pc +// +// Results: +// rax: exception oop +// rdx: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers rax, rdx, rcx, rsi, rdi, r8-r11 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(RDX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RAX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RCX_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + + address start = __ pc(); + + // Exception pc is 'return address' for stack walker + __ push(rdx); + __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog + + // Save callee-saved registers. See x86_64.ad. + + // rbp is an implicitly saved callee saved register (i.e., the calling + // convention will save/restore it in the prolog/epilog). Other than that + // there are no callee save registers now that adapter frames are gone. + + __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + // c_rarg0 is either rdi (Linux) or rcx (Windows). + __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()),rax); + __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), rdx); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + + // At a method handle call, the stack may not be properly aligned + // when returning with an exception. + address the_pc = __ pc(); + __ set_last_Java_frame(noreg, noreg, the_pc, rscratch1); + __ mov(c_rarg0, r15_thread); + __ andptr(rsp, -(StackAlignmentInBytes)); // Align stack + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // rbp is an implicitly saved callee-saved register (i.e., the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + + __ movptr(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt)); + + __ addptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog + __ pop(rdx); // No need for exception pc anymore + + // rax: exception handler + + // We have a handler in rax (could be deopt blob). + __ mov(r8, rax); + + // Get the exception oop + __ movptr(rax, Address(r15_thread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ movptr(rdx, Address(r15_thread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ movptr(Address(r15_thread, JavaThread::exception_handler_pc_offset()), NULL_WORD); + __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), NULL_WORD); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()), NULL_WORD); + + // rax: exception oop + // r8: exception handler + // rdx: exception pc + // Jump to handler + + __ jmp(r8); + + // Make sure all code is generated + masm->flush(); -// This file should really contain the code for generating the OptoRuntime -// exception_blob. However that code uses SimpleRuntimeFrame which only -// exists in sharedRuntime_x86_64.cpp. When there is a sharedRuntime_.hpp -// file and SimpleRuntimeFrame is able to move there then the exception_blob -// code will move here where it belongs. + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index 6303c279195ca..80be7c7d5b642 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -2389,183 +2389,6 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); } - -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // allocate space for the code - ResourceMark rm; - // setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 512, 512); - MacroAssembler* masm = new MacroAssembler(&buffer); - - enum frame_layout { - arg0_off, // thread sp + 0 // Arg location for - arg1_off, // unloaded_class_index sp + 1 // calling C - arg2_off, // exec_mode sp + 2 - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - rbp_off, // callee saved register sp + 3 - return_off, // slot for return address sp + 4 - framesize - }; - - address start = __ pc(); - - // Push self-frame. - __ subptr(rsp, return_off*wordSize); // Epilog! - - // rbp, is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers no that adapter frames are gone. - __ movptr(Address(rsp, rbp_off*wordSize), rbp); - - // Clear the floating point exception stack - __ empty_FPU_stack(); - - // set last_Java_sp - __ get_thread(rdx); - __ set_last_Java_frame(rdx, noreg, noreg, nullptr, noreg); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - __ movptr(Address(rsp, arg0_off*wordSize), rdx); - // argument already in ECX - __ movl(Address(rsp, arg1_off*wordSize),rcx); - __ movl(Address(rsp, arg2_off*wordSize), Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); - - // Set an oopmap for the call site - OopMapSet *oop_maps = new OopMapSet(); - OopMap* map = new OopMap( framesize, 0 ); - // No oopMap for rbp, it is known implicitly - - oop_maps->add_gc_map( __ pc()-start, map); - - __ get_thread(rcx); - - __ reset_last_Java_frame(rcx, false); - - // Load UnrollBlock into EDI - __ movptr(rdi, rax); - -#ifdef ASSERT - { Label L; - __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), - (int32_t)Deoptimization::Unpack_uncommon_trap); - __ jcc(Assembler::equal, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on EAX and ESP. - __ addptr(rsp,(framesize-1)*wordSize); // Epilog! - - // Pop deoptimized frame - __ movl2ptr(rcx, Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); - __ addptr(rsp, rcx); - - // sp should be pointing at the return address to the caller (3) - - // Pick up the initial fp we should save - // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) - __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ bang_stack_size(rbx, rcx); -#endif - - // Load array of frame pcs into ECX - __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset())); - - __ pop(rsi); // trash the pc - - // Load array of frame sizes into ESI - __ movptr(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset())); - - Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset()); - - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset())); - __ movl(counter, rbx); - - // Now adjust the caller's stack to make up for the extra locals - // but record the original sp so that we can save it in the skeletal interpreter - // frame and the stack walking of interpreter_sender will get the unextended sp - // value and not the "real" sp value. - - Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset()); - __ movptr(sp_temp, rsp); - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset())); - __ subptr(rsp, rbx); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ movptr(rbx, Address(rsi, 0)); // Load frame size - __ subptr(rbx, 2*wordSize); // we'll push pc and rbp, by hand - __ pushptr(Address(rcx, 0)); // save return address - __ enter(); // save old & set new rbp, - __ subptr(rsp, rbx); // Prolog! - __ movptr(rbx, sp_temp); // sender's sp - // This value is corrected by layout_activation_impl - __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); - __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable - __ movptr(sp_temp, rsp); // pass to next frame - __ addptr(rsi, wordSize); // Bump array pointer (sizes) - __ addptr(rcx, wordSize); // Bump array pointer (pcs) - __ decrementl(counter); // decrement counter - __ jcc(Assembler::notZero, loop); - __ pushptr(Address(rcx, 0)); // save final return address - - // Re-push self-frame - __ enter(); // save old & set new rbp, - __ subptr(rsp, (framesize-2) * wordSize); // Prolog! - - - // set last_Java_sp, last_Java_fp - __ get_thread(rdi); - __ set_last_Java_frame(rdi, noreg, rbp, nullptr, noreg); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - __ movptr(Address(rsp,arg0_off*wordSize),rdi); - __ movl(Address(rsp,arg1_off*wordSize), Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - // Set an oopmap for the call site - oop_maps->add_gc_map( __ pc()-start, new OopMap( framesize, 0 ) ); - - __ get_thread(rdi); - __ reset_last_Java_frame(rdi, true); - - // Pop self-frame. - __ leave(); // Epilog! - - // Jump to interpreter - __ ret(0); - - // ------------- - // make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index decaa9d1ee914..f2488ef4e7445 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -72,24 +72,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { - - public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, - rbp_off2, - return_off, return_off2, - framesize - }; -}; - class RegisterSaver { // Capture info about frame layout. Layout offsets are in jint // units because compiler frame slots are jints. @@ -2987,182 +2969,6 @@ void SharedRuntime::generate_deopt_blob() { #endif } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address on the - // stack, so rsp is 8-byte aligned until we allocate our frame. - __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! - - // No callee saved registers. rbp is assumed implicitly saved - __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); - - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - __ movl(c_rarg1, j_rarg0); - - __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // Thread is in rdi already. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); - - __ mov(c_rarg0, r15_thread); - __ movl(c_rarg2, Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - - // location of rbp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // Load UnrollBlock* into rdi - __ mov(rdi, rax); - -#ifdef ASSERT - { Label L; - __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), - Deoptimization::Unpack_uncommon_trap); - __ jcc(Assembler::equal, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on rax and rsp. - __ addptr(rsp, (SimpleRuntimeFrame::framesize - 2) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ movl(rcx, Address(rdi, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ addptr(rsp, rcx); - - // rsp should be pointing at the return address to the caller (3) - - // Pick up the initial fp we should save - // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) - __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ bang_stack_size(rbx, rcx); -#endif - - // Load address of array of frame pcs into rcx (address*) - __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Trash the return pc - __ addptr(rsp, wordSize); - - // Load address of array of frame sizes into rsi (intptr_t*) - __ movptr(rsi, Address(rdi, Deoptimization::UnrollBlock:: frame_sizes_offset())); - - // Counter - __ movl(rdx, Address(rdi, Deoptimization::UnrollBlock:: number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = r8; - - __ mov(sender_sp, rsp); - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock:: caller_adjustment_offset())); // (int) - __ subptr(rsp, rbx); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ movptr(rbx, Address(rsi, 0)); // Load frame size - __ subptr(rbx, 2 * wordSize); // We'll push pc and rbp by hand - __ pushptr(Address(rcx, 0)); // Save return address - __ enter(); // Save old & set new rbp - __ subptr(rsp, rbx); // Prolog - __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), - sender_sp); // Make it walkable - // This value is corrected by layout_activation_impl - __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); - __ mov(sender_sp, rsp); // Pass sender_sp to next frame - __ addptr(rsi, wordSize); // Bump array pointer (sizes) - __ addptr(rcx, wordSize); // Bump array pointer (pcs) - __ decrementl(rdx); // Decrement counter - __ jcc(Assembler::notZero, loop); - __ pushptr(Address(rcx, 0)); // Save final return address - - // Re-push self-frame - __ enter(); // Save old & set new rbp - __ subptr(rsp, (SimpleRuntimeFrame::framesize - 4) << LogBytesPerInt); - // Prolog - - // Use rbp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(noreg, rbp, the_pc, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // Thread is in rdi already. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode); - - __ andptr(rsp, -(StackAlignmentInBytes)); // Align SP as required by ABI - __ mov(c_rarg0, r15_thread); - __ movl(c_rarg1, Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(0); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, @@ -3669,136 +3475,3 @@ void SharedRuntime::montgomery_square(jint *a_ints, jint *n_ints, reverse_words(m, (julong *)m_ints, longwords); } -#ifdef COMPILER2 -// This is here instead of runtime_x86_64.cpp because it uses SimpleRuntimeFrame -// -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in x86_64.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// rax: exception oop -// rdx: exception pc -// -// Results: -// rax: exception oop -// rdx: exception pc in caller or ??? -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers rax, rdx, rcx, rsi, rdi, r8-r11 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(RDX_num), ""); - assert(!OptoRuntime::is_callee_saved_register(RAX_num), ""); - assert(!OptoRuntime::is_callee_saved_register(RCX_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - - address start = __ pc(); - - // Exception pc is 'return address' for stack walker - __ push(rdx); - __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog - - // Save callee-saved registers. See x86_64.ad. - - // rbp is an implicitly saved callee saved register (i.e., the calling - // convention will save/restore it in the prolog/epilog). Other than that - // there are no callee save registers now that adapter frames are gone. - - __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); - - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - // c_rarg0 is either rdi (Linux) or rcx (Windows). - __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()),rax); - __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), rdx); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - - // At a method handle call, the stack may not be properly aligned - // when returning with an exception. - address the_pc = __ pc(); - __ set_last_Java_frame(noreg, noreg, the_pc, rscratch1); - __ mov(c_rarg0, r15_thread); - __ andptr(rsp, -(StackAlignmentInBytes)); // Align stack - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // rbp is an implicitly saved callee-saved register (i.e., the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - - __ movptr(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt)); - - __ addptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog - __ pop(rdx); // No need for exception pc anymore - - // rax: exception handler - - // We have a handler in rax (could be deopt blob). - __ mov(r8, rax); - - // Get the exception oop - __ movptr(rax, Address(r15_thread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ movptr(rdx, Address(r15_thread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ movptr(Address(r15_thread, JavaThread::exception_handler_pc_offset()), NULL_WORD); - __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), NULL_WORD); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()), NULL_WORD); - - // rax: exception oop - // r8: exception handler - // rdx: exception pc - // Jump to handler - - __ jmp(r8); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 569d6bc35cd16..3bc5b9a8b2a7d 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2151,7 +2151,7 @@ Node* GraphKit::uncommon_trap(int trap_request, kill_dead_locals(); // Now insert the uncommon trap subroutine call - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypePtr* no_memory_effects = nullptr; // Pass the index of the class to be loaded Node* call = make_runtime_call(RC_NO_LEAF | RC_UNCOMMON | diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 549290397e3b7..9db94748ca27c 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -122,7 +122,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo for (uint i1 = 0; i1 < null_block->number_of_nodes(); i1++) { Node* nn = null_block->get_node(i1); if (nn->is_MachCall() && - nn->as_MachCall()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + nn->as_MachCall()->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point()) { const Type* trtype = nn->in(TypeFunc::Parms)->bottom_type(); if (trtype->isa_int() && trtype->is_int()->is_con()) { jint tr_con = trtype->is_int()->get_con(); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index f1c3e592fff53..9660413dd190f 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -5949,7 +5949,7 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no for (DUIterator_Fast jmax, j = other_proj->fast_outs(jmax); j < jmax; j++) { Node* obs = other_proj->fast_out(j); if (obs->in(0) == other_proj && obs->is_CallStaticJava() && - (obs->as_CallStaticJava()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { + (obs->as_CallStaticJava()->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point())) { return obs->as_CallStaticJava(); } } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 45e59f4e21680..b4c134570e63f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -567,7 +567,7 @@ void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Nod register_control(if_true, loop, parse_predicate); int trap_request = Deoptimization::make_trap_request(reason, Deoptimization::Action_maybe_recompile); - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypePtr* no_memory_effects = nullptr; JVMState* jvms = sfpt->jvms(); CallNode* unc = new CallStaticJavaNode(OptoRuntime::uncommon_trap_Type(), call_addr, "uncommon_trap", diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 4c2f208e445c1..4e2306602e6f7 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -2960,7 +2960,7 @@ bool Matcher::branches_to_uncommon_trap(const Node *n) { } if (call && - call->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + call->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point()) { const Type* trtype = call->in(TypeFunc::Parms)->bottom_type(); if (trtype->isa_int() && trtype->is_int()->is_con()) { jint tr_con = trtype->is_int()->get_con(); diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 465404bb4693f..54408146d0c26 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -114,7 +114,8 @@ address OptoRuntime::_notify_jvmti_vthread_mount = nullptr; address OptoRuntime::_notify_jvmti_vthread_unmount = nullptr; #endif -ExceptionBlob* OptoRuntime::_exception_blob; +UncommonTrapBlob* OptoRuntime::_uncommon_trap_blob; +ExceptionBlob* OptoRuntime::_exception_blob; // This should be called in an assertion at the start of OptoRuntime routines // which are entered from compiled code (all of them) @@ -138,6 +139,7 @@ static bool check_compiled_frame(JavaThread* thread) { bool OptoRuntime::generate(ciEnv* env) { + generate_uncommon_trap_blob(); generate_exception_blob(); // Note: tls: Means fetching the return oop out of the thread-local storage diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 6aadab9712243..34c2780a2f8d7 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -168,7 +168,10 @@ class OptoRuntime : public AllStatic { // CodeBlob support // =================================================================== + static UncommonTrapBlob* _uncommon_trap_blob; static ExceptionBlob* _exception_blob; + + static void generate_uncommon_trap_blob(void); static void generate_exception_blob(); static void register_finalizer(oopDesc* obj, JavaThread* current); @@ -208,6 +211,7 @@ class OptoRuntime : public AllStatic { static address notify_jvmti_vthread_unmount() { return _notify_jvmti_vthread_unmount; } #endif + static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } static ExceptionBlob* exception_blob() { return _exception_blob; } // Implicit exception support diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 2e227be765cfa..7bf75c93055a5 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -203,7 +203,7 @@ class StringConcat : public ResourceObj { Node* uct = _uncommon_traps.at(u); // Build a new call using the jvms state of the allocate - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypeFunc* call_type = OptoRuntime::uncommon_trap_Type(); const TypePtr* no_memory_effects = nullptr; Compile* C = _stringopts->C; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 0e6d367586b9d..0eec7e4c34cae 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -100,10 +100,6 @@ SafepointBlob* SharedRuntime::_polling_page_vectors_safepoint_handler_blob; SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob; SafepointBlob* SharedRuntime::_polling_page_return_handler_blob; -#ifdef COMPILER2 -UncommonTrapBlob* SharedRuntime::_uncommon_trap_blob; -#endif // COMPILER2 - nmethod* SharedRuntime::_cont_doYield_stub; //----------------------------generate_stubs----------------------------------- @@ -129,10 +125,6 @@ void SharedRuntime::generate_stubs() { _polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN); generate_deopt_blob(); - -#ifdef COMPILER2 - generate_uncommon_trap_blob(); -#endif // COMPILER2 } #include diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index da61883b2fe64..9eec8e079ec34 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -62,10 +62,6 @@ class SharedRuntime: AllStatic { static SafepointBlob* _polling_page_safepoint_handler_blob; static SafepointBlob* _polling_page_return_handler_blob; -#ifdef COMPILER2 - static UncommonTrapBlob* _uncommon_trap_blob; -#endif // COMPILER2 - static nmethod* _cont_doYield_stub; #ifndef PRODUCT @@ -223,11 +219,6 @@ class SharedRuntime: AllStatic { return _wrong_method_abstract_blob->entry_point(); } -#ifdef COMPILER2 - static void generate_uncommon_trap_blob(void); - static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } -#endif // COMPILER2 - static address get_resolve_opt_virtual_call_stub() { assert(_resolve_opt_virtual_call_blob != nullptr, "oops"); return _resolve_opt_virtual_call_blob->entry_point();