1 /*
2 * Copyright (c) 2018, 2026, 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/codeCache.hpp"
26 #include "code/nativeInst.hpp"
27 #include "gc/shared/barrierSet.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 #include "utilities/formatBuffer.hpp"
39
40 static int slow_path_size(nmethod* nm) {
41 // The slow path code is out of line with C2
42 return nm->is_compiled_by_c2() ? 0 : 6;
43 }
44
45 // This is the offset of the entry barrier relative to where the frame is completed.
46 // If any code changes between the end of the verified entry where the entry
47 // barrier resides, and the completion of the frame, then
48 // NativeNMethodCmpBarrier::verify() will immediately complain when it does
49 // not find the expected native instruction at this offset, which needs updating.
50 // Note that this offset is invariant of PreserveFramePointer.
51 static int entry_barrier_offset(nmethod* nm) {
52 BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
53 switch (bs_asm->nmethod_patching_type()) {
54 case NMethodPatchingType::stw_instruction_and_data_patch:
55 return -4 * (4 + slow_path_size(nm));
56 case NMethodPatchingType::conc_instruction_and_data_patch:
57 return -4 * (10 + slow_path_size(nm));
58 }
59 ShouldNotReachHere();
60 return 0;
61 }
62
63 class NativeNMethodBarrier {
64 address _instruction_address;
65 int* _guard_addr;
66 nmethod* _nm;
67
68 address instruction_address() const { return _instruction_address; }
69
70 int *guard_addr() {
71 return _guard_addr;
72 }
73
74 int local_guard_offset(nmethod* nm) {
75 // It's the last instruction
76 return (-entry_barrier_offset(nm)) - 4;
77 }
78
79 public:
80 NativeNMethodBarrier(nmethod* nm): _nm(nm) {
81 _instruction_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
82 if (nm->is_compiled_by_c2()) {
83 // With c2 compiled code, the guard is out-of-line in a stub
84 // We find it using the RelocIterator.
85 RelocIterator iter(nm);
86 while (iter.next()) {
87 if (iter.type() == relocInfo::entry_guard_type) {
88 entry_guard_Relocation* const reloc = iter.entry_guard_reloc();
89 _guard_addr = reinterpret_cast<int*>(reloc->addr());
90 return;
91 }
92 }
93 ShouldNotReachHere();
94 }
95 _guard_addr = reinterpret_cast<int*>(instruction_address() + local_guard_offset(nm));
96 }
97
98 int get_value() {
99 return AtomicAccess::load_acquire(guard_addr());
100 }
101
102 void set_value(int value, int bit_mask) {
103 if (bit_mask == ~0) {
104 AtomicAccess::release_store(guard_addr(), value);
105 return;
106 }
107 assert((value & ~bit_mask) == 0, "trying to set bits outside the mask");
108 value &= bit_mask;
109 int old_value = AtomicAccess::load(guard_addr());
110 while (true) {
111 // Only bits in the mask are changed
112 int new_value = value | (old_value & ~bit_mask);
113 if (new_value == old_value) break;
114 int v = AtomicAccess::cmpxchg(guard_addr(), old_value, new_value, memory_order_release);
115 if (v == old_value) break;
116 old_value = v;
117 }
118 }
119
120 bool check_barrier(err_msg& msg) const;
121 void verify() const {
122 err_msg msg("%s", "");
123 assert(check_barrier(msg), "%s", msg.buffer());
124 }
125 };
126
127 // The first instruction of the nmethod entry barrier is an ldr (literal)
128 // instruction. Verify that it's really there, so the offsets are not skewed.
129 bool NativeNMethodBarrier::check_barrier(err_msg& msg) const {
130 uint32_t* addr = (uint32_t*) instruction_address();
131 uint32_t inst = *addr;
132 if ((inst & 0xff000000) != 0x18000000) {
133 msg.print("Nmethod entry barrier did not start with ldr (literal) as expected. "
134 "Addr: " PTR_FORMAT " Code: " UINT32_FORMAT, p2i(addr), inst);
135 return false;
136 }
137 return true;
138 }
139
140
141 /* We're called from an nmethod when we need to deoptimize it. We do
142 this by throwing away the nmethod's frame and jumping to the
143 ic_miss stub. This looks like there has been an IC miss at the
144 entry of the nmethod, so we resolve the call, which will fall back
145 to the interpreter if the nmethod has been unloaded. */
146 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
147
148 typedef struct {
149 intptr_t *sp; intptr_t *fp; address lr; address pc;
150 } frame_pointers_t;
151
152 frame_pointers_t *new_frame = (frame_pointers_t *)(return_address_ptr - 5);
153
154 JavaThread *thread = JavaThread::current();
155 RegisterMap reg_map(thread,
156 RegisterMap::UpdateMap::skip,
157 RegisterMap::ProcessFrames::include,
158 RegisterMap::WalkContinuation::skip);
159 frame frame = thread->last_frame();
160
161 assert(frame.is_compiled_frame() || frame.is_native_frame(), "must be");
162 assert(frame.cb() == nm, "must be");
163 frame = frame.sender(®_map);
164
165 LogTarget(Trace, nmethod, barrier) out;
166 if (out.is_enabled()) {
167 ResourceMark mark;
168 log_trace(nmethod, barrier)("deoptimize(nmethod: %s(%p), return_addr: %p, osr: %d, thread: %p(%s), making rsp: %p) -> %p",
169 nm->method()->name_and_sig_as_C_string(),
170 nm, *(address *) return_address_ptr, nm->is_osr_method(), thread,
171 thread->name(), frame.sp(), nm->verified_entry_point());
172 }
173
174 new_frame->sp = frame.sp();
175 new_frame->fp = frame.fp();
176 new_frame->lr = frame.pc();
177 new_frame->pc = SharedRuntime::get_handle_wrong_method_stub();
178 }
179
180 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
181 if (!supports_entry_barrier(nm)) {
182 return;
183 }
184
185 if (value == disarmed_guard_value()) {
186 // The patching epoch is incremented before the nmethod is disarmed. Disarming
187 // is performed with a release store. In the nmethod entry barrier, the values
188 // are read in the opposite order, such that the load of the nmethod guard
189 // acquires the patching epoch. This way, the guard is guaranteed to block
190 // entries to the nmethod, until it has safely published the requirement for
191 // further fencing by mutators, before they are allowed to enter.
192 BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
193 bs_asm->increment_patching_epoch();
194 }
195
196 // Enable WXWrite: the function is called directly from nmethod_entry_barrier
197 // stub.
198 MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current()));
199
200 NativeNMethodBarrier barrier(nm);
201 barrier.set_value(value, bit_mask);
202 }
203
204 int BarrierSetNMethod::guard_value(nmethod* nm) {
205 if (!supports_entry_barrier(nm)) {
206 return disarmed_guard_value();
207 }
208
209 NativeNMethodBarrier barrier(nm);
210 return barrier.get_value();
211 }