/* * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2022 SAP SE. 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. *
*/
// Dispatch code executed in the prolog of a bytecode which does not do it's // own dispatch. The dispatch address is computed and placed in R24_dispatch_addr. void InterpreterMacroAssembler::dispatch_prolog(TosState state, int bcp_incr) { Register bytecode = R12_scratch2;
lbz(bytecode, bcp_incr, R14_bcp);
// Dispatch code executed in the epilog of a bytecode which does not do it's // own dispatch. The dispatch address in R24_dispatch_addr is used for the // dispatch. void InterpreterMacroAssembler::dispatch_epilog(TosState state, int bcp_incr) { if (bcp_incr) { addi(R14_bcp, R14_bcp, bcp_incr); }
mtctr(R24_dispatch_addr);
bcctr(bcondAlways, 0, bhintbhBCCTRisNotPredictable);
}
void InterpreterMacroAssembler::check_and_handle_popframe(Register scratch_reg) {
assert(scratch_reg != R0, "can't use R0 as scratch_reg here"); if (JvmtiExport::can_pop_frame()) {
Label L;
// Check the "pending popframe condition" flag in the current thread.
lwz(scratch_reg, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);
// Initiate popframe handling only if it is not already being // processed. If the flag has the popframe_processing bit set, it // means that this code is called *during* popframe handling - we // don't want to reenter.
andi_(R0, scratch_reg, JavaThread::popframe_pending_bit);
beq(CCR0, L);
// Call the Interpreter::remove_activation_preserving_args_entry() // func to get the address of the same-named entrypoint in the // generated interpreter code. #ifdefined(ABI_ELFv2)
call_c(CAST_FROM_FN_PTR(address,
Interpreter::remove_activation_preserving_args_entry),
relocInfo::none); #else
call_c(CAST_FROM_FN_PTR(FunctionDescriptor*,
Interpreter::remove_activation_preserving_args_entry),
relocInfo::none); #endif
// Jump to Interpreter::_remove_activation_preserving_args_entry.
mtctr(R3_RET);
bctr();
switch (state) { case atos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState);
std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState); break; case ltos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); break; case btos: // fall through case ztos: // fall through case ctos: // fall through case stos: // fall through case itos: lwz(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); break; case ftos: lfs(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); break; case dtos: lfd(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); break; case vtos: break; default : ShouldNotReachHere();
}
// Clean up tos value in the jvmti thread state.
std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); // Set tos state field to illegal value.
li(Rscratch2, ilgl);
stw(Rscratch2, in_bytes(JvmtiThreadState::earlyret_tos_offset()), RjvmtiState);
}
// Common code to dispatch and dispatch_only. // Dispatch value in Lbyte_code and increment Lbcp.
void InterpreterMacroAssembler::push(TosState state) { switch (state) { case atos: push_ptr(); break; case btos: case ztos: case ctos: case stos: case itos: push_i(); break; case ltos: push_l(); break; case ftos: push_f(); break; case dtos: push_d(); break; case vtos: /* nothing to do */ break; default : ShouldNotReachHere();
}
}
void InterpreterMacroAssembler::pop(TosState state) { switch (state) { case atos: pop_ptr(); break; case btos: case ztos: case ctos: case stos: case itos: pop_i(); break; case ltos: pop_l(); break; case ftos: pop_f(); break; case dtos: pop_d(); break; case vtos: /* nothing to do */ break; default : ShouldNotReachHere();
}
verify_oop(R17_tos, state);
}
// Load 4-byte signed or unsigned integer in Java format (that is, big-endian format) // from (Rsrc)+offset. void InterpreterMacroAssembler::get_u4(Register Rdst, Register Rsrc, int offset,
signedOrNot is_signed) { #ifdefined(VM_LITTLE_ENDIAN) if (offset) {
load_const_optimized(Rdst, offset);
lwbrx(Rdst, Rdst, Rsrc);
} else {
lwbrx(Rdst, Rsrc);
} if (is_signed == Signed) {
extsw(Rdst, Rdst);
} #else if (is_signed == Signed) {
lwa(Rdst, offset, Rsrc);
} else {
lwz(Rdst, offset, Rsrc);
} #endif
}
// Load object from cpool->resolved_references(index). // Kills: // - index void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index, Register tmp1, Register tmp2,
Label *L_handle_null) {
assert_different_registers(result, index, tmp1, tmp2);
assert(index->is_nonvolatile(), "needs to survive C-call in resolve_oop_handle");
get_constant_pool(result);
// Convert from field index to resolved_references() index and from // word index to byte offset. Since this is a java object, it can be compressed.
sldi(index, index, LogBytesPerHeapOop); // Load pointer for resolved_references[] objArray.
ld(result, ConstantPool::cache_offset_in_bytes(), result);
ld(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
resolve_oop_handle(result, tmp1, tmp2, MacroAssembler::PRESERVATION_NONE); #ifdef ASSERT
Label index_ok;
lwa(R0, arrayOopDesc::length_offset_in_bytes(), result);
sldi(R0, R0, LogBytesPerHeapOop);
cmpd(CCR0, index, R0);
blt(CCR0, index_ok);
stop("resolved reference index out of bounds");
bind(index_ok); #endif // Add in the index.
add(result, index, result);
load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), result,
tmp1, tmp2,
MacroAssembler::PRESERVATION_NONE,
0, L_handle_null);
}
ld(method, method_offset, cache); // get f1 Method*
}
// Generate a subtype check: branch to ok_is_subtype if sub_klass is // a subtype of super_klass. Blows registers Rsub_klass, tmp1, tmp2. void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Register Rsuper_klass, Register Rtmp1, Register Rtmp2, Register Rtmp3, Label &ok_is_subtype) { // Profile the not-null value's klass.
profile_typecheck(Rsub_klass, Rtmp1, Rtmp2);
check_klass_subtype(Rsub_klass, Rsuper_klass, Rtmp1, Rtmp2, ok_is_subtype);
profile_typecheck_failed(Rtmp1, Rtmp2);
}
// Separate these two to allow for delay slot in middle. // These are used to do a test and full jump to exception-throwing code.
// Check that index is in range for array, then shift index by index_shift, // and put arrayOop + shifted_index into res. // Note: res is still shy of address by array offset into object.
void InterpreterMacroAssembler::index_check_without_pop(Register Rarray, Register Rindex, int index_shift, Register Rtmp, Register Rres) { // Check that index is in range for array, then shift index by index_shift, // and put arrayOop + shifted_index into res. // Note: res is still shy of address by array offset into object. // Kills: // - Rindex // Writes: // - Rres: Address that corresponds to the array index if check was successful.
verify_oop(Rarray); constRegister Rlength = R0; constRegister RsxtIndex = Rtmp;
Label LisNull, LnotOOR;
// Rindex might contain garbage in upper bits (remember that we don't sign extend // during integer arithmetic operations). So kill them and put value into same register // where ArrayIndexOutOfBounds would expect the index in.
rldicl(RsxtIndex, Rindex, 0, 32); // zero extend 32 bit -> 64 bit
// Index check
lwz(Rlength, arrayOopDesc::length_offset_in_bytes(), Rarray);
cmplw(CCR0, Rindex, Rlength);
sldi(RsxtIndex, RsxtIndex, index_shift);
blt(CCR0, LnotOOR); // Index should be in R17_tos, array should be in R4_ARG2.
mr_if_needed(R17_tos, Rindex);
mr_if_needed(R4_ARG2, Rarray);
load_dispatch_table(Rtmp, (address*)Interpreter::_throw_ArrayIndexOutOfBoundsException_entry);
mtctr(Rtmp);
bctr();
if (!ImplicitNullChecks) {
bind(LisNull);
load_dispatch_table(Rtmp, (address*)Interpreter::_throw_NullPointerException_entry);
mtctr(Rtmp);
bctr();
}
align(32, 16);
bind(LnotOOR);
// Calc address
add(Rres, RsxtIndex, Rarray);
}
void InterpreterMacroAssembler::index_check(Register array, Register index, int index_shift, Register tmp, Register res) { // pop array
pop_ptr(array);
// Unlock if synchronized method. // // Unlock the receiver if this is a synchronized method. // Unlock any Java monitors from synchronized blocks. // // If there are locked Java monitors // If throw_monitor_exception // throws IllegalMonitorStateException // Else if install_monitor_exception // installs IllegalMonitorStateException // Else // no error processing void InterpreterMacroAssembler::unlock_if_synchronized_method(TosState state, bool throw_monitor_exception, bool install_monitor_exception) {
Label Lunlocked, Lno_unlock;
{ Register Rdo_not_unlock_flag = R11_scratch1; Register Raccess_flags = R12_scratch2;
// Check if synchronized method or unlocking prevented by // JavaThread::do_not_unlock_if_synchronized flag.
lbz(Rdo_not_unlock_flag, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
lwz(Raccess_flags, in_bytes(Method::access_flags_offset()), R19_method);
li(R0, 0);
stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); // reset flag
push(state);
// Skip if we don't have to unlock.
rldicl_(R0, Raccess_flags, 64-JVM_ACC_SYNCHRONIZED_BIT, 63); // Extract bit and compare to 0.
beq(CCR0, Lunlocked);
Label Lunlock; // If it's still locked, everything is ok, unlock it.
ld(Rmonitor_base, 0, R1_SP);
addi(Rmonitor_base, Rmonitor_base,
-(frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes())); // Monitor base
// If it's already unlocked, throw exception. if (throw_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception));
should_not_reach_here();
} else { if (install_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception));
b(Lunlocked);
}
}
bind(Lunlock);
unlock_object(Rmonitor_base);
}
// Check that all other monitors are unlocked. Throw IllegelMonitorState exception if not.
bind(Lunlocked);
{
Label Lexception, Lrestart; Register Rcurrent_obj_addr = R11_scratch1; constint delta = frame::interpreter_frame_monitor_size_in_bytes();
assert((delta & LongAlignmentMask) == 0, "sizeof BasicObjectLock must be even number of doublewords");
bind(Lrestart); // Set up search loop: Calc num of iterations.
{ Register Riterations = R12_scratch2; Register Rmonitor_base = Rcurrent_obj_addr;
ld(Rmonitor_base, 0, R1_SP);
addi(Rmonitor_base, Rmonitor_base, - frame::ijava_state_size); // Monitor base
addi(Rcurrent_obj_addr, Rmonitor_base,
BasicObjectLock::obj_offset_in_bytes() - frame::interpreter_frame_monitor_size_in_bytes()); // Check if any monitor is on stack, bail out if not
srdi(Riterations, Riterations, exact_log2(delta));
mtctr(Riterations);
}
// The search loop: Look for locked monitors.
{ constRegister Rcurrent_obj = R0;
Label Lloop;
// Check if current entry is used.
cmpdi(CCR0, Rcurrent_obj, 0);
bne(CCR0, Lexception); // Preload next iteration's compare value.
ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
addi(Rcurrent_obj_addr, Rcurrent_obj_addr, -delta);
bdnz(Lloop);
} // Fell through: Everything's unlocked => finish.
b(Lno_unlock);
// An object is still locked => need to throw exception.
bind(Lexception); if (throw_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception));
should_not_reach_here();
} else { // Stack unrolling. Unlock object and if requested, install illegal_monitor_exception. // Unlock does not block, so don't have to worry about the frame. Register Rmonitor_addr = R11_scratch1;
addi(Rmonitor_addr, Rcurrent_obj_addr, -BasicObjectLock::obj_offset_in_bytes() + delta);
unlock_object(Rmonitor_addr); if (install_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception));
}
b(Lrestart);
}
}
align(32, 12);
bind(Lno_unlock);
pop(state);
}
// Support function for remove_activation & Co. void InterpreterMacroAssembler::merge_frames(Register Rsender_sp, Register return_pc, Register Rscratch1, Register Rscratch2) { // Pop interpreter frame.
ld(Rscratch1, 0, R1_SP); // *SP
ld(Rsender_sp, _ijava_state_neg(sender_sp), Rscratch1); // top_frame_sp
ld(Rscratch2, 0, Rscratch1); // **SP if (return_pc!=noreg) {
ld(return_pc, _abi0(lr), Rscratch1); // LR
}
bind(notChar); // cmpwi(CCR0, ret_type, T_SHORT); // all that's left // bne(CCR0, done);
extsh(result, result);
// Nothing to do for T_INT
bind(done);
}
// Remove activation. // // Apply stack watermark barrier. // Unlock the receiver if this is a synchronized method. // Unlock any Java monitors from synchronized blocks. // Remove the activation from the stack. // // If there are locked Java monitors // If throw_monitor_exception // throws IllegalMonitorStateException // Else if install_monitor_exception // installs IllegalMonitorStateException // Else // no error processing void InterpreterMacroAssembler::remove_activation(TosState state, bool throw_monitor_exception, bool install_monitor_exception) {
BLOCK_COMMENT("remove_activation {");
// The below poll is for the stack watermark barrier. It allows fixing up frames lazily, // that would normally not be safe to use. Such bad returns into unsafe territory of // the stack, will call InterpreterRuntime::at_unwind.
Label slow_path;
Label fast_path;
safepoint_poll(slow_path, R11_scratch1, true/* at_return */, false /* in_nmethod */);
b(fast_path);
bind(slow_path);
push(state);
set_last_Java_frame(R1_SP, noreg);
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::at_unwind), R16_thread);
reset_last_Java_frame();
pop(state);
align(32);
bind(fast_path);
// Save result (push state before jvmti call and pop it afterwards) and notify jvmti.
notify_method_exit(false, state, NotifyJVMTI, true);
BLOCK_COMMENT("reserved_stack_check:"); if (StackReservedPages > 0) { // Test if reserved zone needs to be enabled.
Label no_reserved_zone_enabling;
// Compare frame pointers. There is no good stack pointer, as with stack // frame compression we can get different SPs when we do calls. A subsequent // call could have a smaller SP, so that this compare succeeds for an // inner call of the method annotated with ReservedStack.
ld_ptr(R0, JavaThread::reserved_stack_activation_offset(), R16_thread);
ld_ptr(R11_scratch1, _abi0(callers_sp), R1_SP); // Load frame pointer.
cmpld(CCR0, R11_scratch1, R0);
blt_predict_taken(CCR0, no_reserved_zone_enabling);
// Lock object // // Registers alive // monitor - Address of the BasicObjectLock to be used for locking, // which must be initialized with the object to lock. // object - Address of the object to be locked. // void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { if (UseHeavyMonitors) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
} else { // template code: // // markWord displaced_header = obj->mark().set_unlocked(); // monitor->lock()->set_displaced_header(displaced_header); // if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) { // // We stored the monitor address into the object's mark word. // } else if (THREAD->is_lock_owned((address)displaced_header)) // // Simple recursive case. // monitor->lock()->set_displaced_header(NULL); // } else { // // Slow path. // InterpreterRuntime::monitorenter(THREAD, monitor); // }
// Initialize the box (Must happen before we update the object mark!).
std(displaced_header, BasicObjectLock::lock_offset_in_bytes() +
BasicLock::displaced_header_offset_in_bytes(), monitor);
// if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) {
// Store stack address of the BasicObjectLock (this is monitor) into object.
addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes());
// Must fence, otherwise, preceding store(s) may float below cmpxchg. // CmpxchgX sets CCR0 to cmpX(current, displaced).
cmpxchgd(/*flag=*/CCR0, /*current_value=*/current_header, /*compare_value=*/displaced_header, /*exchange_value=*/monitor, /*where=*/object_mark_addr,
MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
MacroAssembler::cmpxchgx_hint_acquire_lock(),
noreg,
&cas_failed, /*check without membar and ldarx first*/true);
// If the compare-and-exchange succeeded, then we found an unlocked // object and we have now locked it.
b(count_locking);
bind(cas_failed);
// We did not see an unlocked object so try the fast recursive case.
// Check if owner is self by comparing the value in the markWord of object // (current_header) with the stack pointer.
sub(current_header, current_header, R1_SP);
assert(os::vm_page_size() > 0xfff, "page size too small - change the constant");
load_const_optimized(tmp, ~(os::vm_page_size()-1) | markWord::lock_mask_in_place);
and_(R0/*==0?*/, current_header, tmp); // If condition is true we are done and hence we can store 0 in the displaced // header indicating it is a recursive lock.
bne(CCR0, slow_case);
std(R0/*==0!*/, BasicObjectLock::lock_offset_in_bytes() +
BasicLock::displaced_header_offset_in_bytes(), monitor);
b(count_locking);
// None of the above fast optimizations worked so we have to get into the // slow case of monitor enter.
bind(slow_case);
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
b(done); // }
align(32, 12);
bind(count_locking);
inc_held_monitor_count(current_header /*tmp*/);
bind(done);
}
}
// Unlocks an object. Used in monitorexit bytecode and remove_activation. // // Registers alive // monitor - Address of the BasicObjectLock to be used for locking, // which must be initialized with the object to lock. // // Throw IllegalMonitorException if object is not locked by current thread. void InterpreterMacroAssembler::unlock_object(Register monitor) { if (UseHeavyMonitors) {
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor);
} else {
// template code: // // if ((displaced_header = monitor->displaced_header()) == NULL) { // // Recursive unlock. Mark the monitor unlocked by setting the object field to NULL. // monitor->set_obj(NULL); // } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) { // // We swapped the unlocked mark in displaced_header into the object's mark word. // monitor->set_obj(NULL); // } else { // // Slow path. // InterpreterRuntime::monitorexit(monitor); // }
// Test first if we are in the fast recursive case.
ld(displaced_header, BasicObjectLock::lock_offset_in_bytes() +
BasicLock::displaced_header_offset_in_bytes(), monitor);
// If the displaced header is zero, we have a recursive unlock.
cmpdi(CCR0, displaced_header, 0);
beq(CCR0, free_slot); // recursive unlock
// } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) { // // We swapped the unlocked mark in displaced_header into the object's mark word. // monitor->set_obj(NULL);
// If we still have a lightweight lock, unlock the object and be done.
// The object address from the monitor is in object.
ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor);
addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes());
// We have the displaced header in displaced_header. If the lock is still // lightweight, it will contain the monitor address and we'll store the // displaced header back into the object's mark word. // CmpxchgX sets CCR0 to cmpX(current, monitor).
cmpxchgd(/*flag=*/CCR0, /*current_value=*/current_header, /*compare_value=*/monitor, /*exchange_value=*/displaced_header, /*where=*/object_mark_addr,
MacroAssembler::MemBarRel,
MacroAssembler::cmpxchgx_hint_release_lock(),
noreg,
&slow_case);
b(free_slot);
// The lock has been converted into a heavy lock and hence // we need to get into the slow case.
bind(slow_case);
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor); // }
Label done;
b(done); // Monitor register may be overwritten! Runtime has already freed the slot.
// Load compiled (i2c) or interpreter entry when calling from interpreted and // do the call. Centralized so that all interpreter calls will do the same actions. // If jvmti single stepping is on for a thread we must not call compiled code. // // Input: // - Rtarget_method: method to call // - Rret_addr: return address // - 2 scratch regs // void InterpreterMacroAssembler::call_from_interpreter(Register Rtarget_method, Register Rret_addr, Register Rscratch1, Register Rscratch2) {
assert_different_registers(Rscratch1, Rscratch2, Rtarget_method, Rret_addr); // Assume we want to go compiled if available. constRegister Rtarget_addr = Rscratch1; constRegister Rinterp_only = Rscratch2;
if (JvmtiExport::can_post_interpreter_events()) {
lwz(Rinterp_only, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread);
// JVMTI events, such as single-stepping, are implemented partly by avoiding running // compiled code in threads for which the event is enabled. Check here for // interp_only_mode if these events CAN be enabled.
Label done;
verify_thread();
cmpwi(CCR0, Rinterp_only, 0);
beq(CCR0, done);
ld(Rtarget_addr, in_bytes(Method::interpreter_entry_offset()), Rtarget_method);
align(32, 12);
bind(done);
}
// Calc a precise SP for the call. The SP value we calculated in // generate_fixed_frame() is based on the max_stack() value, so we would waste stack space // if esp is not max. Also, the i2c adapter extends the stack space without restoring // our pre-calced value, so repeating calls via i2c would result in stack overflow. // Since esp already points to an empty slot, we just have to sub 1 additional slot // to meet the abi scratch requirements. // The max_stack pointer will get restored by means of the GR_Lmax_stack local in // the return entry of the interpreter.
addi(Rscratch2, R15_esp, Interpreter::stackElementSize - frame::abi_reg_args_size);
clrrdi(Rscratch2, Rscratch2, exact_log2(frame::alignment_in_bytes)); // round towards smaller address
resize_frame_absolute(Rscratch2, Rscratch2, R0);
// Set the method data pointer for the current bcp. void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() {
assert(ProfileInterpreter, "must be profiling interpreter");
Label get_continue;
ld(R28_mdx, in_bytes(Method::method_data_offset()), R19_method);
test_method_data_pointer(get_continue);
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), R19_method, R14_bcp);
// Test ImethodDataPtr. If it is null, continue at the specified label. void InterpreterMacroAssembler::test_method_data_pointer(Label& zero_continue) {
assert(ProfileInterpreter, "must be profiling interpreter");
cmpdi(CCR0, R28_mdx, 0);
beq(CCR0, zero_continue);
}
// If the mdp is valid, it will point to a DataLayout header which is // consistent with the bcp. The converse is highly probable also.
lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx);
ld(R12_scratch2, in_bytes(Method::const_offset()), R19_method);
addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::codes_offset()));
add(R11_scratch1, R12_scratch2, R12_scratch2);
cmpd(CCR0, R11_scratch1, R14_bcp);
beq(CCR0, verify_continue);
// Store a value at some constant offset from the method data pointer. void InterpreterMacroAssembler::set_mdp_data_at(int constant, Register value) {
assert(ProfileInterpreter, "must be profiling interpreter");
std(value, constant, R28_mdx);
}
// Increment the value at some constant offset from the method data pointer. void InterpreterMacroAssembler::increment_mdp_data_at(int constant, Register counter_addr, Register Rbumped_count, bool decrement) { // Locate the counter at a fixed offset from the mdp:
addi(counter_addr, R28_mdx, constant);
increment_mdp_data_at(counter_addr, Rbumped_count, decrement);
}
// Increment the value at some non-fixed (reg + constant) offset from // the method data pointer. void InterpreterMacroAssembler::increment_mdp_data_at(Register reg, int constant, Register scratch, Register Rbumped_count, bool decrement) { // Add the constant to reg to get the offset.
add(scratch, R28_mdx, reg); // Then calculate the counter address.
addi(scratch, scratch, constant);
increment_mdp_data_at(scratch, Rbumped_count, decrement);
}
// Load the counter.
ld(Rbumped_count, 0, counter_addr);
if (decrement) { // Decrement the register. Set condition codes.
addi(Rbumped_count, Rbumped_count, - DataLayout::counter_increment); // Store the decremented counter, if it is still negative.
std(Rbumped_count, 0, counter_addr); // Note: add/sub overflow check are not ported, since 64 bit // calculation should never overflow.
} else { // Increment the register. Set carry flag.
addi(Rbumped_count, Rbumped_count, DataLayout::counter_increment); // Store the incremented counter.
std(Rbumped_count, 0, counter_addr);
}
}
// Set a flag value at the current method data pointer position. void InterpreterMacroAssembler::set_mdp_flag_at(int flag_constant, Register scratch) {
assert(ProfileInterpreter, "must be profiling interpreter"); // Load the data header.
lbz(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx); // Set the flag.
ori(scratch, scratch, flag_constant); // Store the modified header.
stb(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx);
}
// Test the location at some offset from the method data pointer. // If it is not equal to value, branch to the not_equal_continue Label. void InterpreterMacroAssembler::test_mdp_data_at(int offset, Register value,
Label& not_equal_continue, Register test_out) {
assert(ProfileInterpreter, "must be profiling interpreter");
// Update the method data pointer by the displacement located at some fixed // offset from the method data pointer. void InterpreterMacroAssembler::update_mdp_by_offset(int offset_of_disp, Register scratch) {
assert(ProfileInterpreter, "must be profiling interpreter");
// Update the method data pointer by the displacement located at the // offset (reg + offset_of_disp). void InterpreterMacroAssembler::update_mdp_by_offset(Register reg, int offset_of_disp, Register scratch) {
assert(ProfileInterpreter, "must be profiling interpreter");
// Update the method data pointer by a simple constant displacement. void InterpreterMacroAssembler::update_mdp_by_constant(int constant) {
assert(ProfileInterpreter, "must be profiling interpreter");
addi(R28_mdx, R28_mdx, constant);
}
// Update the method data pointer for a _ret bytecode whose target // was not among our cached targets. void InterpreterMacroAssembler::update_mdp_for_ret(TosState state, Register return_bci) {
assert(ProfileInterpreter, "must be profiling interpreter");
// Count a taken branch in the bytecodes. void InterpreterMacroAssembler::profile_taken_branch(Register scratch, Register bumped_count) { if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// We are taking a branch. Increment the taken count.
increment_mdp_data_at(in_bytes(JumpData::taken_offset()), scratch, bumped_count);
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_offset(in_bytes(JumpData::displacement_offset()), scratch);
bind (profile_continue);
}
}
// Count a not-taken branch in the bytecodes. void InterpreterMacroAssembler::profile_not_taken_branch(Register scratch1, Register scratch2) { if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// We are taking a branch. Increment the not taken count.
increment_mdp_data_at(in_bytes(BranchData::not_taken_offset()), scratch1, scratch2);
// The method data pointer needs to be updated to correspond to the // next bytecode.
update_mdp_by_constant(in_bytes(BranchData::branch_data_size()));
bind (profile_continue);
}
}
// Count a non-virtual call in the bytecodes. void InterpreterMacroAssembler::profile_call(Register scratch1, Register scratch2) { if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// We are making a call. Increment the count.
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_constant(in_bytes(CounterData::counter_data_size()));
bind (profile_continue);
}
}
// Count a final call in the bytecodes. void InterpreterMacroAssembler::profile_final_call(Register scratch1, Register scratch2) { if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// We are making a call. Increment the count.
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
bind (profile_continue);
}
}
// Count a virtual call in the bytecodes. void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2, bool receiver_can_be_null) { if (!ProfileInterpreter) { return; }
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
Label skip_receiver_profile; if (receiver_can_be_null) {
Label not_null;
cmpdi(CCR0, Rreceiver, 0);
bne(CCR0, not_null); // We are making a call. Increment the count for null receiver.
increment_mdp_data_at(in_bytes(CounterData::count_offset()), Rscratch1, Rscratch2);
b(skip_receiver_profile);
bind(not_null);
}
// Record the receiver type.
record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2, true);
bind(skip_receiver_profile);
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
bind (profile_continue);
}
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
int count_offset = in_bytes(CounterData::count_offset()); // Back up the address, since we have already bumped the mdp.
count_offset -= in_bytes(VirtualCallData::virtual_call_data_size());
// *Decrement* the counter. We expect to see zero or small negatives.
increment_mdp_data_at(count_offset, Rscratch1, Rscratch2, true);
bind (profile_continue);
}
}
// Count a ret in the bytecodes. void InterpreterMacroAssembler::profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2) { if (ProfileInterpreter) {
Label profile_continue;
uint row;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// Update the total ret count.
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2 );
// See if return_bci is equal to bci[n]:
test_mdp_data_at(in_bytes(RetData::bci_offset(row)), return_bci, next_test, scratch1);
// return_bci is equal to bci[n]. Increment the count.
increment_mdp_data_at(in_bytes(RetData::bci_count_offset(row)), scratch1, scratch2);
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_offset(in_bytes(RetData::bci_displacement_offset(row)), scratch1);
b(profile_continue);
bind(next_test);
}
update_mdp_for_ret(state, return_bci);
bind (profile_continue);
}
}
// Count the default case of a switch construct. void InterpreterMacroAssembler::profile_switch_default(Register scratch1, Register scratch2) { if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// Update the default case count
increment_mdp_data_at(in_bytes(MultiBranchData::default_count_offset()),
scratch1, scratch2);
// The method data pointer needs to be updated.
update_mdp_by_offset(in_bytes(MultiBranchData::default_displacement_offset()),
scratch1);
bind (profile_continue);
}
}
// Count the index'th case of a switch construct. void InterpreterMacroAssembler::profile_switch_case(Register index, Register scratch1, Register scratch2, Register scratch3) { if (ProfileInterpreter) {
assert_different_registers(index, scratch1, scratch2, scratch3);
Label profile_continue;
// If no method data exists, go to profile_continue.
test_method_data_pointer(profile_continue);
// Build the base (index * per_case_size_in_bytes()) + case_array_offset_in_bytes().
li(scratch3, in_bytes(MultiBranchData::case_array_offset()));
// The method data pointer needs to be updated. int mdp_delta = in_bytes(BitData::bit_data_size()); if (TypeProfileCasts) {
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
}
update_mdp_by_constant(mdp_delta);
void InterpreterMacroAssembler::record_klass_in_profile_helper( Register receiver, Register scratch1, Register scratch2, int start_row, Label& done, bool is_virtual_call) { if (TypeProfileWidth == 0) { if (is_virtual_call) {
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
} return;
}
int last_row = VirtualCallData::row_limit() - 1;
assert(start_row <= last_row, "must be work left to do"); // Test this row for both the receiver and for null. // Take any of three different outcomes: // 1. found receiver => increment count and goto done // 2. found null => keep looking for case 1, maybe allocate this cell // 3. found something else => keep looking for cases 1 and 2 // Case 3 is handled by a recursive call. for (int row = start_row; row <= last_row; row++) {
Label next_test; bool test_for_null_also = (row == start_row);
// See if the receiver is receiver[n]. int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row));
test_mdp_data_at(recvr_offset, receiver, next_test, scratch1); // delayed()->tst(scratch);
// The receiver is receiver[n]. Increment count[n]. int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row));
increment_mdp_data_at(count_offset, scratch1, scratch2);
b(done);
bind(next_test);
if (test_for_null_also) {
Label found_null; // Failed the equality check on receiver[n]... Test for null. if (start_row == last_row) { // The only thing left to do is handle the null case. if (is_virtual_call) { // Scratch1 contains test_out from test_mdp_data_at.
cmpdi(CCR0, scratch1, 0);
beq(CCR0, found_null); // Receiver did not match any saved receiver and there is no empty row for it. // Increment total counter to indicate polymorphic case.
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
b(done);
bind(found_null);
} else {
cmpdi(CCR0, scratch1, 0);
bne(CCR0, done);
} break;
} // Since null is rare, make it be the branch-taken case.
cmpdi(CCR0, scratch1, 0);
beq(CCR0, found_null);
// Put all the "Case 3" tests here.
record_klass_in_profile_helper(receiver, scratch1, scratch2, start_row + 1, done, is_virtual_call);
// Found a null. Keep searching for a matching receiver, // but remember that this is an empty (unused) slot.
bind(found_null);
}
}
// In the fall-through case, we found no matching receiver, but we // observed the receiver[start_row] is NULL.
// Fill in the receiver field and increment the count. int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row));
set_mdp_data_at(recvr_offset, receiver); int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
li(scratch1, DataLayout::counter_increment);
set_mdp_data_at(count_offset, scratch1); if (start_row > 0) {
b(done);
}
}
// tmp2 = obj is allowed
assert_different_registers(obj, mdo_addr_base, tmp, R0);
assert_different_registers(tmp2, mdo_addr_base, tmp, R0); constRegister klass = tmp2;
verify_oop(obj);
ld(tmp, mdo_addr_offs, mdo_addr_base);
// Set null_seen if obj is 0.
cmpdi(CCR0, obj, 0);
ori(R0, tmp, TypeEntries::null_seen);
beq(CCR0, do_update);
load_klass(klass, obj);
clrrdi(R0, tmp, exact_log2(-TypeEntries::type_klass_mask)); // Basically same as andi(R0, tmp, TypeEntries::type_klass_mask);
cmpd(CCR1, R0, klass); // Klass seen before, nothing to do (regardless of unknown bit). //beq(CCR1, do_nothing);
andi_(R0, klass, TypeEntries::type_unknown); // Already unknown. Nothing to do anymore. //bne(CCR0, do_nothing);
crorc(CCR0, Assembler::equal, CCR1, Assembler::equal); // cr0 eq = cr1 eq or cr0 ne
beq(CCR0, do_nothing);
clrrdi_(R0, tmp, exact_log2(-TypeEntries::type_mask));
orr(R0, klass, tmp); // Combine klass and null_seen bit (only used if (tmp & type_mask)==0).
beq(CCR0, do_update); // First time here. Set profile type.
// Different than before. Cannot keep accurate profile.
ori(R0, tmp, TypeEntries::type_unknown);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.