1 /*
2 * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26 #include "code/codeCache.hpp"
27 #include "code/nativeInst.hpp"
28 #include "gc/shared/barrierSetAssembler.hpp"
29 #include "gc/shared/barrierSetNMethod.hpp"
30 #include "logging/log.hpp"
31 #include "memory/resourceArea.hpp"
32 #include "runtime/frame.inline.hpp"
33 #include "runtime/javaThread.hpp"
34 #include "runtime/registerMap.hpp"
35 #include "runtime/sharedRuntime.hpp"
36 #include "utilities/align.hpp"
37 #include "utilities/debug.hpp"
38
39 static int slow_path_size(nmethod* nm) {
40 // The slow path code is out of line with C2.
41 return nm->is_compiled_by_c2() ? 0 : 4;
42 }
43
44 static int entry_barrier_offset(nmethod* nm) {
45 BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
46 switch (bs_asm->nmethod_patching_type()) {
47 case NMethodPatchingType::stw_instruction_and_data_patch:
48 return -4 * (5 + slow_path_size(nm));
49 case NMethodPatchingType::conc_instruction_and_data_patch:
50 return -4 * ((UseZtso ? 14 : 16) + slow_path_size(nm));
51 }
52 ShouldNotReachHere();
53 return 0;
54 }
55
56 class NativeNMethodBarrier {
57 address _instruction_address;
58 int* _guard_addr;
59 nmethod* _nm;
60
61 address instruction_address() const { return _instruction_address; }
62
63 int *guard_addr() {
64 return _guard_addr;
65 }
66
67 int local_guard_offset(nmethod* nm) {
68 // It's the last instruction
69 return (-entry_barrier_offset(nm)) - 4;
70 }
71
72 public:
73 NativeNMethodBarrier(nmethod* nm): _nm(nm) {
74 _instruction_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
75 if (nm->is_compiled_by_c2()) {
76 // With c2 compiled code, the guard is out-of-line in a stub
77 // We find it using the RelocIterator.
78 RelocIterator iter(nm);
79 while (iter.next()) {
80 if (iter.type() == relocInfo::entry_guard_type) {
81 entry_guard_Relocation* const reloc = iter.entry_guard_reloc();
82 _guard_addr = reinterpret_cast<int*>(reloc->addr());
83 return;
84 }
85 }
86
87 ShouldNotReachHere();
88 }
89 _guard_addr = reinterpret_cast<int*>(instruction_address() + local_guard_offset(nm));
90
91 // Perform the checking as verification.
92 err_msg msg("%s", "");
93 assert(check_barrier(msg), "%s", msg.buffer());
94 }
95
96 int get_value() {
97 return AtomicAccess::load_acquire(guard_addr());
98 }
99
100 void set_value(int value, int bit_mask) {
101 if (bit_mask == ~0) {
102 AtomicAccess::release_store(guard_addr(), value);
103 return;
104 }
105 assert((value & ~bit_mask) == 0, "trying to set bits outside the mask");
106 value &= bit_mask;
107 int old_value = AtomicAccess::load(guard_addr());
108 while (true) {
109 // Only bits in the mask are changed
110 int new_value = value | (old_value & ~bit_mask);
111 if (new_value == old_value) break;
112 int v = AtomicAccess::cmpxchg(guard_addr(), old_value, new_value, memory_order_release);
113 if (v == old_value) break;
114 old_value = v;
115 }
116 }
117
118 bool check_barrier(err_msg& msg) const;
119 };
120
121 // Store the instruction bitmask, bits and name for checking the barrier.
122 struct CheckInsn {
123 uint32_t mask;
124 uint32_t bits;
125 const char *name;
126 };
127
128 static const struct CheckInsn barrierInsn[] = {
129 { 0x00000fff, 0x00000297, "auipc t0, 0 " },
130 { 0x000fffff, 0x0002e283, "lwu t0, guard_offset(t0)" },
131 /* ...... */
132 /* ...... */
133 /* guard: */
134 /* 32bit nmethod guard value */
135 };
136
137 // The encodings must match the instructions emitted by
138 // BarrierSetAssembler::nmethod_entry_barrier. The matching ignores the specific
139 // register numbers and immediate values in the encoding.
140 bool NativeNMethodBarrier::check_barrier(err_msg& msg) const {
141 address addr = instruction_address();
142 for (unsigned int i = 0; i < sizeof(barrierInsn) / sizeof(struct CheckInsn); i++) {
143 uint32_t inst = Assembler::ld_instr(addr);
144 if ((inst & barrierInsn[i].mask) != barrierInsn[i].bits) {
145 msg.print("Nmethod entry barrier did not start with auipc & lwu as expected. "
146 "Addr: " INTPTR_FORMAT " Code: 0x%x not an %s instruction.", p2i(addr), inst, barrierInsn[i].name);
147 return false;
148 }
149 addr += 4;
150 }
151 return true;
152 }
153
154
155 /* We're called from an nmethod when we need to deoptimize it. We do
156 this by throwing away the nmethod's frame and jumping to the
157 ic_miss stub. This looks like there has been an IC miss at the
158 entry of the nmethod, so we resolve the call, which will fall back
159 to the interpreter if the nmethod has been unloaded. */
160 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
161
162 typedef struct {
163 intptr_t *sp; intptr_t *fp; address ra; address pc;
164 } frame_pointers_t;
165
166 frame_pointers_t *new_frame = (frame_pointers_t *)(return_address_ptr - 5);
167
168 JavaThread *thread = JavaThread::current();
169 RegisterMap reg_map(thread,
170 RegisterMap::UpdateMap::skip,
171 RegisterMap::ProcessFrames::include,
172 RegisterMap::WalkContinuation::skip);
173 frame frame = thread->last_frame();
174
175 assert(frame.is_compiled_frame() || frame.is_native_frame(), "must be");
176 assert(frame.cb() == nm, "must be");
177 frame = frame.sender(®_map);
178
179 LogTarget(Trace, nmethod, barrier) out;
180 if (out.is_enabled()) {
181 ResourceMark mark;
182 log_trace(nmethod, barrier)("deoptimize(nmethod: %s(%p), return_addr: %p, osr: %d, thread: %p(%s), making rsp: %p) -> %p",
183 nm->method()->name_and_sig_as_C_string(),
184 nm, *(address *) return_address_ptr, nm->is_osr_method(), thread,
185 thread->name(), frame.sp(), nm->verified_entry_point());
186 }
187
188 new_frame->sp = frame.sp();
189 new_frame->fp = frame.fp();
190 new_frame->ra = frame.pc();
191 new_frame->pc = SharedRuntime::get_handle_wrong_method_stub();
192 }
193
194 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
195 if (!supports_entry_barrier(nm)) {
196 return;
197 }
198
199 if (value == disarmed_guard_value()) {
200 // The patching epoch is incremented before the nmethod is disarmed. Disarming
201 // is performed with a release store. In the nmethod entry barrier, the values
202 // are read in the opposite order, such that the load of the nmethod guard
203 // acquires the patching epoch. This way, the guard is guaranteed to block
204 // entries to the nmethod, until it has safely published the requirement for
205 // further fencing by mutators, before they are allowed to enter.
206 BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
207 bs_asm->increment_patching_epoch();
208 }
209
210 NativeNMethodBarrier barrier(nm);
211 barrier.set_value(value, bit_mask);
212 }
213
214 int BarrierSetNMethod::guard_value(nmethod* nm) {
215 if (!supports_entry_barrier(nm)) {
216 return disarmed_guard_value();
217 }
218
219 NativeNMethodBarrier barrier(nm);
220 return barrier.get_value();
221 }