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/barrierSetNMethod.hpp"
28 #include "logging/log.hpp"
29 #include "memory/resourceArea.hpp"
30 #include "runtime/javaThread.hpp"
31 #include "runtime/sharedRuntime.hpp"
32 #include "utilities/align.hpp"
33 #include "utilities/debug.hpp"
34 #include "utilities/formatBuffer.hpp"
35 #include "utilities/macros.hpp"
36
37 class NativeNMethodCmpBarrier: public NativeInstruction {
38 public:
39 enum Intel_specific_constants {
40 instruction_code = 0x81,
41 instruction_size = 8,
42 imm_offset = 4,
43 instruction_rex_prefix = Assembler::REX | Assembler::REX_B,
44 instruction_modrm = 0x7f // [r15 + offset]
45 };
46
47 address instruction_address() const { return addr_at(0); }
48 address immediate_address() const { return addr_at(imm_offset); }
49
50 NativeNMethodCmpBarrier* nativeNMethodCmpBarrier_at(address a) { return (NativeNMethodCmpBarrier*)a; }
51
52 jint get_immediate() const { return int_at(imm_offset); }
53 void set_immediate(jint imm, int bit_mask) {
54 if (bit_mask == ~0) {
55 set_int_at(imm_offset, imm);
56 return;
57 }
58
59 assert((imm & ~bit_mask) == 0, "trying to set bits outside the mask");
60 imm &= bit_mask;
61
62 assert(align_up(immediate_address(), sizeof(jint)) ==
63 align_down(immediate_address(), sizeof(jint)), "immediate not aligned");
64 jint* data_addr = (jint*)immediate_address();
65 jint old_value = AtomicAccess::load(data_addr);
66 while (true) {
67 // Only bits in the mask are changed
68 jint new_value = imm | (old_value & ~bit_mask);
69 if (new_value == old_value) break;
70 jint v = AtomicAccess::cmpxchg(data_addr, old_value, new_value, memory_order_release);
71 if (v == old_value) break;
72 old_value = v;
73 }
74 }
75 bool check_barrier(err_msg& msg) const;
76 void verify() const {
77 #ifdef ASSERT
78 err_msg msg("%s", "");
79 assert(check_barrier(msg), "%s", msg.buffer());
80 #endif
81 }
82 };
83
84 bool NativeNMethodCmpBarrier::check_barrier(err_msg& msg) const {
85 // Only require 4 byte alignment
86 if (((uintptr_t) instruction_address()) & 0x3) {
87 msg.print("Addr: " INTPTR_FORMAT " not properly aligned", p2i(instruction_address()));
88 return false;
89 }
90
91 int prefix = ubyte_at(0);
92 if (prefix != instruction_rex_prefix) {
93 msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected 0x%x", p2i(instruction_address()), prefix, instruction_rex_prefix);
94 return false;
95 }
96
97 int inst = ubyte_at(1);
98 if (inst != instruction_code) {
99 msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected 0x%x", p2i(instruction_address()), inst, instruction_code);
100 return false;
101 }
102
103 int modrm = ubyte_at(2);
104 if (modrm != instruction_modrm) {
105 msg.print("Addr: " INTPTR_FORMAT " Code: 0x%x expected mod/rm 0x%x", p2i(instruction_address()), modrm, instruction_modrm);
106 return false;
107 }
108 return true;
109 }
110
111 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
112 /*
113 * [ callers frame ]
114 * [ callers return address ] <- callers rsp
115 * [ callers rbp ] <- callers rbp
116 * [ callers frame slots ]
117 * [ return_address ] <- return_address_ptr
118 * [ cookie ] <- used to write the new rsp (callers rsp)
119 * [ stub rbp ]
120 * [ stub stuff ]
121 */
122
123 address* stub_rbp = return_address_ptr - 2;
124 address* callers_rsp = return_address_ptr + nm->frame_size(); /* points to callers return_address now */
125 address* callers_rbp = callers_rsp - 1; // 1 to move to the callers return address, 1 more to move to the rbp
126 address* cookie = return_address_ptr - 1;
127
128 LogTarget(Trace, nmethod, barrier) out;
129 if (out.is_enabled()) {
130 JavaThread* jth = JavaThread::current();
131 ResourceMark mark;
132 log_trace(nmethod, barrier)("deoptimize(nmethod: %p, return_addr: %p, osr: %d, thread: %p(%s), making rsp: %p) -> %p",
133 nm, (address *) return_address_ptr, nm->is_osr_method(), jth,
134 jth->name(), callers_rsp, nm->verified_entry_point());
135 }
136
137 assert(nm->frame_size() >= 3, "invariant");
138 assert(*cookie == (address) -1, "invariant");
139
140 // Preserve caller rbp.
141 *stub_rbp = *callers_rbp;
142
143 // At the cookie address put the callers rsp.
144 *cookie = (address) callers_rsp; // should point to the return address
145
146 // In the slot that used to be the callers rbp we put the address that our stub needs to jump to at the end.
147 // Overwriting the caller rbp should be okay since our stub rbp has the same value.
148 address* jmp_addr_ptr = callers_rbp;
149 *jmp_addr_ptr = SharedRuntime::get_handle_wrong_method_stub();
150 }
151
152 // This is the offset of the entry barrier from where the frame is completed.
153 // If any code changes between the end of the verified entry where the entry
154 // barrier resides, and the completion of the frame, then
155 // NativeNMethodCmpBarrier::verify() will immediately complain when it does
156 // not find the expected native instruction at this offset, which needs updating.
157 // Note that this offset is invariant of PreserveFramePointer.
158 static int entry_barrier_offset(nmethod* nm) {
159 if (nm->is_compiled_by_c2()) {
160 return -14;
161 } else {
162 return -15;
163 }
164 }
165
166 static NativeNMethodCmpBarrier* native_nmethod_barrier(nmethod* nm) {
167 address barrier_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
168 NativeNMethodCmpBarrier* barrier = reinterpret_cast<NativeNMethodCmpBarrier*>(barrier_address);
169 barrier->verify();
170 return barrier;
171 }
172
173 static void set_immediate(nmethod* nm, jint val, int bit_mask) {
174 NativeNMethodCmpBarrier* cmp1 = native_nmethod_barrier(nm);
175 cmp1->set_immediate(val, bit_mask);
176
177 if (!nm->is_osr_method() && nm->method()->has_scalarized_args()) {
178 // nmethods with scalarized arguments have multiple entry points that each have an own nmethod entry barrier
179 assert(nm->verified_entry_point() != nm->verified_inline_entry_point(), "scalarized entry point not found");
180 address method_body = nm->is_compiled_by_c1() ? nm->verified_inline_entry_point() : nm->verified_entry_point();
181 address entry_point2 = nm->is_compiled_by_c1() ? nm->verified_entry_point() : nm->verified_inline_entry_point();
182
183 int barrier_offset = reinterpret_cast<address>(cmp1) - method_body;
184 NativeNMethodCmpBarrier* cmp2 = reinterpret_cast<NativeNMethodCmpBarrier*>(entry_point2 + barrier_offset);
185 assert(cmp1 != cmp2, "sanity");
186 DEBUG_ONLY(cmp2->verify());
187 cmp2->set_immediate(val, bit_mask);
188
189 if (method_body != nm->verified_inline_ro_entry_point() && entry_point2 != nm->verified_inline_ro_entry_point()) {
190 NativeNMethodCmpBarrier* cmp3 = reinterpret_cast<NativeNMethodCmpBarrier*>(nm->verified_inline_ro_entry_point() + barrier_offset);
191 assert(cmp1 != cmp3 && cmp2 != cmp3, "sanity");
192 DEBUG_ONLY(cmp3->verify());
193 cmp3->set_immediate(val, bit_mask);
194 }
195 }
196 }
197
198 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
199 if (!supports_entry_barrier(nm)) {
200 return;
201 }
202
203 set_immediate(nm, value, bit_mask);
204 }
205
206 int BarrierSetNMethod::guard_value(nmethod* nm) {
207 if (!supports_entry_barrier(nm)) {
208 return disarmed_guard_value();
209 }
210
211 NativeNMethodCmpBarrier* cmp = native_nmethod_barrier(nm);
212 return cmp->get_immediate();
213 }