/* * Copyright (c) 2000, 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. *
*/
// This specifies the stack pointer decrement needed to build the frame. int LIR_Assembler::initial_frame_size_in_bytes() const { return in_bytes(frame_map()->framesize_in_bytes());
}
// Inline cache check: the inline cached class is in inline_cache_reg; // we fetch the class of the receiver and compare it with the cached class. // If they do not match we jump to slow case. int LIR_Assembler::check_icache() { int offset = __ offset();
__ inline_cache_check(R3_ARG1, R19_inline_cache_reg); return offset;
}
void LIR_Assembler::clinit_barrier(ciMethod* method) {
assert(!method->holder()->is_not_initialized(), "initialization should have been started");
void LIR_Assembler::osr_entry() { // On-stack-replacement entry sequence: // // 1. Create a new compiled activation. // 2. Initialize local variables in the compiled activation. The expression // stack must be empty at the osr_bci; it is not initialized. // 3. Jump to the continuation address in compiled code to resume execution.
// OSR entry point
offsets()->set_value(CodeOffsets::OSR_Entry, code_offset());
BlockBegin* osr_entry = compilation()->hir()->osr_entry();
ValueStack* entry_state = osr_entry->end()->state(); int number_of_locks = entry_state->locks_size();
// Create a frame for the compiled activation.
__ build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes());
// OSR buffer is // // locals[nlocals-1..0] // monitors[number_of_locks-1..0] // // Locals is a direct copy of the interpreter frame so in the osr buffer // the first slot in the local array is the last local from the interpreter // and the last slot is local[0] (receiver) from the interpreter. // // Similarly with locks. The first lock slot in the osr buffer is the nth lock // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock // in the interpreter frame (the method lock if a sync method).
// Initialize monitors in the compiled activation. // R3: pointer to osr buffer // // All other registers are dead at this point and the locals will be // copied into place by code emitted in the IR.
Register OSR_buf = osrBufferPointer()->as_register();
{ assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); int monitor_offset = BytesPerWord * method()->max_locals() +
(2 * BytesPerWord) * (number_of_locks - 1); // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in // the OSR buffer using 2 word entries: first the lock and then // the oop. for (int i = 0; i < number_of_locks; i++) { int slot_offset = monitor_offset - ((i * 2) * BytesPerWord); #ifdef ASSERT // Verify the interpreter's monitor has a non-null object.
{
Label L;
__ ld(R0, slot_offset + 1*BytesPerWord, OSR_buf);
__ cmpdi(CCR0, R0, 0);
__ bne(CCR0, L);
__ stop("locked object is NULL");
__ bind(L);
} #endif// ASSERT // Copy the lock field into the compiled activation.
Address ml = frame_map()->address_for_monitor_lock(i),
mo = frame_map()->address_for_monitor_object(i);
assert(ml.index() == noreg && mo.index() == noreg, "sanity");
__ ld(R0, slot_offset + 0, OSR_buf);
__ std(R0, ml.disp(), ml.base());
__ ld(R0, slot_offset + 1*BytesPerWord, OSR_buf);
__ std(R0, mo.disp(), mo.base());
}
}
}
int LIR_Assembler::emit_exception_handler() { // Generate code for the exception handler.
address handler_base = __ start_a_stub(exception_handler_size());
if (handler_base == NULL) { // Not enough space left for the handler.
bailout("exception handler overflow"); return -1;
}
// Emit the code to remove the frame from the stack in the exception // unwind path. int LIR_Assembler::emit_unwind_handler() {
_masm->block_comment("Unwind handler");
void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo *info) { // Allocate a new index in table to hold the object once it's been patched. int oop_index = __ oop_recorder()->allocate_oop_index(NULL);
PatchingStub* patch = new PatchingStub(_masm, patching_id(info), oop_index);
void LIR_Assembler::metadata2reg(Metadata* o, Register reg) {
AddressLiteral md = __ constant_metadata_address(o); // Notify OOP recorder (don't need the relocation)
__ load_const_optimized(reg, md.value(), (reg != R0) ? R0 : noreg);
}
void LIR_Assembler::klass2reg_with_patching(Register reg, CodeEmitInfo *info) { // Allocate a new index in table to hold the klass once it's been patched. int index = __ oop_recorder()->allocate_metadata_index(NULL);
PatchingStub* patch = new PatchingStub(_masm, PatchingStub::load_klass_id, index);
AddressLiteral addrlit((address)NULL, metadata_Relocation::spec(index));
assert(addrlit.rspec().type() == relocInfo::metadata_type, "must be an metadata reloc");
__ load_const(reg, addrlit, R0);
switch(code) { case Bytecodes::_i2l: {
__ extsw(dst->as_register_lo(), src->as_register()); break;
} case Bytecodes::_l2i: {
__ mr_if_needed(dst->as_register(), src->as_register_lo()); // high bits are garbage break;
} case Bytecodes::_i2b: {
__ extsb(dst->as_register(), src->as_register()); break;
} case Bytecodes::_i2c: {
__ clrldi(dst->as_register(), src->as_register(), 64-16); break;
} case Bytecodes::_i2s: {
__ extsh(dst->as_register(), src->as_register()); break;
} case Bytecodes::_i2d: case Bytecodes::_l2d: { bool src_in_memory = !VM_Version::has_mtfprd();
FloatRegister rdst = dst->as_double_reg();
FloatRegister rsrc; if (src_in_memory) {
rsrc = src->as_double_reg(); // via mem
} else { // move src to dst register if (code == Bytecodes::_i2d) {
__ mtfprwa(rdst, src->as_register());
} else {
__ mtfprd(rdst, src->as_register_lo());
}
rsrc = rdst;
}
__ fcfid(rdst, rsrc); break;
} case Bytecodes::_i2f: case Bytecodes::_l2f: { bool src_in_memory = !VM_Version::has_mtfprd();
FloatRegister rdst = dst->as_float_reg();
FloatRegister rsrc; if (src_in_memory) {
rsrc = src->as_double_reg(); // via mem
} else { // move src to dst register if (code == Bytecodes::_i2f) {
__ mtfprwa(rdst, src->as_register());
} else {
__ mtfprd(rdst, src->as_register_lo());
}
rsrc = rdst;
} if (VM_Version::has_fcfids()) {
__ fcfids(rdst, rsrc);
} else {
assert(code == Bytecodes::_i2f, "fcfid+frsp needs fixup code to avoid rounding incompatibility");
__ fcfid(rdst, rsrc);
__ frsp(rdst, rdst);
} break;
} case Bytecodes::_f2d: {
__ fmr_if_needed(dst->as_double_reg(), src->as_float_reg()); break;
} case Bytecodes::_d2f: {
__ frsp(dst->as_float_reg(), src->as_double_reg()); break;
} case Bytecodes::_d2i: case Bytecodes::_f2i: { bool dst_in_memory = !VM_Version::has_mtfprd();
FloatRegister rsrc = (code == Bytecodes::_d2i) ? src->as_double_reg() : src->as_float_reg();
Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : Address();
Label L; // Result must be 0 if value is NaN; test by comparing value to itself.
__ fcmpu(CCR0, rsrc, rsrc); if (dst_in_memory) {
__ li(R0, 0); // 0 in case of NAN
__ std(R0, addr.disp(), addr.base());
} else {
__ li(dst->as_register(), 0);
}
__ bso(CCR0, L);
__ fctiwz(rsrc, rsrc); // USE_KILL if (dst_in_memory) {
__ stfd(rsrc, addr.disp(), addr.base());
} else {
__ mffprd(dst->as_register(), rsrc);
}
__ bind(L); break;
} case Bytecodes::_d2l: case Bytecodes::_f2l: { bool dst_in_memory = !VM_Version::has_mtfprd();
FloatRegister rsrc = (code == Bytecodes::_d2l) ? src->as_double_reg() : src->as_float_reg();
Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : Address();
Label L; // Result must be 0 if value is NaN; test by comparing value to itself.
__ fcmpu(CCR0, rsrc, rsrc); if (dst_in_memory) {
__ li(R0, 0); // 0 in case of NAN
__ std(R0, addr.disp(), addr.base());
} else {
__ li(dst->as_register_lo(), 0);
}
__ bso(CCR0, L);
__ fctidz(rsrc, rsrc); // USE_KILL if (dst_in_memory) {
__ stfd(rsrc, addr.disp(), addr.base());
} else {
__ mffprd(dst->as_register_lo(), rsrc);
}
__ bind(L); break;
}
default: ShouldNotReachHere();
}
}
void LIR_Assembler::align_call(LIR_Code) { // do nothing since all instructions are word aligned on ppc
}
bool LIR_Assembler::emit_trampoline_stub_for_call(address target, Register Rtoc) { int start_offset = __ offset(); // Put the entry point as a constant into the constant pool. const address entry_point_toc_addr = __ address_constant(target, RelocationHolder::none); if (entry_point_toc_addr == NULL) {
bailout("const section overflow"); returnfalse;
} constint entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
// Emit the trampoline stub which will be related to the branch-and-link below.
address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset, Rtoc); if (!stub) {
bailout("no space for trampoline stub"); returnfalse;
} returntrue;
}
bool success = emit_trampoline_stub_for_call(op->addr()); if (!success) { return; }
__ relocate(rtype); // Note: At this point we do not have the address of the trampoline // stub, and the entry point might be too far away for bl, so __ pc() // serves as dummy and the bl will be patched later.
__ code()->set_insts_mark();
__ bl(__ pc());
add_call_info(code_offset(), op->info());
__ post_call_nop();
}
// Virtual call relocation will point to ic load.
address virtual_call_meta_addr = __ pc(); // Load a clear inline cache.
AddressLiteral empty_ic((address) Universe::non_oop_word()); bool success = __ load_const_from_method_toc(R19_inline_cache_reg, empty_ic, R2_TOC); if (!success) {
bailout("const section overflow"); return;
} // Call to fixup routine. Fixup routine uses ScopeDesc info // to determine who we intended to call.
__ relocate(virtual_call_Relocation::spec(virtual_call_meta_addr));
success = emit_trampoline_stub_for_call(op->addr(), R2_TOC); if (!success) { return; }
// Note: At this point we do not have the address of the trampoline // stub, and the entry point might be too far away for bl, so __ pc() // serves as dummy and the bl will be patched later.
__ bl(__ pc());
add_call_info(code_offset(), op->info());
__ post_call_nop();
}
// Remember the offset of the load. The patching_epilog must be done // before the call to add_debug_info, otherwise the PcDescs don't get // entered in increasing order. int offset;
if (disp_reg == noreg) {
assert(Assembler::is_simm16(disp_value), "should have set this up");
offset = load(src, disp_value, to_reg, type, wide);
} else {
offset = load(src, disp_reg, to_reg, type, wide);
}
// remember the offset of the store. The patching_epilog must be done // before the call to add_debug_info_for_null_check, otherwise the PcDescs don't get // entered in increasing order. int offset;
if (compress_oop) { Register co = __ encode_heap_oop(R0, from_reg->as_register());
from_reg = FrameMap::as_opr(co);
}
if (disp_reg == noreg) {
assert(Assembler::is_simm16(disp_value), "should have set this up");
offset = store(from_reg, src, disp_value, type, wide);
} else {
offset = store(from_reg, src, disp_reg, type, wide);
}
if (use_R29) {
__ load_const_optimized(R29_TOC, MacroAssembler::global_toc(), R0); // reinit
}
if (patch != NULL) {
patching_epilog(patch, patch_code, src, info);
}
void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { constRegister return_pc = R31; // Must survive C-call to enable_stack_reserved_zone(). constRegister temp = R12;
// Pop the stack before the safepoint code. int frame_size = initial_frame_size_in_bytes(); if (Assembler::is_simm(frame_size, 16)) {
__ addi(R1_SP, R1_SP, frame_size);
} else {
__ pop_frame();
}
// Restore return pc relative to callers' sp.
__ ld(return_pc, _abi0(lr), R1_SP); // Move return pc to LR.
__ mtlr(return_pc);
if (StackReservedPages > 0 && compilation()->has_reserved_stack_access()) {
__ reserved_stack_check(return_pc);
}
// We need to mark the code position where the load from the safepoint // polling page was emitted as relocInfo::poll_return_type here. if (!UseSIGTRAP) {
code_stub->set_safepoint_offset(__ offset());
__ relocate(relocInfo::poll_return_type);
}
__ safepoint_poll(*code_stub->entry(), temp, true/* at_return */, true /* in_nmethod */);
// Return.
__ blr();
}
int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) { constRegister poll_addr = tmp->as_register();
__ ld(poll_addr, in_bytes(JavaThread::polling_page_offset()), R16_thread); if (info != NULL) {
add_debug_info_for_branch(info);
} int offset = __ offset();
__ relocate(relocInfo::poll_type);
__ load_from_polling_page(poll_addr);
// For java_to_interp stubs we use R11_scratch1 as scratch register // and in call trampoline stubs we use R12_scratch2. This way we // can distinguish them (see is_NativeCallTrampolineStub_at()). constRegister reg_scratch = R11_scratch1;
// Create a static stub relocation which relates this stub // with the call instruction at insts_call_instruction_offset in the // instructions code-section. int start = __ offset();
__ relocate(static_stub_Relocation::spec(call_pc));
// Now, create the stub's code: // - load the TOC // - load the inline cache oop from the constant pool // - load the call target from the constant pool // - call
__ calculate_address_from_global_toc(reg_scratch, __ method_toc());
AddressLiteral ic = __ allocate_metadata_address((Metadata *)NULL); bool success = __ load_const_from_method_toc(R19_inline_cache_reg, ic, reg_scratch, /*fixed_size*/ true);
case T_OBJECT: // There are only equal/notequal comparisons on objects.
{
assert(condition == lir_cond_equal || condition == lir_cond_notEqual, "oops");
jobject con = opr2->as_constant_ptr()->as_jobject(); if (con == NULL) {
__ cmpdi(BOOL_RESULT, opr1->as_register(), 0);
} else {
jobject2reg(con, R0);
__ cmpd(BOOL_RESULT, opr1->as_register(), R0);
}
} break;
case T_METADATA: // We only need, for now, comparison with NULL for metadata.
{
assert(condition == lir_cond_equal || condition == lir_cond_notEqual, "oops");
Metadata* p = opr2->as_constant_ptr()->as_metadata(); if (p == NULL) {
__ cmpdi(BOOL_RESULT, opr1->as_register(), 0);
} else {
ShouldNotReachHere();
}
} break;
bool positive = false;
Assembler::Condition cond = Assembler::equal; switch (condition) { case lir_cond_equal: positive = true ; cond = Assembler::equal ; break; case lir_cond_notEqual: positive = false; cond = Assembler::equal ; break; case lir_cond_less: positive = true ; cond = Assembler::less ; break; case lir_cond_belowEqual: case lir_cond_lessEqual: positive = false; cond = Assembler::greater; break; case lir_cond_greater: positive = true ; cond = Assembler::greater; break; case lir_cond_aboveEqual: case lir_cond_greaterEqual: positive = false; cond = Assembler::less ; break; default: ShouldNotReachHere();
}
// Try to use isel on >=Power7. if (VM_Version::has_isel() && result->is_cpu_register()) { bool o1_is_reg = opr1->is_cpu_register(), o2_is_reg = opr2->is_cpu_register(); constRegister result_reg = result->is_single_cpu() ? result->as_register() : result->as_register_lo();
// We can use result_reg to load one operand if not already in register. Register first = o1_is_reg ? (opr1->is_single_cpu() ? opr1->as_register() : opr1->as_register_lo()) : result_reg,
second = o2_is_reg ? (opr2->is_single_cpu() ? opr2->as_register() : opr2->as_register_lo()) : result_reg;
if (first != second) { if (!o1_is_reg) {
load_to_reg(this, opr1, result);
}
if (!o2_is_reg) {
load_to_reg(this, opr2, result);
}
Register lreg = left->as_register(); Register res = dest->as_register(); Register rreg = right->as_register(); switch (code) { case lir_add: __ add (res, lreg, rreg); break; case lir_sub: __ sub (res, lreg, rreg); break; case lir_mul: __ mullw(res, lreg, rreg); break; default: ShouldNotReachHere();
}
}
} else {
assert (right->is_constant(), "must be constant");
if (dest->is_single_cpu()) { Register lreg = left->as_register(); Register res = dest->as_register(); int simm16 = right->as_constant_ptr()->as_jint();
switch (code) { case lir_sub: assert(Assembler::is_simm16(-simm16), "cannot encode"); // see do_ArithmeticOp_Int
simm16 = -simm16; case lir_add: if (res == lreg && simm16 == 0) break;
__ addi(res, lreg, simm16); break; case lir_mul: if (res == lreg && simm16 == 1) break;
__ mulli(res, lreg, simm16); break; default: ShouldNotReachHere();
}
} else { Register lreg = left->as_pointer_register(); Register res = dest->as_register_lo(); long con = right->as_constant_ptr()->as_jlong();
assert(Assembler::is_simm16(con), "must be simm16");
switch (code) { case lir_sub: assert(Assembler::is_simm16(-con), "cannot encode"); // see do_ArithmeticOp_Long
con = -con; case lir_add: if (res == lreg && con == 0) break;
__ addi(res, lreg, (int)con); break; case lir_mul: if (res == lreg && con == 1) break;
__ mulli(res, lreg, (int)con); break; default: ShouldNotReachHere();
}
}
}
}
// Set up the arraycopy stub information.
ArrayCopyStub* stub = op->stub(); constint frame_resize = frame::abi_reg_args_size - sizeof(frame::jit_abi); // C calls need larger frame.
// Always do stub if no type information is available. It's ok if // the known type isn't loaded since the code sanity checks // in debug mode and the type isn't required when we know the exact type // also check that the type is an array type. if (op->expected_type() == NULL) {
assert(src->is_nonvolatile() && src_pos->is_nonvolatile() && dst->is_nonvolatile() && dst_pos->is_nonvolatile() &&
length->is_nonvolatile(), "must preserve");
address copyfunc_addr = StubRoutines::generic_arraycopy();
assert(copyfunc_addr != NULL, "generic arraycopy stub required");
// If the compiler was not able to prove that exact type of the source or the destination // of the arraycopy is an array type, check at runtime if the source or the destination is // an instance type. if (flags & LIR_OpArrayCopy::type_check) { if (!(flags & LIR_OpArrayCopy::dst_objarray)) {
__ load_klass(tmp, dst);
__ lwz(tmp2, in_bytes(Klass::layout_helper_offset()), tmp);
__ cmpwi(CCR0, tmp2, Klass::_lh_neutral_value);
__ bge(CCR0, slow);
}
if (!(flags & LIR_OpArrayCopy::type_check)) {
__ b(cont);
} else { // We don't know the array types are compatible. if (basic_type != T_OBJECT) { // Simple test for basic type arrays. if (UseCompressedClassPointers) { // We don't need decode because we just need to compare.
__ lwz(tmp, oopDesc::klass_offset_in_bytes(), src);
__ lwz(tmp2, oopDesc::klass_offset_in_bytes(), dst);
__ cmpw(CCR0, tmp, tmp2);
} else {
__ ld(tmp, oopDesc::klass_offset_in_bytes(), src);
__ ld(tmp2, oopDesc::klass_offset_in_bytes(), dst);
__ cmpd(CCR0, tmp, tmp2);
}
__ beq(CCR0, cont);
} else { // For object arrays, if src is a sub class of dst then we can // safely do the copy.
address copyfunc_addr = StubRoutines::checkcast_arraycopy();
if (copyfunc_addr != NULL) { // Use stub if available.
__ bind(copyfunc); // Src is not a sub class of dst so we have to do a // per-element check. int mask = LIR_OpArrayCopy::src_objarray|LIR_OpArrayCopy::dst_objarray; if ((flags & mask) != mask) {
assert(flags & mask, "one of the two should be known to be an object array");
#ifdef ASSERT if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { // Sanity check the known type with the incoming class. For the // primitive case the types must match exactly with src.klass and // dst.klass each exactly matching the default type. For the // object array case, if no type check is needed then either the // dst type is exactly the expected type and the src type is a // subtype which we can't check or src is the same array as dst // but not necessarily exactly of type default_type.
Label known_ok, halt;
metadata2reg(op->expected_type()->constant_encoding(), tmp); if (UseCompressedClassPointers) { // Tmp holds the default type. It currently comes uncompressed after the // load of a constant, so encode it.
__ encode_klass_not_null(tmp); // Load the raw value of the dst klass, since we will be comparing // uncompressed values directly.
__ lwz(tmp2, oopDesc::klass_offset_in_bytes(), dst);
__ cmpw(CCR0, tmp, tmp2); if (basic_type != T_OBJECT) {
__ bne(CCR0, halt); // Load the raw value of the src klass.
__ lwz(tmp2, oopDesc::klass_offset_in_bytes(), src);
__ cmpw(CCR0, tmp, tmp2);
__ beq(CCR0, known_ok);
} else {
__ beq(CCR0, known_ok);
__ cmpw(CCR0, src, dst);
__ beq(CCR0, known_ok);
}
} else {
__ ld(tmp2, oopDesc::klass_offset_in_bytes(), dst);
__ cmpd(CCR0, tmp, tmp2); if (basic_type != T_OBJECT) {
__ bne(CCR0, halt); // Load the raw value of the src klass.
__ ld(tmp2, oopDesc::klass_offset_in_bytes(), src);
__ cmpd(CCR0, tmp, tmp2);
__ beq(CCR0, known_ok);
} else {
__ beq(CCR0, known_ok);
__ cmpd(CCR0, src, dst);
__ beq(CCR0, known_ok);
}
}
__ bind(halt);
__ stop("incorrect type information in arraycopy");
__ bind(known_ok);
} #endif
// Arraycopy stubs takes a length in number of elements, so don't scale it.
__ mr(len, length);
__ call_c_with_frame_resize(entry, /*stub does not need resized frame*/ 0);
// Didn't find receiver; find next empty slot and fill it in. for (i = 0; i < VirtualCallData::row_limit(); i++) {
Label next_test;
__ ld(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)) - mdo_offset_bias, mdo);
__ cmpdi(CCR0, tmp1, 0);
__ bne(CCR0, next_test);
__ li(tmp1, DataLayout::counter_increment);
__ std(recv, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)) - mdo_offset_bias, mdo);
__ std(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
__ b(*update_done);
__ bind(next_test);
}
}
void LIR_Assembler::setup_md_access(ciMethod* method, int bci,
ciMethodData*& md, ciProfileData*& data, int& mdo_offset_bias) {
md = method->method_data_or_null();
assert(md != NULL, "Sanity");
data = md->bci_to_data(bci);
assert(data != NULL, "need data for checkcast");
assert(data->is_ReceiverTypeData(), "need ReceiverTypeData for type check"); if (!Assembler::is_simm16(md->byte_offset_of_slot(data, DataLayout::header_offset()) + data->size_in_bytes())) { // The offset is large so bias the mdo by the base of the slot so // that the ld can use simm16s to reference the slots of the data.
mdo_offset_bias = md->byte_offset_of_slot(data, DataLayout::header_offset());
}
}
void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, Label* failure, Label* obj_is_null) { constRegister obj = op->object()->as_register(); // Needs to live in this register at safepoint (patching stub). Register k_RInfo = op->tmp1()->as_register(); Register klass_RInfo = op->tmp2()->as_register(); Register Rtmp1 = op->tmp3()->as_register(); Register dst = op->result_opr()->as_register();
ciKlass* k = op->klass(); bool should_profile = op->should_profile(); // Attention: do_temp(opTypeCheck->_object) is not used, i.e. obj may be same as one of the temps. bool reg_conflict = false; if (obj == k_RInfo) {
k_RInfo = dst;
reg_conflict = true;
} elseif (obj == klass_RInfo) {
klass_RInfo = dst;
reg_conflict = true;
} elseif (obj == Rtmp1) {
Rtmp1 = dst;
reg_conflict = true;
}
assert_different_registers(obj, k_RInfo, klass_RInfo, Rtmp1);
__ cmpdi(CCR0, obj, 0);
ciMethodData* md = NULL;
ciProfileData* data = NULL; int mdo_offset_bias = 0; if (should_profile) {
ciMethod* method = op->profiled_method();
assert(method != NULL, "Should have method");
setup_md_access(method, op->profiled_bci(), md, data, mdo_offset_bias);
// Get instance klass.
__ ld(k_RInfo, in_bytes(ObjArrayKlass::element_klass_offset()), k_RInfo); // Perform the fast part of the checking logic.
__ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, R0, success_target, &failure, NULL);
// Call out-of-line instance of __ check_klass_subtype_slow_path(...): const address slow_path = Runtime1::entry_for(Runtime1::slow_subtype_check_id); //__ load_const_optimized(R0, slow_path);
__ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(slow_path));
__ mtctr(R0);
__ bctrl(); // sets CR0 if (!should_profile) {
__ beq(CCR0, done);
__ bind(failure);
} else {
__ bne(CCR0, failure); // Fall through to the success case.
// Obj may not be an oop. if (op->code() == lir_lock) {
MonitorEnterStub* stub = (MonitorEnterStub*)op->stub(); if (!UseHeavyMonitors) {
assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // Add debug info for NullPointerException only if one is possible. if (op->info() != NULL) { if (!os::zero_page_read_protected() || !ImplicitNullChecks) {
explicit_null_check(obj, op->info());
} else {
add_debug_info_for_null_check_here(op->info());
}
}
__ lock_object(hdr, obj, lock, op->scratch_opr()->as_register(), *op->stub()->entry());
} else { // always do slow locking // note: The slow locking code could be inlined here, however if we use // slow locking, speed doesn't matter anyway and this solution is // simpler and requires less duplicated code - additionally, the // slow locking code is the same in either case which simplifies // debugging. if (op->info() != NULL) {
add_debug_info_for_null_check_here(op->info());
__ null_check(obj);
}
__ b(*op->stub()->entry());
}
} else {
assert (op->code() == lir_unlock, "Invalid code, expected lir_unlock"); if (!UseHeavyMonitors) {
assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header");
__ unlock_object(hdr, obj, lock, *op->stub()->entry());
} else { // always do slow unlocking // note: The slow unlocking code could be inlined here, however if we use // slow unlocking, speed doesn't matter anyway and this solution is // simpler and requires less duplicated code - additionally, the // slow unlocking code is the same in either case which simplifies // debugging.
__ b(*op->stub()->entry());
}
}
__ bind(*op->stub()->continuation());
}
// Update counter for all call types.
ciMethodData* md = method->method_data_or_null();
assert(md != NULL, "Sanity");
ciProfileData* data = md->bci_to_data(bci);
assert(data != NULL && data->is_CounterData(), "need CounterData for calls");
assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); #ifdef _LP64
assert(op->tmp1()->is_double_cpu(), "tmp1 must be allocated"); Register tmp1 = op->tmp1()->as_register_lo(); #else
assert(op->tmp1()->is_single_cpu(), "tmp1 must be allocated"); Register tmp1 = op->tmp1()->as_register(); #endif
metadata2reg(md->constant_encoding(), mdo); int mdo_offset_bias = 0; if (!Assembler::is_simm16(md->byte_offset_of_slot(data, CounterData::count_offset()) +
data->size_in_bytes())) { // The offset is large so bias the mdo by the base of the slot so // that the ld can use simm16s to reference the slots of the data.
mdo_offset_bias = md->byte_offset_of_slot(data, CounterData::count_offset());
__ add_const_optimized(mdo, mdo, mdo_offset_bias, R0);
}
// Perform additional virtual call profiling for invokevirtual and // invokeinterface bytecodes if (op->should_profile_receiver_type()) {
assert(op->recv()->is_single_cpu(), "recv must be allocated"); Register recv = op->recv()->as_register();
assert_different_registers(mdo, tmp1, recv);
assert(data->is_VirtualCallData(), "need VirtualCallData for virtual calls");
ciKlass* known_klass = op->known_holder(); if (C1OptimizeVirtualCallProfiling && known_klass != NULL) { // We know the type that will be seen at this call site; we can // statically update the MethodData* rather than needing to do // dynamic tests on the receiver type.
// NOTE: we should probably put a lock around this search to // avoid collisions by concurrent compilations.
ciVirtualCallData* vc_data = (ciVirtualCallData*) data;
uint i; for (i = 0; i < VirtualCallData::row_limit(); i++) {
ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) {
__ ld(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
__ addi(tmp1, tmp1, DataLayout::counter_increment);
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo); return;
}
}
// Receiver type not found in profile data; select an empty slot.
// Note that this is less efficient than it should be because it // always does a write to the receiver part of the // VirtualCallData rather than just the first time. for (i = 0; i < VirtualCallData::row_limit(); i++) {
ciKlass* receiver = vc_data->receiver(i); if (receiver == NULL) {
metadata2reg(known_klass->constant_encoding(), tmp1);
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - mdo_offset_bias, mdo);
__ ld(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
__ addi(tmp1, tmp1, DataLayout::counter_increment);
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo); return;
}
}
} else {
__ load_klass(recv, recv);
Label update_done;
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, tmp1, &update_done); // Receiver did not match any saved receiver and there is no empty row for it. // Increment total counter to indicate polymorphic case.
__ ld(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
__ addi(tmp1, tmp1, DataLayout::counter_increment);
__ std(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) { // tmp must be unused
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
assert(left->is_register(), "can only handle registers");
__ bind(Lupdate); if (do_update) {
Label Lnext; constRegister klass = R29_TOC; // kill and reload bool klass_reg_used = false; #ifdef ASSERT if (exact_klass != NULL) {
Label ok;
klass_reg_used = true;
__ load_klass(klass, obj);
metadata2reg(exact_klass->constant_encoding(), R0);
__ cmpd(CCR0, klass, R0);
__ beq(CCR0, ok);
__ stop("exact klass and actual klass differ");
__ bind(ok);
} #endif
if (!no_conflict) { if (exact_klass == NULL || TypeEntries::is_type_none(current_klass)) {
klass_reg_used = true; if (exact_klass != NULL) {
__ ld(tmp, index_or_disp(mdo_addr), mdo_addr->base()->as_pointer_register());
metadata2reg(exact_klass->constant_encoding(), klass);
} else {
__ load_klass(klass, obj);
__ ld(tmp, index_or_disp(mdo_addr), mdo_addr->base()->as_pointer_register()); // may kill obj
}
// Like InterpreterMacroAssembler::profile_obj_type
__ 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, Lnext);
if (TypeEntries::is_type_none(current_klass)) {
__ 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, Ldo_update); // First time here. Set profile type.
}
__ ld(tmp, index_or_disp(mdo_addr), mdo_addr->base()->as_pointer_register());
__ andi_(R0, tmp, TypeEntries::type_unknown); // Already unknown. Nothing to do anymore.
__ bne(CCR0, Lnext);
}
// Different than before. Cannot keep accurate profile.
__ ori(R0, tmp, TypeEntries::type_unknown);
} else { // There's a single possible klass at this profile point
assert(exact_klass != NULL, "should be");
__ ld(tmp, index_or_disp(mdo_addr), mdo_addr->base()->as_pointer_register());
if (TypeEntries::is_type_none(current_klass)) {
klass_reg_used = true;
metadata2reg(exact_klass->constant_encoding(), klass);
__ 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, Lnext); #ifdef ASSERT
{
Label ok;
__ clrrdi_(R0, tmp, exact_log2(-TypeEntries::type_mask));
__ beq(CCR0, ok); // First time here.
__ stop("unexpected profiling mismatch");
__ bind(ok);
} #endif // First time here. Set profile type.
__ orr(R0, klass, tmp); // Combine klass and null_seen bit (only used if (tmp & type_mask)==0).
} else {
assert(ciTypeEntries::valid_ciklass(current_klass) != NULL &&
ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent");
// Already unknown. Nothing to do anymore.
__ andi_(R0, tmp, TypeEntries::type_unknown);
__ bne(CCR0, Lnext);
// Different than before. Cannot keep accurate profile.
__ ori(R0, tmp, TypeEntries::type_unknown);
}
}
void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) {
assert(op->crc()->is_single_cpu(), "crc must be register");
assert(op->val()->is_single_cpu(), "byte value must be register");
assert(op->result_opr()->is_single_cpu(), "result must be register"); Register crc = op->crc()->as_register(); Register val = op->val()->as_register(); Register res = op->result_opr()->as_register();
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.41Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-27)
¤
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 und die Messung sind noch experimentell.