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