1 /*
2 * Copyright (c) 2017, 2023, 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 #ifndef SHARE_OOPS_ACCESS_INLINE_HPP
26 #define SHARE_OOPS_ACCESS_INLINE_HPP
27
28 #include "oops/access.hpp"
29
30 #include "gc/shared/barrierSet.inline.hpp"
31 #include "gc/shared/barrierSetConfig.inline.hpp"
32 #include "oops/accessBackend.inline.hpp"
33
34 // This file outlines the last 2 steps of the template pipeline of accesses going through
35 // the Access API.
36 // * Step 5.a: Barrier resolution. This step is invoked the first time a runtime-dispatch
37 // happens for an access. The appropriate BarrierSet::AccessBarrier accessor
38 // is resolved, then the function pointer is updated to that accessor for
39 // future invocations.
40 // * Step 5.b: Post-runtime dispatch. This step now casts previously unknown types such
41 // as the address type of an oop on the heap (is it oop* or narrowOop*) to
42 // the appropriate type. It also splits sufficiently orthogonal accesses into
43 // different functions, such as whether the access involves oops or primitives
44 // and whether the access is performed on the heap or outside. Then the
45 // appropriate BarrierSet::AccessBarrier is called to perform the access.
46
47 namespace AccessInternal {
48 // Step 5.b: Post-runtime dispatch.
49 // This class is the last step before calling the BarrierSet::AccessBarrier.
50 // Here we make sure to figure out types that were not known prior to the
51 // runtime dispatch, such as whether an oop on the heap is oop or narrowOop.
52 // We also split orthogonal barriers such as handling primitives vs oops
53 // and on-heap vs off-heap into different calls to the barrier set.
54 template <class GCBarrierType, BarrierType type, DecoratorSet decorators>
55 struct PostRuntimeDispatch: public AllStatic { };
56
57 template <class GCBarrierType, DecoratorSet decorators>
58 struct PostRuntimeDispatch<GCBarrierType, BARRIER_STORE, decorators>: public AllStatic {
59 template <typename T>
60 static void access_barrier(void* addr, T value) {
61 GCBarrierType::store_in_heap(reinterpret_cast<T*>(addr), value);
62 }
63
64 static void oop_access_barrier(void* addr, oop value) {
65 typedef typename HeapOopType<decorators>::type OopType;
66 if (HasDecorator<decorators, IN_HEAP>::value) {
67 GCBarrierType::oop_store_in_heap(reinterpret_cast<OopType*>(addr), value);
68 } else {
69 GCBarrierType::oop_store_not_in_heap(reinterpret_cast<OopType*>(addr), value);
70 }
71 }
72 };
73
74 template <class GCBarrierType, DecoratorSet decorators>
75 struct PostRuntimeDispatch<GCBarrierType, BARRIER_LOAD, decorators>: public AllStatic {
76 template <typename T>
77 static T access_barrier(void* addr) {
78 return GCBarrierType::load_in_heap(reinterpret_cast<T*>(addr));
79 }
80
81 static oop oop_access_barrier(void* addr) {
82 typedef typename HeapOopType<decorators>::type OopType;
83 if (HasDecorator<decorators, IN_HEAP>::value) {
84 return GCBarrierType::oop_load_in_heap(reinterpret_cast<OopType*>(addr));
85 } else {
86 return GCBarrierType::oop_load_not_in_heap(reinterpret_cast<OopType*>(addr));
87 }
88 }
89 };
90
91 template <class GCBarrierType, DecoratorSet decorators>
92 struct PostRuntimeDispatch<GCBarrierType, BARRIER_ATOMIC_XCHG, decorators>: public AllStatic {
93 template <typename T>
94 static T access_barrier(void* addr, T new_value) {
95 return GCBarrierType::atomic_xchg_in_heap(reinterpret_cast<T*>(addr), new_value);
96 }
97
98 static oop oop_access_barrier(void* addr, oop new_value) {
99 typedef typename HeapOopType<decorators>::type OopType;
100 if (HasDecorator<decorators, IN_HEAP>::value) {
101 return GCBarrierType::oop_atomic_xchg_in_heap(reinterpret_cast<OopType*>(addr), new_value);
102 } else {
103 return GCBarrierType::oop_atomic_xchg_not_in_heap(reinterpret_cast<OopType*>(addr), new_value);
104 }
105 }
106 };
107
108 template <class GCBarrierType, DecoratorSet decorators>
109 struct PostRuntimeDispatch<GCBarrierType, BARRIER_ATOMIC_CMPXCHG, decorators>: public AllStatic {
110 template <typename T>
111 static T access_barrier(void* addr, T compare_value, T new_value) {
112 return GCBarrierType::atomic_cmpxchg_in_heap(reinterpret_cast<T*>(addr), compare_value, new_value);
113 }
114
115 static oop oop_access_barrier(void* addr, oop compare_value, oop new_value) {
116 typedef typename HeapOopType<decorators>::type OopType;
117 if (HasDecorator<decorators, IN_HEAP>::value) {
118 return GCBarrierType::oop_atomic_cmpxchg_in_heap(reinterpret_cast<OopType*>(addr), compare_value, new_value);
119 } else {
120 return GCBarrierType::oop_atomic_cmpxchg_not_in_heap(reinterpret_cast<OopType*>(addr), compare_value, new_value);
121 }
122 }
123 };
124
125 template <class GCBarrierType, DecoratorSet decorators>
126 struct PostRuntimeDispatch<GCBarrierType, BARRIER_ARRAYCOPY, decorators>: public AllStatic {
127 template <typename T>
128 static bool access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
129 arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
130 size_t length) {
131 GCBarrierType::arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw,
132 dst_obj, dst_offset_in_bytes, dst_raw,
133 length);
134 return true;
135 }
136
137 template <typename T>
138 static bool oop_access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
139 arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
140 size_t length) {
141 typedef typename HeapOopType<decorators>::type OopType;
142 return GCBarrierType::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, reinterpret_cast<OopType*>(src_raw),
143 dst_obj, dst_offset_in_bytes, reinterpret_cast<OopType*>(dst_raw),
144 length);
145 }
146 };
147
148 template <class GCBarrierType, DecoratorSet decorators>
149 struct PostRuntimeDispatch<GCBarrierType, BARRIER_STORE_AT, decorators>: public AllStatic {
150 template <typename T>
151 static void access_barrier(oop base, ptrdiff_t offset, T value) {
152 GCBarrierType::store_in_heap_at(base, offset, value);
153 }
154
155 static void oop_access_barrier(oop base, ptrdiff_t offset, oop value) {
156 GCBarrierType::oop_store_in_heap_at(base, offset, value);
157 }
158 };
159
160 template <class GCBarrierType, DecoratorSet decorators>
161 struct PostRuntimeDispatch<GCBarrierType, BARRIER_LOAD_AT, decorators>: public AllStatic {
162 template <typename T>
163 static T access_barrier(oop base, ptrdiff_t offset) {
164 return GCBarrierType::template load_in_heap_at<T>(base, offset);
165 }
166
167 static oop oop_access_barrier(oop base, ptrdiff_t offset) {
168 return GCBarrierType::oop_load_in_heap_at(base, offset);
169 }
170 };
171
172 template <class GCBarrierType, DecoratorSet decorators>
173 struct PostRuntimeDispatch<GCBarrierType, BARRIER_ATOMIC_XCHG_AT, decorators>: public AllStatic {
174 template <typename T>
175 static T access_barrier(oop base, ptrdiff_t offset, T new_value) {
176 return GCBarrierType::atomic_xchg_in_heap_at(base, offset, new_value);
177 }
178
179 static oop oop_access_barrier(oop base, ptrdiff_t offset, oop new_value) {
180 return GCBarrierType::oop_atomic_xchg_in_heap_at(base, offset, new_value);
181 }
182 };
183
184 template <class GCBarrierType, DecoratorSet decorators>
185 struct PostRuntimeDispatch<GCBarrierType, BARRIER_ATOMIC_CMPXCHG_AT, decorators>: public AllStatic {
186 template <typename T>
187 static T access_barrier(oop base, ptrdiff_t offset, T compare_value, T new_value) {
188 return GCBarrierType::atomic_cmpxchg_in_heap_at(base, offset, compare_value, new_value);
189 }
190
191 static oop oop_access_barrier(oop base, ptrdiff_t offset, oop compare_value, oop new_value) {
192 return GCBarrierType::oop_atomic_cmpxchg_in_heap_at(base, offset, compare_value, new_value);
193 }
194 };
195
196 template <class GCBarrierType, DecoratorSet decorators>
197 struct PostRuntimeDispatch<GCBarrierType, BARRIER_CLONE, decorators>: public AllStatic {
198 static void access_barrier(oop src, oop dst, size_t size) {
199 GCBarrierType::clone_in_heap(src, dst, size);
200 }
201 };
202
203 // Resolving accessors with barriers from the barrier set happens in two steps.
204 // 1. Expand paths with runtime-decorators, e.g. is UseCompressedOops on or off.
205 // 2. Expand paths for each BarrierSet available in the system.
206 template <DecoratorSet decorators, typename FunctionPointerT, BarrierType barrier_type>
207 struct BarrierResolver: public AllStatic {
208 template <DecoratorSet ds>
209 static typename EnableIf<
210 HasDecorator<ds, INTERNAL_VALUE_IS_OOP>::value,
211 FunctionPointerT>::type
212 resolve_barrier_gc() {
213 BarrierSet* bs = BarrierSet::barrier_set();
214 assert(bs != nullptr, "GC barriers invoked before BarrierSet is set");
215 switch (bs->kind()) {
216 #define BARRIER_SET_RESOLVE_BARRIER_CLOSURE(bs_name) \
217 case BarrierSet::bs_name: { \
218 return PostRuntimeDispatch<typename BarrierSet::GetType<BarrierSet::bs_name>::type:: \
219 AccessBarrier<ds>, barrier_type, ds>::oop_access_barrier; \
220 } \
221 break;
222 FOR_EACH_CONCRETE_BARRIER_SET_DO(BARRIER_SET_RESOLVE_BARRIER_CLOSURE)
223 #undef BARRIER_SET_RESOLVE_BARRIER_CLOSURE
224
225 default:
226 fatal("BarrierSet AccessBarrier resolving not implemented");
227 return nullptr;
228 };
229 }
230
231 template <DecoratorSet ds>
232 static typename EnableIf<
233 !HasDecorator<ds, INTERNAL_VALUE_IS_OOP>::value,
234 FunctionPointerT>::type
235 resolve_barrier_gc() {
236 BarrierSet* bs = BarrierSet::barrier_set();
237 assert(bs != nullptr, "GC barriers invoked before BarrierSet is set");
238 switch (bs->kind()) {
239 #define BARRIER_SET_RESOLVE_BARRIER_CLOSURE(bs_name) \
240 case BarrierSet::bs_name: { \
241 return PostRuntimeDispatch<typename BarrierSet::GetType<BarrierSet::bs_name>::type:: \
242 AccessBarrier<ds>, barrier_type, ds>::access_barrier; \
243 } \
244 break;
245 FOR_EACH_CONCRETE_BARRIER_SET_DO(BARRIER_SET_RESOLVE_BARRIER_CLOSURE)
246 #undef BARRIER_SET_RESOLVE_BARRIER_CLOSURE
247
248 default:
249 fatal("BarrierSet AccessBarrier resolving not implemented");
250 return nullptr;
251 };
252 }
253
254 static FunctionPointerT resolve_barrier_rt() {
255 if (UseCompressedOops) {
256 const DecoratorSet expanded_decorators = decorators | INTERNAL_RT_USE_COMPRESSED_OOPS;
257 return resolve_barrier_gc<expanded_decorators>();
258 } else {
259 return resolve_barrier_gc<decorators>();
260 }
261 }
262
263 static FunctionPointerT resolve_barrier() {
264 return resolve_barrier_rt();
265 }
266 };
267
268 // Step 5.a: Barrier resolution
269 // The RuntimeDispatch class is responsible for performing a runtime dispatch of the
270 // accessor. This is required when the access either depends on whether compressed oops
271 // is being used, or it depends on which GC implementation was chosen (e.g. requires GC
272 // barriers). The way it works is that a function pointer initially pointing to an
273 // accessor resolution function gets called for each access. Upon first invocation,
274 // it resolves which accessor to be used in future invocations and patches the
275 // function pointer to this new accessor.
276
277 template <DecoratorSet decorators, typename T>
278 void RuntimeDispatch<decorators, T, BARRIER_STORE>::store_init(void* addr, T value) {
279 func_t function = BarrierResolver<decorators, func_t, BARRIER_STORE>::resolve_barrier();
280 _store_func = function;
281 function(addr, value);
282 }
283
284 template <DecoratorSet decorators, typename T>
285 void RuntimeDispatch<decorators, T, BARRIER_STORE_AT>::store_at_init(oop base, ptrdiff_t offset, T value) {
286 func_t function = BarrierResolver<decorators, func_t, BARRIER_STORE_AT>::resolve_barrier();
287 _store_at_func = function;
288 function(base, offset, value);
289 }
290
291 template <DecoratorSet decorators, typename T>
292 T RuntimeDispatch<decorators, T, BARRIER_LOAD>::load_init(void* addr) {
293 func_t function = BarrierResolver<decorators, func_t, BARRIER_LOAD>::resolve_barrier();
294 _load_func = function;
295 return function(addr);
296 }
297
298 template <DecoratorSet decorators, typename T>
299 T RuntimeDispatch<decorators, T, BARRIER_LOAD_AT>::load_at_init(oop base, ptrdiff_t offset) {
300 func_t function = BarrierResolver<decorators, func_t, BARRIER_LOAD_AT>::resolve_barrier();
301 _load_at_func = function;
302 return function(base, offset);
303 }
304
305 template <DecoratorSet decorators, typename T>
306 T RuntimeDispatch<decorators, T, BARRIER_ATOMIC_CMPXCHG>::atomic_cmpxchg_init(void* addr, T compare_value, T new_value) {
307 func_t function = BarrierResolver<decorators, func_t, BARRIER_ATOMIC_CMPXCHG>::resolve_barrier();
308 _atomic_cmpxchg_func = function;
309 return function(addr, compare_value, new_value);
310 }
311
312 template <DecoratorSet decorators, typename T>
313 T RuntimeDispatch<decorators, T, BARRIER_ATOMIC_CMPXCHG_AT>::atomic_cmpxchg_at_init(oop base, ptrdiff_t offset, T compare_value, T new_value) {
314 func_t function = BarrierResolver<decorators, func_t, BARRIER_ATOMIC_CMPXCHG_AT>::resolve_barrier();
315 _atomic_cmpxchg_at_func = function;
316 return function(base, offset, compare_value, new_value);
317 }
318
319 template <DecoratorSet decorators, typename T>
320 T RuntimeDispatch<decorators, T, BARRIER_ATOMIC_XCHG>::atomic_xchg_init(void* addr, T new_value) {
321 func_t function = BarrierResolver<decorators, func_t, BARRIER_ATOMIC_XCHG>::resolve_barrier();
322 _atomic_xchg_func = function;
323 return function(addr, new_value);
324 }
325
326 template <DecoratorSet decorators, typename T>
327 T RuntimeDispatch<decorators, T, BARRIER_ATOMIC_XCHG_AT>::atomic_xchg_at_init(oop base, ptrdiff_t offset, T new_value) {
328 func_t function = BarrierResolver<decorators, func_t, BARRIER_ATOMIC_XCHG_AT>::resolve_barrier();
329 _atomic_xchg_at_func = function;
330 return function(base, offset, new_value);
331 }
332
333 template <DecoratorSet decorators, typename T>
334 bool RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>::arraycopy_init(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
335 arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
336 size_t length) {
337 func_t function = BarrierResolver<decorators, func_t, BARRIER_ARRAYCOPY>::resolve_barrier();
338 _arraycopy_func = function;
339 return function(src_obj, src_offset_in_bytes, src_raw,
340 dst_obj, dst_offset_in_bytes, dst_raw,
341 length);
342 }
343
344 template <DecoratorSet decorators, typename T>
345 void RuntimeDispatch<decorators, T, BARRIER_CLONE>::clone_init(oop src, oop dst, size_t size) {
346 func_t function = BarrierResolver<decorators, func_t, BARRIER_CLONE>::resolve_barrier();
347 _clone_func = function;
348 function(src, dst, size);
349 }
350 }
351
352 #endif // SHARE_OOPS_ACCESS_INLINE_HPP