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 "code/codeBlob.hpp"
 26 #include "code/nativeInst.hpp"
 27 #include "code/nmethod.hpp"
 28 #include "gc/shared/barrierSet.hpp"
 29 #include "gc/shared/barrierSetAssembler.hpp"
 30 #include "gc/shared/barrierSetNMethod.hpp"
 31 #include "utilities/debug.hpp"
 32 
 33 class NativeNMethodBarrier: public NativeInstruction {
 34 
 35   address get_barrier_start_address() const {
 36     return NativeInstruction::addr_at(0);
 37   }
 38 
 39   NativeMovRegMem* get_patchable_instruction_handle() const {
 40     // Endianness is handled by NativeMovRegMem
 41     return reinterpret_cast<NativeMovRegMem*>(get_barrier_start_address());
 42   }
 43 
 44 public:
 45   int get_guard_value() const {
 46     // Retrieve the guard value (naming of 'offset' function is misleading).
 47     return get_patchable_instruction_handle()->offset();
 48   }
 49 
 50   void release_set_guard_value(int value, int bit_mask) {
 51     // Patching is not atomic.
 52     // Stale observations of the "armed" state is okay as invoking the barrier stub in that case has no
 53     // unwanted side effects. Disarming is thus a non-critical operation.
 54     // The visibility of the "armed" state must be ensured by safepoint/handshake.
 55 
 56     OrderAccess::release(); // Release modified oops
 57 
 58     if (bit_mask == ~0) {
 59       // Set the guard value (naming of 'offset' function is misleading).
 60       get_patchable_instruction_handle()->set_offset(value);
 61       return;
 62     }
 63 
 64     assert((value & ~bit_mask) == 0, "trying to set bits outside the mask");
 65     value &= bit_mask;
 66 
 67     NativeMovRegMem* mov = get_patchable_instruction_handle();
 68     assert(align_up(mov->instruction_address(), sizeof(uint64_t)) ==
 69            align_down(mov->instruction_address(), sizeof(uint64_t)), "instruction not aligned");
 70     uint64_t *instr = (uint64_t*)mov->instruction_address();
 71     assert(NativeMovRegMem::instruction_size == sizeof(*instr), "must be");
 72     union {
 73       u_char buf[NativeMovRegMem::instruction_size];
 74       uint64_t u64;
 75     } new_mov_instr, old_mov_instr;
 76     new_mov_instr.u64 = old_mov_instr.u64 = AtomicAccess::load(instr);
 77     while (true) {
 78       // Only bits in the mask are changed
 79       int old_value = nativeMovRegMem_at(old_mov_instr.buf)->offset();
 80       int new_value = value | (old_value & ~bit_mask);
 81       if (new_value == old_value) return; // skip icache flush if nothing changed
 82       nativeMovRegMem_at(new_mov_instr.buf)->set_offset(new_value, false /* no icache flush */);
 83       // Swap in the new value
 84       uint64_t v = AtomicAccess::cmpxchg(instr, old_mov_instr.u64, new_mov_instr.u64, memory_order_relaxed);
 85       if (v == old_mov_instr.u64) break;
 86       old_mov_instr.u64 = v;
 87     }
 88     ICache::ppc64_flush_icache_bytes(addr_at(0), NativeMovRegMem::instruction_size);
 89   }
 90 
 91   void verify() const {
 92     // Although it's possible to just validate the to-be-patched instruction,
 93     // all instructions are validated to ensure that the barrier is hit properly - especially since
 94     // the pattern used in load_const32 is a quite common one.
 95 
 96     uint* current_instruction = reinterpret_cast<uint*>(get_barrier_start_address());
 97 
 98     get_patchable_instruction_handle()->verify();
 99     current_instruction += 2;
100 
101     verify_op_code(current_instruction, Assembler::LD_OPCODE);
102 
103     if (TrapBasedNMethodEntryBarriers) {
104       verify_op_code(current_instruction, Assembler::TW_OPCODE);
105     } else {
106       // cmpw (mnemonic)
107       verify_op_code(current_instruction, Assembler::CMP_OPCODE);
108 
109       // calculate_address_from_global_toc (compound instruction)
110       verify_op_code_manually(current_instruction, MacroAssembler::is_addis(*current_instruction));
111       verify_op_code_manually(current_instruction, MacroAssembler::is_addi(*current_instruction));
112 
113       verify_op_code_manually(current_instruction, MacroAssembler::is_mtctr(*current_instruction));
114 
115       // bnectrl (mnemonic) (weak check; not checking the exact type)
116       verify_op_code(current_instruction, Assembler::BCCTR_OPCODE);
117     }
118 
119     // isync is optional
120   }
121 
122 private:
123   static void verify_op_code_manually(uint*& current_instruction, bool result) {
124     assert(result, "illegal instruction sequence for nmethod entry barrier");
125     current_instruction++;
126   }
127   static void verify_op_code(uint*& current_instruction, uint expected,
128                              unsigned int mask = 63u << Assembler::OPCODE_SHIFT) {
129     // Masking both, current instruction and opcode, as some opcodes in Assembler contain additional information
130     // to uniquely identify simplified mnemonics.
131     // As long as the caller doesn't provide a custom mask, that additional information is discarded.
132     verify_op_code_manually(current_instruction, (*current_instruction & mask) == (expected & mask));
133   }
134 };
135 
136 static NativeNMethodBarrier* get_nmethod_barrier(nmethod* nm) {
137   BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
138   address barrier_address = nm->code_begin() + nm->frame_complete_offset() -
139                             (TrapBasedNMethodEntryBarriers ? 4 : 8) * BytesPerInstWord;
140   if (bs_asm->nmethod_patching_type() != NMethodPatchingType::stw_instruction_and_data_patch) {
141     barrier_address -= BytesPerInstWord; // isync (see nmethod_entry_barrier)
142   }
143 
144   auto barrier = reinterpret_cast<NativeNMethodBarrier*>(barrier_address);
145   DEBUG_ONLY(barrier->verify());
146   return barrier;
147 }
148 
149 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
150   // As PPC64 always has a valid back chain (unlike other platforms), the stub can simply pop the frame.
151   // Thus, there's nothing to do here.
152 }
153 
154 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
155   if (!supports_entry_barrier(nm)) {
156     return;
157   }
158 
159   assert(nm->is_osr_method() || !nm->method()->has_scalarized_args(), "unsupported");
160 
161   NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
162   barrier->release_set_guard_value(value, bit_mask);
163 }
164 
165 int BarrierSetNMethod::guard_value(nmethod* nm) {
166   if (!supports_entry_barrier(nm)) {
167     return disarmed_guard_value();
168   }
169 
170   NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
171   return barrier->get_guard_value();
172 }