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 NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
160 barrier->release_set_guard_value(value, bit_mask);
161 }
162
163 int BarrierSetNMethod::guard_value(nmethod* nm) {
164 if (!supports_entry_barrier(nm)) {
165 return disarmed_guard_value();
166 }
167
168 NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
169 return barrier->get_guard_value();
170 }