blob: e30e8c82d23c52ca03fcaab4c60100eb3c4b3ea4 [file]
/*
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025 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.
*/
#include "asm/macroAssembler.inline.hpp"
#include "asm/register.hpp"
#include "code/codeBlob.hpp"
#include "code/vmreg.inline.hpp"
#include "gc/z/zAddress.hpp"
#include "gc/z/zBarrier.inline.hpp"
#include "gc/z/zBarrierSet.hpp"
#include "gc/z/zBarrierSetAssembler.hpp"
#include "gc/z/zBarrierSetRuntime.hpp"
#include "gc/z/zThreadLocalData.hpp"
#include "memory/resourceArea.hpp"
#include "register_ppc.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
#ifdef COMPILER1
#include "c1/c1_LIRAssembler.hpp"
#include "c1/c1_MacroAssembler.hpp"
#include "gc/z/c1/zBarrierSetC1.hpp"
#endif // COMPILER1
#ifdef COMPILER2
#include "gc/z/c2/zBarrierSetC2.hpp"
#include "opto/output.hpp"
#endif // COMPILER2
#undef __
#define __ masm->
// Helper for saving and restoring registers across a runtime call that does
// not have any live vector registers.
class ZRuntimeCallSpill {
MacroAssembler* _masm;
Register _result;
bool _needs_frame, _preserve_gp_registers, _preserve_fp_registers;
int _nbytes_save;
void save() {
MacroAssembler* masm = _masm;
if (_needs_frame) {
if (_preserve_gp_registers) {
bool preserve_R3 = _result != R3_ARG1;
_nbytes_save = (MacroAssembler::num_volatile_gp_regs
+ (_preserve_fp_registers ? MacroAssembler::num_volatile_fp_regs : 0)
- (preserve_R3 ? 0 : 1)
) * BytesPerWord;
__ save_volatile_gprs(R1_SP, -_nbytes_save, _preserve_fp_registers, preserve_R3);
}
__ save_LR(R0);
__ push_frame_reg_args(_nbytes_save, R0);
}
}
void restore() {
MacroAssembler* masm = _masm;
Register result = R3_RET;
if (_needs_frame) {
__ pop_frame();
__ restore_LR(R0);
if (_preserve_gp_registers) {
bool restore_R3 = _result != R3_ARG1;
if (restore_R3 && _result != noreg) {
__ mr(R0, R3_RET);
result = R0;
}
__ restore_volatile_gprs(R1_SP, -_nbytes_save, _preserve_fp_registers, restore_R3);
}
}
if (_result != noreg) {
__ mr_if_needed(_result, result);
}
}
public:
ZRuntimeCallSpill(MacroAssembler* masm, Register result, MacroAssembler::PreservationLevel preservation_level)
: _masm(masm),
_result(result),
_needs_frame(preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR),
_preserve_gp_registers(preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS),
_preserve_fp_registers(preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS),
_nbytes_save(0) {
save();
}
~ZRuntimeCallSpill() {
restore();
}
};
void ZBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
Register base, RegisterOrConstant ind_or_offs, Register dst,
Register tmp1, Register tmp2,
MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null) {
__ block_comment("load_at (zgc) {");
// Check whether a special gc barrier is required for this particular load
// (e.g. whether it's a reference load or not)
if (!ZBarrierSet::barrier_needed(decorators, type)) {
BarrierSetAssembler::load_at(masm, decorators, type, base, ind_or_offs, dst,
tmp1, tmp2, preservation_level, L_handle_null);
return;
}
if (ind_or_offs.is_register()) {
assert_different_registers(base, ind_or_offs.as_register(), tmp1, tmp2, R0, noreg);
assert_different_registers(dst, ind_or_offs.as_register(), tmp1, tmp2, R0, noreg);
} else {
assert_different_registers(base, tmp1, tmp2, R0, noreg);
assert_different_registers(dst, tmp1, tmp2, R0, noreg);
}
/* ==== Load the pointer using the standard implementation for the actual heap access
and the decompression of compressed pointers ==== */
// Result of 'load_at' (standard implementation) will be written back to 'dst'.
// As 'base' is required for the C-call, it must be reserved in case of a register clash.
Register saved_base = base;
if (base == dst) {
__ mr(tmp2, base);
saved_base = tmp2;
}
__ ld(dst, ind_or_offs, base);
/* ==== Check whether pointer is dirty ==== */
Label done, uncolor;
const bool on_non_strong =
(decorators & ON_WEAK_OOP_REF) != 0 ||
(decorators & ON_PHANTOM_OOP_REF) != 0;
// Load bad mask into scratch register.
if (on_non_strong) {
__ ld(tmp1, in_bytes(ZThreadLocalData::mark_bad_mask_offset()), R16_thread);
} else {
__ ld(tmp1, in_bytes(ZThreadLocalData::load_bad_mask_offset()), R16_thread);
}
// The color bits of the to-be-tested pointer do not have to be equivalent to the 'bad_mask' testing bits.
// A pointer is classified as dirty if any of the color bits that also match the bad mask is set.
// Conversely, it follows that the logical AND of the bad mask and the pointer must be zero
// if the pointer is not dirty.
// Only dirty pointers must be processed by this barrier, so we can skip it in case the latter condition holds true.
__ and_(tmp1, tmp1, dst);
__ beq(CR0, uncolor);
/* ==== Invoke barrier ==== */
{
ZRuntimeCallSpill rcs(masm, dst, preservation_level);
// Setup arguments
if (saved_base != R3_ARG1 && ind_or_offs.register_or_noreg() != R3_ARG1) {
__ mr_if_needed(R3_ARG1, dst);
__ add(R4_ARG2, ind_or_offs, saved_base);
} else if (dst != R4_ARG2) {
__ add(R4_ARG2, ind_or_offs, saved_base);
__ mr(R3_ARG1, dst);
} else {
__ add(R0, ind_or_offs, saved_base);
__ mr(R3_ARG1, dst);
__ mr(R4_ARG2, R0);
}
__ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators));
}
// Slow-path has already uncolored
if (L_handle_null != nullptr) {
__ cmpdi(CR0, dst, 0);
__ beq(CR0, *L_handle_null);
}
__ b(done);
__ bind(uncolor);
if (L_handle_null == nullptr) {
__ srdi(dst, dst, ZPointerLoadShift);
} else {
__ srdi_(dst, dst, ZPointerLoadShift);
__ beq(CR0, *L_handle_null);
}
__ bind(done);
__ block_comment("} load_at (zgc)");
}
static void load_least_significant_16_oop_bits(MacroAssembler* masm, Register dst, RegisterOrConstant ind_or_offs, Register base) {
assert_different_registers(dst, base);
#ifndef VM_LITTLE_ENDIAN
const int BE_offset = 6;
if (ind_or_offs.is_register()) {
__ addi(dst, ind_or_offs.as_register(), BE_offset);
__ lhzx(dst, base, dst);
} else {
__ lhz(dst, ind_or_offs.as_constant() + BE_offset, base);
}
#else
__ lhz(dst, ind_or_offs, base);
#endif
}
static void emit_store_fast_path_check(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, bool is_atomic, Label& medium_path) {
if (is_atomic) {
assert(ZPointerLoadShift + LogMinObjAlignmentInBytes >= 16, "or replace following code");
load_least_significant_16_oop_bits(masm, R0, ind_or_offs, base);
// Atomic operations must ensure that the contents of memory are store-good before
// an atomic operation can execute.
// A not relocatable object could have spurious raw null pointers in its fields after
// getting promoted to the old generation.
__ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBits);
__ cmplwi(CR0, R0, barrier_Relocation::unpatched);
} else {
__ ld(R0, ind_or_offs, base);
// Stores on relocatable objects never need to deal with raw null pointers in fields.
// Raw null pointers may only exist in the young generation, as they get pruned when
// the object is relocated to old. And no pre-write barrier needs to perform any action
// in the young generation.
__ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreBadMask);
__ andi_(R0, R0, barrier_Relocation::unpatched);
}
__ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CR0, Assembler::equal), medium_path);
}
void ZBarrierSetAssembler::store_barrier_fast(MacroAssembler* masm,
Register ref_base,
RegisterOrConstant ind_or_offset,
Register rnew_zaddress,
Register rnew_zpointer,
bool in_nmethod,
bool is_atomic,
Label& medium_path,
Label& medium_path_continuation) const {
assert_different_registers(ref_base, rnew_zpointer);
assert_different_registers(ind_or_offset.register_or_noreg(), rnew_zpointer);
assert_different_registers(rnew_zaddress, rnew_zpointer);
if (in_nmethod) {
emit_store_fast_path_check(masm, ref_base, ind_or_offset, is_atomic, medium_path);
__ bind(medium_path_continuation);
__ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBits);
__ li(rnew_zpointer, barrier_Relocation::unpatched); // Load color bits.
if (rnew_zaddress == noreg) { // noreg encodes null.
if (ZPointerLoadShift >= 16) {
__ rldicl(rnew_zpointer, rnew_zpointer, 0, 64 - ZPointerLoadShift); // Clear sign extension from li.
}
}
} else {
__ ld(R0, ind_or_offset, ref_base);
__ ld(rnew_zpointer, in_bytes(ZThreadLocalData::store_bad_mask_offset()), R16_thread);
__ and_(R0, R0, rnew_zpointer);
__ bne(CR0, medium_path);
__ bind(medium_path_continuation);
__ ld(rnew_zpointer, in_bytes(ZThreadLocalData::store_good_mask_offset()), R16_thread);
}
if (rnew_zaddress != noreg) { // noreg encodes null.
__ rldimi(rnew_zpointer, rnew_zaddress, ZPointerLoadShift, 0); // Insert shifted pointer.
}
}
static void store_barrier_buffer_add(MacroAssembler* masm,
Register ref_base,
RegisterOrConstant ind_or_offs,
Register tmp1,
Label& slow_path) {
__ ld(tmp1, in_bytes(ZThreadLocalData::store_barrier_buffer_offset()), R16_thread);
// Combined pointer bump and check if the buffer is disabled or full
__ ld(R0, in_bytes(ZStoreBarrierBuffer::current_offset()), tmp1);
__ addic_(R0, R0, -(int)sizeof(ZStoreBarrierEntry));
__ blt(CR0, slow_path);
__ std(R0, in_bytes(ZStoreBarrierBuffer::current_offset()), tmp1);
// Entry is at ZStoreBarrierBuffer (tmp1) + buffer_offset + scaled index (R0)
__ add(tmp1, tmp1, R0);
// Compute and log the store address
Register store_addr = ref_base;
if (!ind_or_offs.is_constant() || ind_or_offs.as_constant() != 0) {
__ add(R0, ind_or_offs, ref_base);
store_addr = R0;
}
__ std(store_addr, in_bytes(ZStoreBarrierBuffer::buffer_offset()) + in_bytes(ZStoreBarrierEntry::p_offset()), tmp1);
// Load and log the prev value
__ ld(R0, ind_or_offs, ref_base);
__ std(R0, in_bytes(ZStoreBarrierBuffer::buffer_offset()) + in_bytes(ZStoreBarrierEntry::prev_offset()), tmp1);
}
void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm,
Register ref_base,
RegisterOrConstant ind_or_offs,
Register tmp,
bool is_atomic,
Label& medium_path_continuation,
Label& slow_path) const {
assert_different_registers(ref_base, tmp, R0);
// The reason to end up in the medium path is that the pre-value was not 'good'.
if (is_atomic) {
// Atomic accesses can get to the medium fast path because the value was a
// raw null value. If it was not null, then there is no doubt we need to take a slow path.
__ ld(tmp, ind_or_offs, ref_base);
__ cmpdi(CR0, tmp, 0);
__ bne(CR0, slow_path);
// If we get this far, we know there is a young raw null value in the field.
// Try to self-heal null values for atomic accesses
bool need_restore = false;
if (!ind_or_offs.is_constant() || ind_or_offs.as_constant() != 0) {
__ add(ref_base, ref_base, ind_or_offs);
need_restore = true;
}
__ ld(R0, in_bytes(ZThreadLocalData::store_good_mask_offset()), R16_thread);
__ cmpxchgd(CR0, tmp, (intptr_t)0, R0, ref_base,
MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(),
noreg, need_restore ? nullptr : &slow_path);
if (need_restore) {
__ sub(ref_base, ref_base, ind_or_offs);
__ bne(CR0, slow_path);
}
} else {
// A non-atomic relocatable object won't get to the medium fast path due to a
// raw null in the young generation. We only get here because the field is bad.
// In this path we don't need any self healing, so we can avoid a runtime call
// most of the time by buffering the store barrier to be applied lazily.
store_barrier_buffer_add(masm,
ref_base,
ind_or_offs,
tmp,
slow_path);
}
__ b(medium_path_continuation);
}
// The Z store barrier only verifies the pointers it is operating on and is thus a sole debugging measure.
void ZBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
Register base, RegisterOrConstant ind_or_offs, Register val,
Register tmp1, Register tmp2, Register tmp3,
MacroAssembler::PreservationLevel preservation_level) {
__ block_comment("store_at (zgc) {");
bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
if (is_reference_type(type)) {
assert_different_registers(base, val, tmp1, tmp2, tmp3);
if (dest_uninitialized) {
// tmp1 = (val << ZPointerLoadShift) | store_good_mask
__ ld(tmp1, in_bytes(ZThreadLocalData::store_good_mask_offset()), R16_thread);
if (val != noreg) { // noreg encodes null.
__ rldimi(tmp1, val, ZPointerLoadShift, 0);
}
} else {
Label done;
Label medium;
Label medium_continuation; // bound in store_barrier_fast
Label slow;
store_barrier_fast(masm, base, ind_or_offs, val, tmp1, false, false, medium, medium_continuation);
__ b(done);
__ bind(medium);
store_barrier_medium(masm, base, ind_or_offs, tmp1, false, medium_continuation, slow);
__ bind(slow);
{
ZRuntimeCallSpill rcs(masm, noreg, preservation_level);
__ add(R3_ARG1, ind_or_offs, base);
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), R3_ARG1);
}
__ b(medium_continuation);
__ bind(done);
}
BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, tmp1, tmp2, tmp3, noreg, preservation_level);
} else {
BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, val, tmp1, tmp2, tmp3, preservation_level);
}
__ block_comment("} store_at (zgc)");
}
/* arraycopy */
const Register _load_bad_mask = R6, _store_bad_mask = R7, _store_good_mask = R8;
void ZBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, DecoratorSet decorators, BasicType type,
Register src, Register dst, Register count,
Register preserve1, Register preserve2) {
bool is_checkcast_copy = (decorators & ARRAYCOPY_CHECKCAST) != 0,
dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
if (!ZBarrierSet::barrier_needed(decorators, type) || is_checkcast_copy) {
// Barrier not needed
return;
}
__ block_comment("arraycopy_prologue (zgc) {");
load_copy_masks(masm, _load_bad_mask, _store_bad_mask, _store_good_mask, dest_uninitialized);
__ block_comment("} arraycopy_prologue (zgc)");
}
void ZBarrierSetAssembler::load_copy_masks(MacroAssembler* masm,
Register load_bad_mask,
Register store_bad_mask,
Register store_good_mask,
bool dest_uninitialized) const {
__ ld(load_bad_mask, in_bytes(ZThreadLocalData::load_bad_mask_offset()), R16_thread);
__ ld(store_good_mask, in_bytes(ZThreadLocalData::store_good_mask_offset()), R16_thread);
if (dest_uninitialized) {
DEBUG_ONLY( __ li(store_bad_mask, -1); )
} else {
__ ld(store_bad_mask, in_bytes(ZThreadLocalData::store_bad_mask_offset()), R16_thread);
}
}
void ZBarrierSetAssembler::copy_load_at_fast(MacroAssembler* masm,
Register zpointer,
Register addr,
Register load_bad_mask,
Label& slow_path,
Label& continuation) const {
__ ldx(zpointer, addr);
__ and_(R0, zpointer, load_bad_mask);
__ bne(CR0, slow_path);
__ bind(continuation);
}
void ZBarrierSetAssembler::copy_load_at_slow(MacroAssembler* masm,
Register zpointer,
Register addr,
Register tmp,
Label& slow_path,
Label& continuation) const {
__ align(32);
__ bind(slow_path);
__ mfctr(tmp); // preserve loop counter
{
ZRuntimeCallSpill rcs(masm, R0, MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS);
assert(zpointer != R4_ARG2, "or change argument setup");
__ mr_if_needed(R4_ARG2, addr);
__ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(), zpointer, R4_ARG2);
}
__ sldi(zpointer, R0, ZPointerLoadShift); // Slow-path has uncolored; revert
__ mtctr(tmp); // restore loop counter
__ b(continuation);
}
void ZBarrierSetAssembler::copy_store_at_fast(MacroAssembler* masm,
Register zpointer,
Register addr,
Register store_bad_mask,
Register store_good_mask,
Label& medium_path,
Label& continuation,
bool dest_uninitialized) const {
if (!dest_uninitialized) {
__ ldx(R0, addr);
__ and_(R0, R0, store_bad_mask);
__ bne(CR0, medium_path);
__ bind(continuation);
}
__ rldimi(zpointer, store_good_mask, 0, 64 - ZPointerLoadShift); // Replace color bits.
__ stdx(zpointer, addr);
}
void ZBarrierSetAssembler::copy_store_at_slow(MacroAssembler* masm,
Register addr,
Register tmp,
Label& medium_path,
Label& continuation,
bool dest_uninitialized) const {
if (!dest_uninitialized) {
Label slow_path;
__ align(32);
__ bind(medium_path);
store_barrier_medium(masm, addr, (intptr_t)0, tmp, false, continuation, slow_path);
__ bind(slow_path);
__ mfctr(tmp); // preserve loop counter
{
ZRuntimeCallSpill rcs(masm, noreg, MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS);
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), addr);
}
__ mtctr(tmp); // restore loop counter
__ b(continuation);
}
}
// Arguments for generated stub:
// from: R3_ARG1
// to: R4_ARG2
// count: R5_ARG3 (int >= 0)
void ZBarrierSetAssembler::generate_disjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized) {
const Register zpointer = R2, tmp = R9;
Label done, loop, load_bad, load_good, store_bad, store_good;
__ cmpdi(CR0, R5_ARG3, 0);
__ beq(CR0, done);
__ mtctr(R5_ARG3);
__ align(32);
__ bind(loop);
copy_load_at_fast(masm, zpointer, R3_ARG1, _load_bad_mask, load_bad, load_good);
copy_store_at_fast(masm, zpointer, R4_ARG2, _store_bad_mask, _store_good_mask, store_bad, store_good, dest_uninitialized);
__ addi(R3_ARG1, R3_ARG1, 8);
__ addi(R4_ARG2, R4_ARG2, 8);
__ bdnz(loop);
__ bind(done);
__ li(R3_RET, 0);
__ blr();
copy_load_at_slow(masm, zpointer, R3_ARG1, tmp, load_bad, load_good);
copy_store_at_slow(masm, R4_ARG2, tmp, store_bad, store_good, dest_uninitialized);
}
void ZBarrierSetAssembler::generate_conjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized) {
const Register zpointer = R2, tmp = R9;
Label done, loop, load_bad, load_good, store_bad, store_good;
__ sldi_(R0, R5_ARG3, 3);
__ beq(CR0, done);
__ mtctr(R5_ARG3);
// Point behind last elements and copy backwards.
__ add(R3_ARG1, R3_ARG1, R0);
__ add(R4_ARG2, R4_ARG2, R0);
__ align(32);
__ bind(loop);
__ addi(R3_ARG1, R3_ARG1, -8);
__ addi(R4_ARG2, R4_ARG2, -8);
copy_load_at_fast(masm, zpointer, R3_ARG1, _load_bad_mask, load_bad, load_good);
copy_store_at_fast(masm, zpointer, R4_ARG2, _store_bad_mask, _store_good_mask, store_bad, store_good, dest_uninitialized);
__ bdnz(loop);
__ bind(done);
__ li(R3_RET, 0);
__ blr();
copy_load_at_slow(masm, zpointer, R3_ARG1, tmp, load_bad, load_good);
copy_store_at_slow(masm, R4_ARG2, tmp, store_bad, store_good, dest_uninitialized);
}
// Verify a colored pointer.
void ZBarrierSetAssembler::check_oop(MacroAssembler *masm, Register obj, const char* msg) {
if (!VerifyOops) {
return;
}
Label done, skip_uncolor;
// Skip (colored) null.
__ srdi_(R0, obj, ZPointerLoadShift);
__ beq(CR0, done);
// Check if ZAddressHeapBase << ZPointerLoadShift is set. If so, we need to uncolor.
__ rldicl_(R0, obj, 64 - ZAddressHeapBaseShift - ZPointerLoadShift, 63);
__ mr(R0, obj);
__ beq(CR0, skip_uncolor);
__ srdi(R0, obj, ZPointerLoadShift);
__ bind(skip_uncolor);
__ verify_oop(R0, msg);
__ bind(done);
}
void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env,
Register obj, Register tmp, Label& slowpath) {
__ block_comment("try_resolve_jobject_in_native (zgc) {");
Label done, tagged, weak_tagged, check_color;
Address load_bad_mask = load_bad_mask_from_jni_env(jni_env),
mark_bad_mask = mark_bad_mask_from_jni_env(jni_env);
// Test for tag
__ andi_(tmp, obj, JNIHandles::tag_mask);
__ bne(CR0, tagged);
// Resolve local handle
__ ld(dst, 0, obj);
__ b(done);
__ bind(tagged);
// Test for weak tag
__ andi_(tmp, obj, JNIHandles::TypeTag::weak_global);
__ clrrdi(dst, obj, JNIHandles::tag_size); // Untag.
__ bne(CR0, weak_tagged);
// Resolve global handle
__ ld(dst, 0, dst);
__ ld(tmp, load_bad_mask);
__ b(check_color);
__ bind(weak_tagged);
// Resolve weak handle
__ ld(dst, 0, dst);
__ ld(tmp, mark_bad_mask);
__ bind(check_color);
__ and_(tmp, tmp, dst);
__ bne(CR0, slowpath);
// Uncolor
__ srdi(dst, dst, ZPointerLoadShift);
__ bind(done);
__ block_comment("} try_resolve_jobject_in_native (zgc)");
}
#undef __
#ifdef COMPILER1
#define __ ce->masm()->
static void z_uncolor(LIR_Assembler* ce, LIR_Opr ref) {
Register r = ref->as_register();
__ srdi(r, r, ZPointerLoadShift);
}
static void check_color(LIR_Assembler* ce, LIR_Opr ref, bool on_non_strong) {
int relocFormat = on_non_strong ? ZBarrierRelocationFormatMarkBadMask
: ZBarrierRelocationFormatLoadBadMask;
__ relocate(barrier_Relocation::spec(), relocFormat);
__ andi_(R0, ref->as_register(), barrier_Relocation::unpatched);
}
static void z_color(LIR_Assembler* ce, LIR_Opr ref) {
__ sldi(ref->as_register(), ref->as_register(), ZPointerLoadShift);
__ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBits);
__ ori(ref->as_register(), ref->as_register(), barrier_Relocation::unpatched);
}
void ZBarrierSetAssembler::generate_c1_uncolor(LIR_Assembler* ce, LIR_Opr ref) const {
z_uncolor(ce, ref);
}
void ZBarrierSetAssembler::generate_c1_color(LIR_Assembler* ce, LIR_Opr ref) const {
z_color(ce, ref);
}
void ZBarrierSetAssembler::generate_c1_load_barrier(LIR_Assembler* ce,
LIR_Opr ref,
ZLoadBarrierStubC1* stub,
bool on_non_strong) const {
check_color(ce, ref, on_non_strong);
__ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CR0, Assembler::equal), *stub->entry());
z_uncolor(ce, ref);
__ bind(*stub->continuation());
}
// Code emitted by code stub "ZLoadBarrierStubC1" which in turn is emitted by ZBarrierSetC1::load_barrier.
// Invokes the runtime stub which is defined just below.
void ZBarrierSetAssembler::generate_c1_load_barrier_stub(LIR_Assembler* ce,
ZLoadBarrierStubC1* stub) const {
__ block_comment("c1_load_barrier_stub (zgc) {");
__ bind(*stub->entry());
/* ==== Determine relevant data registers and ensure register sanity ==== */
Register ref = stub->ref()->as_register();
Register ref_addr = noreg;
// Determine reference address
if (stub->tmp()->is_valid()) {
// 'tmp' register is given, so address might have an index or a displacement.
ce->leal(stub->ref_addr(), stub->tmp());
ref_addr = stub->tmp()->as_pointer_register();
} else {
// 'tmp' register is not given, so address must have neither an index nor a displacement.
// The address' base register is thus usable as-is.
assert(stub->ref_addr()->as_address_ptr()->disp() == 0, "illegal displacement");
assert(!stub->ref_addr()->as_address_ptr()->index()->is_valid(), "illegal index");
ref_addr = stub->ref_addr()->as_address_ptr()->base()->as_pointer_register();
}
assert_different_registers(ref, ref_addr, R0, noreg);
/* ==== Invoke stub ==== */
// Pass arguments via stack. The stack pointer will be bumped by the stub.
__ std(ref, -1 * BytesPerWord, R1_SP);
__ std(ref_addr, -2 * BytesPerWord, R1_SP);
__ load_const_optimized(R0, stub->runtime_stub(), /* temp */ ref);
__ call_stub(R0);
// The runtime stub passes the result via the R0 register, overriding the previously-loaded stub address.
__ mr(ref, R0);
__ b(*stub->continuation());
__ block_comment("} c1_load_barrier_stub (zgc)");
}
void ZBarrierSetAssembler::generate_c1_store_barrier(LIR_Assembler* ce,
LIR_Address* addr,
LIR_Opr new_zaddress,
LIR_Opr new_zpointer,
ZStoreBarrierStubC1* stub) const {
Register rnew_zaddress = new_zaddress->as_register();
Register rnew_zpointer = new_zpointer->as_register();
Register rbase = addr->base()->as_pointer_register();
RegisterOrConstant ind_or_offs = (addr->index()->is_illegal())
? (RegisterOrConstant)addr->disp()
: (RegisterOrConstant)addr->index()->as_pointer_register();
store_barrier_fast(ce->masm(),
rbase,
ind_or_offs,
rnew_zaddress,
rnew_zpointer,
true,
stub->is_atomic(),
*stub->entry(),
*stub->continuation());
}
void ZBarrierSetAssembler::generate_c1_store_barrier_stub(LIR_Assembler* ce,
ZStoreBarrierStubC1* stub) const {
// Stub entry
__ bind(*stub->entry());
Label slow;
LIR_Address* addr = stub->ref_addr()->as_address_ptr();
assert(addr->index()->is_illegal() || addr->disp() == 0, "can't have both");
Register rbase = addr->base()->as_pointer_register();
RegisterOrConstant ind_or_offs = (addr->index()->is_illegal())
? (RegisterOrConstant)addr->disp()
: (RegisterOrConstant)addr->index()->as_pointer_register();
Register new_zpointer = stub->new_zpointer()->as_register();
store_barrier_medium(ce->masm(),
rbase,
ind_or_offs,
new_zpointer, // temp
stub->is_atomic(),
*stub->continuation(),
slow);
__ bind(slow);
__ load_const_optimized(/*stub address*/ new_zpointer, stub->runtime_stub(), R0);
__ add(R0, ind_or_offs, rbase); // pass store address in R0
__ mtctr(new_zpointer);
__ bctrl();
// Stub exit
__ b(*stub->continuation());
}
#undef __
#define __ sasm->
// Code emitted by runtime code stub which in turn is emitted by ZBarrierSetC1::generate_c1_runtime_stubs.
void ZBarrierSetAssembler::generate_c1_load_barrier_runtime_stub(StubAssembler* sasm,
DecoratorSet decorators) const {
__ block_comment("c1_load_barrier_runtime_stub (zgc) {");
const int stack_parameters = 2;
const int nbytes_save = (MacroAssembler::num_volatile_regs + stack_parameters) * BytesPerWord;
__ save_volatile_gprs(R1_SP, -nbytes_save);
__ save_LR(R0);
// Load arguments back again from the stack.
__ ld(R3_ARG1, -1 * BytesPerWord, R1_SP); // ref
__ ld(R4_ARG2, -2 * BytesPerWord, R1_SP); // ref_addr
__ push_frame_reg_args(nbytes_save, R0);
__ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators));
__ verify_oop(R3_RET, "Bad pointer after barrier invocation");
__ mr(R0, R3_RET);
__ pop_frame();
__ restore_LR(R3_RET);
__ restore_volatile_gprs(R1_SP, -nbytes_save);
__ blr();
__ block_comment("} c1_load_barrier_runtime_stub (zgc)");
}
void ZBarrierSetAssembler::generate_c1_store_barrier_runtime_stub(StubAssembler* sasm,
bool self_healing) const {
__ block_comment("c1_store_barrier_runtime_stub (zgc) {");
const int nbytes_save = MacroAssembler::num_volatile_regs * BytesPerWord;
__ save_volatile_gprs(R1_SP, -nbytes_save);
__ mr(R3_ARG1, R0); // store address
__ save_LR(R0);
__ push_frame_reg_args(nbytes_save, R0);
if (self_healing) {
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr());
} else {
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr());
}
__ pop_frame();
__ restore_LR(R3_RET);
__ restore_volatile_gprs(R1_SP, -nbytes_save);
__ blr();
__ block_comment("} c1_store_barrier_runtime_stub (zgc)");
}
#undef __
#endif // COMPILER1
#ifdef COMPILER2
#undef __
#define __ _masm->
class ZSetupArguments {
MacroAssembler* const _masm;
const Register _ref;
const Address _ref_addr;
public:
ZSetupArguments(MacroAssembler* masm, ZLoadBarrierStubC2* stub)
: _masm(masm),
_ref(stub->ref()),
_ref_addr(stub->ref_addr()) {
// Desired register/argument configuration:
// _ref: R3_ARG1
// _ref_addr: R4_ARG2
// '_ref_addr' can be unspecified. In that case, the barrier will not heal the reference.
if (_ref_addr.base() == noreg) {
assert_different_registers(_ref, R0, noreg);
__ mr_if_needed(R3_ARG1, _ref);
__ li(R4_ARG2, 0);
} else {
assert_different_registers(_ref, _ref_addr.base(), R0, noreg);
assert(!_ref_addr.index()->is_valid(), "reference addresses must not contain an index component");
if (_ref != R4_ARG2) {
// Calculate address first as the address' base register might clash with R4_ARG2
__ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp());
__ mr_if_needed(R3_ARG1, _ref);
} else if (_ref_addr.base() != R3_ARG1) {
__ mr(R3_ARG1, _ref);
__ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp()); // Clobbering _ref
} else {
// Arguments are provided in inverse order (i.e. _ref == R4_ARG2, _ref_addr == R3_ARG1)
__ mr(R0, _ref);
__ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp());
__ mr(R3_ARG1, R0);
}
}
}
};
#undef __
#define __ masm->
void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const {
Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
__ block_comment("generate_c2_load_barrier_stub (zgc) {");
__ bind(*stub->entry());
Register ref = stub->ref();
Address ref_addr = stub->ref_addr();
assert_different_registers(ref, ref_addr.base());
{
SaveLiveRegisters save_live_registers(masm, stub);
ZSetupArguments setup_arguments(masm, stub);
__ call_VM_leaf(stub->slow_path());
__ mr_if_needed(ref, R3_RET);
}
__ b(*stub->continuation());
__ block_comment("} generate_c2_load_barrier_stub (zgc)");
}
void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const {
Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
__ block_comment("ZStoreBarrierStubC2");
// Stub entry
__ bind(*stub->entry());
Label slow;
Address addr = stub->ref_addr();
Register rbase = addr.base();
RegisterOrConstant ind_or_offs = (addr.index() == noreg)
? (RegisterOrConstant)addr.disp()
: (RegisterOrConstant)addr.index();
if (!stub->is_native()) {
store_barrier_medium(masm,
rbase,
ind_or_offs,
stub->new_zpointer(),
stub->is_atomic(),
*stub->continuation(),
slow);
}
__ bind(slow);
{
SaveLiveRegisters save_live_registers(masm, stub);
__ add(R3_ARG1, ind_or_offs, rbase);
if (stub->is_native()) {
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr(), R3_ARG1);
} else if (stub->is_atomic()) {
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr(), R3_ARG1);
} else if (stub->is_nokeepalive()) {
__ call_VM_leaf(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr(), R3_ARG1);
} else {
__ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), R3_ARG1);
}
}
// Stub exit
__ b(*stub->continuation());
}
#undef __
#endif // COMPILER2
static uint16_t patch_barrier_relocation_value(int format) {
switch (format) {
case ZBarrierRelocationFormatLoadBadMask:
return (uint16_t)ZPointerLoadBadMask;
case ZBarrierRelocationFormatMarkBadMask:
return (uint16_t)ZPointerMarkBadMask;
case ZBarrierRelocationFormatStoreGoodBits:
return (uint16_t)ZPointerStoreGoodMask;
case ZBarrierRelocationFormatStoreBadMask:
return (uint16_t)ZPointerStoreBadMask;
default:
ShouldNotReachHere();
return 0;
}
}
void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) {
#ifdef ASSERT
int inst = *(int*)addr;
if (format == ZBarrierRelocationFormatStoreGoodBits) {
assert(Assembler::is_li(inst) || Assembler::is_ori(inst) || Assembler::is_cmpli(inst),
"unexpected instruction 0x%04x", inst);
// Note: li uses sign extend, but these bits will get cleared by rldimi.
} else {
assert(Assembler::is_andi(inst), "unexpected instruction 0x%04x", inst);
}
#endif
// Patch the signed/unsigned 16 bit immediate field of the instruction.
*(uint16_t*)(addr BIG_ENDIAN_ONLY(+2)) = patch_barrier_relocation_value(format);
ICache::ppc64_flush_icache_bytes(addr, BytesPerInstWord);
}