Path: blob/master/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp
66646 views
/*1* Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.2* Copyright (c) 2012, 2021 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "gc/shared/gcArguments.hpp"26#include "gc/shared/gc_globals.hpp"27#include "macroAssembler_ppc.hpp"28#include "precompiled.hpp"29#include "asm/macroAssembler.inline.hpp"30#include "gc/shenandoah/shenandoahBarrierSet.hpp"31#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"32#include "gc/shenandoah/shenandoahForwarding.hpp"33#include "gc/shenandoah/shenandoahHeap.hpp"34#include "gc/shenandoah/shenandoahHeap.inline.hpp"35#include "gc/shenandoah/shenandoahHeapRegion.hpp"36#include "gc/shenandoah/shenandoahRuntime.hpp"37#include "gc/shenandoah/shenandoahThreadLocalData.hpp"38#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"39#include "interpreter/interpreter.hpp"40#include "runtime/sharedRuntime.hpp"41#include "runtime/thread.hpp"42#include "utilities/globalDefinitions.hpp"43#include "vm_version_ppc.hpp"4445#ifdef COMPILER14647#include "c1/c1_LIRAssembler.hpp"48#include "c1/c1_MacroAssembler.hpp"49#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp"5051#endif5253#define __ masm->5455void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm,56Register base, RegisterOrConstant ind_or_offs,57Register tmp1, Register tmp2, Register tmp3,58MacroAssembler::PreservationLevel preservation_level) {59if (ShenandoahSATBBarrier) {60__ block_comment("satb_write_barrier (shenandoahgc) {");61satb_write_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level);62__ block_comment("} satb_write_barrier (shenandoahgc)");63}64}6566void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler *masm,67Register val,68Register tmp1, Register tmp2,69MacroAssembler::PreservationLevel preservation_level,70DecoratorSet decorators) {71// IU barriers are also employed to avoid resurrection of weak references,72// even if Shenandoah does not operate in incremental update mode.73if (ShenandoahIUBarrier || ShenandoahSATBBarrier) {74__ block_comment("iu_barrier (shenandoahgc) {");75satb_write_barrier_impl(masm, decorators, noreg, noreg, val, tmp1, tmp2, preservation_level);76__ block_comment("} iu_barrier (shenandoahgc)");77}78}7980void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, DecoratorSet decorators,81Register base, RegisterOrConstant ind_or_offs,82Register dst,83Register tmp1, Register tmp2,84MacroAssembler::PreservationLevel preservation_level) {85if (ShenandoahLoadRefBarrier) {86__ block_comment("load_reference_barrier (shenandoahgc) {");87load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level);88__ block_comment("} load_reference_barrier (shenandoahgc)");89}90}9192void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, DecoratorSet decorators, BasicType type,93Register src, Register dst, Register count,94Register preserve1, Register preserve2) {95__ block_comment("arraycopy_prologue (shenandoahgc) {");9697Register R11_tmp = R11_scratch1;9899assert_different_registers(src, dst, count, R11_tmp, noreg);100if (preserve1 != noreg) {101// Technically not required, but likely to indicate an error.102assert_different_registers(preserve1, preserve2);103}104105/* ==== Check whether barrier is required (optimizations) ==== */106// Fast path: Component type of array is not a reference type.107if (!is_reference_type(type)) {108return;109}110111bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;112113// Fast path: No barrier required if for every barrier type, it is either disabled or would not store114// any useful information.115if ((!ShenandoahSATBBarrier || dest_uninitialized) && !ShenandoahIUBarrier && !ShenandoahLoadRefBarrier) {116return;117}118119Label skip_prologue;120121// Fast path: Array is of length zero.122__ cmpdi(CCR0, count, 0);123__ beq(CCR0, skip_prologue);124125/* ==== Check whether barrier is required (gc state) ==== */126__ lbz(R11_tmp, in_bytes(ShenandoahThreadLocalData::gc_state_offset()),127R16_thread);128129// The set of garbage collection states requiring barriers depends on the available barrier types and the130// type of the reference in question.131// For instance, satb barriers may be skipped if it is certain that the overridden values are not relevant132// for the garbage collector.133const int required_states = ShenandoahSATBBarrier && dest_uninitialized134? ShenandoahHeap::HAS_FORWARDED135: ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING;136137__ andi_(R11_tmp, R11_tmp, required_states);138__ beq(CCR0, skip_prologue);139140/* ==== Invoke runtime ==== */141// Save to-be-preserved registers.142int highest_preserve_register_index = 0;143{144if (preserve1 != noreg && preserve1->is_volatile()) {145__ std(preserve1, -BytesPerWord * ++highest_preserve_register_index, R1_SP);146}147if (preserve2 != noreg && preserve2 != preserve1 && preserve2->is_volatile()) {148__ std(preserve2, -BytesPerWord * ++highest_preserve_register_index, R1_SP);149}150151__ std(src, -BytesPerWord * ++highest_preserve_register_index, R1_SP);152__ std(dst, -BytesPerWord * ++highest_preserve_register_index, R1_SP);153__ std(count, -BytesPerWord * ++highest_preserve_register_index, R1_SP);154155__ save_LR_CR(R11_tmp);156__ push_frame_reg_args(-BytesPerWord * highest_preserve_register_index,157R11_tmp);158}159160// Invoke runtime.161address jrt_address = NULL;162if (UseCompressedOops) {163jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry);164} else {165jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry);166}167assert(jrt_address != nullptr, "jrt routine cannot be found");168169__ call_VM_leaf(jrt_address, src, dst, count);170171// Restore to-be-preserved registers.172{173__ pop_frame();174__ restore_LR_CR(R11_tmp);175176__ ld(count, -BytesPerWord * highest_preserve_register_index--, R1_SP);177__ ld(dst, -BytesPerWord * highest_preserve_register_index--, R1_SP);178__ ld(src, -BytesPerWord * highest_preserve_register_index--, R1_SP);179180if (preserve2 != noreg && preserve2 != preserve1 && preserve2->is_volatile()) {181__ ld(preserve2, -BytesPerWord * highest_preserve_register_index--, R1_SP);182}183if (preserve1 != noreg && preserve1->is_volatile()) {184__ ld(preserve1, -BytesPerWord * highest_preserve_register_index--, R1_SP);185}186}187188__ bind(skip_prologue);189__ block_comment("} arraycopy_prologue (shenandoahgc)");190}191192// The to-be-enqueued value can either be determined193// - dynamically by passing the reference's address information (load mode) or194// - statically by passing a register the value is stored in (preloaded mode)195// - for performance optimizations in cases where the previous value is known (currently not implemented) and196// - for incremental-update barriers.197//198// decorators: The previous value's decorator set.199// In "load mode", the value must equal '0'.200// base: Base register of the reference's address (load mode).201// In "preloaded mode", the register must equal 'noreg'.202// ind_or_offs: Index or offset of the reference's address (load mode).203// If 'base' equals 'noreg' (preloaded mode), the passed value is ignored.204// pre_val: Register holding the to-be-stored value (preloaded mode).205// In "load mode", this register acts as a temporary register and must206// thus not be 'noreg'. In "preloaded mode", its content will be sustained.207// tmp1/tmp2: Temporary registers, one of which must be non-volatile in "preloaded mode".208void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm, DecoratorSet decorators,209Register base, RegisterOrConstant ind_or_offs,210Register pre_val,211Register tmp1, Register tmp2,212MacroAssembler::PreservationLevel preservation_level) {213assert_different_registers(tmp1, tmp2, pre_val, noreg);214215Label skip_barrier;216217/* ==== Determine necessary runtime invocation preservation measures ==== */218const bool needs_frame = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR;219const bool preserve_gp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS;220const bool preserve_fp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS;221222// Check whether marking is active.223__ lbz(tmp1, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread);224225__ andi_(tmp1, tmp1, ShenandoahHeap::MARKING);226__ beq(CCR0, skip_barrier);227228/* ==== Determine the reference's previous value ==== */229bool preloaded_mode = base == noreg;230Register pre_val_save = noreg;231232if (preloaded_mode) {233// Previous value has been passed to the method, so it must not be determined manually.234// In case 'pre_val' is a volatile register, it must be saved across the C-call235// as callers may depend on its value.236// Unless the general purposes registers are saved anyway, one of the temporary registers237// (i.e., 'tmp1' and 'tmp2') is used to the preserve 'pre_val'.238if (!preserve_gp_registers && pre_val->is_volatile()) {239pre_val_save = !tmp1->is_volatile() ? tmp1 : tmp2;240assert(!pre_val_save->is_volatile(), "at least one of the temporary registers must be non-volatile");241}242243if ((decorators & IS_NOT_NULL) != 0) {244#ifdef ASSERT245__ cmpdi(CCR0, pre_val, 0);246__ asm_assert_ne("null oop is not allowed");247#endif // ASSERT248} else {249__ cmpdi(CCR0, pre_val, 0);250__ beq(CCR0, skip_barrier);251}252} else {253// Load from the reference address to determine the reference's current value (before the store is being performed).254// Contrary to the given value in "preloaded mode", it is not necessary to preserve it.255assert(decorators == 0, "decorator set must be empty");256assert(base != noreg, "base must be a register");257assert(!ind_or_offs.is_register() || ind_or_offs.as_register() != noreg, "ind_or_offs must be a register");258if (UseCompressedOops) {259__ lwz(pre_val, ind_or_offs, base);260} else {261__ ld(pre_val, ind_or_offs, base);262}263264__ cmpdi(CCR0, pre_val, 0);265__ beq(CCR0, skip_barrier);266267if (UseCompressedOops) {268__ decode_heap_oop_not_null(pre_val);269}270}271272/* ==== Try to enqueue the to-be-stored value directly into thread's local SATB mark queue ==== */273{274Label runtime;275Register Rbuffer = tmp1, Rindex = tmp2;276277// Check whether the queue has enough capacity to store another oop.278// If not, jump to the runtime to commit the buffer and to allocate a new one.279// (The buffer's index corresponds to the amount of remaining free space.)280__ ld(Rindex, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread);281__ cmpdi(CCR0, Rindex, 0);282__ beq(CCR0, runtime); // If index == 0 (buffer is full), goto runtime.283284// Capacity suffices. Decrement the queue's size by the size of one oop.285// (The buffer is filled contrary to the heap's growing direction, i.e., it is filled downwards.)286__ addi(Rindex, Rindex, -wordSize);287__ std(Rindex, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread);288289// Enqueue the previous value and skip the invocation of the runtime.290__ ld(Rbuffer, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread);291__ stdx(pre_val, Rbuffer, Rindex);292__ b(skip_barrier);293294__ bind(runtime);295}296297/* ==== Invoke runtime to commit SATB mark queue to gc and allocate a new buffer ==== */298// Save to-be-preserved registers.299int nbytes_save = 0;300301if (needs_frame) {302if (preserve_gp_registers) {303nbytes_save = (preserve_fp_registers304? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs305: MacroAssembler::num_volatile_gp_regs) * BytesPerWord;306__ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers);307}308309__ save_LR_CR(tmp1);310__ push_frame_reg_args(nbytes_save, tmp2);311}312313if (!preserve_gp_registers && preloaded_mode && pre_val->is_volatile()) {314assert(pre_val_save != noreg, "nv_save must not be noreg");315316// 'pre_val' register must be saved manually unless general-purpose are preserved in general.317__ mr(pre_val_save, pre_val);318}319320// Invoke runtime.321__ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, R16_thread);322323// Restore to-be-preserved registers.324if (!preserve_gp_registers && preloaded_mode && pre_val->is_volatile()) {325__ mr(pre_val, pre_val_save);326}327328if (needs_frame) {329__ pop_frame();330__ restore_LR_CR(tmp1);331332if (preserve_gp_registers) {333__ restore_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers);334}335}336337__ bind(skip_barrier);338}339340void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler *masm, Register dst, Register tmp) {341__ block_comment("resolve_forward_pointer_not_null (shenandoahgc) {");342343Register tmp1 = tmp,344R0_tmp2 = R0;345assert_different_registers(dst, tmp1, R0_tmp2, noreg);346347// If the object has been evacuated, the mark word layout is as follows:348// | forwarding pointer (62-bit) | '11' (2-bit) |349350// The invariant that stack/thread pointers have the lowest two bits cleared permits retrieving351// the forwarding pointer solely by inversing the lowest two bits.352// This invariant follows inevitably from hotspot's minimal alignment.353assert(markWord::marked_value <= (unsigned long) MinObjAlignmentInBytes,354"marked value must not be higher than hotspot's minimal alignment");355356Label done;357358// Load the object's mark word.359__ ld(tmp1, oopDesc::mark_offset_in_bytes(), dst);360361// Load the bit mask for the lock bits.362__ li(R0_tmp2, markWord::lock_mask_in_place);363364// Check whether all bits matching the bit mask are set.365// If that is the case, the object has been evacuated and the most significant bits form the forward pointer.366__ andc_(R0_tmp2, R0_tmp2, tmp1);367368assert(markWord::lock_mask_in_place == markWord::marked_value,369"marked value must equal the value obtained when all lock bits are being set");370if (VM_Version::has_isel()) {371__ xori(tmp1, tmp1, markWord::lock_mask_in_place);372__ isel(dst, CCR0, Assembler::equal, false, tmp1);373} else {374__ bne(CCR0, done);375__ xori(dst, tmp1, markWord::lock_mask_in_place);376}377378__ bind(done);379__ block_comment("} resolve_forward_pointer_not_null (shenandoahgc)");380}381382// base: Base register of the reference's address.383// ind_or_offs: Index or offset of the reference's address (load mode).384// dst: Reference's address. In case the object has been evacuated, this is the to-space version385// of that object.386void ShenandoahBarrierSetAssembler::load_reference_barrier_impl(387MacroAssembler *masm, DecoratorSet decorators,388Register base, RegisterOrConstant ind_or_offs,389Register dst,390Register tmp1, Register tmp2,391MacroAssembler::PreservationLevel preservation_level) {392if (ind_or_offs.is_register()) {393assert_different_registers(tmp1, tmp2, base, ind_or_offs.as_register(), dst, noreg);394} else {395assert_different_registers(tmp1, tmp2, base, dst, noreg);396}397398Label skip_barrier;399400bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);401bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);402bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);403bool is_native = ShenandoahBarrierSet::is_native_access(decorators);404bool is_narrow = UseCompressedOops && !is_native;405406/* ==== Check whether heap is stable ==== */407__ lbz(tmp2, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread);408409if (is_strong) {410// For strong references, the heap is considered stable if "has forwarded" is not active.411__ andi_(tmp1, tmp2, ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::EVACUATION);412__ beq(CCR0, skip_barrier);413#ifdef ASSERT414// "evacuation" -> (implies) "has forwarded". If we reach this code, "has forwarded" must thus be set.415__ andi_(tmp1, tmp1, ShenandoahHeap::HAS_FORWARDED);416__ asm_assert_ne("'has forwarded' is missing");417#endif // ASSERT418} else {419// For all non-strong references, the heap is considered stable if not any of "has forwarded",420// "root set processing", and "weak reference processing" is active.421// The additional phase conditions are in place to avoid the resurrection of weak references (see JDK-8266440).422Label skip_fastpath;423__ andi_(tmp1, tmp2, ShenandoahHeap::WEAK_ROOTS);424__ bne(CCR0, skip_fastpath);425426__ andi_(tmp1, tmp2, ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::EVACUATION);427__ beq(CCR0, skip_barrier);428#ifdef ASSERT429// "evacuation" -> (implies) "has forwarded". If we reach this code, "has forwarded" must thus be set.430__ andi_(tmp1, tmp1, ShenandoahHeap::HAS_FORWARDED);431__ asm_assert_ne("'has forwarded' is missing");432#endif // ASSERT433434__ bind(skip_fastpath);435}436437/* ==== Check whether region is in collection set ==== */438if (is_strong) {439// Shenandoah stores metadata on regions in a continuous area of memory in which a single byte corresponds to440// an entire region of the shenandoah heap. At present, only the least significant bit is of significance441// and indicates whether the region is part of the collection set.442//443// All regions are of the same size and are always aligned by a power of two.444// Any address can thus be shifted by a fixed number of bits to retrieve the address prefix shared by445// all objects within that region (region identification bits).446//447// | unused bits | region identification bits | object identification bits |448// (Region size depends on a couple of criteria, such as page size, user-provided arguments and the max heap size.449// The number of object identification bits can thus not be determined at compile time.)450//451// ------------------------------------------------------- <--- cs (collection set) base address452// | lost space due to heap space base address -> 'ShenandoahHeap::in_cset_fast_test_addr()'453// | (region identification bits contain heap base offset)454// |------------------------------------------------------ <--- cs base address + (heap_base >> region size shift)455// | collection set in the proper -> shift: 'region_size_bytes_shift_jint()'456// |457// |------------------------------------------------------ <--- cs base address + (heap_base >> region size shift)458// + number of regions459__ load_const_optimized(tmp2, ShenandoahHeap::in_cset_fast_test_addr(), tmp1);460__ srdi(tmp1, dst, ShenandoahHeapRegion::region_size_bytes_shift_jint());461__ lbzx(tmp2, tmp1, tmp2);462__ andi_(tmp2, tmp2, 1);463__ beq(CCR0, skip_barrier);464}465466/* ==== Invoke runtime ==== */467// Save to-be-preserved registers.468int nbytes_save = 0;469470const bool needs_frame = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR;471const bool preserve_gp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS;472const bool preserve_fp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS;473474if (needs_frame) {475if (preserve_gp_registers) {476nbytes_save = (preserve_fp_registers477? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs478: MacroAssembler::num_volatile_gp_regs) * BytesPerWord;479__ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers);480}481482__ save_LR_CR(tmp1);483__ push_frame_reg_args(nbytes_save, tmp1);484}485486// Calculate the reference's absolute address.487__ add(R4_ARG2, ind_or_offs, base);488489// Invoke runtime.490address jrt_address = nullptr;491492if (is_strong) {493if (is_narrow) {494jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);495} else {496jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);497}498} else if (is_weak) {499if (is_narrow) {500jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);501} else {502jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);503}504} else {505assert(is_phantom, "only remaining strength");506assert(!is_narrow, "phantom access cannot be narrow");507jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);508}509assert(jrt_address != nullptr, "jrt routine cannot be found");510511__ call_VM_leaf(jrt_address, dst /* reference */, R4_ARG2 /* reference address */);512513// Restore to-be-preserved registers.514if (preserve_gp_registers) {515__ mr(R0, R3_RET);516} else {517__ mr_if_needed(dst, R3_RET);518}519520if (needs_frame) {521__ pop_frame();522__ restore_LR_CR(tmp1);523524if (preserve_gp_registers) {525__ restore_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers);526__ mr(dst, R0);527}528}529530__ bind(skip_barrier);531}532533// base: Base register of the reference's address.534// ind_or_offs: Index or offset of the reference's address.535// L_handle_null: An optional label that will be jumped to if the reference is null.536void ShenandoahBarrierSetAssembler::load_at(537MacroAssembler *masm, DecoratorSet decorators, BasicType type,538Register base, RegisterOrConstant ind_or_offs, Register dst,539Register tmp1, Register tmp2,540MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null) {541// Register must not clash, except 'base' and 'dst'.542if (ind_or_offs.is_register()) {543if (base != noreg) {544assert_different_registers(tmp1, tmp2, base, ind_or_offs.register_or_noreg(), R0, noreg);545}546assert_different_registers(tmp1, tmp2, dst, ind_or_offs.register_or_noreg(), R0, noreg);547} else {548if (base == noreg) {549assert_different_registers(tmp1, tmp2, base, R0, noreg);550}551assert_different_registers(tmp1, tmp2, dst, R0, noreg);552}553554/* ==== Apply load barrier, if required ==== */555if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) {556assert(is_reference_type(type), "need_load_reference_barrier must check whether type is a reference type");557558// If 'dst' clashes with either 'base' or 'ind_or_offs', use an intermediate result register559// to keep the values of those alive until the load reference barrier is applied.560Register intermediate_dst = (dst == base || (ind_or_offs.is_register() && dst == ind_or_offs.as_register()))561? tmp2562: dst;563564BarrierSetAssembler::load_at(masm, decorators, type,565base, ind_or_offs,566intermediate_dst,567tmp1, noreg,568preservation_level, L_handle_null);569570load_reference_barrier(masm, decorators,571base, ind_or_offs,572intermediate_dst,573tmp1, R0,574preservation_level);575576__ mr_if_needed(dst, intermediate_dst);577} else {578BarrierSetAssembler::load_at(masm, decorators, type,579base, ind_or_offs,580dst,581tmp1, tmp2,582preservation_level, L_handle_null);583}584585/* ==== Apply keep-alive barrier, if required (e.g., to inhibit weak reference resurrection) ==== */586if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {587iu_barrier(masm, dst, tmp1, tmp2, preservation_level);588}589}590591// base: Base register of the reference's address.592// ind_or_offs: Index or offset of the reference's address.593// val: To-be-stored value/reference's new value.594void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet decorators, BasicType type,595Register base, RegisterOrConstant ind_or_offs, Register val,596Register tmp1, Register tmp2, Register tmp3,597MacroAssembler::PreservationLevel preservation_level) {598if (is_reference_type(type)) {599if (ShenandoahSATBBarrier) {600satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level);601}602603if (ShenandoahIUBarrier && val != noreg) {604iu_barrier(masm, val, tmp1, tmp2, preservation_level, decorators);605}606}607608BarrierSetAssembler::store_at(masm, decorators, type,609base, ind_or_offs,610val,611tmp1, tmp2, tmp3,612preservation_level);613}614615void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler *masm,616Register dst, Register jni_env, Register obj,617Register tmp, Label &slowpath) {618__ block_comment("try_resolve_jobject_in_native (shenandoahgc) {");619620assert_different_registers(jni_env, obj, tmp);621622Label done;623624// Fast path: Reference is null (JNI tags are zero for null pointers).625__ cmpdi(CCR0, obj, 0);626__ beq(CCR0, done);627628// Resolve jobject using standard implementation.629BarrierSetAssembler::try_resolve_jobject_in_native(masm, dst, jni_env, obj, tmp, slowpath);630631// Check whether heap is stable.632__ lbz(tmp,633in_bytes(ShenandoahThreadLocalData::gc_state_offset() - JavaThread::jni_environment_offset()),634jni_env);635636__ andi_(tmp, tmp, ShenandoahHeap::EVACUATION | ShenandoahHeap::HAS_FORWARDED);637__ bne(CCR0, slowpath);638639__ bind(done);640__ block_comment("} try_resolve_jobject_in_native (shenandoahgc)");641}642643// Special shenandoah CAS implementation that handles false negatives due644// to concurrent evacuation. That is, the CAS operation is intended to succeed in645// the following scenarios (success criteria):646// s1) The reference pointer ('base_addr') equals the expected ('expected') pointer.647// s2) The reference pointer refers to the from-space version of an already-evacuated648// object, whereas the expected pointer refers to the to-space version of the same object.649// Situations in which the reference pointer refers to the to-space version of an object650// and the expected pointer refers to the from-space version of the same object can not occur due to651// shenandoah's strong to-space invariant. This also implies that the reference stored in 'new_val'652// can not refer to the from-space version of an already-evacuated object.653//654// To guarantee correct behavior in concurrent environments, two races must be addressed:655// r1) A concurrent thread may heal the reference pointer (i.e., it is no longer referring to the656// from-space version but to the to-space version of the object in question).657// In this case, the CAS operation should succeed.658// r2) A concurrent thread may mutate the reference (i.e., the reference pointer refers to an entirely different object).659// In this case, the CAS operation should fail.660//661// By default, the value held in the 'result' register is zero to indicate failure of CAS,662// non-zero to indicate success. If 'is_cae' is set, the result is the most recently fetched663// value from 'base_addr' rather than a boolean success indicator.664void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler *masm, Register base_addr,665Register expected, Register new_val, Register tmp1, Register tmp2,666bool is_cae, Register result) {667__ block_comment("cmpxchg_oop (shenandoahgc) {");668669assert_different_registers(base_addr, new_val, tmp1, tmp2, result, R0);670assert_different_registers(base_addr, expected, tmp1, tmp2, result, R0);671672// Potential clash of 'success_flag' and 'tmp' is being accounted for.673Register success_flag = is_cae ? noreg : result,674current_value = is_cae ? result : tmp1,675tmp = is_cae ? tmp1 : result,676initial_value = tmp2;677678Label done, step_four;679680__ bind(step_four);681682/* ==== Step 1 ("Standard" CAS) ==== */683// Fast path: The values stored in 'expected' and 'base_addr' are equal.684// Given that 'expected' must refer to the to-space object of an evacuated object (strong to-space invariant),685// no special processing is required.686if (UseCompressedOops) {687__ cmpxchgw(CCR0, current_value, expected, new_val, base_addr, MacroAssembler::MemBarNone,688false, success_flag, true);689} else {690__ cmpxchgd(CCR0, current_value, expected, new_val, base_addr, MacroAssembler::MemBarNone,691false, success_flag, NULL, true);692}693694// Skip the rest of the barrier if the CAS operation succeeds immediately.695// If it does not, the value stored at the address is either the from-space pointer of the696// referenced object (success criteria s2)) or simply another object.697__ beq(CCR0, done);698699/* ==== Step 2 (Null check) ==== */700// The success criteria s2) cannot be matched with a null pointer701// (null pointers cannot be subject to concurrent evacuation). The failure of the CAS operation is thus legitimate.702__ cmpdi(CCR0, current_value, 0);703__ beq(CCR0, done);704705/* ==== Step 3 (reference pointer refers to from-space version; success criteria s2)) ==== */706// To check whether the reference pointer refers to the from-space version, the forward707// pointer of the object referred to by the reference is resolved and compared against the expected pointer.708// If this check succeed, another CAS operation is issued with the from-space pointer being the expected pointer.709//710// Save the potential from-space pointer.711__ mr(initial_value, current_value);712713// Resolve forward pointer.714if (UseCompressedOops) { __ decode_heap_oop_not_null(current_value); }715resolve_forward_pointer_not_null(masm, current_value, tmp);716if (UseCompressedOops) { __ encode_heap_oop_not_null(current_value); }717718if (!is_cae) {719// 'success_flag' was overwritten by call to 'resovle_forward_pointer_not_null'.720// Load zero into register for the potential failure case.721__ li(success_flag, 0);722}723__ cmpd(CCR0, current_value, expected);724__ bne(CCR0, done);725726// Discard fetched value as it might be a reference to the from-space version of an object.727if (UseCompressedOops) {728__ cmpxchgw(CCR0, R0, initial_value, new_val, base_addr, MacroAssembler::MemBarNone,729false, success_flag);730} else {731__ cmpxchgd(CCR0, R0, initial_value, new_val, base_addr, MacroAssembler::MemBarNone,732false, success_flag);733}734735/* ==== Step 4 (Retry CAS with to-space pointer (success criteria s2) under race r1)) ==== */736// The reference pointer could have been healed whilst the previous CAS operation was being performed.737// Another CAS operation must thus be issued with the to-space pointer being the expected pointer.738// If that CAS operation fails as well, race r2) must have occurred, indicating that739// the operation failure is legitimate.740//741// To keep the code's size small and thus improving cache (icache) performance, this highly742// unlikely case should be handled by the smallest possible code. Instead of emitting a third,743// explicit CAS operation, the code jumps back and reuses the first CAS operation (step 1)744// (passed arguments are identical).745//746// A failure of the CAS operation in step 1 would imply that the overall CAS operation is supposed747// to fail. Jumping back to step 1 requires, however, that step 2 and step 3 are re-executed as well.748// It is thus important to ensure that a re-execution of those steps does not put program correctness749// at risk:750// - Step 2: Either terminates in failure (desired result) or falls through to step 3.751// - Step 3: Terminates if the comparison between the forwarded, fetched pointer and the expected value752// fails. Unless the reference has been updated in the meanwhile once again, this is753// guaranteed to be the case.754// In case of a concurrent update, the CAS would be retried again. This is legitimate755// in terms of program correctness (even though it is not desired).756__ bne(CCR0, step_four);757758__ bind(done);759__ block_comment("} cmpxchg_oop (shenandoahgc)");760}761762#undef __763764#ifdef COMPILER1765766#define __ ce->masm()->767768void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler *ce, ShenandoahPreBarrierStub *stub) {769__ block_comment("gen_pre_barrier_stub (shenandoahgc) {");770771ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1();772__ bind(*stub->entry());773774// GC status has already been verified by 'ShenandoahBarrierSetC1::pre_barrier'.775// This stub is the slowpath of that function.776777assert(stub->pre_val()->is_register(), "pre_val must be a register");778Register pre_val = stub->pre_val()->as_register();779780// If 'do_load()' returns false, the to-be-stored value is already available in 'stub->pre_val()'781// ("preloaded mode" of the store barrier).782if (stub->do_load()) {783ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false, false /*unaligned*/);784}785786// Fast path: Reference is null.787__ cmpdi(CCR0, pre_val, 0);788__ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CCR0, Assembler::equal), *stub->continuation());789790// Argument passing via the stack.791__ std(pre_val, -8, R1_SP);792793__ load_const_optimized(R0, bs->pre_barrier_c1_runtime_code_blob()->code_begin());794__ call_stub(R0);795796__ b(*stub->continuation());797__ block_comment("} gen_pre_barrier_stub (shenandoahgc)");798}799800void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler *ce,801ShenandoahLoadReferenceBarrierStub *stub) {802__ block_comment("gen_load_reference_barrier_stub (shenandoahgc) {");803804ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1();805__ bind(*stub->entry());806807Register obj = stub->obj()->as_register();808Register res = stub->result()->as_register();809Register addr = stub->addr()->as_pointer_register();810Register tmp1 = stub->tmp1()->as_register();811Register tmp2 = stub->tmp2()->as_register();812assert_different_registers(addr, res, tmp1, tmp2);813814#ifdef ASSERT815// Ensure that 'res' is 'R3_ARG1' and contains the same value as 'obj' to reduce the number of required816// copy instructions.817assert(R3_RET == res, "res must be r3");818__ cmpd(CCR0, res, obj);819__ asm_assert_eq("result register must contain the reference stored in obj");820#endif821822DecoratorSet decorators = stub->decorators();823824/* ==== Check whether region is in collection set ==== */825// GC status (unstable) has already been verified by 'ShenandoahBarrierSetC1::load_reference_barrier_impl'.826// This stub is the slowpath of that function.827828bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);829bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);830bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);831bool is_native = ShenandoahBarrierSet::is_native_access(decorators);832833if (is_strong) {834// Check whether object is in collection set.835__ load_const_optimized(tmp2, ShenandoahHeap::in_cset_fast_test_addr(), tmp1);836__ srdi(tmp1, obj, ShenandoahHeapRegion::region_size_bytes_shift_jint());837__ lbzx(tmp2, tmp1, tmp2);838839__ andi_(tmp2, tmp2, 1);840__ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CCR0, Assembler::equal), *stub->continuation());841}842843address blob_addr = nullptr;844845if (is_strong) {846if (is_native) {847blob_addr = bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin();848} else {849blob_addr = bs->load_reference_barrier_strong_rt_code_blob()->code_begin();850}851} else if (is_weak) {852blob_addr = bs->load_reference_barrier_weak_rt_code_blob()->code_begin();853} else {854assert(is_phantom, "only remaining strength");855blob_addr = bs->load_reference_barrier_phantom_rt_code_blob()->code_begin();856}857858assert(blob_addr != nullptr, "code blob cannot be found");859860// Argument passing via the stack. 'obj' is passed implicitly (as asserted above).861__ std(addr, -8, R1_SP);862863__ load_const_optimized(tmp1, blob_addr, tmp2);864__ call_stub(tmp1);865866// 'res' is 'R3_RET'. The result is thus already in the correct register.867868__ b(*stub->continuation());869__ block_comment("} gen_load_reference_barrier_stub (shenandoahgc)");870}871872#undef __873874#define __ sasm->875876void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler *sasm) {877__ block_comment("generate_c1_pre_barrier_runtime_stub (shenandoahgc) {");878879Label runtime, skip_barrier;880BarrierSet *bs = BarrierSet::barrier_set();881882// Argument passing via the stack.883const int caller_stack_slots = 3;884885Register R0_pre_val = R0;886__ ld(R0, -8, R1_SP);887Register R11_tmp1 = R11_scratch1;888__ std(R11_tmp1, -16, R1_SP);889Register R12_tmp2 = R12_scratch2;890__ std(R12_tmp2, -24, R1_SP);891892/* ==== Check whether marking is active ==== */893// Even though gc status was checked in 'ShenandoahBarrierSetAssembler::gen_pre_barrier_stub',894// another check is required as a safepoint might have been reached in the meantime (JDK-8140588).895__ lbz(R12_tmp2, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread);896897__ andi_(R12_tmp2, R12_tmp2, ShenandoahHeap::MARKING);898__ beq(CCR0, skip_barrier);899900/* ==== Add previous value directly to thread-local SATB mark queue ==== */901// Check queue's capacity. Jump to runtime if no free slot is available.902__ ld(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread);903__ cmpdi(CCR0, R12_tmp2, 0);904__ beq(CCR0, runtime);905906// Capacity suffices. Decrement the queue's size by one slot (size of one oop).907__ addi(R12_tmp2, R12_tmp2, -wordSize);908__ std(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread);909910// Enqueue the previous value and skip the runtime invocation.911__ ld(R11_tmp1, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread);912__ stdx(R0_pre_val, R11_tmp1, R12_tmp2);913__ b(skip_barrier);914915__ bind(runtime);916917/* ==== Invoke runtime to commit SATB mark queue to gc and allocate a new buffer ==== */918// Save to-be-preserved registers.919const int nbytes_save = (MacroAssembler::num_volatile_regs + caller_stack_slots) * BytesPerWord;920__ save_volatile_gprs(R1_SP, -nbytes_save);921__ save_LR_CR(R11_tmp1);922__ push_frame_reg_args(nbytes_save, R11_tmp1);923924// Invoke runtime.925__ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), R0_pre_val, R16_thread);926927// Restore to-be-preserved registers.928__ pop_frame();929__ restore_LR_CR(R11_tmp1);930__ restore_volatile_gprs(R1_SP, -nbytes_save);931932__ bind(skip_barrier);933934// Restore spilled registers.935__ ld(R11_tmp1, -16, R1_SP);936__ ld(R12_tmp2, -24, R1_SP);937938__ blr();939__ block_comment("} generate_c1_pre_barrier_runtime_stub (shenandoahgc)");940}941942void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler *sasm,943DecoratorSet decorators) {944__ block_comment("generate_c1_load_reference_barrier_runtime_stub (shenandoahgc) {");945946// Argument passing via the stack.947const int caller_stack_slots = 1;948949// Save to-be-preserved registers.950const int nbytes_save = (MacroAssembler::num_volatile_regs - 1 // 'R3_ARG1' is skipped951+ caller_stack_slots) * BytesPerWord;952__ save_volatile_gprs(R1_SP, -nbytes_save, true, false);953954// Load arguments from stack.955// No load required, as assured by assertions in 'ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub'.956Register R3_obj = R3_ARG1;957Register R4_load_addr = R4_ARG2;958__ ld(R4_load_addr, -8, R1_SP);959960Register R11_tmp = R11_scratch1;961962/* ==== Invoke runtime ==== */963bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);964bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);965bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);966bool is_native = ShenandoahBarrierSet::is_native_access(decorators);967968address jrt_address = NULL;969970if (is_strong) {971if (is_native) {972jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);973} else {974if (UseCompressedOops) {975jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);976} else {977jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);978}979}980} else if (is_weak) {981assert(!is_native, "weak load reference barrier must not be called off-heap");982if (UseCompressedOops) {983jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);984} else {985jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);986}987} else {988assert(is_phantom, "reference type must be phantom");989assert(is_native, "phantom load reference barrier must be called off-heap");990jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);991}992assert(jrt_address != NULL, "load reference barrier runtime routine cannot be found");993994__ save_LR_CR(R11_tmp);995__ push_frame_reg_args(nbytes_save, R11_tmp);996997// Invoke runtime. Arguments are already stored in the corresponding registers.998__ call_VM_leaf(jrt_address, R3_obj, R4_load_addr);9991000// Restore to-be-preserved registers.1001__ pop_frame();1002__ restore_LR_CR(R11_tmp);1003__ restore_volatile_gprs(R1_SP, -nbytes_save, true, false); // Skip 'R3_RET' register.10041005__ blr();1006__ block_comment("} generate_c1_load_reference_barrier_runtime_stub (shenandoahgc)");1007}10081009#undef __10101011#endif // COMPILER1101210131014