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