1 /* 2 * Copyright (c) 2018, 2025, Oracle and/or its affiliates. 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 it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 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 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "ci/ciUtilities.hpp" 26 #include "code/aotCodeCache.hpp" 27 #include "gc/shared/c2/cardTableBarrierSetC2.hpp" 28 #include "gc/shared/cardTable.hpp" 29 #include "gc/shared/cardTableBarrierSet.hpp" 30 #include "gc/shared/gc_globals.hpp" 31 #include "opto/arraycopynode.hpp" 32 #include "opto/graphKit.hpp" 33 #include "opto/idealKit.hpp" 34 #include "opto/macro.hpp" 35 #include "utilities/macros.hpp" 36 37 #define __ ideal. 38 39 Node* CardTableBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) const { 40 DecoratorSet decorators = access.decorators(); 41 42 Node* adr = access.addr().node(); 43 44 bool is_array = (decorators & IS_ARRAY) != 0; 45 bool anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; 46 bool in_heap = (decorators & IN_HEAP) != 0; 47 bool use_precise = is_array || anonymous; 48 bool tightly_coupled_alloc = (decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0; 49 50 if (!access.is_oop() || tightly_coupled_alloc || (!in_heap && !anonymous)) { 51 return BarrierSetC2::store_at_resolved(access, val); 52 } 53 54 assert(access.is_parse_access(), "entry not supported at optimization time"); 55 C2ParseAccess& parse_access = static_cast<C2ParseAccess&>(access); 56 57 Node* store = BarrierSetC2::store_at_resolved(access, val); 58 post_barrier(parse_access.kit(), access.base(), adr, val.node(), use_precise); 59 60 return store; 61 } 62 63 Node* CardTableBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, 64 Node* new_val, const Type* value_type) const { 65 if (!access.is_oop()) { 66 return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); 67 } 68 69 Node* result = BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); 70 71 post_barrier(access.kit(), access.base(), access.addr().node(), new_val, true); 72 73 return result; 74 } 75 76 Node* CardTableBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val, 77 Node* new_val, const Type* value_type) const { 78 GraphKit* kit = access.kit(); 79 80 if (!access.is_oop()) { 81 return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); 82 } 83 84 Node* load_store = BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); 85 86 // Emit the post barrier only when the actual store happened. This makes sense 87 // to check only for LS_cmp_* that can fail to set the value. 88 // LS_cmp_exchange does not produce any branches by default, so there is no 89 // boolean result to piggyback on. TODO: When we merge CompareAndSwap with 90 // CompareAndExchange and move branches here, it would make sense to conditionalize 91 // post_barriers for LS_cmp_exchange as well. 92 // 93 // CAS success path is marked more likely since we anticipate this is a performance 94 // critical path, while CAS failure path can use the penalty for going through unlikely 95 // path as backoff. Which is still better than doing a store barrier there. 96 IdealKit ideal(kit); 97 ideal.if_then(load_store, BoolTest::ne, ideal.ConI(0), PROB_STATIC_FREQUENT); { 98 kit->sync_kit(ideal); 99 post_barrier(kit, access.base(), access.addr().node(), new_val, true); 100 ideal.sync_kit(kit); 101 } ideal.end_if(); 102 kit->final_sync(ideal); 103 104 return load_store; 105 } 106 107 Node* CardTableBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const { 108 Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, new_val, value_type); 109 if (!access.is_oop()) { 110 return result; 111 } 112 113 post_barrier(access.kit(), access.base(), access.addr().node(), new_val, true); 114 115 return result; 116 } 117 118 Node* CardTableBarrierSetC2::byte_map_base_node(IdealKit* kit) const { 119 // Get base of card map 120 #if INCLUDE_CDS 121 if (AOTCodeCache::is_on_for_dump()) { 122 // load the card table address from the AOT Runtime Constants area 123 Node* byte_map_base_adr = kit->makecon(TypeRawPtr::make(AOTRuntimeConstants::card_table_address())); 124 return kit->load_aot_const(byte_map_base_adr, TypeRawPtr::NOTNULL); 125 } 126 #endif 127 CardTable::CardValue* card_table_base = ci_card_table_address_const(); 128 if (card_table_base != nullptr) { 129 return kit->makecon(TypeRawPtr::make((address)card_table_base)); 130 } else { 131 return kit->makecon(Type::get_zero_type(T_ADDRESS)); 132 } 133 } 134 135 // vanilla post barrier 136 // Insert a write-barrier store. This is to let generational GC work; we have 137 // to flag all oop-stores before the next GC point. 138 void CardTableBarrierSetC2::post_barrier(GraphKit* kit, 139 Node* obj, 140 Node* adr, 141 Node* val, 142 bool use_precise) const { 143 // No store check needed if we're storing a null. 144 if (val != nullptr && val->is_Con()) { 145 const Type* t = val->bottom_type(); 146 if (t == TypePtr::NULL_PTR || t == Type::TOP) { 147 return; 148 } 149 } 150 151 if (use_ReduceInitialCardMarks() 152 && obj == kit->just_allocated_object(kit->control())) { 153 // We can skip marks on a freshly-allocated object in Eden. 154 // Keep this code in sync with CardTableBarrierSet::on_slowpath_allocation_exit. 155 // That routine informs GC to take appropriate compensating steps, 156 // upon a slow-path allocation, so as to make this card-mark 157 // elision safe. 158 return; 159 } 160 161 if (!use_precise) { 162 // All card marks for a (non-array) instance are in one place: 163 adr = obj; 164 } else { 165 // Else it's an array (or unknown), and we want more precise card marks. 166 } 167 168 assert(adr != nullptr, ""); 169 170 IdealKit ideal(kit, true); 171 172 // Convert the pointer to an int prior to doing math on it 173 Node* cast = __ CastPX(__ ctrl(), adr); 174 175 // Divide by card size 176 Node* card_offset = __ URShiftX(cast, __ ConI(CardTable::card_shift())); 177 178 // Combine card table base and card offset 179 Node* card_adr = __ AddP(__ top(), byte_map_base_node(&ideal), card_offset); 180 181 // Get the alias_index for raw card-mark memory 182 int adr_type = Compile::AliasIdxRaw; 183 184 // Dirty card value to store 185 Node* dirty = __ ConI(CardTable::dirty_card_val()); 186 187 if (UseCondCardMark) { 188 // The classic GC reference write barrier is typically implemented 189 // as a store into the global card mark table. Unfortunately 190 // unconditional stores can result in false sharing and excessive 191 // coherence traffic as well as false transactional aborts. 192 // UseCondCardMark enables MP "polite" conditional card mark 193 // stores. In theory we could relax the load from ctrl() to 194 // no_ctrl, but that doesn't buy much latitude. 195 Node* card_val = __ load( __ ctrl(), card_adr, TypeInt::BYTE, T_BYTE, adr_type); 196 __ if_then(card_val, BoolTest::ne, dirty); 197 } 198 199 // Smash dirty value into card 200 __ store(__ ctrl(), card_adr, dirty, T_BYTE, adr_type, MemNode::unordered); 201 202 if (UseCondCardMark) { 203 __ end_if(); 204 } 205 206 // Final sync IdealKit and GraphKit. 207 kit->final_sync(ideal); 208 } 209 210 bool CardTableBarrierSetC2::use_ReduceInitialCardMarks() { 211 return ReduceInitialCardMarks; 212 } 213 214 void CardTableBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { 215 assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required"); 216 Node *shift = node->unique_out(); 217 Node *addp = shift->unique_out(); 218 for (DUIterator_Last jmin, j = addp->last_outs(jmin); j >= jmin; --j) { 219 Node *mem = addp->last_out(j); 220 if (UseCondCardMark && mem->is_Load()) { 221 assert(mem->Opcode() == Op_LoadB, "unexpected code shape"); 222 // The load is checking if the card has been written so 223 // replace it with zero to fold the test. 224 macro->replace_node(mem, macro->intcon(0)); 225 continue; 226 } 227 assert(mem->is_Store(), "store required"); 228 macro->replace_node(mem, mem->in(MemNode::Memory)); 229 } 230 } 231 232 bool CardTableBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, bool is_clone_instance, ArrayCopyPhase phase) const { 233 bool is_oop = is_reference_type(type); 234 return is_oop && (!tightly_coupled_alloc || !use_ReduceInitialCardMarks()); 235 } --- EOF ---