xref: /petsc/include/petscdevicetypes.h (revision 1d27aa22b2f6148b2c4e3f06a75e0638d6493e09)
1 #pragma once
2 
3 #include <petscsys.h> /*I <petscdevicetypes.h> I*/
4 
5 // Some overzealous older gcc versions warn that the comparisons below are always true. Neat
6 // that it can detect this, but the tautology *is* the point of the static_assert()!
7 #if defined(__GNUC__) && __GNUC__ >= 6 && !PetscDefined(HAVE_WINDOWS_COMPILERS)
8   #define PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING 1
9 #else
10   #define PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING 0
11 #endif
12 
13 /* SUBMANSEC = Sys */
14 
15 /*E
16   PetscMemType - Memory type of a pointer
17 
18   Level: intermediate
19 
20   Note:
21   `PETSC_MEMTYPE_KOKKOS` depends on the Kokkos backend configuration
22 
23   Developer Notes:
24   This enum uses a function (`PetscMemTypeToString()`) to convert to string representation so
25   cannot be used in `PetscOptionsEnum()`.
26 
27   Encoding of the bitmask in binary: xxxxyyyz
28 .vb
29  z = 0                - Host memory
30  z = 1                - Device memory
31  yyy = 000            - CUDA-related memory
32  yyy = 001            - HIP-related memory
33  yyy = 010            - SYCL-related memory
34  xxxxyyy1 = 0000,0001 - CUDA memory
35  xxxxyyy1 = 0001,0001 - CUDA NVSHMEM memory
36  xxxxyyy1 = 0000,0011 - HIP memory
37  xxxxyyy1 = 0000,0101 - SYCL memory
38 .ve
39 
40   Other types of memory, e.g., CUDA managed memory, can be added when needed.
41 
42 .seealso: `PetscMemTypeToString()`, `VecGetArrayAndMemType()`,
43 `PetscSFBcastWithMemTypeBegin()`, `PetscSFReduceWithMemTypeBegin()`
44 E*/
45 typedef enum {
46   PETSC_MEMTYPE_HOST    = 0,
47   PETSC_MEMTYPE_DEVICE  = 0x01,
48   PETSC_MEMTYPE_CUDA    = 0x01,
49   PETSC_MEMTYPE_NVSHMEM = 0x11,
50   PETSC_MEMTYPE_HIP     = 0x03,
51   PETSC_MEMTYPE_SYCL    = 0x05,
52 } PetscMemType;
53 #if PetscDefined(HAVE_CUDA)
54   #define PETSC_MEMTYPE_KOKKOS PETSC_MEMTYPE_CUDA
55 #elif PetscDefined(HAVE_HIP)
56   #define PETSC_MEMTYPE_KOKKOS PETSC_MEMTYPE_HIP
57 #elif PetscDefined(HAVE_SYCL)
58   #define PETSC_MEMTYPE_KOKKOS PETSC_MEMTYPE_SYCL
59 #else
60   #define PETSC_MEMTYPE_KOKKOS PETSC_MEMTYPE_HOST
61 #endif
62 
63 #define PetscMemTypeHost(m)    (((m)&0x1) == PETSC_MEMTYPE_HOST)
64 #define PetscMemTypeDevice(m)  (((m)&0x1) == PETSC_MEMTYPE_DEVICE)
65 #define PetscMemTypeCUDA(m)    (((m)&0xF) == PETSC_MEMTYPE_CUDA)
66 #define PetscMemTypeHIP(m)     (((m)&0xF) == PETSC_MEMTYPE_HIP)
67 #define PetscMemTypeSYCL(m)    (((m)&0xF) == PETSC_MEMTYPE_SYCL)
68 #define PetscMemTypeNVSHMEM(m) ((m) == PETSC_MEMTYPE_NVSHMEM)
69 
70 #if defined(__cplusplus)
71   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
72     #pragma GCC diagnostic push
73     #pragma GCC diagnostic ignored "-Wtautological-compare"
74   #endif
75 static_assert(PetscMemTypeHost(PETSC_MEMTYPE_HOST), "");
76 static_assert(!PetscMemTypeHost(PETSC_MEMTYPE_DEVICE), "");
77 static_assert(!PetscMemTypeHost(PETSC_MEMTYPE_CUDA), "");
78 static_assert(!PetscMemTypeHost(PETSC_MEMTYPE_HIP), "");
79 static_assert(!PetscMemTypeHost(PETSC_MEMTYPE_SYCL), "");
80 static_assert(!PetscMemTypeHost(PETSC_MEMTYPE_NVSHMEM), "");
81 
82 static_assert(!PetscMemTypeDevice(PETSC_MEMTYPE_HOST), "");
83 static_assert(PetscMemTypeDevice(PETSC_MEMTYPE_DEVICE), "");
84 static_assert(PetscMemTypeDevice(PETSC_MEMTYPE_CUDA), "");
85 static_assert(PetscMemTypeDevice(PETSC_MEMTYPE_HIP), "");
86 static_assert(PetscMemTypeDevice(PETSC_MEMTYPE_SYCL), "");
87 static_assert(PetscMemTypeDevice(PETSC_MEMTYPE_NVSHMEM), "");
88 
89 static_assert(PetscMemTypeCUDA(PETSC_MEMTYPE_CUDA), "");
90 static_assert(PetscMemTypeCUDA(PETSC_MEMTYPE_NVSHMEM), "");
91   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
92     #pragma GCC diagnostic pop
93   #endif
94 #endif // __cplusplus
95 
96 PETSC_NODISCARD static inline PETSC_CONSTEXPR_14 const char *PetscMemTypeToString(PetscMemType mtype)
97 {
98 #ifdef __cplusplus
99   static_assert(PETSC_MEMTYPE_CUDA == PETSC_MEMTYPE_DEVICE, "");
100 #endif
101 #define PETSC_CASE_NAME(v) \
102   case v: \
103     return PetscStringize(v)
104 
105   switch (mtype) {
106     PETSC_CASE_NAME(PETSC_MEMTYPE_HOST);
107     /* PETSC_CASE_NAME(PETSC_MEMTYPE_DEVICE); same as PETSC_MEMTYPE_CUDA */
108     PETSC_CASE_NAME(PETSC_MEMTYPE_CUDA);
109     PETSC_CASE_NAME(PETSC_MEMTYPE_NVSHMEM);
110     PETSC_CASE_NAME(PETSC_MEMTYPE_HIP);
111     PETSC_CASE_NAME(PETSC_MEMTYPE_SYCL);
112   }
113   PetscUnreachable();
114   return "invalid";
115 #undef PETSC_CASE_NAME
116 }
117 
118 #define PETSC_OFFLOAD_VECKOKKOS_DEPRECATED PETSC_OFFLOAD_VECKOKKOS PETSC_DEPRECATED_ENUM(3, 17, 0, "PETSC_OFFLOAD_KOKKOS", )
119 
120 /*E
121   PetscOffloadMask - indicates which memory (CPU, GPU, or none) contains valid data
122 
123   Values:
124 + `PETSC_OFFLOAD_UNALLOCATED` - no memory contains valid matrix entries; NEVER used for vectors
125 . `PETSC_OFFLOAD_GPU`         - GPU has valid vector/matrix entries
126 . `PETSC_OFFLOAD_CPU`         - CPU has valid vector/matrix entries
127 . `PETSC_OFFLOAD_BOTH`        - Both GPU and CPU have valid vector/matrix entries and they match
128 - `PETSC_OFFLOAD_KOKKOS`      - Reserved for Kokkos matrix and vector. It means the offload is managed by Kokkos, thus this flag itself cannot tell you where the valid data is.
129 
130   Level: developer
131 
132   Developer Note:
133   This enum uses a function (`PetscOffloadMaskToString()`) to convert to string representation so
134   cannot be used in `PetscOptionsEnum()`.
135 
136 .seealso: `PetscOffloadMaskToString()`, `PetscOffloadMaskToMemType()`, `PetscOffloadMaskToDeviceCopyMode()`
137 E*/
138 typedef enum {
139   PETSC_OFFLOAD_UNALLOCATED          = 0x0,
140   PETSC_OFFLOAD_CPU                  = 0x1,
141   PETSC_OFFLOAD_GPU                  = 0x2,
142   PETSC_OFFLOAD_BOTH                 = 0x3,
143   PETSC_OFFLOAD_VECKOKKOS_DEPRECATED = 0x100,
144   PETSC_OFFLOAD_KOKKOS               = 0x100
145 } PetscOffloadMask;
146 
147 #define PetscOffloadUnallocated(m) ((m) == PETSC_OFFLOAD_UNALLOCATED)
148 #define PetscOffloadHost(m)        (((m)&PETSC_OFFLOAD_CPU) == PETSC_OFFLOAD_CPU)
149 #define PetscOffloadDevice(m)      (((m)&PETSC_OFFLOAD_GPU) == PETSC_OFFLOAD_GPU)
150 #define PetscOffloadBoth(m)        ((m) == PETSC_OFFLOAD_BOTH)
151 
152 #if defined(__cplusplus)
153   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
154     #pragma GCC diagnostic push
155     #pragma GCC diagnostic ignored "-Wtautological-compare"
156   #endif
157 static_assert(!PetscOffloadHost(PETSC_OFFLOAD_UNALLOCATED), "");
158 static_assert(PetscOffloadHost(PETSC_OFFLOAD_BOTH), "");
159 static_assert(!PetscOffloadHost(PETSC_OFFLOAD_GPU), "");
160 static_assert(PetscOffloadHost(PETSC_OFFLOAD_BOTH), "");
161 static_assert(!PetscOffloadHost(PETSC_OFFLOAD_KOKKOS), "");
162 
163 static_assert(!PetscOffloadDevice(PETSC_OFFLOAD_UNALLOCATED), "");
164 static_assert(!PetscOffloadDevice(PETSC_OFFLOAD_CPU), "");
165 static_assert(PetscOffloadDevice(PETSC_OFFLOAD_GPU), "");
166 static_assert(PetscOffloadDevice(PETSC_OFFLOAD_BOTH), "");
167 static_assert(!PetscOffloadDevice(PETSC_OFFLOAD_KOKKOS), "");
168 
169 static_assert(PetscOffloadBoth(PETSC_OFFLOAD_BOTH), "");
170 static_assert(!PetscOffloadBoth(PETSC_OFFLOAD_CPU), "");
171 static_assert(!PetscOffloadBoth(PETSC_OFFLOAD_GPU), "");
172 static_assert(!PetscOffloadBoth(PETSC_OFFLOAD_GPU), "");
173 static_assert(!PetscOffloadBoth(PETSC_OFFLOAD_KOKKOS), "");
174   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
175     #pragma GCC diagnostic pop
176   #endif
177 #endif // __cplusplus
178 
179 PETSC_NODISCARD static inline PETSC_CONSTEXPR_14 const char *PetscOffloadMaskToString(PetscOffloadMask mask)
180 {
181 #define PETSC_CASE_RETURN(v) \
182   case v: \
183     return PetscStringize(v)
184 
185   switch (mask) {
186     PETSC_CASE_RETURN(PETSC_OFFLOAD_UNALLOCATED);
187     PETSC_CASE_RETURN(PETSC_OFFLOAD_CPU);
188     PETSC_CASE_RETURN(PETSC_OFFLOAD_GPU);
189     PETSC_CASE_RETURN(PETSC_OFFLOAD_BOTH);
190     PETSC_CASE_RETURN(PETSC_OFFLOAD_KOKKOS);
191   }
192   PetscUnreachable();
193   return "invalid";
194 #undef PETSC_CASE_RETURN
195 }
196 
197 PETSC_NODISCARD static inline PETSC_CONSTEXPR_14 PetscMemType PetscOffloadMaskToMemType(PetscOffloadMask mask)
198 {
199   switch (mask) {
200   case PETSC_OFFLOAD_UNALLOCATED:
201   case PETSC_OFFLOAD_CPU:
202     return PETSC_MEMTYPE_HOST;
203   case PETSC_OFFLOAD_GPU:
204   case PETSC_OFFLOAD_BOTH:
205     return PETSC_MEMTYPE_DEVICE;
206   case PETSC_OFFLOAD_KOKKOS:
207     return PETSC_MEMTYPE_KOKKOS;
208   }
209   PetscUnreachable();
210   return PETSC_MEMTYPE_HOST;
211 }
212 
213 /*E
214   PetscDeviceInitType - Initialization strategy for `PetscDevice`
215 
216   Values:
217 + `PETSC_DEVICE_INIT_NONE`  - PetscDevice is never initialized
218 . `PETSC_DEVICE_INIT_LAZY`  - PetscDevice is initialized on demand
219 - `PETSC_DEVICE_INIT_EAGER` - PetscDevice is initialized as soon as possible
220 
221   Level: beginner
222 
223   Note:
224   `PETSC_DEVICE_INIT_NONE` implies that any initialization of `PetscDevice` is disallowed and
225   doing so results in an error. Useful to ensure that no accelerator is used in a program.
226 
227 .seealso: `PetscDevice`, `PetscDeviceType`, `PetscDeviceInitialize()`,
228 `PetscDeviceInitialized()`, `PetscDeviceCreate()`
229 E*/
230 typedef enum {
231   PETSC_DEVICE_INIT_NONE,
232   PETSC_DEVICE_INIT_LAZY,
233   PETSC_DEVICE_INIT_EAGER
234 } PetscDeviceInitType;
235 PETSC_EXTERN const char *const PetscDeviceInitTypes[];
236 
237 /*E
238   PetscDeviceType - Kind of accelerator device backend
239 
240   Values:
241 + `PETSC_DEVICE_HOST` - Host, no accelerator backend found
242 . `PETSC_DEVICE_CUDA` - CUDA enabled GPU
243 . `PETSC_DEVICE_HIP`  - ROCM/HIP enabled GPU
244 . `PETSC_DEVICE_SYCL` - SYCL enabled device
245 - `PETSC_DEVICE_MAX`  - Always 1 greater than the largest valid `PetscDeviceType`, invalid type, do not use
246 
247   Level: beginner
248 
249   Note:
250   One can also use the `PETSC_DEVICE_DEFAULT()` routine to get the current default `PetscDeviceType`.
251 
252 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceCreate()`, `PETSC_DEVICE_DEFAULT()`
253 E*/
254 typedef enum {
255   PETSC_DEVICE_HOST,
256   PETSC_DEVICE_CUDA,
257   PETSC_DEVICE_HIP,
258   PETSC_DEVICE_SYCL,
259   PETSC_DEVICE_MAX
260 } PetscDeviceType;
261 PETSC_EXTERN const char *const PetscDeviceTypes[];
262 
263 /*E
264   PetscDeviceAttribute - Attribute detailing a property or feature of a `PetscDevice`
265 
266   Values:
267 + `PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK` - The maximum amount of shared memory per block in a device kernel
268 - `PETSC_DEVICE_ATTR_MAX`                         - Invalid attribute, do not use
269 
270   Level: beginner
271 
272 .seealso: `PetscDevice`, `PetscDeviceGetAttribute()`
273 E*/
274 typedef enum {
275   PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK,
276   PETSC_DEVICE_ATTR_MAX
277 } PetscDeviceAttribute;
278 PETSC_EXTERN const char *const PetscDeviceAttributes[];
279 
280 /*S
281   PetscDevice - Object to manage an accelerator "device" (usually a GPU)
282 
283   Level: beginner
284 
285   Notes:
286   This object is used to house configuration and state of a device, but does not offer any
287   ability to interact with or drive device computation. This functionality is facilitated
288   instead by the `PetscDeviceContext` object.
289 
290 .seealso: `PetscDeviceType`, `PetscDeviceInitType`, `PetscDeviceCreate()`,
291 `PetscDeviceConfigure()`, `PetscDeviceDestroy()`, `PetscDeviceContext`,
292 `PetscDeviceContextSetDevice()`, `PetscDeviceContextGetDevice()`, `PetscDeviceGetAttribute()`
293 S*/
294 typedef struct _n_PetscDevice *PetscDevice;
295 
296 /*E
297   PetscStreamType - Stream blocking mode, indicates how a stream implementation will interact
298   with the default `NULL` stream, which is usually blocking.
299 
300   Values:
301 + `PETSC_STREAM_GLOBAL_BLOCKING`    - Alias for `NULL` stream. Any stream of this type will block the host for all other streams to finish work before starting its operations.
302 . `PETSC_STREAM_DEFAULT_BLOCKING`   - Stream will act independent of other streams, but will still be blocked by actions on the `NULL` stream.
303 . `PETSC_STREAM_GLOBAL_NONBLOCKING` - Stream is truly asynchronous, and is blocked by nothing, not even the `NULL` stream.
304 - `PETSC_STREAM_MAX`                - Always 1 greater than the largest `PetscStreamType`, do not use
305 
306   Level: intermediate
307 
308 .seealso: `PetscDeviceContextSetStreamType()`, `PetscDeviceContextGetStreamType()`
309 E*/
310 typedef enum {
311   PETSC_STREAM_GLOBAL_BLOCKING,
312   PETSC_STREAM_DEFAULT_BLOCKING,
313   PETSC_STREAM_GLOBAL_NONBLOCKING,
314   PETSC_STREAM_MAX
315 } PetscStreamType;
316 PETSC_EXTERN const char *const PetscStreamTypes[];
317 
318 /*E
319   PetscDeviceContextJoinMode - Describes the type of join operation to perform in
320   `PetscDeviceContextJoin()`
321 
322   Values:
323 + `PETSC_DEVICE_CONTEXT_JOIN_DESTROY` - Destroy all incoming sub-contexts after join.
324 . `PETSC_DEVICE_CONTEXT_JOIN_SYNC`    - Synchronize incoming sub-contexts after join.
325 - `PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC` - Do not synchronize incoming sub-contexts after join.
326 
327   Level: beginner
328 
329 .seealso: `PetscDeviceContext`, `PetscDeviceContextFork()`, `PetscDeviceContextJoin()`
330 E*/
331 typedef enum {
332   PETSC_DEVICE_CONTEXT_JOIN_DESTROY,
333   PETSC_DEVICE_CONTEXT_JOIN_SYNC,
334   PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC
335 } PetscDeviceContextJoinMode;
336 PETSC_EXTERN const char *const PetscDeviceContextJoinModes[];
337 
338 /*S
339   PetscDeviceContext - Container to manage stream dependencies and the various solver handles
340   for asynchronous device compute.
341 
342   Level: beginner
343 
344 .seealso: `PetscDevice`, `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`,
345 `PetscDeviceContextDestroy()`, `PetscDeviceContextFork()`, `PetscDeviceContextJoin()`
346 S*/
347 typedef struct _p_PetscDeviceContext *PetscDeviceContext;
348 
349 /*E
350   PetscDeviceCopyMode - Describes the copy direction of a device-aware `memcpy`
351 
352   Values:
353 + `PETSC_DEVICE_COPY_HTOH` - Copy from host memory to host memory
354 . `PETSC_DEVICE_COPY_DTOH` - Copy from device memory to host memory
355 . `PETSC_DEVICE_COPY_HTOD` - Copy from host memory to device memory
356 . `PETSC_DEVICE_COPY_DTOD` - Copy from device memory to device memory
357 - `PETSC_DEVICE_COPY_AUTO` - Infer the copy direction from the pointers
358 
359   Level: beginner
360 
361 .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMemcpy()`
362 E*/
363 typedef enum {
364   PETSC_DEVICE_COPY_HTOH,
365   PETSC_DEVICE_COPY_DTOH,
366   PETSC_DEVICE_COPY_HTOD,
367   PETSC_DEVICE_COPY_DTOD,
368   PETSC_DEVICE_COPY_AUTO,
369 } PetscDeviceCopyMode;
370 PETSC_EXTERN const char *const PetscDeviceCopyModes[];
371 
372 PETSC_NODISCARD static inline PetscDeviceCopyMode PetscOffloadMaskToDeviceCopyMode(PetscOffloadMask dest, PetscOffloadMask src)
373 {
374   PetscDeviceCopyMode mode;
375 
376   PetscFunctionBegin;
377   PetscAssertAbort(dest != PETSC_OFFLOAD_UNALLOCATED, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot copy to unallocated");
378   PetscAssertAbort(src != PETSC_OFFLOAD_UNALLOCATED, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot copy from unallocated");
379 
380   if (PetscOffloadDevice(dest)) {
381     mode = PetscOffloadHost(src) ? PETSC_DEVICE_COPY_HTOD : PETSC_DEVICE_COPY_DTOD;
382   } else {
383     mode = PetscOffloadHost(src) ? PETSC_DEVICE_COPY_HTOH : PETSC_DEVICE_COPY_DTOH;
384   }
385   PetscFunctionReturn(mode);
386 }
387 
388 PETSC_NODISCARD static inline PETSC_CONSTEXPR_14 PetscDeviceCopyMode PetscMemTypeToDeviceCopyMode(PetscMemType dest, PetscMemType src)
389 {
390   if (PetscMemTypeHost(dest)) {
391     return PetscMemTypeHost(src) ? PETSC_DEVICE_COPY_HTOH : PETSC_DEVICE_COPY_DTOH;
392   } else {
393     return PetscMemTypeDevice(src) ? PETSC_DEVICE_COPY_DTOD : PETSC_DEVICE_COPY_HTOD;
394   }
395 }
396 
397 /*E
398   PetscMemoryAccessMode - Describes the intended usage of a memory region
399 
400   Values:
401 + `PETSC_MEMORY_ACCESS_READ`       - Read only
402 . `PETSC_MEMORY_ACCESS_WRITE`      - Write only
403 - `PETSC_MEMORY_ACCESS_READ_WRITE` - Read and write
404 
405   Level: beginner
406 
407   Notes:
408   This `enum` is a bitmask with the following encoding (assuming 2 bit)\:
409 
410 .vb
411   PETSC_MEMORY_ACCESS_READ       = 0b01
412   PETSC_MEMORY_ACCESS_WRITE      = 0b10
413   PETSC_MEMORY_ACCESS_READ_WRITE = 0b11
414 
415   // consequently
416   PETSC_MEMORY_ACCESS_READ | PETSC_MEMORY_ACCESS_WRITE = PETSC_MEMORY_ACCESS_READ_WRITE
417 .ve
418 
419   The following convenience macros are also provided\:
420 
421 + `PetscMemoryAccessRead(mode)` - `true` if `mode` is any kind of read, `false` otherwise
422 - `PetscMemoryAccessWrite(mode)` - `true` if `mode` is any kind of write, `false` otherwise
423 
424   Developer Notes:
425   This enum uses a function (`PetscMemoryAccessModeToString()`) to convert values to string
426   representation, so cannot be used in `PetscOptionsEnum()`.
427 
428 .seealso: `PetscMemoryAccessModeToString()`, `PetscDevice`, `PetscDeviceContext`
429 E*/
430 typedef enum {
431   PETSC_MEMORY_ACCESS_READ       = 0x1, // 01
432   PETSC_MEMORY_ACCESS_WRITE      = 0x2, // 10
433   PETSC_MEMORY_ACCESS_READ_WRITE = 0x3, // 11
434 } PetscMemoryAccessMode;
435 
436 #define PetscMemoryAccessRead(m)  (((m)&PETSC_MEMORY_ACCESS_READ) == PETSC_MEMORY_ACCESS_READ)
437 #define PetscMemoryAccessWrite(m) (((m)&PETSC_MEMORY_ACCESS_WRITE) == PETSC_MEMORY_ACCESS_WRITE)
438 
439 #if defined(__cplusplus)
440   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
441     #pragma GCC diagnostic push
442     #pragma GCC diagnostic ignored "-Wtautological-compare"
443   #endif
444 static_assert(PetscMemoryAccessRead(PETSC_MEMORY_ACCESS_READ), "");
445 static_assert(PetscMemoryAccessRead(PETSC_MEMORY_ACCESS_READ_WRITE), "");
446 static_assert(!PetscMemoryAccessRead(PETSC_MEMORY_ACCESS_WRITE), "");
447 static_assert(PetscMemoryAccessWrite(PETSC_MEMORY_ACCESS_WRITE), "");
448 static_assert(PetscMemoryAccessWrite(PETSC_MEMORY_ACCESS_READ_WRITE), "");
449 static_assert(!PetscMemoryAccessWrite(PETSC_MEMORY_ACCESS_READ), "");
450 static_assert((PETSC_MEMORY_ACCESS_READ | PETSC_MEMORY_ACCESS_WRITE) == PETSC_MEMORY_ACCESS_READ_WRITE, "");
451   #if PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
452     #pragma GCC diagnostic pop
453   #endif
454 #endif
455 
456 PETSC_NODISCARD static inline PETSC_CONSTEXPR_14 const char *PetscMemoryAccessModeToString(PetscMemoryAccessMode mode)
457 {
458 #define PETSC_CASE_RETURN(v) \
459   case v: \
460     return PetscStringize(v)
461 
462   switch (mode) {
463     PETSC_CASE_RETURN(PETSC_MEMORY_ACCESS_READ);
464     PETSC_CASE_RETURN(PETSC_MEMORY_ACCESS_WRITE);
465     PETSC_CASE_RETURN(PETSC_MEMORY_ACCESS_READ_WRITE);
466   }
467   PetscUnreachable();
468   return "invalid";
469 #undef PETSC_CASE_RETURN
470 }
471 
472 #undef PETSC_SHOULD_SILENCE_GCC_TAUTOLOGICAL_COMPARE_WARNING
473