/* * Copyright (c) 1997, 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. *
*/
#ifdef ASSERT // On RISC, there's no benefit to verifying instruction boundaries. bool AbstractAssembler::pd_check_instruction_mark() { returnfalse; } #endif
void MacroAssembler::ld_largeoffset_unchecked(Register d, int si31, Register a, int emit_filler_nop) {
assert(Assembler::is_simm(si31, 31) && si31 >= 0, "si31 out of range"); if (Assembler::is_simm(si31, 16)) {
ld(d, si31, a); if (emit_filler_nop) nop();
} else { constint hi = MacroAssembler::largeoffset_si16_si16_hi(si31); constint lo = MacroAssembler::largeoffset_si16_si16_lo(si31);
addis(d, a, hi);
ld(d, lo, d);
}
}
void MacroAssembler::ld_largeoffset(Register d, int si31, Register a, int emit_filler_nop) {
assert_different_registers(d, a);
ld_largeoffset_unchecked(d, si31, a, emit_filler_nop);
}
// Issue instructions that calculate given TOC from global TOC. void MacroAssembler::calculate_address_from_global_toc(Register dst, address addr, bool hi16, bool lo16, bool add_relocation, bool emit_dummy_addr) { int offset = -1; if (emit_dummy_addr) {
offset = -128; // dummy address
} elseif (addr != (address)(intptr_t)-1) {
offset = MacroAssembler::offset_to_global_toc(addr);
}
if (hi16) {
addis(dst, R29_TOC, MacroAssembler::largeoffset_si16_si16_hi(offset));
} if (lo16) { if (add_relocation) { // Relocate at the addi to avoid confusion with a load from the method's TOC.
relocate(internal_word_Relocation::spec(addr));
}
addi(dst, dst, MacroAssembler::largeoffset_si16_si16_lo(offset));
}
}
// The relocation points to the second instruction, the addi, // and the addi reads and writes the same register dst. constint dst = inv_rt_field(inst2);
assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst");
// Now, find the preceding addis which writes to dst. int inst1 = 0;
address inst1_addr = inst2_addr - BytesPerInstWord; while (inst1_addr >= bound) {
inst1 = *(int *) inst1_addr; if (is_addis(inst1) && inv_rt_field(inst1) == dst) { // Stop, found the addis which writes dst. break;
}
inst1_addr -= BytesPerInstWord;
}
assert(is_addis(inst1) && inv_ra_field(inst1) == 29 /* R29 */, "source must be global TOC");
set_imm((int *)inst1_addr, MacroAssembler::largeoffset_si16_si16_hi(offset));
set_imm((int *)inst2_addr, MacroAssembler::largeoffset_si16_si16_lo(offset)); return inst1_addr;
}
// The relocation points to the second instruction, the addi, // and the addi reads and writes the same register dst. constint dst = inv_rt_field(inst2);
assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst");
// Now, find the preceding addis which writes to dst. int inst1 = 0;
address inst1_addr = inst2_addr - BytesPerInstWord; while (inst1_addr >= bound) {
inst1 = *(int *) inst1_addr; if (is_addis(inst1) && inv_rt_field(inst1) == dst) { // stop, found the addis which writes dst break;
}
inst1_addr -= BytesPerInstWord;
}
assert(is_addis(inst1) && inv_ra_field(inst1) == 29 /* R29 */, "source must be global TOC");
int offset = (get_imm(inst1_addr, 0) << 16) + get_imm(inst2_addr, 0); // -1 is a special case if (offset == -1) { return (address)(intptr_t)-1;
} else { return global_toc() + offset;
}
}
#ifdef _LP64 // Patch compressed oops or klass constants. // Assembler sequence is // 1) compressed oops: // lis rx = const.hi // ori rx = rx | const.lo // 2) compressed klass: // lis rx = const.hi // clrldi rx = rx & 0xFFFFffff // clearMS32b, optional // ori rx = rx | const.lo // Clrldi will be passed by.
address MacroAssembler::patch_set_narrow_oop(address a, address bound, narrowOop data) {
assert(UseCompressedOops, "Should only patch compressed oops");
// The relocation points to the second instruction, the ori, // and the ori reads and writes the same register dst. constint dst = inv_rta_field(inst2);
assert(is_ori(inst2) && inv_rs_field(inst2) == dst, "must be ori reading and writing dst"); // Now, find the preceding addis which writes to dst. int inst1 = 0;
address inst1_addr = inst2_addr - BytesPerInstWord; bool inst1_found = false; while (inst1_addr >= bound) {
inst1 = *(int *)inst1_addr; if (is_lis(inst1) && inv_rs_field(inst1) == dst) { inst1_found = true; break; }
inst1_addr -= BytesPerInstWord;
}
assert(inst1_found, "inst is not lis");
uint32_t data_value = CompressedOops::narrow_oop_value(data); int xc = (data_value >> 16) & 0xffff; int xd = (data_value >> 0) & 0xffff;
set_imm((int *)inst1_addr, (short)(xc)); // see enc_load_con_narrow_hi/_lo
set_imm((int *)inst2_addr, (xd)); // unsigned int return inst1_addr;
}
// Get compressed oop constant.
narrowOop MacroAssembler::get_narrow_oop(address a, address bound) {
assert(UseCompressedOops, "Should only patch compressed oops");
// The relocation points to the second instruction, the ori, // and the ori reads and writes the same register dst. constint dst = inv_rta_field(inst2);
assert(is_ori(inst2) && inv_rs_field(inst2) == dst, "must be ori reading and writing dst"); // Now, find the preceding lis which writes to dst. int inst1 = 0;
address inst1_addr = inst2_addr - BytesPerInstWord; bool inst1_found = false;
while (inst1_addr >= bound) {
inst1 = *(int *) inst1_addr; if (is_lis(inst1) && inv_rs_field(inst1) == dst) { inst1_found = true; break;}
inst1_addr -= BytesPerInstWord;
}
assert(inst1_found, "inst is not lis");
// Returns true if successful. bool MacroAssembler::load_const_from_method_toc(Register dst, AddressLiteral& a, Register toc, bool fixed_size) { int toc_offset = 0; // Use RelocationHolder::none for the constant pool entry, otherwise // we will end up with a failing NativeCall::verify(x) where x is // the address of the constant pool entry. // FIXME: We should insert relocation information for oops at the constant // pool entries instead of inserting it at the loads; patching of a constant // pool entry should be less expensive.
address const_address = address_constant((address)a.value(), RelocationHolder::none); if (const_address == NULL) { returnfalse; } // allocation failure // Relocate at the pc of the load.
relocate(a.rspec());
toc_offset = (int)(const_address - code()->consts()->start());
ld_largeoffset_unchecked(dst, toc_offset, toc, fixed_size); returntrue;
}
// The relocation points to the ld or the addis. return (is_ld(inst1)) ||
(is_addis(inst1) && inv_ra_field(inst1) != 0);
}
int MacroAssembler::get_offset_of_load_const_from_method_toc_at(address a) {
assert(is_load_const_from_method_toc_at(a), "must be load_const_from_method_toc");
// Conditional far branch for destinations encodable in 24+2 bits. void MacroAssembler::bc_far(int boint, int biint, Label& dest, int optimize) {
// If requested by flag optimize, relocate the bc_far as a // runtime_call and prepare for optimizing it when the code gets // relocated. if (optimize == bc_far_optimize_on_relocate) {
relocate(relocInfo::runtime_call_type);
}
// We emit two branches. // First, a conditional branch which jumps around the far branch. const address not_taken_pc = pc() + 2 * BytesPerInstWord; const address bc_pc = pc();
bc(opposite_boint, biint, not_taken_pc);
// Second, an unconditional far branch which jumps to dest. // Note: target(dest) remembers the current pc (see CodeSection::target) // and returns the current pc if the label is not bound yet; when // the label gets bound, the unconditional far branch will be patched. const address target_pc = target(dest); const address b_pc = pc();
b(target_pc);
if (is_bc_far_variant3_at(instruction_addr)) { // variant 3, far cond branch to the next instruction, already patched to nops: // // nop // endgroup // SKIP/DEST: // return;
}
// first, extract boint and biint from the current branch int boint = 0; int biint = 0;
ResourceMark rm; constint code_size = 2 * BytesPerInstWord;
CodeBuffer buf(instruction_addr, code_size);
MacroAssembler masm(&buf); if (is_bc_far_variant2_at(instruction_addr) && dest == instruction_addr + 8) { // Far branch to next instruction: Optimize it by patching nops (produce variant 3).
masm.nop();
masm.endgroup();
} else { if (is_bc_far_variant1_at(instruction_addr)) { // variant 1, the 1st instruction contains the destination address: // // bcxx DEST // nop // constint instruction_1 = *(int*)(instruction_addr);
boint = inv_bo_field(instruction_1);
biint = inv_bi_field(instruction_1);
} elseif (is_bc_far_variant2_at(instruction_addr)) { // variant 2, the 2nd instruction contains the destination address: // // b!cxx SKIP // bxx DEST // SKIP: // constint instruction_1 = *(int*)(instruction_addr);
boint = add_bhint_to_boint(opposite_bhint(inv_boint_bhint(inv_bo_field(instruction_1))),
opposite_bcond(inv_boint_bcond(inv_bo_field(instruction_1))));
biint = inv_bi_field(instruction_1);
} else { // variant 4???
ShouldNotReachHere();
}
// second, set the new branch destination and optimize the code if (dest != instruction_addr + 4 && // the bc_far is still unbound!
masm.is_within_range_of_bcxx(dest, instruction_addr)) { // variant 1: // // bcxx DEST // nop //
masm.bc(boint, biint, dest);
masm.nop();
} else { // variant 2: // // b!cxx SKIP // bxx DEST // SKIP: // constint opposite_boint = add_bhint_to_boint(opposite_bhint(inv_boint_bhint(boint)),
opposite_bcond(inv_boint_bcond(boint))); const address not_taken_pc = masm.pc() + 2 * BytesPerInstWord;
masm.bc(opposite_boint, biint, not_taken_pc);
masm.b(dest);
}
}
ICache::ppc64_flush_icache_bytes(instruction_addr, code_size);
}
// Emit a NOT mt-safe patchable 64 bit absolute call/jump. void MacroAssembler::bxx64_patchable(address dest, relocInfo::relocType rt, bool link) { // get current pc
uint64_t start_pc = (uint64_t) pc();
const address pc_of_bl = (address) (start_pc + (6*BytesPerInstWord)); // bl is last const address pc_of_b = (address) (start_pc + (0*BytesPerInstWord)); // b is first
// relocate here if (rt != relocInfo::none) {
relocate(rt);
}
if ( ReoptimizeCallSequences &&
(( link && is_within_range_of_b(dest, pc_of_bl)) ||
(!link && is_within_range_of_b(dest, pc_of_b)))) { // variant 2: // Emit an optimized, pc-relative call/jump.
if (link) { // some padding
nop();
nop();
nop();
nop();
nop();
nop();
// do the call
assert(pc() == pc_of_bl, "just checking");
bl(dest, relocInfo::none);
} else { // do the jump
assert(pc() == pc_of_b, "just checking");
b(dest, relocInfo::none);
// some padding
nop();
nop();
nop();
nop();
nop();
nop();
}
// Assert that we can identify the emitted call/jump.
assert(is_bxx64_patchable_variant2_at((address)start_pc, link), "can't identify emitted call");
} else { // variant 1:
mr(R0, R11); // spill R11 -> R0.
// Load the destination address into CTR, // calculate destination relative to global toc.
calculate_address_from_global_toc(R11, dest, true, true, false);
// do the call/jump if (link) {
bctrl();
} else{
bctr();
} // Assert that we can identify the emitted call/jump.
assert(is_bxx64_patchable_variant1b_at((address)start_pc, link), "can't identify emitted call");
}
// Assert that we can identify the emitted call/jump.
assert(is_bxx64_patchable_at((address)start_pc, link), "can't identify emitted call");
assert(get_dest_of_bxx64_patchable_at((address)start_pc, link) == dest, "wrong encoding of dest address");
}
// Does the call64_patchable instruction use a pc-relative encoding of // the call destination? bool MacroAssembler::is_bxx64_patchable_pcrelative_at(address instruction_addr, bool link) { // variant 2 is pc-relative return is_bxx64_patchable_variant2_at(instruction_addr, link);
}
// Preserve stack pointer register (R1_SP) and system thread id register (R13); // although they're technically volatile for (int i = 2; i < 13; i++) { Register reg = as_Register(i); if (reg == excluded_register) { continue;
}
void MacroAssembler::save_LR_CR(Register tmp) {
mfcr(tmp);
std(tmp, _abi0(cr), R1_SP);
mflr(tmp);
std(tmp, _abi0(lr), R1_SP); // Tmp must contain lr on exit! (see return_addr and prolog in ppc64.ad)
}
// Push a frame of size `bytes'. void MacroAssembler::push_frame(unsignedint bytes, Register tmp) { long offset = align_addr(bytes, frame::alignment_in_bytes); if (is_simm(-offset, 16)) {
stdu(R1_SP, -offset, R1_SP);
} else {
load_const_optimized(tmp, -offset);
stdux(R1_SP, R1_SP, tmp);
}
}
// Push a frame of size `bytes' plus abi_reg_args on top. void MacroAssembler::push_frame_reg_args(unsignedint bytes, Register tmp) {
push_frame(bytes + frame::abi_reg_args_size, tmp);
}
// Setup up a new C frame with a spill area for non-volatile GPRs and // additional space for local variables. void MacroAssembler::push_frame_reg_args_nonvolatiles(unsignedint bytes, Register tmp) {
push_frame(bytes + frame::abi_reg_args_size + frame::spill_nonvolatiles_size, tmp);
}
// Pop current C frame. void MacroAssembler::pop_frame() {
ld(R1_SP, _abi0(callers_sp), R1_SP);
}
#ifdefined(ABI_ELFv2)
address MacroAssembler::branch_to(Register r_function_entry, bool and_link) { // TODO(asmundak): make sure the caller uses R12 as function descriptor // most of the times. if (R12 != r_function_entry) {
mr(R12, r_function_entry);
}
mtctr(R12); // Do a call or a branch. if (and_link) {
bctrl();
} else {
bctr();
}
_last_calls_return_pc = pc();
return _last_calls_return_pc;
}
// Call a C function via a function descriptor and use full C // calling conventions. Updates and returns _last_calls_return_pc.
address MacroAssembler::call_c(Register r_function_entry) { return branch_to(r_function_entry, /*and_link=*/true);
}
// For tail calls: only branch, don't link, so callee returns to caller of this function.
address MacroAssembler::call_c_and_return_to_caller(Register r_function_entry) { return branch_to(r_function_entry, /*and_link=*/false);
}
#else // Generic version of a call to C function via a function descriptor // with variable support for C calling conventions (TOC, ENV, etc.). // Updates and returns _last_calls_return_pc.
address MacroAssembler::branch_to(Register function_descriptor, bool and_link, bool save_toc_before_call, bool restore_toc_after_call, bool load_toc_of_callee, bool load_env_of_callee) { // we emit standard ptrgl glue code here
assert((function_descriptor != R0), "function_descriptor cannot be R0");
// retrieve necessary entries from the function descriptor
ld(R0, in_bytes(FunctionDescriptor::entry_offset()), function_descriptor);
mtctr(R0);
if (load_toc_of_callee) {
ld(R2_TOC, in_bytes(FunctionDescriptor::toc_offset()), function_descriptor);
} if (load_env_of_callee) {
ld(R11, in_bytes(FunctionDescriptor::env_offset()), function_descriptor);
} elseif (load_toc_of_callee) {
li(R11, 0);
}
// do a call or a branch if (and_link) {
bctrl();
} else {
bctr();
}
_last_calls_return_pc = pc();
return _last_calls_return_pc;
}
// Call a C function via a function descriptor and use full C calling // conventions. // We don't use the TOC in generated code, so there is no need to save // and restore its value.
address MacroAssembler::call_c(Register fd) { return branch_to(fd, /*and_link=*/true, /*save toc=*/false, /*restore toc=*/false, /*load toc=*/true, /*load env=*/true);
}
address MacroAssembler::call_c(const FunctionDescriptor* fd, relocInfo::relocType rt) { if (rt != relocInfo::none) { // this call needs to be relocatable if (!ReoptimizeCallSequences
|| (rt != relocInfo::runtime_call_type && rt != relocInfo::none)
|| fd == NULL // support code-size estimation
|| !fd->is_friend_function()
|| fd->entry() == NULL) { // it's not a friend function as defined by class FunctionDescriptor, // so do a full call-c here.
load_const(R11, (address)fd, R0);
bool has_env = (fd != NULL && fd->env() != NULL); return branch_to(R11, /*and_link=*/true, /*save toc=*/false, /*restore toc=*/false, /*load toc=*/true, /*load env=*/has_env);
} else { // It's a friend function. Load the entry point and don't care about // toc and env. Use an optimizable call instruction, but ensure the // same code-size as in the case of a non-friend function.
nop();
nop();
nop();
bl64_patchable(fd->entry(), rt);
_last_calls_return_pc = pc(); return _last_calls_return_pc;
}
} else { // This call does not need to be relocatable, do more aggressive // optimizations. if (!ReoptimizeCallSequences
|| !fd->is_friend_function()) { // It's not a friend function as defined by class FunctionDescriptor, // so do a full call-c here.
load_const(R11, (address)fd, R0); return branch_to(R11, /*and_link=*/true, /*save toc=*/false, /*restore toc=*/false, /*load toc=*/true, /*load env=*/true);
} else { // it's a friend function, load the entry point and don't care about // toc and env.
address dest = fd->entry(); if (is_within_range_of_b(dest, pc())) {
bl(dest);
} else {
bl64_patchable(dest, rt);
}
_last_calls_return_pc = pc(); return _last_calls_return_pc;
}
}
}
// Call a C function. All constants needed reside in TOC. // // Read the address to call from the TOC. // Read env from TOC, if fd specifies an env. // Read new TOC from TOC.
address MacroAssembler::call_c_using_toc(const FunctionDescriptor* fd,
relocInfo::relocType rt, Register toc) { if (!ReoptimizeCallSequences
|| (rt != relocInfo::runtime_call_type && rt != relocInfo::none)
|| !fd->is_friend_function()) { // It's not a friend function as defined by class FunctionDescriptor, // so do a full call-c here.
assert(fd->entry() != NULL, "function must be linked");
AddressLiteral fd_entry(fd->entry()); bool success = load_const_from_method_toc(R11, fd_entry, toc, /*fixed_size*/ true);
mtctr(R11); if (fd->env() == NULL) {
li(R11, 0);
nop();
} else {
AddressLiteral fd_env(fd->env());
success = success && load_const_from_method_toc(R11, fd_env, toc, /*fixed_size*/ true);
}
AddressLiteral fd_toc(fd->toc()); // Set R2_TOC (load from toc)
success = success && load_const_from_method_toc(R2_TOC, fd_toc, toc, /*fixed_size*/ true);
bctrl();
_last_calls_return_pc = pc(); if (!success) { return NULL; }
} else { // It's a friend function, load the entry point and don't care about // toc and env. Use an optimizable call instruction, but ensure the // same code-size as in the case of a non-friend function.
nop();
bl64_patchable(fd->entry(), rt);
_last_calls_return_pc = pc();
} return _last_calls_return_pc;
} #endif// ABI_ELFv2
void MacroAssembler::post_call_nop() { // Make inline again when loom is always enabled. if (!Continuations::enabled()) { return;
}
nop();
}
// Check whether instruction is a read access to the polling page // which was emitted by load_from_polling_page(..). bool MacroAssembler::is_load_from_polling_page(int instruction, void* ucontext,
address* polling_address_ptr) { if (!is_ld(instruction)) returnfalse; // It's not a ld. Fail.
int rt = inv_rt_field(instruction); int ra = inv_ra_field(instruction); int ds = inv_ds_field(instruction); if (!(ds == 0 && ra != 0 && rt == 0)) { returnfalse; // It's not a ld(r0, X, ra). Fail.
}
if (!ucontext) { // Set polling address. if (polling_address_ptr != NULL) {
*polling_address_ptr = NULL;
} returntrue; // No ucontext given. Can't check value of ra. Assume true.
}
#ifdef LINUX // Ucontext given. Check that register ra contains the address of // the safepoing polling page.
ucontext_t* uc = (ucontext_t*) ucontext; // Set polling address.
address addr = (address)uc->uc_mcontext.regs->gpr[ra] + (ssize_t)ds; if (polling_address_ptr != NULL) {
*polling_address_ptr = addr;
} return SafepointMechanism::is_poll_address(addr); #else // Not on Linux, ucontext must be NULL.
ShouldNotReachHere(); returnfalse; #endif
}
void MacroAssembler::bang_stack_with_offset(int offset) { // When increasing the stack, the old stack pointer will be written // to the new top of stack according to the PPC64 abi. // Therefore, stack banging is not necessary when increasing // the stack by <= os::vm_page_size() bytes. // When increasing the stack by a larger amount, this method is // called repeatedly to bang the intermediate pages.
// Stack grows down, caller passes positive offset.
assert(offset > 0, "must bang with positive offset");
long stdoffset = -offset;
if (is_simm(stdoffset, 16)) { // Signed 16 bit offset, a simple std is ok. if (UseLoadInstructionsForStackBangingPPC64) {
ld(R0, (int)(signedshort)stdoffset, R1_SP);
} else {
std(R0,(int)(signedshort)stdoffset, R1_SP);
}
} elseif (is_simm(stdoffset, 31)) { constint hi = MacroAssembler::largeoffset_si16_si16_hi(stdoffset); constint lo = MacroAssembler::largeoffset_si16_si16_lo(stdoffset);
// Temps and addr_base are killed if size < 4 and processor does not support respective instructions. // Only signed types are supported with size < 4. // Atomic add always kills tmp1. void MacroAssembler::atomic_get_and_modify_generic(Register dest_current_value, Register exchange_value, Register addr_base, Register tmp1, Register tmp2, Register tmp3, bool cmpxchgx_hint, bool is_add, int size) { // Sub-word instructions are available since Power 8. // For older processors, instruction_type != size holds, and we // emulate the sub-word instructions by constructing a 4-byte value // that leaves the other bytes unchanged. constint instruction_type = VM_Version::has_lqarx() ? size : 4;
switch (instruction_type) { case 4: lwarx(val32, addr_base, cmpxchgx_hint); break; case 2: lharx(val32, addr_base, cmpxchgx_hint); break; case 1: lbarx(val32, addr_base, cmpxchgx_hint); break; default: ShouldNotReachHere();
}
if (instruction_type != size) {
srw(dest_current_value, val32, shift_amount);
}
if (is_add) { add(modval, dest_current_value, exchange_value); }
if (instruction_type != size) { // Transform exchange value such that the replacement can be done by one xor instruction.
xorr(modval, dest_current_value, is_add ? modval : exchange_value);
clrldi(modval, modval, (size == 1) ? 56 : 48);
slw(modval, modval, shift_amount);
xorr(modval, val32, modval);
}
switch (instruction_type) { case 4: stwcx_(modval, addr_base); break; case 2: sthcx_(modval, addr_base); break; case 1: stbcx_(modval, addr_base); break; default: ShouldNotReachHere();
}
// Temps, addr_base and exchange_value are killed if size < 4 and processor does not support respective instructions. // Only signed types are supported with size < 4. void MacroAssembler::cmpxchg_loop_body(ConditionRegister flag, Register dest_current_value, Register compare_value, Register exchange_value, Register addr_base, Register tmp1, Register tmp2,
Label &retry, Label &failed, bool cmpxchgx_hint, int size) { // Sub-word instructions are available since Power 8. // For older processors, instruction_type != size holds, and we // emulate the sub-word instructions by constructing a 4-byte value // that leaves the other bytes unchanged. constint instruction_type = VM_Version::has_lqarx() ? size : 4;
// Save one branch if result is returned via register and // result register is different from the other ones. bool use_result_reg = (int_flag_success != noreg); bool preset_result_reg = (int_flag_success != dest_current_value && int_flag_success != compare_value &&
int_flag_success != exchange_value && int_flag_success != addr_base &&
int_flag_success != tmp1 && int_flag_success != tmp2);
assert(!weak || flag == CCR0, "weak only supported with CCR0");
assert(size == 1 || size == 2 || size == 4, "unsupported");
if (use_result_reg && preset_result_reg) {
li(int_flag_success, 0); // preset (assume cas failed)
}
// Add simple guard in order to reduce risk of starving under high contention (recommended by IBM). if (contention_hint) { // Don't try to reserve if cmp fails. switch (size) { case 1: lbz(dest_current_value, 0, addr_base); extsb(dest_current_value, dest_current_value); break; case 2: lha(dest_current_value, 0, addr_base); break; case 4: lwz(dest_current_value, 0, addr_base); break; default: ShouldNotReachHere();
}
cmpw(flag, dest_current_value, compare_value);
bne(flag, failed);
}
// release/fence semantics if (semantics & MemBarRel) {
release();
}
// Result in register (must do this at the end because int_flag_success can be the // same register as one above). if (use_result_reg) {
li(int_flag_success, 1);
}
// Performs atomic compare exchange: // if (compare_value == *addr_base) // *addr_base = exchange_value // int_flag_success = 1; // else // int_flag_success = 0; // // ConditionRegister flag = cmp(compare_value, *addr_base) // Register dest_current_value = *addr_base // Register compare_value Used to compare with value in memory // Register exchange_value Written to memory if compare_value == *addr_base // Register addr_base The memory location to compareXChange // Register int_flag_success Set to 1 if exchange_value was written to *addr_base // // To avoid the costly compare exchange the value is tested beforehand. // Several special cases exist to avoid that unnecessary information is generated. // void MacroAssembler::cmpxchgd(ConditionRegister flag, Register dest_current_value, RegisterOrConstant compare_value, Register exchange_value, Register addr_base, int semantics, bool cmpxchgx_hint, Register int_flag_success, Label* failed_ext, bool contention_hint, bool weak) {
Label retry;
Label failed_int;
Label& failed = (failed_ext != NULL) ? *failed_ext : failed_int;
Label done;
// Save one branch if result is returned via register and result register is different from the other ones. bool use_result_reg = (int_flag_success!=noreg); bool preset_result_reg = (int_flag_success!=dest_current_value && int_flag_success!=compare_value.register_or_noreg() &&
int_flag_success!=exchange_value && int_flag_success!=addr_base);
assert(!weak || flag == CCR0, "weak only supported with CCR0");
assert(int_flag_success == noreg || failed_ext == NULL, "cannot have both");
if (use_result_reg && preset_result_reg) {
li(int_flag_success, 0); // preset (assume cas failed)
}
// Add simple guard in order to reduce risk of starving under high contention (recommended by IBM). if (contention_hint) { // Don't try to reserve if cmp fails.
ld(dest_current_value, 0, addr_base);
cmpd(flag, compare_value, dest_current_value);
bne(flag, failed);
}
// release/fence semantics if (semantics & MemBarRel) {
release();
}
// result in register (must do this at the end because int_flag_success can be the same register as one above) if (use_result_reg) {
li(int_flag_success, 1);
}
// Look up the method for a megamorphic invokeinterface call. // The target method is determined by <intf_klass, itable_index>. // The receiver klass is in recv_klass. // On success, the result will be in method_result, and execution falls through. // On failure, execution transfers to the given label. void MacroAssembler::lookup_interface_method(Register recv_klass, Register intf_klass,
RegisterOrConstant itable_index, Register method_result, Register scan_temp, Register temp2,
Label& L_no_such_interface, bool return_method) {
assert_different_registers(recv_klass, intf_klass, method_result, scan_temp);
// Compute start of first itableOffsetEntry (which is at the end of the vtable). int vtable_base = in_bytes(Klass::vtable_start_offset()); int itentry_off = itableMethodEntry::method_offset_in_bytes(); int logMEsize = exact_log2(itableMethodEntry::size() * wordSize); int scan_step = itableOffsetEntry::size() * wordSize; int log_vte_size= exact_log2(vtableEntry::size_in_bytes());
lwz(scan_temp, in_bytes(Klass::vtable_length_offset()), recv_klass); // %%% We should store the aligned, prescaled offset in the klassoop. // Then the next several instructions would fold away.
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.