xref: /aosp_15_r20/external/llvm-libc/libc/shared/rpc_util.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1*71db0c75SAndroid Build Coastguard Worker //===-- Shared memory RPC client / server utilities -------------*- C++ -*-===//
2*71db0c75SAndroid Build Coastguard Worker //
3*71db0c75SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*71db0c75SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*71db0c75SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*71db0c75SAndroid Build Coastguard Worker //
7*71db0c75SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*71db0c75SAndroid Build Coastguard Worker 
9*71db0c75SAndroid Build Coastguard Worker #ifndef LLVM_LIBC_SHARED_RPC_UTIL_H
10*71db0c75SAndroid Build Coastguard Worker #define LLVM_LIBC_SHARED_RPC_UTIL_H
11*71db0c75SAndroid Build Coastguard Worker 
12*71db0c75SAndroid Build Coastguard Worker #include <stddef.h>
13*71db0c75SAndroid Build Coastguard Worker #include <stdint.h>
14*71db0c75SAndroid Build Coastguard Worker 
15*71db0c75SAndroid Build Coastguard Worker #if defined(__NVPTX__) || defined(__AMDGPU__)
16*71db0c75SAndroid Build Coastguard Worker #include <gpuintrin.h>
17*71db0c75SAndroid Build Coastguard Worker #define RPC_TARGET_IS_GPU
18*71db0c75SAndroid Build Coastguard Worker #endif
19*71db0c75SAndroid Build Coastguard Worker 
20*71db0c75SAndroid Build Coastguard Worker // Workaround for missing __has_builtin in < GCC 10.
21*71db0c75SAndroid Build Coastguard Worker #ifndef __has_builtin
22*71db0c75SAndroid Build Coastguard Worker #define __has_builtin(x) 0
23*71db0c75SAndroid Build Coastguard Worker #endif
24*71db0c75SAndroid Build Coastguard Worker 
25*71db0c75SAndroid Build Coastguard Worker #ifndef RPC_INLINE
26*71db0c75SAndroid Build Coastguard Worker #define RPC_INLINE inline
27*71db0c75SAndroid Build Coastguard Worker #endif
28*71db0c75SAndroid Build Coastguard Worker 
29*71db0c75SAndroid Build Coastguard Worker namespace rpc {
30*71db0c75SAndroid Build Coastguard Worker 
31*71db0c75SAndroid Build Coastguard Worker template <typename T> struct type_identity {
32*71db0c75SAndroid Build Coastguard Worker   using type = T;
33*71db0c75SAndroid Build Coastguard Worker };
34*71db0c75SAndroid Build Coastguard Worker 
35*71db0c75SAndroid Build Coastguard Worker template <class T, T v> struct type_constant {
36*71db0c75SAndroid Build Coastguard Worker   static inline constexpr T value = v;
37*71db0c75SAndroid Build Coastguard Worker };
38*71db0c75SAndroid Build Coastguard Worker 
39*71db0c75SAndroid Build Coastguard Worker template <class T> struct remove_reference : type_identity<T> {};
40*71db0c75SAndroid Build Coastguard Worker template <class T> struct remove_reference<T &> : type_identity<T> {};
41*71db0c75SAndroid Build Coastguard Worker template <class T> struct remove_reference<T &&> : type_identity<T> {};
42*71db0c75SAndroid Build Coastguard Worker 
43*71db0c75SAndroid Build Coastguard Worker template <class T> struct is_const : type_constant<bool, false> {};
44*71db0c75SAndroid Build Coastguard Worker template <class T> struct is_const<const T> : type_constant<bool, true> {};
45*71db0c75SAndroid Build Coastguard Worker 
46*71db0c75SAndroid Build Coastguard Worker /// Freestanding implementation of std::move.
47*71db0c75SAndroid Build Coastguard Worker template <class T>
48*71db0c75SAndroid Build Coastguard Worker RPC_INLINE constexpr typename remove_reference<T>::type &&move(T &&t) {
49*71db0c75SAndroid Build Coastguard Worker   return static_cast<typename remove_reference<T>::type &&>(t);
50*71db0c75SAndroid Build Coastguard Worker }
51*71db0c75SAndroid Build Coastguard Worker 
52*71db0c75SAndroid Build Coastguard Worker /// Freestanding implementation of std::forward.
53*71db0c75SAndroid Build Coastguard Worker template <typename T>
54*71db0c75SAndroid Build Coastguard Worker RPC_INLINE constexpr T &&forward(typename remove_reference<T>::type &value) {
55*71db0c75SAndroid Build Coastguard Worker   return static_cast<T &&>(value);
56*71db0c75SAndroid Build Coastguard Worker }
57*71db0c75SAndroid Build Coastguard Worker template <typename T>
58*71db0c75SAndroid Build Coastguard Worker RPC_INLINE constexpr T &&forward(typename remove_reference<T>::type &&value) {
59*71db0c75SAndroid Build Coastguard Worker   return static_cast<T &&>(value);
60*71db0c75SAndroid Build Coastguard Worker }
61*71db0c75SAndroid Build Coastguard Worker 
62*71db0c75SAndroid Build Coastguard Worker struct in_place_t {
63*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE explicit in_place_t() = default;
64*71db0c75SAndroid Build Coastguard Worker };
65*71db0c75SAndroid Build Coastguard Worker 
66*71db0c75SAndroid Build Coastguard Worker struct nullopt_t {
67*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr explicit nullopt_t() = default;
68*71db0c75SAndroid Build Coastguard Worker };
69*71db0c75SAndroid Build Coastguard Worker 
70*71db0c75SAndroid Build Coastguard Worker constexpr inline in_place_t in_place{};
71*71db0c75SAndroid Build Coastguard Worker constexpr inline nullopt_t nullopt{};
72*71db0c75SAndroid Build Coastguard Worker 
73*71db0c75SAndroid Build Coastguard Worker /// Freestanding and minimal implementation of std::optional.
74*71db0c75SAndroid Build Coastguard Worker template <typename T> class optional {
75*71db0c75SAndroid Build Coastguard Worker   template <typename U> struct OptionalStorage {
76*71db0c75SAndroid Build Coastguard Worker     union {
77*71db0c75SAndroid Build Coastguard Worker       char empty;
78*71db0c75SAndroid Build Coastguard Worker       U stored_value;
79*71db0c75SAndroid Build Coastguard Worker     };
80*71db0c75SAndroid Build Coastguard Worker 
81*71db0c75SAndroid Build Coastguard Worker     bool in_use = false;
82*71db0c75SAndroid Build Coastguard Worker 
83*71db0c75SAndroid Build Coastguard Worker     RPC_INLINE ~OptionalStorage() { reset(); }
84*71db0c75SAndroid Build Coastguard Worker 
85*71db0c75SAndroid Build Coastguard Worker     RPC_INLINE constexpr OptionalStorage() : empty() {}
86*71db0c75SAndroid Build Coastguard Worker 
87*71db0c75SAndroid Build Coastguard Worker     template <typename... Args>
88*71db0c75SAndroid Build Coastguard Worker     RPC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args)
89*71db0c75SAndroid Build Coastguard Worker         : stored_value(forward<Args>(args)...) {}
90*71db0c75SAndroid Build Coastguard Worker 
91*71db0c75SAndroid Build Coastguard Worker     RPC_INLINE constexpr void reset() {
92*71db0c75SAndroid Build Coastguard Worker       if (in_use)
93*71db0c75SAndroid Build Coastguard Worker         stored_value.~U();
94*71db0c75SAndroid Build Coastguard Worker       in_use = false;
95*71db0c75SAndroid Build Coastguard Worker     }
96*71db0c75SAndroid Build Coastguard Worker   };
97*71db0c75SAndroid Build Coastguard Worker 
98*71db0c75SAndroid Build Coastguard Worker   OptionalStorage<T> storage;
99*71db0c75SAndroid Build Coastguard Worker 
100*71db0c75SAndroid Build Coastguard Worker public:
101*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional() = default;
102*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional(nullopt_t) {}
103*71db0c75SAndroid Build Coastguard Worker 
104*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional(const T &t) : storage(in_place, t) {
105*71db0c75SAndroid Build Coastguard Worker     storage.in_use = true;
106*71db0c75SAndroid Build Coastguard Worker   }
107*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional(const optional &) = default;
108*71db0c75SAndroid Build Coastguard Worker 
109*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) {
110*71db0c75SAndroid Build Coastguard Worker     storage.in_use = true;
111*71db0c75SAndroid Build Coastguard Worker   }
112*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional(optional &&O) = default;
113*71db0c75SAndroid Build Coastguard Worker 
114*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional &operator=(T &&t) {
115*71db0c75SAndroid Build Coastguard Worker     storage = move(t);
116*71db0c75SAndroid Build Coastguard Worker     return *this;
117*71db0c75SAndroid Build Coastguard Worker   }
118*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional &operator=(optional &&) = default;
119*71db0c75SAndroid Build Coastguard Worker 
120*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional &operator=(const T &t) {
121*71db0c75SAndroid Build Coastguard Worker     storage = t;
122*71db0c75SAndroid Build Coastguard Worker     return *this;
123*71db0c75SAndroid Build Coastguard Worker   }
124*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr optional &operator=(const optional &) = default;
125*71db0c75SAndroid Build Coastguard Worker 
126*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr void reset() { storage.reset(); }
127*71db0c75SAndroid Build Coastguard Worker 
128*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr const T &value() const & { return storage.stored_value; }
129*71db0c75SAndroid Build Coastguard Worker 
130*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr T &value() & { return storage.stored_value; }
131*71db0c75SAndroid Build Coastguard Worker 
132*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr explicit operator bool() const { return storage.in_use; }
133*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr bool has_value() const { return storage.in_use; }
134*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr const T *operator->() const {
135*71db0c75SAndroid Build Coastguard Worker     return &storage.stored_value;
136*71db0c75SAndroid Build Coastguard Worker   }
137*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr T *operator->() { return &storage.stored_value; }
138*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr const T &operator*() const & {
139*71db0c75SAndroid Build Coastguard Worker     return storage.stored_value;
140*71db0c75SAndroid Build Coastguard Worker   }
141*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr T &operator*() & { return storage.stored_value; }
142*71db0c75SAndroid Build Coastguard Worker 
143*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr T &&value() && { return move(storage.stored_value); }
144*71db0c75SAndroid Build Coastguard Worker   RPC_INLINE constexpr T &&operator*() && { return move(storage.stored_value); }
145*71db0c75SAndroid Build Coastguard Worker };
146*71db0c75SAndroid Build Coastguard Worker 
147*71db0c75SAndroid Build Coastguard Worker /// Suspend the thread briefly to assist the thread scheduler during busy loops.
148*71db0c75SAndroid Build Coastguard Worker RPC_INLINE void sleep_briefly() {
149*71db0c75SAndroid Build Coastguard Worker #if defined(__NVPTX__)
150*71db0c75SAndroid Build Coastguard Worker   if (__nvvm_reflect("__CUDA_ARCH") >= 700)
151*71db0c75SAndroid Build Coastguard Worker     asm("nanosleep.u32 64;" ::: "memory");
152*71db0c75SAndroid Build Coastguard Worker #elif defined(__AMDGPU__)
153*71db0c75SAndroid Build Coastguard Worker   __builtin_amdgcn_s_sleep(2);
154*71db0c75SAndroid Build Coastguard Worker #elif __has_builtin(__builtin_ia32_pause)
155*71db0c75SAndroid Build Coastguard Worker   __builtin_ia32_pause();
156*71db0c75SAndroid Build Coastguard Worker #elif __has_builtin(__builtin_arm_isb)
157*71db0c75SAndroid Build Coastguard Worker   __builtin_arm_isb(0xf);
158*71db0c75SAndroid Build Coastguard Worker #else
159*71db0c75SAndroid Build Coastguard Worker   // Simply do nothing if sleeping isn't supported on this platform.
160*71db0c75SAndroid Build Coastguard Worker #endif
161*71db0c75SAndroid Build Coastguard Worker }
162*71db0c75SAndroid Build Coastguard Worker 
163*71db0c75SAndroid Build Coastguard Worker /// Conditional to indicate if this process is running on the GPU.
164*71db0c75SAndroid Build Coastguard Worker RPC_INLINE constexpr bool is_process_gpu() {
165*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
166*71db0c75SAndroid Build Coastguard Worker   return true;
167*71db0c75SAndroid Build Coastguard Worker #else
168*71db0c75SAndroid Build Coastguard Worker   return false;
169*71db0c75SAndroid Build Coastguard Worker #endif
170*71db0c75SAndroid Build Coastguard Worker }
171*71db0c75SAndroid Build Coastguard Worker 
172*71db0c75SAndroid Build Coastguard Worker /// Wait for all lanes in the group to complete.
173*71db0c75SAndroid Build Coastguard Worker RPC_INLINE void sync_lane(uint64_t lane_mask) {
174*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
175*71db0c75SAndroid Build Coastguard Worker   return __gpu_sync_lane(lane_mask);
176*71db0c75SAndroid Build Coastguard Worker #endif
177*71db0c75SAndroid Build Coastguard Worker }
178*71db0c75SAndroid Build Coastguard Worker 
179*71db0c75SAndroid Build Coastguard Worker /// Copies the value from the first active thread to the rest.
180*71db0c75SAndroid Build Coastguard Worker RPC_INLINE uint32_t broadcast_value(uint64_t lane_mask, uint32_t x) {
181*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
182*71db0c75SAndroid Build Coastguard Worker   return __gpu_read_first_lane_u32(lane_mask, x);
183*71db0c75SAndroid Build Coastguard Worker #else
184*71db0c75SAndroid Build Coastguard Worker   return x;
185*71db0c75SAndroid Build Coastguard Worker #endif
186*71db0c75SAndroid Build Coastguard Worker }
187*71db0c75SAndroid Build Coastguard Worker 
188*71db0c75SAndroid Build Coastguard Worker /// Returns the number lanes that participate in the RPC interface.
189*71db0c75SAndroid Build Coastguard Worker RPC_INLINE uint32_t get_num_lanes() {
190*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
191*71db0c75SAndroid Build Coastguard Worker   return __gpu_num_lanes();
192*71db0c75SAndroid Build Coastguard Worker #else
193*71db0c75SAndroid Build Coastguard Worker   return 1;
194*71db0c75SAndroid Build Coastguard Worker #endif
195*71db0c75SAndroid Build Coastguard Worker }
196*71db0c75SAndroid Build Coastguard Worker 
197*71db0c75SAndroid Build Coastguard Worker /// Returns the id of the thread inside of an AMD wavefront executing together.
198*71db0c75SAndroid Build Coastguard Worker RPC_INLINE uint64_t get_lane_mask() {
199*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
200*71db0c75SAndroid Build Coastguard Worker   return __gpu_lane_mask();
201*71db0c75SAndroid Build Coastguard Worker #else
202*71db0c75SAndroid Build Coastguard Worker   return 1;
203*71db0c75SAndroid Build Coastguard Worker #endif
204*71db0c75SAndroid Build Coastguard Worker }
205*71db0c75SAndroid Build Coastguard Worker 
206*71db0c75SAndroid Build Coastguard Worker /// Returns the id of the thread inside of an AMD wavefront executing together.
207*71db0c75SAndroid Build Coastguard Worker RPC_INLINE uint32_t get_lane_id() {
208*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
209*71db0c75SAndroid Build Coastguard Worker   return __gpu_lane_id();
210*71db0c75SAndroid Build Coastguard Worker #else
211*71db0c75SAndroid Build Coastguard Worker   return 0;
212*71db0c75SAndroid Build Coastguard Worker #endif
213*71db0c75SAndroid Build Coastguard Worker }
214*71db0c75SAndroid Build Coastguard Worker 
215*71db0c75SAndroid Build Coastguard Worker /// Conditional that is only true for a single thread in a lane.
216*71db0c75SAndroid Build Coastguard Worker RPC_INLINE bool is_first_lane(uint64_t lane_mask) {
217*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
218*71db0c75SAndroid Build Coastguard Worker   return __gpu_is_first_in_lane(lane_mask);
219*71db0c75SAndroid Build Coastguard Worker #else
220*71db0c75SAndroid Build Coastguard Worker   return true;
221*71db0c75SAndroid Build Coastguard Worker #endif
222*71db0c75SAndroid Build Coastguard Worker }
223*71db0c75SAndroid Build Coastguard Worker 
224*71db0c75SAndroid Build Coastguard Worker /// Returns a bitmask of threads in the current lane for which \p x is true.
225*71db0c75SAndroid Build Coastguard Worker RPC_INLINE uint64_t ballot(uint64_t lane_mask, bool x) {
226*71db0c75SAndroid Build Coastguard Worker #ifdef RPC_TARGET_IS_GPU
227*71db0c75SAndroid Build Coastguard Worker   return __gpu_ballot(lane_mask, x);
228*71db0c75SAndroid Build Coastguard Worker #else
229*71db0c75SAndroid Build Coastguard Worker   return x;
230*71db0c75SAndroid Build Coastguard Worker #endif
231*71db0c75SAndroid Build Coastguard Worker }
232*71db0c75SAndroid Build Coastguard Worker 
233*71db0c75SAndroid Build Coastguard Worker /// Return \p val aligned "upwards" according to \p align.
234*71db0c75SAndroid Build Coastguard Worker template <typename V, typename A>
235*71db0c75SAndroid Build Coastguard Worker RPC_INLINE constexpr V align_up(V val, A align) {
236*71db0c75SAndroid Build Coastguard Worker   return ((val + V(align) - 1) / V(align)) * V(align);
237*71db0c75SAndroid Build Coastguard Worker }
238*71db0c75SAndroid Build Coastguard Worker 
239*71db0c75SAndroid Build Coastguard Worker /// Utility to provide a unified interface between the CPU and GPU's memory
240*71db0c75SAndroid Build Coastguard Worker /// model. On the GPU stack variables are always private to a lane so we can
241*71db0c75SAndroid Build Coastguard Worker /// simply use the variable passed in. On the CPU we need to allocate enough
242*71db0c75SAndroid Build Coastguard Worker /// space for the whole lane and index into it.
243*71db0c75SAndroid Build Coastguard Worker template <typename V> RPC_INLINE V &lane_value(V *val, uint32_t id) {
244*71db0c75SAndroid Build Coastguard Worker   if constexpr (is_process_gpu())
245*71db0c75SAndroid Build Coastguard Worker     return *val;
246*71db0c75SAndroid Build Coastguard Worker   return val[id];
247*71db0c75SAndroid Build Coastguard Worker }
248*71db0c75SAndroid Build Coastguard Worker 
249*71db0c75SAndroid Build Coastguard Worker /// Advance the \p p by \p bytes.
250*71db0c75SAndroid Build Coastguard Worker template <typename T, typename U> RPC_INLINE T *advance(T *ptr, U bytes) {
251*71db0c75SAndroid Build Coastguard Worker   if constexpr (is_const<T>::value)
252*71db0c75SAndroid Build Coastguard Worker     return reinterpret_cast<T *>(reinterpret_cast<const uint8_t *>(ptr) +
253*71db0c75SAndroid Build Coastguard Worker                                  bytes);
254*71db0c75SAndroid Build Coastguard Worker   else
255*71db0c75SAndroid Build Coastguard Worker     return reinterpret_cast<T *>(reinterpret_cast<uint8_t *>(ptr) + bytes);
256*71db0c75SAndroid Build Coastguard Worker }
257*71db0c75SAndroid Build Coastguard Worker 
258*71db0c75SAndroid Build Coastguard Worker /// Wrapper around the optimal memory copy implementation for the target.
259*71db0c75SAndroid Build Coastguard Worker RPC_INLINE void rpc_memcpy(void *dst, const void *src, size_t count) {
260*71db0c75SAndroid Build Coastguard Worker   __builtin_memcpy(dst, src, count);
261*71db0c75SAndroid Build Coastguard Worker }
262*71db0c75SAndroid Build Coastguard Worker 
263*71db0c75SAndroid Build Coastguard Worker template <class T> RPC_INLINE constexpr const T &max(const T &a, const T &b) {
264*71db0c75SAndroid Build Coastguard Worker   return (a < b) ? b : a;
265*71db0c75SAndroid Build Coastguard Worker }
266*71db0c75SAndroid Build Coastguard Worker 
267*71db0c75SAndroid Build Coastguard Worker } // namespace rpc
268*71db0c75SAndroid Build Coastguard Worker 
269*71db0c75SAndroid Build Coastguard Worker #endif // LLVM_LIBC_SHARED_RPC_UTIL_H
270