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