offtarget 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (886) hide show
  1. offload/__init__.py +10 -0
  2. offload/__main__.py +5 -0
  3. offload/_bundle.py +74 -0
  4. offload/_root/catalog/acc/claims.yaml +197 -0
  5. offload/_root/catalog/acc/clauses.yaml +391 -0
  6. offload/_root/catalog/acc/no_oracle.yaml +89 -0
  7. offload/_root/catalog/acc/out_of_scope.yaml +17 -0
  8. offload/_root/catalog/acc/rules.yaml +433 -0
  9. offload/_root/catalog/acc/semantic_atoms.yaml +27 -0
  10. offload/_root/catalog/acc/skeleton.yaml +1567 -0
  11. offload/_root/catalog/acc/spec_overrides.yaml +72 -0
  12. offload/_root/catalog/later_forms.yaml +76 -0
  13. offload/_root/catalog/machines.yaml +218 -0
  14. offload/_root/catalog/omp/claims.yaml +2047 -0
  15. offload/_root/catalog/omp/clauses.yaml +914 -0
  16. offload/_root/catalog/omp/derivations.yaml +177 -0
  17. offload/_root/catalog/omp/memory_modes.yaml +40 -0
  18. offload/_root/catalog/omp/no_oracle.yaml +373 -0
  19. offload/_root/catalog/omp/omp50_delta.yaml +377 -0
  20. offload/_root/catalog/omp/out_of_scope.yaml +64 -0
  21. offload/_root/catalog/omp/port_equivalence.yaml +352 -0
  22. offload/_root/catalog/omp/rules.yaml +3131 -0
  23. offload/_root/catalog/omp/rules_coverage.yaml +9939 -0
  24. offload/_root/catalog/omp/semantic_atoms.yaml +34 -0
  25. offload/_root/catalog/omp/skeleton.yaml +9181 -0
  26. offload/_root/catalog/omp/spec_overrides.yaml +932 -0
  27. offload/_root/catalog/toolchains.yaml +472 -0
  28. offload/_root/catalog/vendor_claims_generic.yaml +40925 -0
  29. offload/_root/data/canuse/b.json +1 -0
  30. offload/_root/data/canuse/c.json +1 -0
  31. offload/_root/data/canuse/columns.json +1 -0
  32. offload/_root/data/canuse/d.json +1 -0
  33. offload/_root/data/canuse/f.json +1 -0
  34. offload/_root/data/canuse/index.json +1 -0
  35. offload/_root/data/canuse/p.json +1 -0
  36. offload/_root/data/compilers/afar.json +1 -0
  37. offload/_root/data/compilers/cce19.json +1 -0
  38. offload/_root/data/compilers/cce20.json +1 -0
  39. offload/_root/data/compilers/cce21.json +1 -0
  40. offload/_root/data/compilers/cce21_4.json +1 -0
  41. offload/_root/data/compilers/gcc.json +1 -0
  42. offload/_root/data/compilers/gcc_sm80.json +1 -0
  43. offload/_root/data/compilers/nvhpc.json +1 -0
  44. offload/_root/data/compilers/nvhpc269.json +1 -0
  45. offload/_root/data/findings/AUTHORING.md +107 -0
  46. offload/_root/data/findings/README.md +33 -0
  47. offload/_root/data/findings/afar-c-error-directive-in-target-link.c +37 -0
  48. offload/_root/data/findings/afar-c-lastprivate-distribute-parallel-for-small-type.c +56 -0
  49. offload/_root/data/findings/afar-c-teams-distribute-reduction-first-team.c +59 -0
  50. offload/_root/data/findings/afar-c-update-iterator-copy-mode.c +45 -0
  51. offload/_root/data/findings/afar-c-xnack1-target-is-present.c +31 -0
  52. offload/_root/data/findings/afar-cce19-character-defaultmap.f90 +27 -0
  53. offload/_root/data/findings/afar-cce19-task-in-target-ice.f90 +23 -0
  54. offload/_root/data/findings/afar-clang-requires-device-safesync-hang.c +24 -0
  55. offload/_root/data/findings/afar-flang-firstprivate-map-two-targets.f90 +34 -0
  56. offload/_root/data/findings/afar-flang-loop-bind-teams-reduction.f90 +27 -0
  57. offload/_root/data/findings/afar-flang-task-reduction-max.f90 +31 -0
  58. offload/_root/data/findings/afar-flang-thread-limit-target-parallel-do.f90 +22 -0
  59. offload/_root/data/findings/afar-linear-ref-omp50.f90 +29 -0
  60. offload/_root/data/findings/afar-loop-bind-thread-in-teams.f90 +44 -0
  61. offload/_root/data/findings/afar-nontemporal-assumed-shape-ice.f90 +38 -0
  62. offload/_root/data/findings/afar-udr-derived-type-link.f90 +41 -0
  63. offload/_root/data/findings/afar-xnack1-atomic-min-nested-parallel.f90 +53 -0
  64. offload/_root/data/findings/afar-xnack1-present-not-enforced.f90 +36 -0
  65. offload/_root/data/findings/afar-xnack1-target-is-present.f90 +47 -0
  66. offload/_root/data/findings/apps/afar-cce-teams-private-array-nested-parallel-wrong.f90 +100 -0
  67. offload/_root/data/findings/apps/cce20-device-link-infer-address-spaces-crash.f90 +159 -0
  68. offload/_root/data/findings/apps/cce21-device-link-agpr-copy-mfma-assert.f90 +343 -0
  69. offload/_root/data/findings/apps/cce21-private-array-loaded-subscript-wrong.f90 +113 -0
  70. offload/_root/data/findings/apps/controls/bubble-simple-rddot.f90 +284 -0
  71. offload/_root/data/findings/apps/controls/bubble-two-substeps.f90 +287 -0
  72. offload/_root/data/findings/apps/controls/cbc-inline.f90 +97 -0
  73. offload/_root/data/findings/apps/controls/cbc-integers.f90 +91 -0
  74. offload/_root/data/findings/apps/controls/cbc-only-density.f90 +97 -0
  75. offload/_root/data/findings/apps/controls/cbc-only-velocity.f90 +97 -0
  76. offload/_root/data/findings/apps/controls/gcc-acc-c-tile-control.c +26 -0
  77. offload/_root/data/findings/apps/controls/gcc-acc-tile-control.f90 +29 -0
  78. offload/_root/data/findings/apps/controls/private-array-subscript-probes.f90 +45 -0
  79. offload/_root/data/findings/apps/controls/teams-private-array-read-par.f90 +31 -0
  80. offload/_root/data/findings/apps/controls/teams-private-array-seq.f90 +30 -0
  81. offload/_root/data/findings/apps/controls/teams-private-array-shared.f90 +34 -0
  82. offload/_root/data/findings/apps/controls/teams-private-array-split.f90 +36 -0
  83. offload/_root/data/findings/apps/controls/teams-private-array-write-par.f90 +31 -0
  84. offload/_root/data/findings/apps/gcc-acc-c-tile-partial-tiles-wrong.c +60 -0
  85. offload/_root/data/findings/apps/gcc-acc-tile-partial-tiles-wrong.f90 +64 -0
  86. offload/_root/data/findings/catalog/afar-c-complex-reduction-link.c +66 -0
  87. offload/_root/data/findings/catalog/afar-c-critical-hint-link.c +68 -0
  88. offload/_root/data/findings/catalog/afar-c-ordered-doacross-link.c +62 -0
  89. offload/_root/data/findings/catalog/afar-c-reduction-task-link.c +81 -0
  90. offload/_root/data/findings/catalog/afar-flang-absent-optional-target-crash.f90 +76 -0
  91. offload/_root/data/findings/catalog/afar-flang-critical-target-parallel-wrong.f90 +74 -0
  92. offload/_root/data/findings/catalog/afar-flang-dist-schedule-chunk.f90 +80 -0
  93. offload/_root/data/findings/catalog/afar-flang-linear-parallel-ice.f90 +68 -0
  94. offload/_root/data/findings/catalog/afar-flang-named-critical-link.f90 +67 -0
  95. offload/_root/data/findings/catalog/afar-flang-ordered-wrong.f90 +78 -0
  96. offload/_root/data/findings/catalog/afar-flang-reduction-minus-rejected.f90 +77 -0
  97. offload/_root/data/findings/catalog/afar-flang-reduction-task-ice.f90 +68 -0
  98. offload/_root/data/findings/catalog/afar-flang-sections-in-target-ice.f90 +67 -0
  99. offload/_root/data/findings/catalog/afar-flang-workdistribute-ice.f90 +71 -0
  100. offload/_root/data/findings/catalog/c-allocate-const-mem-firstprivate-wrong.c +79 -0
  101. offload/_root/data/findings/catalog/cce-acc-allocate-in-compute-crash.f90 +65 -0
  102. offload/_root/data/findings/catalog/cce-acc-character-dummy-routine-ice.f90 +81 -0
  103. offload/_root/data/findings/catalog/cce-acc-loop-seq-in-routine-wrong.f90 +81 -0
  104. offload/_root/data/findings/catalog/cce-acc-stop-in-compute-crash.f90 +62 -0
  105. offload/_root/data/findings/catalog/cce-allocators-stack-ice.f90 +81 -0
  106. offload/_root/data/findings/catalog/cce-atomic-default-mem-order-acquire-release-rejected.f90 +61 -0
  107. offload/_root/data/findings/catalog/cce-c-allocate-align-link.c +83 -0
  108. offload/_root/data/findings/catalog/cce-c-allocate-allocator-modifier-rejected.c +77 -0
  109. offload/_root/data/findings/catalog/cce-c-device-type-directive-name-ice.c +70 -0
  110. offload/_root/data/findings/catalog/cce-c-dist-schedule-chunk.c +81 -0
  111. offload/_root/data/findings/catalog/cce-c-f-pointer-device-link.f90 +70 -0
  112. offload/_root/data/findings/catalog/cce-c-lastprivate-parallel-in-seq-loop-wrong.c +64 -0
  113. offload/_root/data/findings/catalog/cce-c-long-double-ice.c +61 -0
  114. offload/_root/data/findings/catalog/cce-c-map-iterator-ice.c +92 -0
  115. offload/_root/data/findings/catalog/cce-c-motion-iterator-rejected.c +68 -0
  116. offload/_root/data/findings/catalog/cce-c-ordered-doacross-link.c +64 -0
  117. offload/_root/data/findings/catalog/cce-c-ordered-link.c +77 -0
  118. offload/_root/data/findings/catalog/cce-c-recursive-function-ice.c +62 -0
  119. offload/_root/data/findings/catalog/cce-c-reduction-inscan-ice.c +67 -0
  120. offload/_root/data/findings/catalog/cce-c-reduction-task-link.c +73 -0
  121. offload/_root/data/findings/catalog/cce-c-sections-in-target-wrong.c +70 -0
  122. offload/_root/data/findings/catalog/cce-c-single-masked-task-link.c +66 -0
  123. offload/_root/data/findings/catalog/cce-c-task-in-target-link.c +75 -0
  124. offload/_root/data/findings/catalog/cce-c-uses-allocators-traits-rejected.c +71 -0
  125. offload/_root/data/findings/catalog/cce-flang-allocate-const-mem-ice.f90 +67 -0
  126. offload/_root/data/findings/catalog/cce-flang-allocate-in-target-crash.f90 +68 -0
  127. offload/_root/data/findings/catalog/cce-flang-lastprivate-parallel-in-seq-loop-wrong.f90 +83 -0
  128. offload/_root/data/findings/catalog/cce-flang-stop-in-target-crash.f90 +64 -0
  129. offload/_root/data/findings/catalog/cce-iterator-link.f90 +67 -0
  130. offload/_root/data/findings/catalog/cce-linear-lastprivate-ice.f90 +69 -0
  131. offload/_root/data/findings/catalog/cce-nohost-link.f90 +90 -0
  132. offload/_root/data/findings/catalog/cce-reduction-inscan-ice.f90 +66 -0
  133. offload/_root/data/findings/catalog/cce-reduction-task-link.f90 +76 -0
  134. offload/_root/data/findings/catalog/cce19-reduction-task-addrspacecast-ice.f90 +77 -0
  135. offload/_root/data/findings/catalog/cce20-c-defaultmap-private-storage-rejected.c +85 -0
  136. offload/_root/data/findings/catalog/cce20-map-self-rejected.f90 +64 -0
  137. offload/_root/data/findings/catalog/cce21-c-declare-target-local-rejected.c +73 -0
  138. offload/_root/data/findings/catalog/cce21-c-target-loop-ice.c +77 -0
  139. offload/_root/data/findings/catalog/defaultmap-present-private-array-abort.f90 +73 -0
  140. offload/_root/data/findings/catalog/nvhpc-acc-associate-illegal-address.f90 +66 -0
  141. offload/_root/data/findings/catalog/nvhpc-c-function-pointer-kernel-missing.c +68 -0
  142. offload/_root/data/findings/catalog/nvhpc-c-long-double-kernel-missing.c +64 -0
  143. offload/_root/data/findings/catalog/nvhpc-c-static-local-kernel-missing.c +73 -0
  144. offload/_root/data/findings/catalog/nvhpc-c-vla-kernel-missing.c +63 -0
  145. offload/_root/data/findings/catalog/nvhpc-flang-type-bound-kernel-missing.f90 +86 -0
  146. offload/_root/data/findings/cce-acc-init-set-runtime-assert.f90 +35 -0
  147. offload/_root/data/findings/cce-acc-reduction-200-int8-scalars-wrong.f90 +1278 -0
  148. offload/_root/data/findings/cce-acc-routine-bind-module-ice.f90 +35 -0
  149. offload/_root/data/findings/cce-acc-self-condition-ignored.f90 +17 -0
  150. offload/_root/data/findings/cce-c-dist-schedule-segv.c +34 -0
  151. offload/_root/data/findings/cce-c-map-delete-refcount.c +47 -0
  152. offload/_root/data/findings/cce-c-map-present-assert.c +45 -0
  153. offload/_root/data/findings/cce-c-openmp45-device-link.c +34 -0
  154. offload/_root/data/findings/cce-c-parallel-for-in-target.c +73 -0
  155. offload/_root/data/findings/cce-defaultmap-tofrom-explicit-map.f90 +92 -0
  156. offload/_root/data/findings/cce-mapper-next-map-copy-back.f90 +48 -0
  157. offload/_root/data/findings/cce-trim-merge-output-one-character.f90 +40 -0
  158. offload/_root/data/findings/cce-uses-allocators-traits-omp50.f90 +28 -0
  159. offload/_root/data/findings/cce19-acc-if-false-link.f90 +17 -0
  160. offload/_root/data/findings/cce19-c-reduction-63-scalars-one-wrong.c +235 -0
  161. offload/_root/data/findings/cce19-target-parallel-guarded-loop.f90 +35 -0
  162. offload/_root/data/findings/cce19-target-parallel-single-ice.f90 +38 -0
  163. offload/_root/data/findings/cce21-alloc-2e32-sum-wrong.f90 +68 -0
  164. offload/_root/data/findings/cce21-defaultmap-aggregate-flag.f90 +52 -0
  165. offload/_root/data/findings/cce21-in-reduction-assumed-shape-write-lost.f90 +61 -0
  166. offload/_root/data/findings/cce21-in-reduction-taskwait-link.f90 +59 -0
  167. offload/_root/data/findings/cce21-linear-modifier-default-none.f90 +59 -0
  168. offload/_root/data/findings/clang-c-declare-target-to-omp52.c +50 -0
  169. offload/_root/data/findings/clang-c-linear-directive-name-hang.c +30 -0
  170. offload/_root/data/findings/clang-c-map-iterator-omp51.c +51 -0
  171. offload/_root/data/findings/clang-c-shared-declare-target-combined.c +58 -0
  172. offload/_root/data/findings/dataenv/cce-value-dummy-target-write-visible.f90 +74 -0
  173. offload/_root/data/findings/defaultmap-none-loop-variable.f90 +69 -0
  174. offload/_root/data/findings/fortran-loop-bind-thread-in-parallel.f90 +53 -0
  175. offload/_root/data/findings/fortran-schedule-static-in-target.f90 +55 -0
  176. offload/_root/data/findings/fresh/cce-common-block-name-in-map-rejected.f90 +47 -0
  177. offload/_root/data/findings/fresh/cce-if-stop-in-target-ice-one-condition.f90 +38 -0
  178. offload/_root/data/findings/fresh/cce-if-stop-in-target-ice.f90 +43 -0
  179. offload/_root/data/findings/fresh/cce-list-directed-print-in-target-copy-crash.f90 +40 -0
  180. offload/_root/data/findings/fresh/cce19-acc-automatic-array-copyout-copy-crash.f90 +56 -0
  181. offload/_root/data/findings/fresh/cce21-combined-if-runtime-expr-loop-skipped.f90 +57 -0
  182. offload/_root/data/findings/fresh/nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.f90 +67 -0
  183. offload/_root/data/findings/fresh/nvhpc-teams-loop-module-allocatable-assign-fort2-crash.f90 +73 -0
  184. offload/_root/data/findings/fuzz/1131-afar.f90 +48 -0
  185. offload/_root/data/findings/fuzz/1391-cce20.f90 +40 -0
  186. offload/_root/data/findings/fuzz/14-cce20.f90 +36 -0
  187. offload/_root/data/findings/fuzz/1505-cce20.f90 +88 -0
  188. offload/_root/data/findings/fuzz/1540-afar.f90 +44 -0
  189. offload/_root/data/findings/fuzz/158-afar.f90 +63 -0
  190. offload/_root/data/findings/fuzz/16-cce20.f90 +39 -0
  191. offload/_root/data/findings/fuzz/1644-cce21.f90 +31 -0
  192. offload/_root/data/findings/fuzz/165-cce19.f90 +52 -0
  193. offload/_root/data/findings/fuzz/1749-cce20.f90 +34 -0
  194. offload/_root/data/findings/fuzz/1936-afar.f90 +61 -0
  195. offload/_root/data/findings/fuzz/1978-cce19.f90 +41 -0
  196. offload/_root/data/findings/fuzz/2014-cce19.f90 +46 -0
  197. offload/_root/data/findings/fuzz/2224-cce19.f90 +30 -0
  198. offload/_root/data/findings/fuzz/2334-cce19.f90 +57 -0
  199. offload/_root/data/findings/fuzz/536-cce19.f90 +38 -0
  200. offload/_root/data/findings/fuzz/591-cce19.f90 +54 -0
  201. offload/_root/data/findings/fuzz/650-afar.f90 +58 -0
  202. offload/_root/data/findings/fuzz/698-afar.f90 +68 -0
  203. offload/_root/data/findings/fuzz/700-afar.f90 +46 -0
  204. offload/_root/data/findings/fuzz/775-cce21.f90 +33 -0
  205. offload/_root/data/findings/fuzz/87-afar.f90 +47 -0
  206. offload/_root/data/findings/fuzz/916-afar.f90 +67 -0
  207. offload/_root/data/findings/fuzz/958-cce20.f90 +44 -0
  208. offload/_root/data/findings/fuzz/98-cce19.f90 +40 -0
  209. offload/_root/data/findings/fuzz/README.md +236 -0
  210. offload/_root/data/findings/fuzz/b1-121-afar.f90 +47 -0
  211. offload/_root/data/findings/fuzz/b1-183-afar.f90 +55 -0
  212. offload/_root/data/findings/fuzz/b1-22-cce20.f90 +38 -0
  213. offload/_root/data/findings/fuzz/b1-231-afar.f90 +56 -0
  214. offload/_root/data/findings/fuzz/b1-268-afar.f90 +46 -0
  215. offload/_root/data/findings/fuzz/b2-183-afar.f90 +57 -0
  216. offload/_root/data/findings/fuzz/b2-209-cce19.f90 +51 -0
  217. offload/_root/data/findings/fuzz/b2-213-afar.f90 +63 -0
  218. offload/_root/data/findings/fuzz/b2-52-afar.f90 +56 -0
  219. offload/_root/data/findings/fuzz/b2-68-afar.f90 +54 -0
  220. offload/_root/data/findings/fuzz/b2-85-cce20.f90 +55 -0
  221. offload/_root/data/findings/fuzz/b3-188-cce19.f90 +90 -0
  222. offload/_root/data/findings/fuzz/b3-190-cce21.f90 +88 -0
  223. offload/_root/data/findings/fuzz/b3-197-cce19.f90 +77 -0
  224. offload/_root/data/findings/fuzz/b3-250-cce20.f90 +89 -0
  225. offload/_root/data/findings/fuzz/b3-278-cce19.f90 +70 -0
  226. offload/_root/data/findings/fuzz/b3-45-cce21.f90 +83 -0
  227. offload/_root/data/findings/fuzz/b3-54-afar.f90 +67 -0
  228. offload/_root/data/findings/fuzz/b3-61-cce20.f90 +100 -0
  229. offload/_root/data/findings/fuzz/b3-9-afar.f90 +100 -0
  230. offload/_root/data/findings/fuzz/b3-9-cce21.f90 +103 -0
  231. offload/_root/data/findings/fuzz/b3-94-afar.f90 +80 -0
  232. offload/_root/data/findings/fuzz/b4-39-afar.f90 +141 -0
  233. offload/_root/data/findings/fuzz/b4-86-cce19.f90 +71 -0
  234. offload/_root/data/findings/fuzz/controls/gcc-complex-pointer-store-control.f90 +28 -0
  235. offload/_root/data/findings/fuzz/controls/gcc-declare-reduction-module-control.f90 +29 -0
  236. offload/_root/data/findings/fuzz/controls/gcc-target-simd-map-nontemporal-control.f90 +21 -0
  237. offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-mapped-control.f90 +22 -0
  238. offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-unreferenced-control.f90 +22 -0
  239. offload/_root/data/findings/fuzz/controls/nvhpc-default-none-removed-control.f90 +18 -0
  240. offload/_root/data/findings/fuzz/controls/nvhpc-default-shared-control.f90 +18 -0
  241. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-firstprivate-removed-control.f90 +15 -0
  242. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-reduction-mapped-control.f90 +17 -0
  243. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-removed-control.f90 +17 -0
  244. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-tofrom-lastprivate-control.f90 +15 -0
  245. offload/_root/data/findings/fuzz/controls/nvhpc-depend-explicit-map-control.f90 +22 -0
  246. offload/_root/data/findings/fuzz/controls/nvhpc-depend-removed-control.f90 +22 -0
  247. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-end-distribute-control.f90 +16 -0
  248. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-num-threads-removed-control.f90 +26 -0
  249. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-removed-control.f90 +22 -0
  250. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-simd-no-end-directive-control.f90 +15 -0
  251. offload/_root/data/findings/fuzz/controls/nvhpc-explicit-length-character-dummy-control.f90 +18 -0
  252. offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-array-value-control.f90 +23 -0
  253. offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-distribute-control.f90 +20 -0
  254. offload/_root/data/findings/fuzz/controls/nvhpc-map-always-first-control.f90 +15 -0
  255. offload/_root/data/findings/fuzz/controls/nvhpc-map-always-removed-control.f90 +15 -0
  256. offload/_root/data/findings/fuzz/controls/nvhpc-num-threads-removed-control.f90 +12 -0
  257. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-firstprivate-dropped-control.f90 +15 -0
  258. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-single-nowait-control.f90 +13 -0
  259. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-split-control.f90 +15 -0
  260. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-collapse-split-control.f90 +21 -0
  261. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-end-directive-control.f90 +14 -0
  262. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-no-collapse-control.f90 +19 -0
  263. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-split-no-end-directive-control.f90 +15 -0
  264. offload/_root/data/findings/fuzz/gcc-declare-reduction-module-not-found.f90 +66 -0
  265. offload/_root/data/findings/fuzz/gcc-strided-target-update-rejected.f90 +58 -0
  266. offload/_root/data/findings/fuzz/gcc-target-simd-map-nontemporal-rejected.f90 +54 -0
  267. offload/_root/data/findings/fuzz/nvhpc-allocatable-component-implicit-map-illegal-address.f90 +84 -0
  268. offload/_root/data/findings/fuzz/nvhpc-assumed-length-character-dummy-map-ice.f90 +69 -0
  269. offload/_root/data/findings/fuzz/nvhpc-default-none-reduction-lastprivate-fort1-crash.f90 +70 -0
  270. offload/_root/data/findings/fuzz/nvhpc-defaultmap-firstprivate-lastprivate-ice.f90 +71 -0
  271. offload/_root/data/findings/fuzz/nvhpc-defaultmap-to-scalar-reduction-lost.f90 +82 -0
  272. offload/_root/data/findings/fuzz/nvhpc-depend-implicit-map-illegal-address.f90 +81 -0
  273. offload/_root/data/findings/fuzz/nvhpc-distribute-parallel-num-threads-writes-lost.f90 +79 -0
  274. offload/_root/data/findings/fuzz/nvhpc-end-distribute-simd-rejected.f90 +70 -0
  275. offload/_root/data/findings/fuzz/nvhpc-loop-character-achar-unsupported.f90 +75 -0
  276. offload/_root/data/findings/fuzz/nvhpc-map-always-after-map-to-not-copied-back.f90 +70 -0
  277. offload/_root/data/findings/fuzz/nvhpc-num-threads-reduction-identity-lost.f90 +54 -0
  278. offload/_root/data/findings/fuzz/nvhpc-target-parallel-firstprivate-translate-failure.f90 +66 -0
  279. offload/_root/data/findings/fuzz/nvhpc-target-parallel-single-hang.f90 +68 -0
  280. offload/_root/data/findings/fuzz/nvhpc-target-simd-collapse-translate-failure.f90 +73 -0
  281. offload/_root/data/findings/fuzz/nvhpc-target-simd-no-end-directive-rejected.f90 +68 -0
  282. offload/_root/data/findings/fuzz/par-s1-2082-gcc_g90.f90 +71 -0
  283. offload/_root/data/findings/fuzz/s1-2370-cce21.f90 +67 -0
  284. offload/_root/data/findings/fuzz/s1-2592-cce21.f90 +56 -0
  285. offload/_root/data/findings/fuzz/s1-3463-cce21.f90 +92 -0
  286. offload/_root/data/findings/fuzz/s1-4339-afar.f90 +79 -0
  287. offload/_root/data/findings/fuzz/s1-6085-cce21.f90 +96 -0
  288. offload/_root/data/findings/fuzz/s1-6760-cce20.f90 +65 -0
  289. offload/_root/data/findings/fuzz/s1-6888-cce21.f90 +76 -0
  290. offload/_root/data/findings/fuzz/s1-7882-cce20.f90 +63 -0
  291. offload/_root/data/findings/fuzz/s1-8634-cce21.f90 +45 -0
  292. offload/_root/data/findings/fuzz/s1-91-cce21.f90 +92 -0
  293. offload/_root/data/findings/fuzz/s1-9443-afar.f90 +59 -0
  294. offload/_root/data/findings/fuzz/s1-9914-cce19.f90 +78 -0
  295. offload/_root/data/findings/gcc/controls/gcc-acc-c-detach-control.c +34 -0
  296. offload/_root/data/findings/gcc/controls/gcc-acc-detach-control.f90 +26 -0
  297. offload/_root/data/findings/gcc/controls/gcc-defaultmap-to-pointer-control.f90 +31 -0
  298. offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.c +26 -0
  299. offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.f90 +21 -0
  300. offload/_root/data/findings/gcc/gcc-acc-bind-device-type-rejected.f90 +71 -0
  301. offload/_root/data/findings/gcc/gcc-acc-c-bind-device-type-rejected.c +76 -0
  302. offload/_root/data/findings/gcc/gcc-acc-c-detach-delete-attach-underflow.c +74 -0
  303. offload/_root/data/findings/gcc/gcc-acc-c-gang-dim-rejected.c +79 -0
  304. offload/_root/data/findings/gcc/gcc-acc-c-link-in-routine-rejected.c +82 -0
  305. offload/_root/data/findings/gcc/gcc-acc-detach-delete-attach-underflow.f90 +66 -0
  306. offload/_root/data/findings/gcc/gcc-acc-gang-dim-collapse-force-rejected.f90 +69 -0
  307. offload/_root/data/findings/gcc/gcc-acc-init-set-shutdown-rejected.f90 +70 -0
  308. offload/_root/data/findings/gcc/gcc-acc-link-in-routine-rejected.f90 +82 -0
  309. offload/_root/data/findings/gcc/gcc-acc-use-device-allocatable-rejected.f90 +88 -0
  310. offload/_root/data/findings/gcc/gcc-c-motion-mapper-rejected.c +110 -0
  311. offload/_root/data/findings/gcc/gcc-c-omp60-undocumented-rejected.c +76 -0
  312. offload/_root/data/findings/gcc/gcc-c-omp60-unimplemented-rejected.c +119 -0
  313. offload/_root/data/findings/gcc/gcc-c-target-in-task-mandatory-device-not-found.c +87 -0
  314. offload/_root/data/findings/gcc/gcc-c-uses-allocators-traits-unimplemented.c +85 -0
  315. offload/_root/data/findings/gcc/gcc-declare-mapper-rejected.f90 +85 -0
  316. offload/_root/data/findings/gcc/gcc-defaultmap-to-pointer-copy-back.f90 +66 -0
  317. offload/_root/data/findings/gcc/gcc-loop-bind-default-none-rejected.f90 +93 -0
  318. offload/_root/data/findings/gcc/gcc-omp60-undocumented-rejected.f90 +72 -0
  319. offload/_root/data/findings/gcc/gcc-omp60-unimplemented-rejected.f90 +112 -0
  320. offload/_root/data/findings/gcc/gcc-target-in-task-mandatory-device-not-found.f90 +83 -0
  321. offload/_root/data/findings/gcc/gcc-uses-allocators-traits-unimplemented.f90 +78 -0
  322. offload/_root/data/findings/harvest/afar-flang-strided-target-update.f90 +96 -0
  323. offload/_root/data/findings/harvest/cce-acc-declare-create-module-allocatable-xnack1-fault.f90 +58 -0
  324. offload/_root/data/findings/harvest/cce-c-implicit-struct-map-partly-present.c +100 -0
  325. offload/_root/data/findings/harvest/cce-c-strided-target-update.c +74 -0
  326. offload/_root/data/findings/harvest/cce-ftn-target-parallel-thread-query-ice.f90 +67 -0
  327. offload/_root/data/findings/harvest/cce-named-constant-array-declare-target-routine-rejected.f90 +69 -0
  328. offload/_root/data/findings/harvest/cce-use-device-ptr-c-ptr-not-converted.f90 +64 -0
  329. offload/_root/data/findings/harvest/cce21-declare-target-link-module-variable-write-lost.f90 +63 -0
  330. offload/_root/data/findings/harvest/cce21-internal-procedure-calls-internal-in-target-fault.f90 +71 -0
  331. offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-target-only-control.c +22 -0
  332. offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-whole-control.c +23 -0
  333. offload/_root/data/findings/harvest/controls/gcc-acc-declare-create-allocatable-control.f90 +34 -0
  334. offload/_root/data/findings/harvest/controls/gcc-c-same-item-two-maps-control.c +17 -0
  335. offload/_root/data/findings/harvest/controls/gcc-c-target-data-struct-pointer-section-control.c +27 -0
  336. offload/_root/data/findings/harvest/controls/gcc-c-target-data-two-maps-control.c +20 -0
  337. offload/_root/data/findings/harvest/controls/gcc-same-item-two-maps-control.f90 +16 -0
  338. offload/_root/data/findings/harvest/controls/gcc-teams-simd-matmul-allocatable-control.f90 +27 -0
  339. offload/_root/data/findings/harvest/gcc-acc-declare-create-allocatable-not-mapped.f90 +66 -0
  340. offload/_root/data/findings/harvest/gcc-c-same-item-two-maps-rejected.c +45 -0
  341. offload/_root/data/findings/harvest/gcc-c-strided-section-rejected.c +70 -0
  342. offload/_root/data/findings/harvest/gcc-c-target-data-struct-pointer-section-ice.c +78 -0
  343. offload/_root/data/findings/harvest/gcc-c-target-data-two-maps-ice.c +49 -0
  344. offload/_root/data/findings/harvest/gcc-same-item-two-maps-rejected.f90 +46 -0
  345. offload/_root/data/findings/harvest/gcc-teams-simd-matmul-allocatable-ice.f90 +53 -0
  346. offload/_root/data/findings/harvest/nvhpc-c-target-data-if-false-use-device-ptr-garbage.c +49 -0
  347. offload/_root/data/findings/harvest/nvhpc-named-constant-array-declare-target-routine-rejected.f90 +65 -0
  348. offload/_root/data/findings/harvest/nvhpc-use-device-ptr-c-ptr-not-found.f90 +60 -0
  349. offload/_root/data/findings/harvest/nvhpc2511-target-nowait-in-parallel-for-ice.c +59 -0
  350. offload/_root/data/findings/hecbench/controls/f16-ctl-float-array.c +28 -0
  351. offload/_root/data/findings/hecbench/controls/pc-ctl-builtin-popcount.c +37 -0
  352. offload/_root/data/findings/hecbench/controls/pc-ctl-shift-sum.c +43 -0
  353. offload/_root/data/findings/hecbench/gcc-c-float16-array-in-target-lto-ice.c +65 -0
  354. offload/_root/data/findings/hecbench/gcc-c-swar-popcount-assembler-error.c +80 -0
  355. offload/_root/data/findings/index.yaml +2453 -0
  356. offload/_root/data/findings/malloc/afar-device-malloc-in-kernel-corrupts-records.c +371 -0
  357. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-direct.c +52 -0
  358. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-fence.c +57 -0
  359. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-flush.c +57 -0
  360. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-stack.c +52 -0
  361. offload/_root/data/findings/multithread-allocatable-section-maps.f90 +91 -0
  362. offload/_root/data/findings/nested/afar-cce-c-call-result-member-in-target-rejected.c +69 -0
  363. offload/_root/data/findings/nested/afar-cce-c-update-struct-and-member-rejected.c +92 -0
  364. offload/_root/data/findings/nested/afar-flang-pointer-root-allocatable-component-enter-data-fault.f90 +121 -0
  365. offload/_root/data/findings/nested/cce-c-struct-and-nested-pointee-sections-ice.c +79 -0
  366. offload/_root/data/findings/nested/cce-fortran-nested-allocatable-component-copy-fault.f90 +64 -0
  367. offload/_root/data/findings/nested/cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.f90 +93 -0
  368. offload/_root/data/findings/nested/cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.f90 +80 -0
  369. offload/_root/data/findings/nested/controls/c3-ctl-nested-data.c +27 -0
  370. offload/_root/data/findings/nested/controls/c5-acc-serial-struct-and-array.c +26 -0
  371. offload/_root/data/findings/nested/controls/e2-ctl-two-directives.c +42 -0
  372. offload/_root/data/findings/nested/controls/e3-ctl-attach-clause.c +42 -0
  373. offload/_root/data/findings/nested/controls/f3-pointer-array-only.c +33 -0
  374. offload/_root/data/findings/nested/controls/f5-ctl-copyout-exit.c +33 -0
  375. offload/_root/data/findings/nested/controls/f6-ctl-pointer-to-struct-array.c +28 -0
  376. offload/_root/data/findings/nested/controls/g2-ctl-allocatable.f90 +34 -0
  377. offload/_root/data/findings/nested/controls/g2-ctl-entered-to.f90 +34 -0
  378. offload/_root/data/findings/nested/controls/g3-ctl-from-struct-no-element.c +27 -0
  379. offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-to.c +27 -0
  380. offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-tofrom.c +27 -0
  381. offload/_root/data/findings/nested/controls/g5-ctl-element-pointers-only.f90 +36 -0
  382. offload/_root/data/findings/nested/controls/g5-ctl-root-pointer-only.f90 +36 -0
  383. offload/_root/data/findings/nested/controls/gp2-ctl-no-mapper-explicit-maps.c +24 -0
  384. offload/_root/data/findings/nested/controls/gt2-ctl-no-always.c +26 -0
  385. offload/_root/data/findings/nested/controls/gt3-ctl-always-on-structure-only.c +26 -0
  386. offload/_root/data/findings/nested/controls/j1-embedded-struct-pointer-to-struct.c +24 -0
  387. offload/_root/data/findings/nested/controls/j2-ctl-pointer-instead-of-embedded.c +24 -0
  388. offload/_root/data/findings/nested/controls/j6-ctl-struct-and-one-pointee-level.c +23 -0
  389. offload/_root/data/findings/nested/controls/j7-ctl-struct-and-embedded-pointee.c +24 -0
  390. offload/_root/data/findings/nested/controls/m2-ctl-variable-root.f90 +32 -0
  391. offload/_root/data/findings/nested/controls/m3-ctl-fixed-array-component.f90 +32 -0
  392. offload/_root/data/findings/nested/controls/m5-ctl-no-enter-data.f90 +33 -0
  393. offload/_root/data/findings/nested/controls/n8-ctl-fixed-array-in-component.f90 +33 -0
  394. offload/_root/data/findings/nested/controls/n9-ctl-allocatable-in-the-variable.f90 +33 -0
  395. offload/_root/data/findings/nested/controls/ns4-ctl-two-directives-struct-then-pointee.c +27 -0
  396. offload/_root/data/findings/nested/controls/ns8-ctl-enter-together-exit-split.c +26 -0
  397. offload/_root/data/findings/nested/controls/nu3-ctl-update-pointee-only.c +28 -0
  398. offload/_root/data/findings/nested/controls/p2-ctl-pointer-variable.c +27 -0
  399. offload/_root/data/findings/nested/controls/p3-ctl-direct-member.c +26 -0
  400. offload/_root/data/findings/nested/controls/q2-ctl-fixed-array.f90 +37 -0
  401. offload/_root/data/findings/nested/controls/q3-ctl-allocatable-component-of-the-variable.f90 +38 -0
  402. offload/_root/data/findings/nested/controls/ta2-ctl-no-allocatable-component.f90 +42 -0
  403. offload/_root/data/findings/nested/controls/ta3-ctl-allocatable-scalar-in-the-variable.f90 +38 -0
  404. offload/_root/data/findings/nested/controls/ta4-ctl-pointer-component-only.f90 +42 -0
  405. offload/_root/data/findings/nested/controls/u3-ctl-update-member-only.c +24 -0
  406. offload/_root/data/findings/nested/controls/u4-ctl-two-updates.c +25 -0
  407. offload/_root/data/findings/nested/controls/u5-ctl-same-list-in-map.c +25 -0
  408. offload/_root/data/findings/nested/controls/ua2-ctl-variable-root.f90 +37 -0
  409. offload/_root/data/findings/nested/controls/ua4-ctl-to-in-place-of-alloc-and-update.f90 +37 -0
  410. offload/_root/data/findings/nested/controls/v2-ctl-parent-first.c +25 -0
  411. offload/_root/data/findings/nested/controls/v4-ctl-embedded-member.c +24 -0
  412. offload/_root/data/findings/nested/gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.c +83 -0
  413. offload/_root/data/findings/nested/gcc-acc-c-parent-and-shared-pointee-one-directive.c +88 -0
  414. offload/_root/data/findings/nested/gcc-acc-c-struct-and-member-array-one-directive.c +84 -0
  415. offload/_root/data/findings/nested/gcc-c-always-on-pointer-member-section-ice.c +75 -0
  416. offload/_root/data/findings/nested/gcc-c-declare-mapper-pointer-member-section-gpu-fault.c +93 -0
  417. offload/_root/data/findings/nested/gcc-c-from-struct-and-pointee-section-rejected.c +83 -0
  418. offload/_root/data/findings/nested/gcc-fortran-pointer-component-alloc-bounds-lost.f90 +62 -0
  419. offload/_root/data/findings/nested/gcc-fortran-root-and-element-pointer-components-ice.f90 +77 -0
  420. offload/_root/data/findings/nested/nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.c +55 -0
  421. offload/_root/data/findings/nested/nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.c +86 -0
  422. offload/_root/data/findings/nvhpc-acc-atomic-update-sum-fort2-crash.f90 +53 -0
  423. offload/_root/data/findings/nvhpc-c-acc-atomic-update-sum-ice.c +43 -0
  424. offload/_root/data/findings/nvhpc-c-dist-schedule-chunk-compile-hang.c +41 -0
  425. offload/_root/data/findings/nvhpc-c-map-present-ignored.c +42 -0
  426. offload/_root/data/findings/nvhpc-c-ordered-in-target-nvcpfe-crash.c +92 -0
  427. offload/_root/data/findings/nvhpc-c-parallel-for-firstprivate-in-target.c +62 -0
  428. offload/_root/data/findings/nvhpc-c-parallel-if-in-target-nvvm-ice.c +56 -0
  429. offload/_root/data/findings/nvhpc-defaultmap-firstprivate-explicit-map.f90 +45 -0
  430. offload/_root/data/findings/nvhpc-defaultmap-from-allocatable-indexing.f90 +60 -0
  431. offload/_root/data/findings/nvhpc-depend-inoutset-ignored.f90 +64 -0
  432. offload/_root/data/findings/nvhpc-dist-schedule-chunk-illegal-address.f90 +52 -0
  433. offload/_root/data/findings/nvhpc-managed-acc-shutdown-host-access.f90 +42 -0
  434. offload/_root/data/findings/nvhpc-parallel-if-in-target-nvvm-ice.f90 +60 -0
  435. offload/_root/data/findings/oop/afar-final-dealloc-device-link.f90 +85 -0
  436. offload/_root/data/findings/oop/afar-pdt-length-parameter-not-implemented.f90 +65 -0
  437. offload/_root/data/findings/oop/cce-class-is-device-link.f90 +86 -0
  438. offload/_root/data/findings/oop/cce-copy-alloc-component-section-fault.f90 +73 -0
  439. offload/_root/data/findings/oop/cce-copy-parent-to-component-tofrom-lost.f90 +65 -0
  440. offload/_root/data/findings/oop/cce-copy-second-member-section-lost.f90 +65 -0
  441. offload/_root/data/findings/oop/cce-element-component-map-lost.f90 +64 -0
  442. offload/_root/data/findings/oop/cce-final-dealloc-device-abort.f90 +85 -0
  443. offload/_root/data/findings/oop/cce-pdt-default-mapper-rejected.f90 +71 -0
  444. offload/_root/data/findings/oop/cce-polymorphic-dispatch-device-link.f90 +100 -0
  445. offload/_root/data/findings/oop/cce-section-of-element-component-overlap.f90 +76 -0
  446. offload/_root/data/findings/oop/cce-select-type-guard-not-taken.f90 +91 -0
  447. offload/_root/data/findings/oop/cce-udr-derived-type-unsupported.f90 +69 -0
  448. offload/_root/data/findings/oop/cce21-element-component-map-lost.f90 +62 -0
  449. offload/_root/data/findings/oop/controls/alloccomp-comp-only.f90 +44 -0
  450. offload/_root/data/findings/oop/controls/alloccomp-whole-explicit-component.f90 +50 -0
  451. offload/_root/data/findings/oop/controls/alloccomp-whole.f90 +49 -0
  452. offload/_root/data/findings/oop/controls/elemsec-element.f90 +47 -0
  453. offload/_root/data/findings/oop/controls/final-local.f90 +64 -0
  454. offload/_root/data/findings/oop/controls/host-class-star-select-type.f90 +58 -0
  455. offload/_root/data/findings/oop/controls/host-firstprivate-defined-assignment.f90 +54 -0
  456. offload/_root/data/findings/oop/controls/host-lastprivate-defined-assignment.f90 +58 -0
  457. offload/_root/data/findings/oop/controls/host-private-finalization.f90 +51 -0
  458. offload/_root/data/findings/oop/controls/host-select-type.f90 +65 -0
  459. offload/_root/data/findings/oop/controls/parent-and-comp-component-only.f90 +45 -0
  460. offload/_root/data/findings/oop/controls/pdt-kind.f90 +46 -0
  461. offload/_root/data/findings/oop/controls/pdt-len-host.f90 +43 -0
  462. offload/_root/data/findings/oop/controls/pdt-len-unparameterized.f90 +46 -0
  463. offload/_root/data/findings/oop/controls/poly-dispatch-static.f90 +75 -0
  464. offload/_root/data/findings/oop/controls/section-alloc-whole-component.f90 +49 -0
  465. offload/_root/data/findings/oop/controls/section-element-whole-array.f90 +50 -0
  466. offload/_root/data/findings/oop/controls/section-element-whole-component.f90 +50 -0
  467. offload/_root/data/findings/oop/controls/section-fixed.f90 +43 -0
  468. offload/_root/data/findings/oop/controls/tbp-override.f90 +64 -0
  469. offload/_root/data/findings/oop/gcc-acc-parent-and-member-data-rejected.f90 +63 -0
  470. offload/_root/data/findings/oop/gcc-pdt-length-parameter-abort.f90 +68 -0
  471. offload/_root/data/findings/oop/gcc-polymorphic-dispatch-abort.f90 +97 -0
  472. offload/_root/data/findings/oop/gcc-vtab-not-marked-for-offload.f90 +94 -0
  473. offload/_root/data/findings/oop/nvhpc-copy-alloc-component-illegal-address.f90 +76 -0
  474. offload/_root/data/findings/oop/nvhpc-copy-pdt-length-parameter-illegal-address.f90 +69 -0
  475. offload/_root/data/findings/oop/nvhpc-named-mapper-rejected.f90 +65 -0
  476. offload/_root/data/findings/oop/nvhpc-polymorphic-dispatch-translate-failure.f90 +90 -0
  477. offload/_root/data/findings/oop/nvhpc-polymorphism-runtime-unsupported.f90 +94 -0
  478. offload/_root/data/findings/oop/nvhpc-type-bound-call-rejected.f90 +89 -0
  479. offload/_root/data/findings/oop/nvhpc-udr-derived-type-rejected.f90 +67 -0
  480. offload/_root/data/findings/reviews/afar-c-error-directive-in-target-link.json +217 -0
  481. offload/_root/data/findings/reviews/afar-c-lastprivate-distribute-parallel-for-small-type.json +113 -0
  482. offload/_root/data/findings/reviews/afar-c-teams-distribute-reduction-first-team.json +130 -0
  483. offload/_root/data/findings/reviews/afar-c-update-iterator-copy-mode.json +113 -0
  484. offload/_root/data/findings/reviews/afar-cce19-character-defaultmap.json +292 -0
  485. offload/_root/data/findings/reviews/afar-clang-requires-device-safesync-hang.json +191 -0
  486. offload/_root/data/findings/reviews/afar-flang-firstprivate-map-two-targets.json +130 -0
  487. offload/_root/data/findings/reviews/afar-flang-loop-bind-teams-reduction.json +126 -0
  488. offload/_root/data/findings/reviews/afar-flang-task-reduction-max.json +113 -0
  489. offload/_root/data/findings/reviews/afar-flang-thread-limit-target-parallel-do.json +113 -0
  490. offload/_root/data/findings/reviews/afar-loop-bind-thread-in-teams.json +191 -0
  491. offload/_root/data/findings/reviews/afar-nontemporal-assumed-shape-ice.json +113 -0
  492. offload/_root/data/findings/reviews/afar-udr-derived-type-link.json +113 -0
  493. offload/_root/data/findings/reviews/afar-xnack1-atomic-min-nested-parallel.json +113 -0
  494. offload/_root/data/findings/reviews/afar-xnack1-target-is-present.json +113 -0
  495. offload/_root/data/findings/reviews/apps--afar-cce-teams-private-array-nested-parallel-wrong.json +113 -0
  496. offload/_root/data/findings/reviews/apps--cce20-device-link-infer-address-spaces-crash.json +113 -0
  497. offload/_root/data/findings/reviews/apps--cce21-device-link-agpr-copy-mfma-assert.json +113 -0
  498. offload/_root/data/findings/reviews/apps--cce21-private-array-loaded-subscript-wrong.json +113 -0
  499. offload/_root/data/findings/reviews/apps--gcc-acc-c-tile-partial-tiles-wrong.json +113 -0
  500. offload/_root/data/findings/reviews/catalog--afar-c-ordered-doacross-link.json +113 -0
  501. offload/_root/data/findings/reviews/catalog--afar-c-reduction-task-link.json +113 -0
  502. offload/_root/data/findings/reviews/catalog--afar-flang-absent-optional-target-crash.json +113 -0
  503. offload/_root/data/findings/reviews/catalog--afar-flang-critical-target-parallel-wrong.json +113 -0
  504. offload/_root/data/findings/reviews/catalog--afar-flang-dist-schedule-chunk.json +113 -0
  505. offload/_root/data/findings/reviews/catalog--afar-flang-linear-parallel-ice.json +113 -0
  506. offload/_root/data/findings/reviews/catalog--afar-flang-named-critical-link.json +126 -0
  507. offload/_root/data/findings/reviews/catalog--afar-flang-ordered-wrong.json +113 -0
  508. offload/_root/data/findings/reviews/catalog--afar-flang-reduction-minus-rejected.json +113 -0
  509. offload/_root/data/findings/reviews/catalog--afar-flang-sections-in-target-ice.json +113 -0
  510. offload/_root/data/findings/reviews/catalog--afar-flang-workdistribute-ice.json +113 -0
  511. offload/_root/data/findings/reviews/catalog--c-allocate-const-mem-firstprivate-wrong.json +113 -0
  512. offload/_root/data/findings/reviews/catalog--cce-acc-character-dummy-routine-ice.json +113 -0
  513. offload/_root/data/findings/reviews/catalog--cce-acc-loop-seq-in-routine-wrong.json +113 -0
  514. offload/_root/data/findings/reviews/catalog--cce-allocators-stack-ice.json +113 -0
  515. offload/_root/data/findings/reviews/catalog--cce-c-allocate-align-link.json +113 -0
  516. offload/_root/data/findings/reviews/catalog--cce-c-allocate-allocator-modifier-rejected.json +113 -0
  517. offload/_root/data/findings/reviews/catalog--cce-c-dist-schedule-chunk.json +113 -0
  518. offload/_root/data/findings/reviews/catalog--cce-c-f-pointer-device-link.json +130 -0
  519. offload/_root/data/findings/reviews/catalog--cce-c-long-double-ice.json +217 -0
  520. offload/_root/data/findings/reviews/catalog--cce-c-map-iterator-ice.json +217 -0
  521. offload/_root/data/findings/reviews/catalog--cce-c-recursive-function-ice.json +113 -0
  522. offload/_root/data/findings/reviews/catalog--cce-c-sections-in-target-wrong.json +113 -0
  523. offload/_root/data/findings/reviews/catalog--cce-flang-allocate-in-target-crash.json +113 -0
  524. offload/_root/data/findings/reviews/catalog--cce-flang-lastprivate-parallel-in-seq-loop-wrong.json +113 -0
  525. offload/_root/data/findings/reviews/catalog--cce-flang-stop-in-target-crash.json +113 -0
  526. offload/_root/data/findings/reviews/catalog--cce-iterator-link.json +113 -0
  527. offload/_root/data/findings/reviews/catalog--cce-linear-lastprivate-ice.json +113 -0
  528. offload/_root/data/findings/reviews/catalog--cce-reduction-inscan-ice.json +113 -0
  529. offload/_root/data/findings/reviews/catalog--cce-reduction-task-link.json +113 -0
  530. offload/_root/data/findings/reviews/catalog--cce19-reduction-task-addrspacecast-ice.json +113 -0
  531. offload/_root/data/findings/reviews/catalog--cce20-c-defaultmap-private-storage-rejected.json +217 -0
  532. offload/_root/data/findings/reviews/catalog--cce20-map-self-rejected.json +204 -0
  533. offload/_root/data/findings/reviews/catalog--cce21-c-target-loop-ice.json +126 -0
  534. offload/_root/data/findings/reviews/catalog--defaultmap-present-private-array-abort.json +236 -0
  535. offload/_root/data/findings/reviews/catalog--nvhpc-acc-associate-illegal-address.json +113 -0
  536. offload/_root/data/findings/reviews/catalog--nvhpc-c-function-pointer-kernel-missing.json +268 -0
  537. offload/_root/data/findings/reviews/cce-acc-init-set-runtime-assert.json +113 -0
  538. offload/_root/data/findings/reviews/cce-acc-reduction-200-int8-scalars-wrong.json +113 -0
  539. offload/_root/data/findings/reviews/cce-acc-routine-bind-module-ice.json +113 -0
  540. offload/_root/data/findings/reviews/cce-c-dist-schedule-segv.json +113 -0
  541. offload/_root/data/findings/reviews/cce-c-map-delete-refcount.json +149 -0
  542. offload/_root/data/findings/reviews/cce-c-map-present-assert.json +126 -0
  543. offload/_root/data/findings/reviews/cce-c-openmp45-device-link.json +113 -0
  544. offload/_root/data/findings/reviews/cce-c-parallel-for-in-target.json +141 -0
  545. offload/_root/data/findings/reviews/cce-mapper-next-map-copy-back.json +208 -0
  546. offload/_root/data/findings/reviews/cce-uses-allocators-traits-omp50.json +191 -0
  547. offload/_root/data/findings/reviews/cce19-c-reduction-63-scalars-one-wrong.json +113 -0
  548. offload/_root/data/findings/reviews/cce19-target-parallel-guarded-loop.json +113 -0
  549. offload/_root/data/findings/reviews/cce19-target-parallel-single-ice.json +113 -0
  550. offload/_root/data/findings/reviews/cce21-alloc-2e32-sum-wrong.json +113 -0
  551. offload/_root/data/findings/reviews/cce21-defaultmap-aggregate-flag.json +210 -0
  552. offload/_root/data/findings/reviews/cce21-in-reduction-assumed-shape-write-lost.json +113 -0
  553. offload/_root/data/findings/reviews/cce21-in-reduction-taskwait-link.json +113 -0
  554. offload/_root/data/findings/reviews/clang-c-declare-target-to-omp52.json +113 -0
  555. offload/_root/data/findings/reviews/clang-c-linear-directive-name-hang.json +113 -0
  556. offload/_root/data/findings/reviews/clang-c-shared-declare-target-combined.json +132 -0
  557. offload/_root/data/findings/reviews/dataenv--cce-value-dummy-target-write-visible.json +113 -0
  558. offload/_root/data/findings/reviews/defaultmap-none-loop-variable.json +113 -0
  559. offload/_root/data/findings/reviews/fortran-schedule-static-in-target.json +113 -0
  560. offload/_root/data/findings/reviews/fresh--cce-common-block-name-in-map-rejected.json +113 -0
  561. offload/_root/data/findings/reviews/fresh--cce-list-directed-print-in-target-copy-crash.json +217 -0
  562. offload/_root/data/findings/reviews/fresh--cce19-acc-automatic-array-copyout-copy-crash.json +113 -0
  563. offload/_root/data/findings/reviews/fresh--cce21-combined-if-runtime-expr-loop-skipped.json +113 -0
  564. offload/_root/data/findings/reviews/fresh--nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.json +113 -0
  565. offload/_root/data/findings/reviews/fresh--nvhpc-teams-loop-module-allocatable-assign-fort2-crash.json +113 -0
  566. offload/_root/data/findings/reviews/fuzz--1505-cce20.json +113 -0
  567. offload/_root/data/findings/reviews/fuzz--1540-afar.json +113 -0
  568. offload/_root/data/findings/reviews/fuzz--1978-cce19.json +130 -0
  569. offload/_root/data/findings/reviews/fuzz--2334-cce19.json +113 -0
  570. offload/_root/data/findings/reviews/fuzz--591-cce19.json +147 -0
  571. offload/_root/data/findings/reviews/fuzz--650-afar.json +113 -0
  572. offload/_root/data/findings/reviews/fuzz--916-afar.json +130 -0
  573. offload/_root/data/findings/reviews/fuzz--98-cce19.json +113 -0
  574. offload/_root/data/findings/reviews/fuzz--b1-121-afar.json +160 -0
  575. offload/_root/data/findings/reviews/fuzz--b2-209-cce19.json +141 -0
  576. offload/_root/data/findings/reviews/fuzz--b2-68-afar.json +113 -0
  577. offload/_root/data/findings/reviews/fuzz--b2-85-cce20.json +217 -0
  578. offload/_root/data/findings/reviews/fuzz--b3-188-cce19.json +113 -0
  579. offload/_root/data/findings/reviews/fuzz--b3-278-cce19.json +212 -0
  580. offload/_root/data/findings/reviews/fuzz--b3-54-afar.json +113 -0
  581. offload/_root/data/findings/reviews/fuzz--b3-61-cce20.json +191 -0
  582. offload/_root/data/findings/reviews/fuzz--b4-86-cce19.json +113 -0
  583. offload/_root/data/findings/reviews/fuzz--gcc-declare-reduction-module-not-found.json +113 -0
  584. offload/_root/data/findings/reviews/fuzz--gcc-target-simd-map-nontemporal-rejected.json +113 -0
  585. offload/_root/data/findings/reviews/fuzz--nvhpc-allocatable-component-implicit-map-illegal-address.json +141 -0
  586. offload/_root/data/findings/reviews/fuzz--nvhpc-assumed-length-character-dummy-map-ice.json +113 -0
  587. offload/_root/data/findings/reviews/fuzz--nvhpc-default-none-reduction-lastprivate-fort1-crash.json +113 -0
  588. offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-firstprivate-lastprivate-ice.json +113 -0
  589. offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-to-scalar-reduction-lost.json +113 -0
  590. offload/_root/data/findings/reviews/fuzz--nvhpc-depend-implicit-map-illegal-address.json +113 -0
  591. offload/_root/data/findings/reviews/fuzz--nvhpc-distribute-parallel-num-threads-writes-lost.json +113 -0
  592. offload/_root/data/findings/reviews/fuzz--nvhpc-end-distribute-simd-rejected.json +113 -0
  593. offload/_root/data/findings/reviews/fuzz--nvhpc-loop-character-achar-unsupported.json +113 -0
  594. offload/_root/data/findings/reviews/fuzz--nvhpc-map-always-after-map-to-not-copied-back.json +113 -0
  595. offload/_root/data/findings/reviews/fuzz--nvhpc-num-threads-reduction-identity-lost.json +113 -0
  596. offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-firstprivate-translate-failure.json +191 -0
  597. offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-single-hang.json +113 -0
  598. offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-collapse-translate-failure.json +113 -0
  599. offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-no-end-directive-rejected.json +113 -0
  600. offload/_root/data/findings/reviews/fuzz--par-s1-2082-gcc_g90.json +126 -0
  601. offload/_root/data/findings/reviews/fuzz--s1-2370-cce21.json +113 -0
  602. offload/_root/data/findings/reviews/fuzz--s1-2592-cce21.json +113 -0
  603. offload/_root/data/findings/reviews/fuzz--s1-3463-cce21.json +113 -0
  604. offload/_root/data/findings/reviews/fuzz--s1-6760-cce20.json +130 -0
  605. offload/_root/data/findings/reviews/fuzz--s1-6888-cce21.json +113 -0
  606. offload/_root/data/findings/reviews/fuzz--s1-7882-cce20.json +113 -0
  607. offload/_root/data/findings/reviews/fuzz--s1-8634-cce21.json +113 -0
  608. offload/_root/data/findings/reviews/fuzz--s1-91-cce21.json +113 -0
  609. offload/_root/data/findings/reviews/fuzz--s1-9443-afar.json +113 -0
  610. offload/_root/data/findings/reviews/fuzz--s1-9914-cce19.json +141 -0
  611. offload/_root/data/findings/reviews/gcc--gcc-acc-bind-device-type-rejected.json +113 -0
  612. offload/_root/data/findings/reviews/gcc--gcc-acc-detach-delete-attach-underflow.json +314 -0
  613. offload/_root/data/findings/reviews/gcc--gcc-acc-gang-dim-collapse-force-rejected.json +113 -0
  614. offload/_root/data/findings/reviews/gcc--gcc-acc-init-set-shutdown-rejected.json +113 -0
  615. offload/_root/data/findings/reviews/gcc--gcc-acc-link-in-routine-rejected.json +113 -0
  616. offload/_root/data/findings/reviews/gcc--gcc-acc-use-device-allocatable-rejected.json +113 -0
  617. offload/_root/data/findings/reviews/gcc--gcc-c-motion-mapper-rejected.json +113 -0
  618. offload/_root/data/findings/reviews/gcc--gcc-c-omp60-undocumented-rejected.json +217 -0
  619. offload/_root/data/findings/reviews/gcc--gcc-c-omp60-unimplemented-rejected.json +236 -0
  620. offload/_root/data/findings/reviews/gcc--gcc-c-target-in-task-mandatory-device-not-found.json +236 -0
  621. offload/_root/data/findings/reviews/gcc--gcc-c-uses-allocators-traits-unimplemented.json +113 -0
  622. offload/_root/data/findings/reviews/gcc--gcc-declare-mapper-rejected.json +113 -0
  623. offload/_root/data/findings/reviews/gcc--gcc-defaultmap-to-pointer-copy-back.json +217 -0
  624. offload/_root/data/findings/reviews/gcc--gcc-loop-bind-default-none-rejected.json +113 -0
  625. offload/_root/data/findings/reviews/harvest--afar-flang-strided-target-update.json +141 -0
  626. offload/_root/data/findings/reviews/harvest--cce-acc-declare-create-module-allocatable-xnack1-fault.json +113 -0
  627. offload/_root/data/findings/reviews/harvest--cce-c-implicit-struct-map-partly-present.json +217 -0
  628. offload/_root/data/findings/reviews/harvest--cce-c-strided-target-update.json +141 -0
  629. offload/_root/data/findings/reviews/harvest--cce-ftn-target-parallel-thread-query-ice.json +130 -0
  630. offload/_root/data/findings/reviews/harvest--cce-named-constant-array-declare-target-routine-rejected.json +113 -0
  631. offload/_root/data/findings/reviews/harvest--cce-use-device-ptr-c-ptr-not-converted.json +113 -0
  632. offload/_root/data/findings/reviews/harvest--cce21-declare-target-link-module-variable-write-lost.json +113 -0
  633. offload/_root/data/findings/reviews/harvest--cce21-internal-procedure-calls-internal-in-target-fault.json +158 -0
  634. offload/_root/data/findings/reviews/harvest--gcc-acc-declare-create-allocatable-not-mapped.json +204 -0
  635. offload/_root/data/findings/reviews/harvest--gcc-c-same-item-two-maps-rejected.json +143 -0
  636. offload/_root/data/findings/reviews/harvest--gcc-c-strided-section-rejected.json +164 -0
  637. offload/_root/data/findings/reviews/harvest--gcc-c-target-data-struct-pointer-section-ice.json +113 -0
  638. offload/_root/data/findings/reviews/harvest--gcc-c-target-data-two-maps-ice.json +113 -0
  639. offload/_root/data/findings/reviews/harvest--gcc-teams-simd-matmul-allocatable-ice.json +113 -0
  640. offload/_root/data/findings/reviews/harvest--nvhpc-c-target-data-if-false-use-device-ptr-garbage.json +113 -0
  641. offload/_root/data/findings/reviews/harvest--nvhpc-named-constant-array-declare-target-routine-rejected.json +126 -0
  642. offload/_root/data/findings/reviews/harvest--nvhpc-use-device-ptr-c-ptr-not-found.json +113 -0
  643. offload/_root/data/findings/reviews/harvest--nvhpc2511-target-nowait-in-parallel-for-ice.json +130 -0
  644. offload/_root/data/findings/reviews/hecbench--gcc-c-float16-array-in-target-lto-ice.json +217 -0
  645. offload/_root/data/findings/reviews/hecbench--gcc-c-swar-popcount-assembler-error.json +126 -0
  646. offload/_root/data/findings/reviews/malloc--afar-device-malloc-in-kernel-corrupts-records.json +270 -0
  647. offload/_root/data/findings/reviews/multithread-allocatable-section-maps.json +191 -0
  648. offload/_root/data/findings/reviews/nested--afar-cce-c-call-result-member-in-target-rejected.json +113 -0
  649. offload/_root/data/findings/reviews/nested--afar-cce-c-update-struct-and-member-rejected.json +126 -0
  650. offload/_root/data/findings/reviews/nested--afar-flang-pointer-root-allocatable-component-enter-data-fault.json +191 -0
  651. offload/_root/data/findings/reviews/nested--cce-c-struct-and-nested-pointee-sections-ice.json +113 -0
  652. offload/_root/data/findings/reviews/nested--cce-fortran-nested-allocatable-component-copy-fault.json +214 -0
  653. offload/_root/data/findings/reviews/nested--cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.json +191 -0
  654. offload/_root/data/findings/reviews/nested--cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.json +297 -0
  655. offload/_root/data/findings/reviews/nested--gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.json +204 -0
  656. offload/_root/data/findings/reviews/nested--gcc-acc-c-parent-and-shared-pointee-one-directive.json +130 -0
  657. offload/_root/data/findings/reviews/nested--gcc-acc-c-struct-and-member-array-one-directive.json +141 -0
  658. offload/_root/data/findings/reviews/nested--gcc-c-always-on-pointer-member-section-ice.json +113 -0
  659. offload/_root/data/findings/reviews/nested--gcc-c-declare-mapper-pointer-member-section-gpu-fault.json +113 -0
  660. offload/_root/data/findings/reviews/nested--gcc-c-from-struct-and-pointee-section-rejected.json +113 -0
  661. offload/_root/data/findings/reviews/nested--gcc-fortran-pointer-component-alloc-bounds-lost.json +217 -0
  662. offload/_root/data/findings/reviews/nested--gcc-fortran-root-and-element-pointer-components-ice.json +217 -0
  663. offload/_root/data/findings/reviews/nested--nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.json +204 -0
  664. offload/_root/data/findings/reviews/nested--nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.json +191 -0
  665. offload/_root/data/findings/reviews/nvhpc-c-acc-atomic-update-sum-ice.json +277 -0
  666. offload/_root/data/findings/reviews/nvhpc-c-dist-schedule-chunk-compile-hang.json +113 -0
  667. offload/_root/data/findings/reviews/nvhpc-c-map-present-ignored.json +113 -0
  668. offload/_root/data/findings/reviews/nvhpc-c-ordered-in-target-nvcpfe-crash.json +113 -0
  669. offload/_root/data/findings/reviews/nvhpc-c-parallel-for-firstprivate-in-target.json +113 -0
  670. offload/_root/data/findings/reviews/nvhpc-defaultmap-firstprivate-explicit-map.json +113 -0
  671. offload/_root/data/findings/reviews/nvhpc-defaultmap-from-allocatable-indexing.json +132 -0
  672. offload/_root/data/findings/reviews/nvhpc-depend-inoutset-ignored.json +113 -0
  673. offload/_root/data/findings/reviews/nvhpc-dist-schedule-chunk-illegal-address.json +113 -0
  674. offload/_root/data/findings/reviews/nvhpc-managed-acc-shutdown-host-access.json +113 -0
  675. offload/_root/data/findings/reviews/nvhpc-parallel-if-in-target-nvvm-ice.json +113 -0
  676. offload/_root/data/findings/reviews/oop--afar-final-dealloc-device-link.json +132 -0
  677. offload/_root/data/findings/reviews/oop--afar-pdt-length-parameter-not-implemented.json +113 -0
  678. offload/_root/data/findings/reviews/oop--cce-class-is-device-link.json +130 -0
  679. offload/_root/data/findings/reviews/oop--cce-copy-alloc-component-section-fault.json +217 -0
  680. offload/_root/data/findings/reviews/oop--cce-copy-parent-to-component-tofrom-lost.json +217 -0
  681. offload/_root/data/findings/reviews/oop--cce-copy-second-member-section-lost.json +113 -0
  682. offload/_root/data/findings/reviews/oop--cce-element-component-map-lost.json +113 -0
  683. offload/_root/data/findings/reviews/oop--cce-final-dealloc-device-abort.json +208 -0
  684. offload/_root/data/findings/reviews/oop--cce-pdt-default-mapper-rejected.json +191 -0
  685. offload/_root/data/findings/reviews/oop--cce-polymorphic-dispatch-device-link.json +247 -0
  686. offload/_root/data/findings/reviews/oop--cce-section-of-element-component-overlap.json +113 -0
  687. offload/_root/data/findings/reviews/oop--cce-select-type-guard-not-taken.json +191 -0
  688. offload/_root/data/findings/reviews/oop--cce-udr-derived-type-unsupported.json +132 -0
  689. offload/_root/data/findings/reviews/oop--gcc-acc-parent-and-member-data-rejected.json +141 -0
  690. offload/_root/data/findings/reviews/oop--gcc-pdt-length-parameter-abort.json +141 -0
  691. offload/_root/data/findings/reviews/oop--gcc-polymorphic-dispatch-abort.json +217 -0
  692. offload/_root/data/findings/reviews/oop--gcc-vtab-not-marked-for-offload.json +113 -0
  693. offload/_root/data/findings/reviews/oop--nvhpc-copy-alloc-component-illegal-address.json +113 -0
  694. offload/_root/data/findings/reviews/oop--nvhpc-copy-pdt-length-parameter-illegal-address.json +236 -0
  695. offload/_root/data/findings/reviews/oop--nvhpc-named-mapper-rejected.json +113 -0
  696. offload/_root/data/findings/reviews/oop--nvhpc-polymorphic-dispatch-translate-failure.json +223 -0
  697. offload/_root/data/findings/reviews/oop--nvhpc-polymorphism-runtime-unsupported.json +126 -0
  698. offload/_root/data/findings/reviews/oop--nvhpc-type-bound-call-rejected.json +113 -0
  699. offload/_root/data/findings/reviews/oop--nvhpc-udr-derived-type-rejected.json +113 -0
  700. offload/_root/data/findings/reviews/stress--afar-c-lastprivate-combined-target-not-mapped.json +113 -0
  701. offload/_root/data/findings/reviews/stress--afar-target-scalar-implicit-firstprivate.json +175 -0
  702. offload/_root/data/findings/reviews/stress--cce-acc-declare-create-scalar-serial-firstprivate.json +113 -0
  703. offload/_root/data/findings/reviews/stress--cce-c-distribute-lastprivate-lost.json +113 -0
  704. offload/_root/data/findings/reviews/stress--cce-c-flto-offload-dropped.json +204 -0
  705. offload/_root/data/findings/reviews/stress--cce-ftn-cif-record-split-abort.json +113 -0
  706. offload/_root/data/findings/reviews/stress--cce-lastprivate-num-threads-collapse-simd.json +113 -0
  707. offload/_root/data/findings/reviews/stress--cce-teams-reduction-many-teams-wrap.json +113 -0
  708. offload/_root/data/findings/reviews/stress--cce21-declare-target-scalar-map-write-lost.json +113 -0
  709. offload/_root/data/findings/reviews/stress--gcc-declare-target-allocatable-component-ice.json +191 -0
  710. offload/_root/data/findings/reviews/stress--nvhpc-acc-derived-type-component-not-copied.json +126 -0
  711. offload/_root/data/findings/reviews/stress--nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.json +113 -0
  712. offload/_root/data/findings/reviews/stress--nvhpc-declare-target-scalar-write-lost.json +113 -0
  713. offload/_root/data/findings/reviews/stress--nvhpc-lastprivate-combined-target-not-mapped.json +113 -0
  714. offload/_root/data/findings/reviews/stress--nvhpc-target-scalar-implicit-firstprivate.json +130 -0
  715. offload/_root/data/findings/reviews/target-update-sections-in-parallel.json +113 -0
  716. offload/_root/data/findings/stress/afar-c-lastprivate-combined-target-not-mapped.c +72 -0
  717. offload/_root/data/findings/stress/afar-target-scalar-implicit-firstprivate.f90 +47 -0
  718. offload/_root/data/findings/stress/cce-acc-declare-create-scalar-serial-firstprivate.f90 +43 -0
  719. offload/_root/data/findings/stress/cce-c-distribute-lastprivate-lost.c +52 -0
  720. offload/_root/data/findings/stress/cce-c-flto-offload-dropped.c +73 -0
  721. offload/_root/data/findings/stress/cce-ftn-cif-record-split-abort.f90 +109 -0
  722. offload/_root/data/findings/stress/cce-lastprivate-num-threads-collapse-simd.f90 +66 -0
  723. offload/_root/data/findings/stress/cce-teams-reduction-many-teams-wrap.f90 +55 -0
  724. offload/_root/data/findings/stress/cce21-declare-target-scalar-map-write-lost.f90 +58 -0
  725. offload/_root/data/findings/stress/controls/gcc-declare-target-allocatable-component-control.f90 +29 -0
  726. offload/_root/data/findings/stress/gcc-declare-target-allocatable-component-ice.f90 +61 -0
  727. offload/_root/data/findings/stress/nvhpc-acc-derived-type-component-not-copied.f90 +33 -0
  728. offload/_root/data/findings/stress/nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.c +62 -0
  729. offload/_root/data/findings/stress/nvhpc-declare-target-scalar-write-lost.f90 +43 -0
  730. offload/_root/data/findings/stress/nvhpc-lastprivate-combined-target-not-mapped.f90 +60 -0
  731. offload/_root/data/findings/stress/nvhpc-target-scalar-implicit-firstprivate.f90 +41 -0
  732. offload/_root/data/findings/target-update-sections-in-parallel.f90 +53 -0
  733. offload/_root/data/issues.json +1 -0
  734. offload/_root/data/search.json +1 -0
  735. offload/_root/data/vendor/checks/build.py +288 -0
  736. offload/_version.py +24 -0
  737. offload/canuse.py +88 -0
  738. offload/cli.py +52 -0
  739. offload/data.py +85 -0
  740. offload/engine/__init__.py +0 -0
  741. offload/engine/acc_c_gen.py +415 -0
  742. offload/engine/acc_gen.py +1104 -0
  743. offload/engine/acc_rules.py +656 -0
  744. offload/engine/apps.py +804 -0
  745. offload/engine/baseline.py +324 -0
  746. offload/engine/c_gen.py +883 -0
  747. offload/engine/c_program.py +913 -0
  748. offload/engine/c_rules.py +503 -0
  749. offload/engine/campaign.py +1180 -0
  750. offload/engine/campaigns/__init__.py +2 -0
  751. offload/engine/campaigns/hecbench.py +153 -0
  752. offload/engine/campaigns/nested.py +76 -0
  753. offload/engine/campaigns/oop.py +61 -0
  754. offload/engine/canary.py +275 -0
  755. offload/engine/catalog.py +860 -0
  756. offload/engine/citations.py +532 -0
  757. offload/engine/clang_oracle.py +208 -0
  758. offload/engine/combos.py +234 -0
  759. offload/engine/confirm/__init__.py +2 -0
  760. offload/engine/confirm/backlog.py +159 -0
  761. offload/engine/confirm/claims.py +423 -0
  762. offload/engine/confirm/collectors.py +665 -0
  763. offload/engine/confirm/decisions.py +202 -0
  764. offload/engine/confirm/draft.py +195 -0
  765. offload/engine/confirm/engine.py +157 -0
  766. offload/engine/confirm/execute.py +382 -0
  767. offload/engine/confirm/facts.py +467 -0
  768. offload/engine/confirm/findings.py +617 -0
  769. offload/engine/confirm/forms.py +145 -0
  770. offload/engine/confirm/ingest.py +372 -0
  771. offload/engine/confirm/jobs.py +215 -0
  772. offload/engine/confirm/legality.py +323 -0
  773. offload/engine/confirm/machines.py +254 -0
  774. offload/engine/confirm/minimize.py +268 -0
  775. offload/engine/confirm/model.py +249 -0
  776. offload/engine/confirm/ports.py +107 -0
  777. offload/engine/confirm/programs.py +222 -0
  778. offload/engine/confirm/registry.py +151 -0
  779. offload/engine/confirm/regress.py +278 -0
  780. offload/engine/confirm/rules.py +565 -0
  781. offload/engine/confirm/store.py +63 -0
  782. offload/engine/confirm/triage.py +838 -0
  783. offload/engine/confirm/validate.py +607 -0
  784. offload/engine/confirm/witness.py +355 -0
  785. offload/engine/corpus_check.py +388 -0
  786. offload/engine/coverage.py +318 -0
  787. offload/engine/dataenv_gen.py +1318 -0
  788. offload/engine/dataenv_rules.py +278 -0
  789. offload/engine/devtemps.py +146 -0
  790. offload/engine/expected.py +2559 -0
  791. offload/engine/f2c.py +1783 -0
  792. offload/engine/finding_claims.py +330 -0
  793. offload/engine/finding_header.py +229 -0
  794. offload/engine/finding_rows.py +382 -0
  795. offload/engine/flang_oracle.py +299 -0
  796. offload/engine/fsource.py +620 -0
  797. offload/engine/fuzz.py +2455 -0
  798. offload/engine/fuzz_base.py +2016 -0
  799. offload/engine/fuzz_campaign.py +592 -0
  800. offload/engine/fuzz_exec.py +798 -0
  801. offload/engine/fuzz_min.py +1049 -0
  802. offload/engine/fuzz_profile.py +340 -0
  803. offload/engine/fuzz_report.py +1270 -0
  804. offload/engine/fuzz_steer.py +181 -0
  805. offload/engine/gate.py +1013 -0
  806. offload/engine/gate_report.py +711 -0
  807. offload/engine/gate_run.py +118 -0
  808. offload/engine/gate_stored.py +466 -0
  809. offload/engine/gen.py +1114 -0
  810. offload/engine/gen_templates.py +2583 -0
  811. offload/engine/grammar.py +695 -0
  812. offload/engine/harness.py +1472 -0
  813. offload/engine/harvest/__init__.py +3 -0
  814. offload/engine/harvest/c_runtime.py +465 -0
  815. offload/engine/harvest/checks.py +534 -0
  816. offload/engine/harvest/convert.py +784 -0
  817. offload/engine/harvest/f_runtime.py +668 -0
  818. offload/engine/harvest/filters.py +338 -0
  819. offload/engine/harvest/history.py +122 -0
  820. offload/engine/harvest/inventory.py +399 -0
  821. offload/engine/harvest2/__init__.py +5 -0
  822. offload/engine/harvest2/aomp.py +119 -0
  823. offload/engine/harvest2/examples.py +116 -0
  824. offload/engine/harvest2/gomp.py +679 -0
  825. offload/engine/harvest2/hand.py +150 -0
  826. offload/engine/harvest2/split.py +78 -0
  827. offload/engine/harvest_known/__init__.py +2 -0
  828. offload/engine/harvest_known/apps.py +65 -0
  829. offload/engine/harvest_known/cases.py +128 -0
  830. offload/engine/harvest_known/centres.py +184 -0
  831. offload/engine/harvest_known/forum.py +180 -0
  832. offload/engine/harvest_known/relnotes.py +42 -0
  833. offload/engine/harvest_known/textscan.py +107 -0
  834. offload/engine/hdrwrap.py +42 -0
  835. offload/engine/hecbench.py +568 -0
  836. offload/engine/hecbench_report.py +265 -0
  837. offload/engine/hecbench_run.py +483 -0
  838. offload/engine/idioms.py +1427 -0
  839. offload/engine/kbody_cites.py +81 -0
  840. offload/engine/kbody_emit.py +949 -0
  841. offload/engine/kbody_gen.py +795 -0
  842. offload/engine/legality_review.py +236 -0
  843. offload/engine/localcfg.py +58 -0
  844. offload/engine/malloc_dd.py +267 -0
  845. offload/engine/mutate.py +1933 -0
  846. offload/engine/nested_cites.py +196 -0
  847. offload/engine/nested_emit.py +789 -0
  848. offload/engine/nested_gen.py +1721 -0
  849. offload/engine/offload_scope.py +197 -0
  850. offload/engine/ompvv.py +624 -0
  851. offload/engine/oop_gen.py +2249 -0
  852. offload/engine/prerun.py +1435 -0
  853. offload/engine/program_source.py +739 -0
  854. offload/engine/provenance.py +103 -0
  855. offload/engine/reduce.py +2098 -0
  856. offload/engine/report.py +3374 -0
  857. offload/engine/root.py +33 -0
  858. offload/engine/rules.py +4256 -0
  859. offload/engine/run_matrix.py +325 -0
  860. offload/engine/site/__init__.py +1 -0
  861. offload/engine/site/columns.py +53 -0
  862. offload/engine/site/data.py +648 -0
  863. offload/engine/site/header.py +228 -0
  864. offload/engine/site/inputs.py +115 -0
  865. offload/engine/site/matrix.py +434 -0
  866. offload/engine/site/scrub.py +294 -0
  867. offload/engine/skeleton.py +121 -0
  868. offload/engine/spec_text.py +242 -0
  869. offload/engine/stress.py +4122 -0
  870. offload/engine/stress_c.py +1000 -0
  871. offload/engine/stress_exec.py +805 -0
  872. offload/engine/stress_min.py +248 -0
  873. offload/engine/stress_report.py +552 -0
  874. offload/engine/textclean.py +97 -0
  875. offload/engine/toolchains.py +1070 -0
  876. offload/engine/transforms.py +511 -0
  877. offload/engine/usage.py +1252 -0
  878. offload/engine/widen.py +673 -0
  879. offload/engine/widen_progs.py +2396 -0
  880. offload/issues.py +146 -0
  881. offtarget-0.1.0.dist-info/METADATA +86 -0
  882. offtarget-0.1.0.dist-info/RECORD +886 -0
  883. offtarget-0.1.0.dist-info/WHEEL +4 -0
  884. offtarget-0.1.0.dist-info/entry_points.txt +2 -0
  885. offtarget-0.1.0.dist-info/licenses/LICENSE +21 -0
  886. offtarget-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +20 -0
@@ -0,0 +1,2583 @@
1
+ """Fortran templates and per-atom recipes for ``offload.engine.gen``.
2
+
3
+ Every program has the same frame (``Src.render``): the module ``gen_mod`` when
4
+ a recipe needs one, then ``program gen_prog`` with the standard data
5
+ (``a(i) = i``, ``b(i) = 0``, host reference ``ref(i)``, ``host_flag = 1``),
6
+ a region of interest where the device computes ``b``, and
7
+ ``ok = all(b == ref)`` plus the recipe's extra checks. The program prints
8
+ ``RESULT ran_on_host=<0|1> check=<pass|fail>`` and always exits 0.
9
+ ``host_flag`` is set inside the target region of interest (``region``), or in
10
+ a separate probe target region before it where that region may not contain an
11
+ OpenMP API call. Programs are deterministic: integers only, n = 64, one
12
+ writer per element, order-independent reductions, explicit ``map`` clauses.
13
+ Template families (``Region.kind``): ``loop``, ``block``, ``data``,
14
+ ``declare`` and ``none`` (``requires`` with the standard kernel).
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ from dataclasses import dataclass, field
20
+
21
+ from offload.engine.catalog import (default_grammar, default_skeleton,
22
+ rule_valid, spelling_valid)
23
+ from offload.engine.rules import association, leaves
24
+
25
+ FLAG = "host_flag = merge(1, 0, omp_is_initial_device())"
26
+ BODY = ("b(i) = 2*a(i) + 1",)
27
+ STD_MAPS = "map(to: a) map(from: b)"
28
+ FLAG_MAP = "map(tofrom: host_flag)"
29
+ MAX_LINE = 96
30
+ # The host value of an item a region maps back (from) and the
31
+ # check reads, set before the region: no thread, team or device number,
32
+ # count or ticket the checks accept is negative, so a lost copy-back
33
+ # (a mutant, a compiler) fails deterministically instead of being judged
34
+ # on uninitialized memory
35
+ SENTINEL = -1
36
+
37
+
38
+ class TemplateError(Exception):
39
+ """A recipe could not be built (a generator bug, never data)."""
40
+
41
+
42
+ # --- the frame ----------------------------------------------------------------
43
+
44
+ @dataclass
45
+ class Src:
46
+ """One program before rendering. Lists hold source lines without the
47
+ frame's indentation."""
48
+ module: list = field(default_factory=list) # gen_mod body (+contains)
49
+ uses: list = field(default_factory=list) # extra USE statements
50
+ spec: list = field(default_factory=list) # directives in spec part
51
+ decls: list = field(default_factory=list)
52
+ init: list = field(default_factory=list) # host setup statements
53
+ region: list = field(default_factory=list)
54
+ post: list = field(default_factory=list)
55
+ checks: list = field(default_factory=list) # extra logical checks
56
+ ref: str = "2*i + 1"
57
+ observable: bool = True
58
+ note: str = ""
59
+ probe: bool = False
60
+ focus: str = "" # the clause text that exercises the atom, if any
61
+ std: str = "f2008"
62
+ needs: tuple = () # run-time needs (offload.engine.gen.NEEDS)
63
+ evidence: str = "construct" # offload.engine.gen.EVIDENCE
64
+ expect: str = "result" # offload.engine.gen.EXPECT
65
+ token: str = "" # compile-output probes: offload.engine.gen
66
+ header_notes: list = field(default_factory=list) # extra header lines
67
+ basis: str = "" # no-oracle programs: catalog/omp/no_oracle.yaml
68
+ legality_rule: str = "" # legality programs: the rule stem
69
+
70
+ def _strip_marks(self):
71
+ self.header_notes = [h for h in self.header_notes if not h.startswith(
72
+ ("unverified (", "no oracle ("))]
73
+ self.basis = ""
74
+
75
+ def unverified(self, kind: str, reason: str,
76
+ checked: bool = False) -> "Src":
77
+ """Mark the program evidence="unverified" (a deterministic
78
+ oracle for behaviour the spec fixes is possible but missing) with
79
+ its header line ``! unverified (<kind>): <reason>``
80
+ (offload.engine.gen.UNVERIFIED). Unless `checked` (the check does test
81
+ what the atom requires), the program is observable=False: it checks
82
+ the device kernel, not the atom."""
83
+ self.evidence = "unverified"
84
+ if not checked:
85
+ self.observable = False
86
+ self.note = "unverified: the check covers the device kernel, " \
87
+ "not the atom"
88
+ self._strip_marks()
89
+ self.header_notes.append(f"unverified ({kind}): {reason}")
90
+ return self
91
+
92
+ def no_oracle(self, kind: str, reason: str, basis: str,
93
+ checked: bool = False) -> "Src":
94
+ """Mark a program whose atom no single run can separate from its
95
+ absence, for the reason the spec gives: evidence `kind`
96
+ (offload.engine.gen.NO_ORACLE: hint_only, implementation_defined,
97
+ equivalent_default), header lines ``! no oracle (<kind>):
98
+ <reason>`` and ``! basis: <cite>: "<quote>"`` (the basis id's
99
+ sentence in catalog/omp/no_oracle.yaml). Unless `checked` (the check
100
+ tests what the atom requires, so a wrong answer is a real failure),
101
+ the program is observable=False."""
102
+ self.evidence = kind
103
+ if not checked:
104
+ self.observable = False
105
+ self.note = f"{kind}: the check covers the device kernel, " \
106
+ "not the atom"
107
+ self._strip_marks()
108
+ self.basis = basis
109
+ self.header_notes.append(f"no oracle ({kind}): {reason}")
110
+ return self
111
+
112
+ def presence(self, names, present: bool) -> list[str]:
113
+ """Host statements asserting (into `pres`, checked at the end) that
114
+ each array is (not) present in the default device's data
115
+ environment, in copy mode only.
116
+
117
+ Under unified memory (HSA_XNACK=1) AFAR maps nothing and
118
+ omp_target_is_present returns 0 even after target enter data, so
119
+ the assertions are skipped when the frame detects shared storage:
120
+ a target region writes a map(to:) array, and the host sees the
121
+ write only if host and device share the storage. Such programs
122
+ carry evidence copy_mode_only."""
123
+ self.shared_setup()
124
+ op = "/=" if present else "=="
125
+ return [f"if (.not. shared_mem) pres = pres .and. "
126
+ f"omp_target_is_present(c_loc({x}), "
127
+ f"omp_get_default_device()) {op} 0" for x in names]
128
+
129
+ def shared_setup(self):
130
+ """`pres` (checked at the end) and `shared_mem` (host and device
131
+ share storage), once per program."""
132
+ if "logical :: pres, shared_mem" not in self.decls:
133
+ self.decls += ["logical :: pres, shared_mem", "integer :: zc(1)"]
134
+ self.init += ["pres = .true.", "zc = 0",
135
+ omp("target", "map(to: zc)"), "zc(1) = 1",
136
+ omp("end target"), "shared_mem = zc(1) == 1"]
137
+ self.checks.append("pres")
138
+ self.uses.append("use, intrinsic :: iso_c_binding, only: c_loc")
139
+ self.evidence = "copy_mode_only"
140
+
141
+ def copy_check(self, cond: str) -> list[str]:
142
+ """A host statement asserting `cond` (into `pres`) in copy mode only:
143
+ a host value that a data transfer would change (a device write that
144
+ must not come back, a stale device copy). Under unified memory host
145
+ and device share the storage, so the program carries evidence
146
+ copy_mode_only."""
147
+ self.shared_setup()
148
+ return [f"if (.not. shared_mem) pres = pres .and. ({cond})"]
149
+
150
+ def address_moved(self, names) -> list[str]:
151
+ """Inside a use_device_addr region: assert (copy mode only) that
152
+ c_loc of each array is no longer its host address, saved as
153
+ h<name> before the region by `save_addresses`."""
154
+ self.shared_setup()
155
+ return [f"if (.not. shared_mem) pres = pres .and. .not. "
156
+ f"c_associated(c_loc({x}), h{x})" for x in names]
157
+
158
+ def save_addresses(self, names) -> list[str]:
159
+ self.decls.append("type(c_ptr) :: " + ", ".join(f"h{x}" for x in
160
+ names))
161
+ self.uses.append("use, intrinsic :: iso_c_binding, only: c_ptr, "
162
+ "c_associated")
163
+ return [f"h{x} = c_loc({x})" for x in names]
164
+
165
+ def omp_names(self) -> list[str]:
166
+ """The omp_lib names the program's statements and directives use
167
+ (the ONLY list: a USE without ONLY would hide the program's
168
+ declarations from the restriction rules)."""
169
+ names = {"omp_is_initial_device"}
170
+ for line in self.spec + self.decls + self.init + self.region \
171
+ + self.post:
172
+ low = line.lower()
173
+ if low.lstrip().startswith("!$omp") or \
174
+ not low.lstrip().startswith("!"):
175
+ names.update(re.findall(r"\b(omp_\w+)", low))
176
+ return sorted(names)
177
+
178
+ def render(self, header: list[str]) -> str:
179
+ out = [f"! {h}" for h in header]
180
+ if self.module:
181
+ out += ["module gen_mod", " implicit none"]
182
+ out += _indent(self.module, 2)
183
+ out += ["end module gen_mod", ""]
184
+ out.append("program gen_prog")
185
+ out.append(" use omp_lib, only: " + ", ".join(self.omp_names()))
186
+ if self.module:
187
+ out.append(" use gen_mod")
188
+ out += _indent(self.uses, 2)
189
+ out.append(" implicit none")
190
+ body = list(self.spec) + [
191
+ "integer, parameter :: n = 64",
192
+ "integer, allocatable, target :: a(:), b(:)",
193
+ "integer :: ref(n)",
194
+ "integer :: i, host_flag",
195
+ "logical :: ok"] + list(self.decls) + [
196
+ "allocate(a(n), b(n))",
197
+ "do i = 1, n",
198
+ " a(i) = i",
199
+ " b(i) = 0",
200
+ f" ref(i) = {self.ref}",
201
+ "end do",
202
+ "host_flag = 1"] + list(self.init) + list(self.region) + list(
203
+ self.post)
204
+ ok = " .and. ".join(["all(b == ref)"] + list(self.checks))
205
+ body += [f"ok = {ok}",
206
+ "print '(a,i0,a,a)', 'RESULT ran_on_host=', host_flag, "
207
+ "' check=', merge('pass', 'fail', ok)",
208
+ "deallocate(a, b)"]
209
+ out += _indent(body, 2)
210
+ out.append("end program gen_prog")
211
+ return "\n".join(_wrap(line) for line in out) + "\n"
212
+
213
+
214
+ def _indent(lines, n):
215
+ """Indent `lines` by n blanks plus two per open construct (DO, IF ...
216
+ THEN, block and loop directives up to their end directives)."""
217
+ out, depth = [], 0
218
+ for line in lines:
219
+ low = line.strip().lower()
220
+ opens, closes = _opens(low), _closes(low)
221
+ depth -= closes
222
+ out.append(" " * (n + 2 * max(depth, 0)) + line.strip()
223
+ if line else "")
224
+ depth += opens
225
+ return out
226
+
227
+
228
+ def _opens(low: str) -> int:
229
+ if re.match(r"do(\s|$)", low) or re.match(r"if\s*\(.*\)\s*then$", low) \
230
+ or low.startswith("contains") or re.match(
231
+ r"(integer|logical)\s+function\b|subroutine\b", low) \
232
+ or re.match(r"type\s*::", low):
233
+ return 1
234
+ if low.startswith("!$omp") and not low.startswith("!$omp&"):
235
+ name = _omp_key(low)
236
+ return int(name is not None and association(name) in
237
+ ("block", "loop"))
238
+ return 0
239
+
240
+
241
+ def _closes(low: str) -> int:
242
+ if re.match(r"end\s*(do|if|function|subroutine|type)\b", low):
243
+ return 1
244
+ if low.startswith("!$omp end"):
245
+ return 1
246
+ return 0
247
+
248
+
249
+ def _omp_key(low: str):
250
+ """Skeleton key of a directive line (not an end directive), or None."""
251
+ from offload.engine.grammar import GrammarError, parse_directive
252
+ if low.startswith("!$omp end"):
253
+ return None
254
+ text = low.rstrip("&").rstrip()
255
+ try:
256
+ return parse_directive(text, "fortran", 60).name
257
+ except GrammarError:
258
+ try:
259
+ return parse_directive(text, "fortran", 52).name
260
+ except GrammarError:
261
+ return None
262
+
263
+
264
+ def _wrap(line: str) -> str:
265
+ """Split a long directive line at clause boundaries (free-form
266
+ continuation: ``&`` at the end, ``!$omp&`` on the next line)."""
267
+ if len(line) <= MAX_LINE or not line.lstrip().lower().startswith("!$omp"):
268
+ return line
269
+ ind = line[:len(line) - len(line.lstrip())]
270
+ parts, depth, cur = [], 0, ""
271
+ for ch in line.strip():
272
+ if ch == "(":
273
+ depth += 1
274
+ elif ch == ")":
275
+ depth -= 1
276
+ if ch == " " and depth == 0:
277
+ parts.append(cur)
278
+ cur = ""
279
+ continue
280
+ cur += ch
281
+ parts.append(cur)
282
+ lines, cur = [], ""
283
+ for p in parts:
284
+ if cur and len(ind) + len(cur) + len(p) + 3 > MAX_LINE:
285
+ lines.append(cur)
286
+ cur = "!$omp& " + p
287
+ else:
288
+ cur = f"{cur} {p}" if cur else p
289
+ lines.append(cur)
290
+ return " &\n".join(ind + x for x in lines)
291
+
292
+
293
+ # --- directive facts ------------------------------------------------------------
294
+
295
+ def spell(key: str, version: int) -> str:
296
+ """A valid spelling of skeleton directive `key` at `version` (blanks
297
+ preferred)."""
298
+ sk, g = default_skeleton(), default_grammar()
299
+ cands = [key, key.replace(" ", "_")] + [
300
+ s.text for s in sk.directive(key).spellings] + [
301
+ e.spelling for e in g.spellings if e.directive == key]
302
+ for c in cands:
303
+ if spelling_valid(sk, g, key, c, version):
304
+ return c
305
+ raise TemplateError(f"no spelling of {key!r} at {version}")
306
+
307
+
308
+ def permits(key: str, clause: str, version: int) -> bool:
309
+ rule = default_grammar().allowed(default_skeleton(), key).get(clause)
310
+ return rule is not None and rule_valid(rule, version)
311
+
312
+
313
+ def tablegen_permits(key: str, clause: str, version: int) -> bool:
314
+ """Whether the TableGen skeleton itself (without spec overrides) allows
315
+ `clause` on `key` at `version`."""
316
+ rule = default_skeleton().directive(key).allowed.get(clause)
317
+ return rule is not None and rule_valid(rule, version)
318
+
319
+
320
+ def hygiene(key: str, version: int, shared) -> str:
321
+ """``default(none) shared(...)`` where the directive allows both. The
322
+ hygiene clause is added only where TableGen allows `default` too: where
323
+ only a provisional spec override allows it (target teams distribute simd
324
+ at 5.2) a compiler may reject it, and the rejection would be read as one
325
+ of the atom under test."""
326
+ if shared and permits(key, "default", version) \
327
+ and tablegen_permits(key, "default", version) \
328
+ and permits(key, "shared", version):
329
+ return f"default(none) shared({', '.join(shared)})"
330
+ return ""
331
+
332
+
333
+ def concurrent(key: str, clauses: str) -> bool:
334
+ """Whether the region may not contain an OpenMP API call."""
335
+ return bool({"loop", "simd", "workdistribute"} & set(leaves(key))) \
336
+ or bool(re.search(r"\border\s*\(", clauses))
337
+
338
+
339
+ def omp(*parts: str) -> str:
340
+ return "!$omp " + " ".join(p for p in parts if p)
341
+
342
+
343
+ def seq_loop(body=BODY, flag=False, var="i"):
344
+ out = [f"do {var} = 1, n"] + [" " + b for b in body]
345
+ if flag:
346
+ out.append(f" if ({var} == 1) {FLAG}")
347
+ return out + ["end do"]
348
+
349
+
350
+ def guarded(cond: str, lines) -> list:
351
+ return [f"if ({cond}) then"] + [" " + x for x in lines] + ["end if"]
352
+
353
+
354
+ # A teams region may not call omp_is_initial_device directly (5.2
355
+ # §10.2, p.232: omp_get_num_teams and omp_get_team_num regions are the only
356
+ # routine regions that may be strictly nested in it; 6.0 §12.2, p.396: only
357
+ # teams-nestable routines, and omp_is_initial_device is not one, §24.9), so
358
+ # a teams region reports the device from a one-thread parallel region
359
+ # (parallel is teams-nestable).
360
+ TEAMS_FLAG = [omp("parallel", "num_threads(1)"), FLAG, omp("end parallel")]
361
+
362
+
363
+ # The probe region: reports the device in host_flag and sets the device
364
+ # copy of the declare-target variable on_dev (host copy 0) to 1. The
365
+ # construct of interest then computes b(i) = 2*a(i) + on_dev, so a host
366
+ # fallback of the construct alone reads the host copy: b = 2*a, the check
367
+ # fails and the frame sets host_flag = 1 (PROBE_POST).
368
+ PROBE = [omp("target", FLAG_MAP), FLAG, "on_dev = 1", omp("end target")]
369
+ # The extended-list form (5.2 §7.8.1, p.206; 6.0 §9.9.1, p.346: as if an
370
+ # enter clause were given; 5.0 §2.12.7, p.180: as if a to clause were
371
+ # given) is legal at every version and accepted by AFAR and CCE 19 (which
372
+ # lacks the enter clause).
373
+ PROBE_MODULE = {v: ["integer :: on_dev = 0", "!$omp declare target(on_dev)"]
374
+ for v in (50, 52, 60)}
375
+ PROBE_POST = ["if (all(b == 2*a)) host_flag = 1"]
376
+
377
+
378
+ # --- the region of interest ---------------------------------------------------------
379
+
380
+ @dataclass
381
+ class Region:
382
+ """The construct of interest and how it is embedded.
383
+
384
+ `clauses` go on the construct of interest; `maps` (and the host_flag
385
+ map) on the outermost target construct; `wrap` are extra clauses for a
386
+ wrapper target construct; `shared` the variables the construct shares
387
+ (for the default(none) hygiene); `loop` replaces the DO loop (a list of
388
+ lines, or a function of `flag` returning lines); `body` the loop body.
389
+ """
390
+ key: str
391
+ clauses: str = ""
392
+ maps: str = STD_MAPS
393
+ wrap: str = ""
394
+ shared: tuple = ("a", "b")
395
+ body: tuple = BODY
396
+ loop: object = None
397
+ hyg: bool = True
398
+ wrapper: str | None = None # force the wrapper construct
399
+ pre: tuple = () # wrapper statements before the construct (nested form)
400
+ # the region may call the OpenMP API although a leaf is simd
401
+ # (simd restricts constructs, not routines, 5.2 §10.4, 6.0 §12.4)
402
+ api: bool = False
403
+ # the loop body of a probe region (default: b(i) = 2*a(i) + on_dev),
404
+ # which must compute b = 2*a + on_dev
405
+ probe_body: tuple = ()
406
+
407
+ def lines(self, version: int) -> tuple[list, bool]:
408
+ """(source lines, whether a separate probe region is used)."""
409
+ key, v = self.key, version
410
+ ls = leaves(key)
411
+ sp = spell(key, v)
412
+ conc = concurrent(key, self.clauses) and not self.api
413
+ assoc = association(key)
414
+
415
+ def hyg(extra=()):
416
+ if not self.hyg or re.search(r"\bdefault\s*\(", self.clauses):
417
+ return ""
418
+ h = hygiene(key, v, tuple(self.shared) + tuple(extra))
419
+ # 5.0: a sequential loop's variable is predetermined
420
+ # private only in a parallel or task generating construct (5.0
421
+ # §2.19.1.1 p.271; 5.2 adds teams), so under default(none) a
422
+ # teams block lists the DO variable (AFAR -fopenmp-version=50
423
+ # rejects it otherwise)
424
+ if h and v == 50 and assoc != "loop" and "teams" in ls \
425
+ and "parallel" not in ls:
426
+ h += " private(i)"
427
+ return h
428
+
429
+ def loop(flag):
430
+ if self.loop is None:
431
+ return seq_loop(self.body, flag)
432
+ return self.loop(flag) if callable(self.loop) else list(self.loop)
433
+
434
+ def probe_loop():
435
+ if self.probe_body:
436
+ return seq_loop(self.probe_body)
437
+ if self.loop is not None or tuple(self.body) != BODY:
438
+ raise TemplateError(f"probe region for {key!r} needs the "
439
+ "standard body")
440
+ return seq_loop(("b(i) = 2*a(i) + on_dev",))
441
+
442
+ array_probe = "b = 2*a + on_dev"
443
+
444
+ def end():
445
+ return [omp("end", sp)]
446
+
447
+ if ls[0] == "target":
448
+ inner = ls[1:]
449
+ if assoc == "loop":
450
+ if conc:
451
+ return PROBE + [omp(sp, self.maps, hyg(["on_dev"]),
452
+ self.clauses)] \
453
+ + probe_loop() + end(), True
454
+ return [omp(sp, self.maps, FLAG_MAP, hyg(["host_flag"]),
455
+ self.clauses)] + loop(True) + end(), False
456
+ if inner and inner[-1] == "workdistribute":
457
+ return PROBE + [omp(sp, self.maps, hyg(["on_dev"]),
458
+ self.clauses), array_probe] + end(), True
459
+ head = omp(sp, self.maps, FLAG_MAP, hyg(["host_flag"]),
460
+ self.clauses)
461
+ if not inner:
462
+ return [head, FLAG] + loop(False) + end(), False
463
+ cond = {"parallel": "omp_get_thread_num() == 0",
464
+ "teams": "omp_get_team_num() == 0"}[inner[-1]]
465
+ flag = TEAMS_FLAG if inner[-1] == "teams" else [FLAG]
466
+ return [head] + guarded(cond, flag + loop(False)) + end(), \
467
+ False
468
+ if ls[0] == "teams":
469
+ wrapper = omp("target", self.maps, self.wrap)
470
+ if assoc == "loop":
471
+ if conc:
472
+ return PROBE + [wrapper, omp(sp, hyg(["on_dev"]),
473
+ self.clauses)] + \
474
+ probe_loop() + end() + [omp("end target")], True
475
+ return [omp("target", self.maps, FLAG_MAP, self.wrap),
476
+ omp(sp, hyg(["host_flag"]), self.clauses)] + \
477
+ loop(True) + end() + [omp("end target")], False
478
+ if ls[-1] == "workdistribute":
479
+ return PROBE + [wrapper, omp(sp, hyg(["on_dev"]),
480
+ self.clauses), array_probe] + \
481
+ end() + [omp("end target")], True
482
+ return [omp("target", self.maps, FLAG_MAP, self.wrap),
483
+ omp(sp, hyg(["host_flag"]), self.clauses)] + \
484
+ guarded("omp_get_team_num() == 0", TEAMS_FLAG + loop(False)) + \
485
+ end() + [omp("end target")], False
486
+ # a construct nested in a wrapper target region with a statement
487
+ kind = self.wrapper
488
+ if kind is None:
489
+ if ls[0] == "distribute":
490
+ kind = "target teams"
491
+ elif ls[0] == "loop":
492
+ b = re.search(r"\bbind\s*\(\s*(\w+)", self.clauses)
493
+ kind = {"parallel": "target parallel", "thread": "target"}.get(
494
+ b.group(1) if b else "", "target teams")
495
+ else:
496
+ kind = "target"
497
+ flag = {"target": [FLAG],
498
+ "target teams": guarded("omp_get_team_num() == 0",
499
+ TEAMS_FLAG),
500
+ "target parallel": [f"if (omp_get_thread_num() == 0) {FLAG}"]
501
+ }[kind]
502
+ return [omp(kind, self.maps, FLAG_MAP, self.wrap)] + flag + list(
503
+ self.pre) + [omp(sp, hyg(), self.clauses)] + loop(False) + \
504
+ end() + [omp("end", kind)], False
505
+
506
+
507
+ def kernel(src: Src, region: Region, version: int) -> Src:
508
+ lines, probe = region.lines(version)
509
+ src.region += lines
510
+ if probe:
511
+ src.probe = True
512
+ src.module = PROBE_MODULE[version] + src.module
513
+ src.post = PROBE_POST + src.post
514
+ if "workdistribute" in leaves(region.key):
515
+ src.header_notes.append("unverified: no toolchain compiles this "
516
+ "(6.0 workdistribute), so the on_dev "
517
+ "evidence is untested here")
518
+ if not src.focus:
519
+ src.focus = region.clauses
520
+ return src
521
+
522
+
523
+ DEFAULT_KEY = "target teams distribute parallel do"
524
+
525
+
526
+ # --- recipes -------------------------------------------------------------------------
527
+ #
528
+ # recipe(atom, version) -> Src. Directive atoms use the directive's own
529
+ # family; clause atoms put the clause on a construct that allows it at the
530
+ # version, with the context it needs. Each builder states its choice.
531
+
532
+ def _k(version, region: Region, **src) -> Src:
533
+ return kernel(Src(**src), region, version)
534
+
535
+
536
+ # Compute directives: what the check reads to tell the construct from the
537
+ # loop or block run without it.
538
+ #
539
+ # * distribute / loop forms in a target teams region of num_teams(4) teams
540
+ # (exactly 4: 5.2 §10.2.1, 6.0 §12.2.1): every iteration counts itself in
541
+ # its team's column of cnt (the team number tm, taken in the teams region
542
+ # before the construct, so no API call is needed in a simd or loop
543
+ # region); with the construct each iteration runs once in the league,
544
+ # without it every team runs every iteration (sum 4).
545
+ # * teams forms nested in target, with num_teams(4): the region reads
546
+ # omp_get_num_teams(); without the construct it is 1.
547
+ # * parallel do: the team size (omp_get_num_threads in the loop) must be
548
+ # more than one whenever a plain parallel region before it got more than
549
+ # one thread (the reference region; a one-thread team is conforming and
550
+ # then nothing is required); without the construct the loop runs on the
551
+ # target's one thread.
552
+ LEAGUE = {"distribute", "distribute parallel do",
553
+ "distribute parallel do simd", "distribute simd", "loop"}
554
+ TEAMS_N = {"teams", "teams distribute", "teams distribute parallel do",
555
+ "teams distribute simd", "teams distribute parallel do simd"}
556
+ # A simd region may call the OpenMP API (simd
557
+ # restricts only the constructs in its region to SIMDizable ones, 5.2
558
+ # §10.4 p.234, 6.0 §12.4 p.398), so the simd compound forms read the team
559
+ # or league like their non-simd counterparts.
560
+ SIMD_API = {"parallel do simd", "teams distribute simd",
561
+ "teams distribute parallel do simd"}
562
+ # parallel loop and teams loop: the region may not call the
563
+ # OpenMP API (a loop construct behaves as if order(concurrent) were
564
+ # specified, 5.2 §11.7 / 6.0 §13.8, and such a region may not contain API
565
+ # calls, 5.2 §10.3, or only order-concurrent-nestable routines, 6.0 §12.3,
566
+ # none of which queries the team or league), and the combined construct
567
+ # leaves no statement between its constituents, so who ran the iterations
568
+ # is not observable in one run. What is observable is the data environment
569
+ # the construct creates: t (mapped tofrom, 7) is private on the construct
570
+ # and every iteration writes it; with the construct the target's t stays 7,
571
+ # without it (or with the construct run as a plain loop that does not
572
+ # privatize) the iterations write the target's t. The check therefore
573
+ # shows that the construct and its data environment were created on the
574
+ # device, not that more than one thread or team ran the loop.
575
+ PRIVATE_LOOP = {"parallel loop", "teams loop"}
576
+
577
+
578
+ def compute_prog(key: str, version: int) -> Src:
579
+ if key in LEAGUE:
580
+ shared = ("a", "b", "cnt", "tm")
581
+ return _k(version, Region(
582
+ key, maps=f"{STD_MAPS} map(tofrom: cnt)",
583
+ wrap="num_teams(4) private(tm)", shared=shared,
584
+ pre=("tm = omp_get_team_num()",),
585
+ body=BODY + ("cnt(i, tm + 1) = cnt(i, tm + 1) + 1",)),
586
+ decls=["integer :: cnt(n, 4), tm"], init=["cnt = 0"],
587
+ checks=["all(sum(cnt, dim=2) == 1)"])
588
+ if key == "teams":
589
+ def loop(flag):
590
+ return ["nt = omp_get_num_teams()"] + seq_loop()
591
+ return _k(version, Region(key, "num_teams(4)",
592
+ maps=f"{STD_MAPS} map(from: nt)",
593
+ shared=("a", "b", "nt"), loop=loop),
594
+ decls=["integer :: nt"], init=[f"nt = {SENTINEL}"],
595
+ checks=["(nt == 4)"],
596
+ focus="")
597
+ if key in TEAMS_N:
598
+ return _k(version, Region(
599
+ key, "num_teams(4)", maps=f"{STD_MAPS} map(from: nt)",
600
+ shared=("a", "b", "nt"), api=key in SIMD_API,
601
+ body=BODY + ("if (i == 1) nt = omp_get_num_teams()",)),
602
+ decls=["integer :: nt"], init=[f"nt = {SENTINEL}"],
603
+ checks=["(nt == 4)"], focus="")
604
+ if key in ("parallel do", "parallel do simd"):
605
+ return _k(version, Region(
606
+ key, maps=f"{STD_MAPS} map(from: nref, nthr)",
607
+ shared=("a", "b", "nthr"),
608
+ pre=(omp("parallel"),
609
+ "if (omp_get_thread_num() == 0) nref = omp_get_num_threads()",
610
+ omp("end parallel")),
611
+ body=BODY + ("if (i == 1) nthr = omp_get_num_threads()",)),
612
+ decls=["integer :: nref, nthr"],
613
+ init=[f"nref = {SENTINEL}", f"nthr = {SENTINEL}"],
614
+ checks=["(nref == 1 .or. nthr > 1)"])
615
+ if key in PRIVATE_LOOP:
616
+ return _k(version, Region(
617
+ key, "private(t)", maps=f"{STD_MAPS} map(tofrom: t)",
618
+ body=("t = 2*a(i)", "b(i) = t + 1"),
619
+ probe_body=("t = 2*a(i)", "b(i) = 2*a(i) + on_dev")),
620
+ decls=["integer :: t"], init=["t = 7"], checks=["(t == 7)"],
621
+ focus="")
622
+ return _k(version, Region(key))
623
+
624
+
625
+ def directive_recipe(key: str, version: int) -> Src:
626
+ if key == "target data":
627
+ return data_prog(version)
628
+ if key == "target enter data" or key == "target exit data":
629
+ return enter_exit_prog(version)
630
+ if key == "target update":
631
+ return update_prog(version)
632
+ if key == "declare target":
633
+ # the extended-list form on a variable: a declare target procedure
634
+ # cannot be told from an implicitly declare target one (see
635
+ # IMPLICIT_PROCEDURE_NOTE), a variable's device copy can
636
+ return declare_var_prog(version, "!$omp declare target(dv)")
637
+ if key == "requires":
638
+ return requires_prog(version, "atomic_default_mem_order(seq_cst)")
639
+ return compute_prog(key, version)
640
+
641
+
642
+ # target data / enter / exit / update ------------------------------------------------
643
+
644
+ INNER = [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop() + \
645
+ [omp("end target")]
646
+ # The kernel inside a data construct: map(alloc: ...) moves no data, so the
647
+ # result depends on the construct's own maps (copy mode); the presence
648
+ # assertions of the frame depend on it in every mode where the runtime
649
+ # keeps a mapping table.
650
+ INNER_ALLOC = [omp("target", "map(alloc: a, b)", FLAG_MAP), FLAG] + \
651
+ seq_loop() + [omp("end target")]
652
+
653
+
654
+ def data_prog(version, clauses="", maps=STD_MAPS, **src):
655
+ """target data around an alloc-mapped kernel; a and b must be present
656
+ inside the region and absent after it."""
657
+ sp = spell("target data", version)
658
+ s = Src(**src)
659
+ s.region += [omp(sp, maps, clauses)] + s.presence(("a", "b"), True) + \
660
+ INNER_ALLOC + [omp("end", sp)] + s.presence(("a", "b"), False)
661
+ s.focus = s.focus or clauses
662
+ return s
663
+
664
+
665
+ def enter_exit_prog(version, enter="map(to: a) map(alloc: b)",
666
+ exit_="map(from: b) map(release: a)", twice=False,
667
+ **src):
668
+ """enter data, an alloc-mapped kernel, exit data, with presence
669
+ assertions after each. With `twice`, a is entered a second time
670
+ (reference count 2) before exit data, which must then leave a present
671
+ when it releases a and absent when it deletes a."""
672
+ s = Src(**src)
673
+ ent, ext = spell("target enter data", version), spell("target exit "
674
+ "data", version)
675
+ s.region += [omp(ent, enter)] + s.presence(("a", "b"), True)
676
+ if twice:
677
+ s.region.append(omp(ent, "map(to: a)"))
678
+ s.region += INNER_ALLOC + [omp(ext, exit_)]
679
+ if twice and "release: a" in exit_:
680
+ s.region += s.presence(("a",), True) + [omp(ext, "map(release: a)")]
681
+ s.region += s.presence(("a", "b"), False)
682
+ return s
683
+
684
+
685
+ def update_prog(version, to="to(a)", frm="from(b)", **src):
686
+ """Copy-mode evidence only: under unified memory a missing update
687
+ still gives the right answer (see offload.engine.gen)."""
688
+ s = Src(ref="2*(i + 10) + 1", evidence="copy_mode_only", **src)
689
+ s.region += [omp(spell("target enter data", version),
690
+ "map(to: a) map(alloc: b)")] + [
691
+ "do i = 1, n", " a(i) = a(i) + 10", "end do",
692
+ omp(spell("target update", version), to)] + INNER + [
693
+ omp(spell("target update", version), frm),
694
+ omp(spell("target exit data", version), "map(release: a, b)")]
695
+ return s
696
+
697
+
698
+ # declare target ---------------------------------------------------------------------
699
+
700
+ def proc_list(version: int, name: str = "f") -> str:
701
+ """The list clause of a procedure's declare target directive: ``to`` at
702
+ 5.2 (deprecated, still valid) and ``enter`` at 6.0 (``to`` is no longer
703
+ allowed there). CCE 19 rejects ``enter`` (ftn-1725), which at 5.2 would
704
+ otherwise be read as a rejection of the clause under test."""
705
+ return f"to({name})" if version < 60 else f"enter({name})"
706
+
707
+
708
+ def declare_prog(version, var="", func="", body=BODY, maps=STD_MAPS,
709
+ pre=(), **src):
710
+ """Module gen_mod with variables dv, dw (= 1), the function f(x) =
711
+ 2*x + 1 and the host function g; the declare target directive `var` in
712
+ the module's specification part, `func` in f's."""
713
+ s = Src(**src)
714
+ s.module = ["integer :: dv = 1", "integer :: dw = 1"] + (
715
+ [var] if var else []) + [
716
+ "contains", " integer function f(x)"] + (
717
+ [" " + func] if func else []) + [
718
+ " integer, intent(in) :: x", " f = 2*x + 1",
719
+ " end function f"] + s.module
720
+ s.region += [omp("target", maps, FLAG_MAP), FLAG] + list(pre) + \
721
+ seq_loop(body) + [omp("end target")]
722
+ return s
723
+
724
+
725
+ def declare_var_prog(version, directive, focus="", data=False):
726
+ """Device-copy oracle for a declare target variable: a first target
727
+ region sets dv = 5 with no map clause, the checked region computes
728
+ b = 2*a + dv - 4 (also with no map of dv). With the directive both
729
+ regions use the one device copy of dv (enter/extended list: allocated
730
+ for the whole program; link: inside a target data region that maps dv
731
+ with `data`; local: a device-local copy). Without it dv is a scalar
732
+ referenced in a target construct, implicitly firstprivate (5.2 §5.8.1,
733
+ 6.0 §7.9.3), so the first region's write is lost, dv is 1 in the
734
+ checked region and the check fails, in both memory modes
735
+ (mutation-checked under HSA_XNACK 0 and 1 on AFAR, and on CCE 19 for
736
+ link and the extended list; CCE 19 rejects enter(dv) and local)."""
737
+ s = declare_prog(version, var=directive, body=("b(i) = 2*a(i) + dv - 4",),
738
+ focus=focus)
739
+ first = [omp("target"), "dv = 5", omp("end target")]
740
+ if data:
741
+ sp = spell("target data", version)
742
+ s.init += [omp(sp, "map(tofrom: dv)")] + first
743
+ s.post.insert(0, omp("end", sp))
744
+ else:
745
+ s.init += first
746
+ return s
747
+
748
+
749
+ # A procedure referenced in a target construct is treated as if it appeared
750
+ # in an enter clause (5.2 §13.8 p.285, 6.0 §15.8 p.461), so in a one-file
751
+ # program a declare target directive on f changes nothing observable.
752
+ IMPLICIT_PROCEDURE_NOTE = (
753
+ "f is implicitly declare target without the directive (a procedure "
754
+ "referenced in a target construct, 5.2 §13.8 p.285, 6.0 §15.8 p.461), "
755
+ "so the clause has no checked effect")
756
+
757
+
758
+ # requires clauses whose programs need memory the device can access
759
+ # (under HSA_XNACK=0 AFAR aborts and CCE reports an unsupported requirement)
760
+ ZERO_COPY_REQUIRES = ("unified_shared_memory", "self_maps")
761
+
762
+
763
+ def requires_prog(version, clause, **src):
764
+ if clause.split("(")[0] in ZERO_COPY_REQUIRES:
765
+ src.setdefault("needs", ("zero_copy",))
766
+ s = Src(observable=False, note="requires only changes semantics: no "
767
+ "checked device effect", **src)
768
+ s.spec.append(omp("requires", clause))
769
+ s.focus = clause
770
+ return kernel(s, Region(DEFAULT_KEY), version)
771
+
772
+
773
+ # reductions ---------------------------------------------------------------------------
774
+
775
+ # identifier -> (operator text, type, initial value, update statement)
776
+ RED = {
777
+ "plus": ("+", "integer", "0", "{x} = {x} + a(i)"),
778
+ "minus": ("-", "integer", "0", "{x} = {x} - a(i)"),
779
+ "times": ("*", "integer", "1", "{x} = {x} * merge(2, 1, a(i) <= 10)"),
780
+ "max": ("max", "integer", "0", "{x} = max({x}, a(i))"),
781
+ "min": ("min", "integer", "1000", "{x} = min({x}, a(i))"),
782
+ "iand": ("iand", "integer", "-1", "{x} = iand({x}, a(i) + 64)"),
783
+ "ior": ("ior", "integer", "0", "{x} = ior({x}, a(i))"),
784
+ "ieor": ("ieor", "integer", "0", "{x} = ieor({x}, a(i))"),
785
+ "and": (".and.", "logical", ".true.", "{x} = {x} .and. (a(i) > 0)"),
786
+ "or": (".or.", "logical", ".false.", "{x} = {x} .or. (a(i) == n)"),
787
+ "eqv": (".eqv.", "logical", ".true.", "{x} = {x} .eqv. (a(i) > 0)"),
788
+ "neqv": (".neqv.", "logical", ".false.",
789
+ "{x} = {x} .neqv. (mod(a(i), 3) == 0)"),
790
+ }
791
+
792
+
793
+ # Logical reductions whose combined value does not show a missing
794
+ # privatisation (every contribution is .true.): the original value is
795
+ # .false. and the body reads the reduction variable before its update,
796
+ # which is the private copy, initialised to the identity .true. (5.2
797
+ # §5.5.5, 6.0 §7.6.6); without the reduction the body reads the shared
798
+ # .false. and b is wrong.
799
+ RED["and"] = (".and.", "logical", ".false.", "{x} = {x} .and. (a(i) > 0)")
800
+ RED["eqv"] = (".eqv.", "logical", ".false.", "{x} = {x} .eqv. (a(i) > 0)")
801
+ RED_BODY = {"and": ("b(i) = 2*a(i) + merge(1, 5, ls)",),
802
+ "eqv": ("b(i) = 2*a(i) + merge(1, 5, ls)",)}
803
+
804
+
805
+ def red_body(ident):
806
+ """The loop body before the reduction update."""
807
+ return RED_BODY.get(ident, BODY)
808
+
809
+
810
+ # in_reduction on target need not create a private copy (5.2 §5.5.11, 6.0
811
+ # §7.6.12: "a private copy may be created"), so its programs cannot read
812
+ # the identity: their logical reductions start from .true. and combine to
813
+ # .false. instead (a value the target's firstprivate copy, which is what
814
+ # the scalar becomes without the clause, never brings back): 17 is the
815
+ # one false contribution of and, the 9 multiples of 7 make eqv false.
816
+ RED_TASK = {"and": (".and.", "logical", ".true.",
817
+ "{x} = {x} .and. (a(i) /= 17)"),
818
+ "eqv": (".eqv.", "logical", ".true.",
819
+ "{x} = {x} .eqv. (mod(a(i), 7) /= 0)")}
820
+
821
+
822
+ def red_vars(ident, table=None):
823
+ """(op, var, declarations, host init + sequential reference, check)."""
824
+ op, typ, init, upd = (table or RED).get(ident, RED[ident])
825
+ x = "s" if typ == "integer" else "ls"
826
+ decls = [f"{typ} :: {x}, {x}ref"]
827
+ host = [f"{x} = {init}", f"{x}ref = {init}", "do i = 1, n",
828
+ " " + upd.format(x=f"{x}ref"), "end do"]
829
+ check = f"({x} == {x}ref)" if typ == "integer" else f"({x} .eqv. {x}ref)"
830
+ return op, x, decls, host, check, upd.format(x=x)
831
+
832
+
833
+ def reduction_prog(version, ident="plus", modifier="", key=DEFAULT_KEY,
834
+ hyg=True):
835
+ op, x, decls, host, check, upd = red_vars(ident)
836
+ mod = f"{modifier}, " if modifier else ""
837
+ clause = f"reduction({mod}{op}: {x})"
838
+ return _k(version, Region(key, clause, maps=f"{STD_MAPS} map(tofrom: {x})",
839
+ body=red_body(ident) + (upd,), hyg=hyg),
840
+ decls=decls, init=host, checks=[check], focus=clause)
841
+
842
+
843
+ def in_reduction_prog(version, ident="plus"):
844
+ op, x, decls, host, check, upd = red_vars(ident, RED_TASK)
845
+ clause = f"in_reduction({op}: {x})"
846
+ s = Src(decls=decls, init=host, checks=[check], focus=clause)
847
+ s.region += [omp("taskgroup", f"task_reduction({op}: {x})"),
848
+ omp("target", STD_MAPS, FLAG_MAP, clause), FLAG] + \
849
+ seq_loop(BODY + (upd,)) + [omp("end target"), omp("end taskgroup")]
850
+ return s
851
+
852
+
853
+ def task_reduction_prog(version):
854
+ """reduction(task, ...) on a worksharing-loop construct in a parallel
855
+ region in the target region (the do and its end directive are
856
+ separate, for the nowait transform of reduction-clause-005)."""
857
+ op, x, decls, host, check, upd = red_vars("plus")
858
+ clause = f"reduction(task, {op}: {x})"
859
+ s = Src(decls=decls, init=host, checks=[check], focus=clause)
860
+ s.region += [omp("target", STD_MAPS, FLAG_MAP, f"map(tofrom: {x})"),
861
+ FLAG, omp("parallel", f"default(none) shared(a, b, {x})"),
862
+ omp("do", clause)] + seq_loop(BODY + (upd,)) + [
863
+ omp("end do"), omp("end parallel"), omp("end target")]
864
+ return s
865
+
866
+
867
+ def inscan_prog(version):
868
+ """An inclusive scan: b(i) = a(1) + ... + a(i)."""
869
+ def loop(flag):
870
+ return ["do i = 1, n", " s = s + a(i)", " " + omp("scan",
871
+ "inclusive(s)"), " b(i) = s", "end do"]
872
+ clause = "reduction(inscan, +: s)"
873
+ return _k(version, Region("parallel do", clause,
874
+ maps=f"{STD_MAPS} map(tofrom: s)", loop=loop),
875
+ decls=["integer :: s"], init=["s = 0"], ref="i*(i + 1)/2",
876
+ focus=clause)
877
+
878
+
879
+ # shared helpers ----------------------------------------------------------------------
880
+
881
+ def pdo(version, clauses, **kw) -> Src:
882
+ """The clause on parallel do in a target region."""
883
+ src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
884
+ return _k(version, Region("parallel do", clauses, **kw), **src)
885
+
886
+
887
+ def pdo_simd(version, clauses, **kw) -> Src:
888
+ src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
889
+ return _k(version, Region("parallel do simd", clauses, **kw), **src)
890
+
891
+
892
+ def combined(version, clauses, key=DEFAULT_KEY, **kw) -> Src:
893
+ src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
894
+ return _k(version, Region(key, clauses, **kw), **src)
895
+
896
+
897
+ def collapse_loop(flag):
898
+ """A 2 x 32 nest over b; each iteration records the thread that ran it
899
+ (tid) and iteration (1, 1) the team size (nthr)."""
900
+ idx = "(j - 1)*32 + k"
901
+ return ["do j = 1, 2", " do k = 1, 32",
902
+ f" b({idx}) = 2*a({idx}) + 1",
903
+ f" tid({idx}) = omp_get_thread_num()",
904
+ " if (j == 1 .and. k == 1) nthr = omp_get_num_threads()",
905
+ " end do", "end do"]
906
+
907
+
908
+ def count_owners(arr: str, var: str, upto: str) -> list[str]:
909
+ """Host statements: `var` = how many of the values 0 .. upto-1 occur
910
+ in `arr` (the threads or teams that ran at least one iteration)."""
911
+ return [f"{var} = 0", f"do i = 0, {upto} - 1",
912
+ f" if (any({arr} == i)) {var} = {var} + 1", "end do"]
913
+
914
+
915
+ def blocked(arr: str, m: str, parts: str, var: str) -> list[str]:
916
+ """Host statements: `var` = what the static schedule without a chunk
917
+ size guarantees for the m iterations recorded in `arr` (owner per
918
+ iteration; the owners are the `parts` threads or teams): every
919
+ iteration ran on a maintainer 0 .. parts - 1 (its slot holds a maintainer
920
+ number, not the sentinel), and each owner's iterations are one
921
+ contiguous run. The spec text: chunks are "contiguous non-empty subsets"
922
+ and "at most one chunk is distributed to each thread" (5.2 §11.5.3
923
+ p.252; dist_schedule: each initial team, §11.6.1 p.256; 5.0 §2.9.2
924
+ p.103 (chunks), p.104 (Table 2.5), §2.9.4.1 p.122; 6.0 §13.6.3 p.418,
925
+ §13.7.1 p.422, contiguity from the Glossary "chunk" p.31). Nothing about the chunk
926
+ sizes is checked: "approximately equal" is illustrated by the Note
927
+ (5.2 §11.5.3 p.253, 5.0 §2.9.2 p.105) with two compliant
928
+ implementations, chunks of ceil(n/p) (the last shorter) and q / q - 1
929
+ splits, and neither the number of chunks nor their order over the
930
+ owners is fixed."""
931
+ return [f"{var} = all({arr} >= 0 .and. {arr} < {parts})",
932
+ f"do i = 2, {m}",
933
+ f" if ({arr}(i) /= {arr}(i - 1)) {var} = {var} .and. "
934
+ f".not. any({arr}(1:i - 1) == {arr}(i))", "end do"]
935
+
936
+
937
+ def round_robin(arr: str, m: str, chunk: int, parts: str,
938
+ var: str) -> list[str]:
939
+ """Host statements: `var` = whether iteration i ran on owner
940
+ mod((i - 1)/chunk, parts): chunks of `chunk` iterations assigned to
941
+ the owners round-robin in the order of their number (schedule(static,
942
+ c), dist_schedule(static, c))."""
943
+ return [f"{var} = .true.", f"do i = 1, {m}",
944
+ f" {var} = {var} .and. {arr}(i) == mod((i - 1)/{chunk}, "
945
+ f"{parts})", "end do"]
946
+
947
+
948
+ # The schedule programs run a loop of m = 1000 iterations (b is written by
949
+ # the first n), more than the team of either toolchain has threads, so the
950
+ # assignment of iterations to threads shows the schedule.
951
+ SCHED_M = 1000
952
+
953
+
954
+ def schedule_prog(version, clause, chunk=None):
955
+ """schedule(static[, chunk]) on parallel do in a target region: each
956
+ iteration records its thread; the static schedule fixes the assignment
957
+ (5.2 §11.5.3, 6.0 §13.6.3: without a chunk size one chunk of
958
+ approximately m/T iterations per thread, with chunk size c chunks of c
959
+ assigned round-robin in thread order)."""
960
+ def loop(flag):
961
+ return ["do i = 1, m", " if (i <= n) b(i) = 2*a(i) + 1",
962
+ " if (i == 1) nthr = omp_get_num_threads()",
963
+ " tid(i) = omp_get_thread_num()", "end do"]
964
+ check = blocked("tid", "m", "nthr", "sok") if chunk is None else \
965
+ round_robin("tid", "m", chunk, "nthr", "sok")
966
+ s = pdo(version, clause, loop=loop,
967
+ maps=f"{STD_MAPS} map(from: tid, nthr)",
968
+ shared=("a", "b", "tid", "nthr"),
969
+ decls=[f"integer, parameter :: m = {SCHED_M}",
970
+ "integer :: tid(m), nthr", "logical :: sok"],
971
+ init=[f"tid = {SENTINEL}", f"nthr = {SENTINEL}"],
972
+ post=check, checks=["(nthr >= 1)", "sok"])
973
+ if chunk is None:
974
+ # See STATIC_NO_CHUNK_WHY
975
+ s.no_oracle("implementation_defined", STATIC_NO_CHUNK_WHY,
976
+ "schedule_default", checked=True)
977
+ return s
978
+
979
+
980
+ def team_param(version, clause, record, decls, check, key=DEFAULT_KEY,
981
+ extra_decls=(), extra_shared=(), **kw):
982
+ """`clause` on `key` (a target construct); iteration 1 (or every
983
+ iteration) records what the clause controls with an OpenMP API call
984
+ (`record`, lines of the loop body), checked on the host (`check`).
985
+ `extra_shared`: variables of a clause expression evaluated inside an
986
+ outer leaf, listed under default(none)."""
987
+ names = [d.split("::")[1].strip() for d in decls]
988
+ names = [x.split("(")[0] for n in names for x in n.split(", ")]
989
+ # Every recorded item starts at the sentinel, which the
990
+ # check rejects, so a lost copy-back fails deterministically
991
+ kw["init"] = list(kw.get("init", [])) + [f"{x} = {SENTINEL}"
992
+ for x in names]
993
+ return combined(version, clause, key=key, body=BODY + tuple(record),
994
+ maps=f"{STD_MAPS} map(from: {', '.join(names)})",
995
+ shared=("a", "b") + tuple(names) + tuple(extra_shared),
996
+ decls=list(decls) + list(extra_decls),
997
+ checks=list(check), **kw)
998
+
999
+
1000
+ def linear_prog(version, clause, var="x", step=1):
1001
+ """x is linear with `step`: at the start of iteration i it is
1002
+ 3 + step*(i - 1) (the body adds the step, so the value at the end of
1003
+ an iteration is the next one's), and n*step + 3 after the loop."""
1004
+ body = (f"b(i) = 2*a(i) + 1 + ({var} - ({step}*i + {3 - step}))",
1005
+ f"{var} = {var} + {step}")
1006
+ return pdo(version, clause, maps=f"{STD_MAPS} map(tofrom: {var})",
1007
+ body=body, decls=[f"integer :: {var}"], init=[f"{var} = 3"],
1008
+ checks=[f"({var} == {step}*n + 3)"])
1009
+
1010
+
1011
+ def linear_simd_decl_prog(version, mod):
1012
+ """ref / uval are allowed only on declare simd (5.2 §5.4.6, 6.0
1013
+ §7.5.6): a declare simd function called in the target region. 5.0
1014
+ : the modifier(list) syntax, and every modifier (val too) is
1015
+ for declare simd only, since the worksharing-loop and simd constructs
1016
+ take none (5.0 §2.9.2 p.107, §2.9.3.1 p.113); a ref list item must not
1017
+ be an assumed-shape array (5.0 §2.19.4.6 p.293), so x is a scalar
1018
+ dummy argument."""
1019
+ ds = spell("declare simd", version)
1020
+ if version == 50:
1021
+ func = [" integer function lf(x)", " " + omp("declare target"),
1022
+ " " + omp(f"{ds}(lf)", f"linear({mod}(x))"),
1023
+ " integer, intent(in) :: x",
1024
+ " lf = 2*x + 1", " end function lf"]
1025
+ s = Src(module=["contains"] + func, focus=f"linear({mod}(x))",
1026
+ note=f"linear({mod}(x)) on a declare simd directive; the "
1027
+ "SIMD variant need not be called", observable=False)
1028
+ s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop(
1029
+ ("b(i) = lf(a(i))",)) + [omp("end target")]
1030
+ return s
1031
+ if mod == "ref":
1032
+ func = [" integer function lf(x)", " " + omp("declare target"),
1033
+ " " + omp(f"{ds}(lf)", "linear(x: ref)"),
1034
+ " integer, intent(in) :: x(:)",
1035
+ " lf = 2*x(1) + 1", " end function lf"]
1036
+ body = ("b(i) = lf(a(i:i))",)
1037
+ else:
1038
+ func = [" integer function lf(x)", " " + omp("declare target"),
1039
+ " " + omp(f"{ds}(lf)", "linear(x: uval)"),
1040
+ " integer, intent(in) :: x",
1041
+ " lf = 2*x + 1", " end function lf"]
1042
+ body = ("b(i) = lf(a(i))",)
1043
+ s = Src(module=["contains"] + func, focus=f"linear(x: {mod})",
1044
+ note=f"linear({mod}) on a declare simd directive; the SIMD "
1045
+ "variant need not be called", observable=False)
1046
+ s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop(body) + \
1047
+ [omp("end target")]
1048
+ return s
1049
+
1050
+
1051
+ def defaultmap_prog(version, mtype, cat=""):
1052
+ """defaultmap on target; the region writes the implicitly mapped
1053
+ variables of the category (scalar c, array w, allocatable wa, pointer
1054
+ wp) before reading them. What tells the clause from its absence (a
1055
+ scalar is firstprivate and an array tofrom without it):
1056
+
1057
+ * tofrom / from on a category with the scalar c: the device's c = 1
1058
+ comes back to the host (both memory modes);
1059
+ * to / alloc on the arrays: the device's writes stay on the device, so
1060
+ the host copy is unchanged (copy mode only);
1061
+ * present: c and w are present (enter data) with values the host then
1062
+ changes without an update: the region must use the device copies
1063
+ (copy mode only; without the clause c is firstprivate, the host's).
1064
+ """
1065
+ clause = f"defaultmap({mtype}: {cat})" if cat else f"defaultmap({mtype})"
1066
+ decls = ["integer :: c", "integer :: w(n)"]
1067
+ init, post, pre = [], [], []
1068
+ arr = None
1069
+ if mtype == "none":
1070
+ pre, body = [], BODY
1071
+ elif mtype == "present":
1072
+ body = ("b(i) = w(i) + c",)
1073
+ elif cat == "scalar":
1074
+ pre, body = ["c = 1"], ("b(i) = 2*a(i) + c",)
1075
+ elif cat == "allocatable":
1076
+ decls.append("integer, allocatable :: wa(:)")
1077
+ init, post = ["allocate(wa(n))", "wa = 0"], ["deallocate(wa)"]
1078
+ body, arr = ("wa(i) = 2*a(i)", "b(i) = wa(i) + 1"), "wa"
1079
+ elif cat == "pointer":
1080
+ decls.append("integer, pointer :: wp(:)")
1081
+ init, post = ["allocate(wp(n))", "wp = 0"], ["deallocate(wp)"]
1082
+ body, arr = ("wp(i) = 2*a(i)", "b(i) = wp(i) + 1"), "wp"
1083
+ elif cat == "aggregate":
1084
+ body, arr = ("w(i) = 2*a(i)", "b(i) = w(i) + 1"), "w"
1085
+ else: # all variables / no category: a scalar and an aggregate
1086
+ pre, body = ["c = 1"], ("w(i) = 2*a(i)", "b(i) = w(i) + c")
1087
+ arr = "w"
1088
+ init = ["c = 0", "w = 0"] + init
1089
+ s = Src(decls=decls, init=init, post=post, focus=clause)
1090
+ if mtype == "present":
1091
+ ent = spell("target enter data", version)
1092
+ ext = spell("target exit data", version)
1093
+ s.shared_setup()
1094
+ s.init += ["c = 1", "w = 2*a", omp(ent, "map(to: c, w)"),
1095
+ "if (.not. shared_mem) then", " c = 7", " w = 0",
1096
+ "end if"]
1097
+ s.post.insert(0, omp(ext, "map(release: c, w)"))
1098
+ if mtype in ("tofrom", "from") and cat in ("", "scalar"):
1099
+ s.checks.append("(c == 1)")
1100
+ if mtype in ("to", "alloc") and arr is not None:
1101
+ s.post[0:0] = s.copy_check(f"all({arr} == 0)")
1102
+ if mtype == "self":
1103
+ s.note = ("self maps the original storage: needs memory the device "
1104
+ "can access (zero-copy)")
1105
+ s.needs = ("zero_copy",)
1106
+ # i is predetermined private (5.2 §5.1.1); listed anyway under
1107
+ # defaultmap(none), which AFAR otherwise rejects, and defaultmap(present),
1108
+ # under which AFAR's runtime otherwise requires i to be present
1109
+ # ("device mapping required by 'present' ... (4 bytes)") and aborts
1110
+ extra = "private(i)" if mtype in ("none", "present") else ""
1111
+ s.region += [omp("target", STD_MAPS, FLAG_MAP, clause, extra),
1112
+ FLAG] + pre + seq_loop(body) + [omp("end target")]
1113
+ return s
1114
+
1115
+
1116
+ def defaultmap_character_prog(version):
1117
+ """omp.clause.defaultmap.character (a semantic atom): no
1118
+ defaultmap clause; the target region writes a character scalar ch, a
1119
+ character(len=4) s and an integer scalar k. For Fortran a variable of
1120
+ character type is an aggregate, not a scalar (5.2 §1.2.6 p.14, 6.0
1121
+ Glossary pp.20, 94), so ch and s are implicitly mapped tofrom and the
1122
+ device's writes come back (5.2 §5.8.1 p.148, 6.0 §7.9.3 p.277); k is a
1123
+ scalar without TARGET, ALLOCATABLE or POINTER, so it is firstprivate and
1124
+ the host's k stays 1. Both memory modes: under unified memory a tofrom
1125
+ map shares the storage (the write is seen) and firstprivate is still a
1126
+ private copy. A host fallback writes k too (check fails)."""
1127
+ s = Src(decls=["character :: ch", "character(len=4) :: s", "integer :: k"],
1128
+ init=["ch = 'a'", "s = 'abcd'", "k = 1"],
1129
+ checks=["(ch == 'b')", "(s == 'wxyz')", "(k == 1)"],
1130
+ focus="implicit data-mapping of character variables (no "
1131
+ "defaultmap clause)")
1132
+ s.header_notes.append(
1133
+ "semantic atom: no defaultmap clause; Fortran character variables "
1134
+ "are aggregates (implicitly mapped tofrom), the integer scalar k is "
1135
+ "firstprivate")
1136
+ s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG,
1137
+ "ch = 'b'", "s = 'wxyz'", "k = 2"] + seq_loop() + [
1138
+ omp("end target")]
1139
+ return s
1140
+
1141
+
1142
+ # Device spin of the producer target task: about one second of
1143
+ # integer work on the MI300A (AFAR and CCE), long enough for a consumer
1144
+ # target task that ignores the dependence to run first.
1145
+ DEPEND_SPIN = 20000000
1146
+
1147
+
1148
+ def _depend_clauses(s: Src, first: str, second: str) -> tuple[str, str]:
1149
+ d1 = f"depend({first}: w)"
1150
+ d2 = f"depend({second}: w)"
1151
+ if first == "iterator":
1152
+ s.decls.append("integer :: k")
1153
+ d1 = "depend(iterator(k = 1:n), out: w(k))"
1154
+ d2 = "depend(iterator(k = 1:n), in: w(k))"
1155
+ if first == "depobj":
1156
+ s.decls.append("integer(omp_depend_kind) :: obj")
1157
+ s.init.append(omp("depobj(obj)", "depend(out: w)"))
1158
+ d1 = "depend(depobj: obj)"
1159
+ return d1, d2
1160
+
1161
+
1162
+ def depend_prog(version, first="out", second="in", writer=False):
1163
+ """Device only: two target tasks ordered only by a dependence
1164
+ on w. The producer (`first` dependence, target nowait) spins on the
1165
+ device for about a second (DEPEND_SPIN iterations; the spin value goes
1166
+ to spin(1), so it cannot be optimised away) and then writes w = 2*a;
1167
+ the consumer (`second` dependence, target nowait) maps w to the device
1168
+ and computes b = w + 1; a taskwait completes both. Without the
1169
+ dependence a runtime that runs the two target tasks concurrently gives
1170
+ the consumer w before the producer's write (w = 0, or a partial w) and
1171
+ the check fails; a runtime that runs each target task to completion
1172
+ when it is encountered cannot fail it (the audit says which toolchain
1173
+ does what). w is mapped tofrom by the producer and to by the consumer,
1174
+ so the order also holds in copy mode. `writer` (the dependence types a
1175
+ producer carries: out, inout, inoutset, mutexinoutset): the producer
1176
+ also has a dependence of another type on a token tk, so that its mutant
1177
+ keeps a depend clause: CCE's C runtime starts the consumer's
1178
+ depend(in) task only after a producer without any depend clause has
1179
+ finished (the mutant survived), not after one with a dependence on
1180
+ something else."""
1181
+ s = Src(decls=["integer :: w(n)", "integer :: j, sp, spin(1)"],
1182
+ init=["w = 0", "spin = 0"])
1183
+ d1, d2 = _depend_clauses(s, first, second)
1184
+ if writer:
1185
+ s.decls.append("integer :: tk")
1186
+ s.init.append("tk = 0")
1187
+ d1 += f" depend({'out' if first == 'inout' else 'inout'}: tk)"
1188
+ s.region += [omp("target", "nowait", "map(to: a) map(tofrom: w)",
1189
+ "map(from: spin)", d1),
1190
+ "sp = 1", f"do j = 1, {DEPEND_SPIN}",
1191
+ " sp = mod(7*sp + j, 1000003)", "end do", "spin(1) = sp",
1192
+ "do j = 1, n", " w(j) = 2*a(j)", "end do",
1193
+ omp("end target"),
1194
+ omp("target", "nowait", "map(to: w) map(from: b)", FLAG_MAP,
1195
+ d2), FLAG] + \
1196
+ seq_loop(("b(i) = w(i) + 1",)) + [omp("end target"), omp("taskwait")]
1197
+ if first == "depobj":
1198
+ s.post.append(omp("depobj(obj)", "destroy"))
1199
+ s.header_notes.append(
1200
+ "device only: the dependence orders two target tasks (target nowait "
1201
+ "producer and consumer, completed by taskwait)")
1202
+ s.focus = d2 if first == "out" and second == "in" else d1
1203
+ return s
1204
+
1205
+
1206
+ # The realistic use each host+device pattern stands for (owner
1207
+ # exception): its header line ``! scope: host+device: <use>``.
1208
+ DEPEND_HOST_USE = ("CPU producer / GPU consumer pipeline: a host task "
1209
+ "prepares data (depend(out)) that a dependent target task "
1210
+ "consumes on the device (depend(in)), as in task-based "
1211
+ "codes that overlap host preprocessing with offload")
1212
+ THREADS_USE = ("multi-threaded offload: host threads (one per chunk, as in "
1213
+ "MPI+OpenMP or task-parallel host codes) each launch their "
1214
+ "own target region on the same device")
1215
+ THREADS_NOWAIT_USE = ("multi-threaded asynchronous offload: host threads "
1216
+ "each launch a target nowait region on the same "
1217
+ "device; the parallel region's end completes the "
1218
+ "target tasks")
1219
+
1220
+
1221
+ def depend_host_prog(version, first="out", second="in"):
1222
+ """host+device pattern (the earlier depend programs): a
1223
+ host task and a target task generated in a host parallel region's
1224
+ single construct, ordered only by a dependence on w: the task (`first`
1225
+ dependence) spins for 0.5 s of wall time before writing w = 2*a on the
1226
+ host, the target task (`second` dependence) maps w to the device and
1227
+ computes b = w + 1. Without the dependence the target task maps the
1228
+ host's w, still 0, while the task spins, and the check fails (a
1229
+ runtime that runs the task at once, undeferred, cannot fail it). The
1230
+ spin is on the host clock, so the ordering does not depend on how the
1231
+ device schedules its kernels. Judged on the device result (b, and
1232
+ ran_on_host from the target task)."""
1233
+ s = Src(decls=["integer :: w(n)", "double precision :: t0"],
1234
+ init=["w = 0"])
1235
+ d1, d2 = _depend_clauses(s, first, second)
1236
+ s.region += [omp("target", "map(tofrom: w)"), "w(1) = 0",
1237
+ omp("end target"),
1238
+ omp("parallel", "num_threads(2)"), omp("single"),
1239
+ omp("task", d1),
1240
+ "t0 = omp_get_wtime()",
1241
+ "do while (omp_get_wtime() - t0 < 0.5d0)", "end do",
1242
+ "w = 2*a", omp("end task"),
1243
+ omp("target", "map(to: w) map(from: b)", FLAG_MAP, d2),
1244
+ FLAG] + \
1245
+ seq_loop(("b(i) = w(i) + 1",)) + [omp("end target"), omp("taskwait"),
1246
+ omp("end single"),
1247
+ omp("end parallel")]
1248
+ if first == "depobj":
1249
+ s.post.append(omp("depobj(obj)", "destroy"))
1250
+ s.header_notes.append(f"scope: host+device: {DEPEND_HOST_USE}")
1251
+ s.focus = d2 if first == "out" and second == "in" else d1
1252
+ return s
1253
+
1254
+
1255
+ def threads_offload_prog(version, nowait=False):
1256
+ """host+device pattern: a host parallel do over four chunks of
1257
+ b; each iteration (one host thread each, num_threads(4)) launches its
1258
+ own target region for its chunk (`nowait`: a target nowait region,
1259
+ completed by the parallel region's end). Each target region reports
1260
+ the device into hf(c); the check is the device result b and
1261
+ ran_on_host = 1 when any hf(c) is 1.
1262
+
1263
+ A and b (and hf) are mapped whole once, by target enter data
1264
+ before the parallel region, and copied back by target exit data after
1265
+ it. The form mapped each thread's sections (map(to: a(lo:hi))
1266
+ map(from: b(lo:hi))) on the threads' target constructs; the target
1267
+ region references the whole arrays, so they are also implicitly
1268
+ mapped (5.2 §5.8.1), and once sections created by two earlier
1269
+ constructs are present when a third thread's construct maps the array
1270
+ "the behavior is unspecified" (5.2 §5.8.3 p.155; 6.0 §7.9.6 p.286:
1271
+ the storage "must correspond to a single mappable storage block").
1272
+ Without nowait each thread moves its own section with target update
1273
+ (to before, from after its region)."""
1274
+ c = "nowait" if nowait else ""
1275
+ s = Src(decls=["integer :: c, lo, hi, hf(4)"], init=["hf = 1"])
1276
+ if nowait:
1277
+ enter = omp("target enter data", "map(to: a, hf) map(alloc: b)")
1278
+ exit_ = omp("target exit data", "map(from: b, hf) map(release: a)")
1279
+ before, after = [], []
1280
+ else:
1281
+ enter = omp("target enter data", "map(alloc: a, b) map(to: hf)")
1282
+ exit_ = omp("target exit data", "map(from: hf) map(release: a, b)")
1283
+ before = [" " + omp("target update", "to(a(lo:hi))")]
1284
+ after = [" " + omp("target update", "from(b(lo:hi))")]
1285
+ s.region += [enter, omp("parallel do", "num_threads(4) private(lo, hi)"),
1286
+ "do c = 1, 4", " lo = (c - 1)*16 + 1", " hi = c*16"] \
1287
+ + before + [" " + omp("target", c),
1288
+ " hf(c) = merge(1, 0, omp_is_initial_device())",
1289
+ " do i = lo, hi", " b(i) = 2*a(i) + 1",
1290
+ " end do", " " + omp("end target")] + after \
1291
+ + ["end do", omp("end parallel do"), exit_, "host_flag = 0",
1292
+ "do c = 1, 4", " if (hf(c) == 1) host_flag = 1", "end do"]
1293
+ s.header_notes.append("scope: host+device: " + (
1294
+ THREADS_NOWAIT_USE if nowait else THREADS_USE))
1295
+ s.focus = c or "target"
1296
+ return s
1297
+
1298
+
1299
+ # CCE 19 does not run target nowait tasks synchronously (it
1300
+ # kills every depend mutant: without the dependence the consumer target
1301
+ # task reads the producer's output before it is written; audit);
1302
+ # AFAR completes each one before the next outside a parallel region (it
1303
+ # survives them)
1304
+ NOWAIT_WHY = ("an implementation may run a target nowait task synchronously "
1305
+ "(AFAR does outside a parallel region; CCE 19 runs two of them "
1306
+ "concurrently, as the depend programs show), so no single run "
1307
+ "of a conforming program can tell nowait from its absence")
1308
+
1309
+
1310
+ def device_ptr_prog(version, data_extra=""):
1311
+ """use_device_ptr on target data, is_device_ptr on the target inside.
1312
+ 5.0: an is_device_ptr list item must be a dummy argument
1313
+ without the ALLOCATABLE, POINTER or VALUE attribute (5.0 §2.12.5
1314
+ p.176), so the target region is in a module subroutine whose dummy
1315
+ arguments are the two pointers."""
1316
+ sp = spell("target data", version)
1317
+ if version == 50:
1318
+ s = Src(uses=["use, intrinsic :: iso_c_binding, only: c_ptr, c_loc"],
1319
+ decls=["type(c_ptr) :: pa, pb"],
1320
+ init=["pa = c_loc(a)", "pb = c_loc(b)"],
1321
+ needs=("device_c_f_pointer",), evidence="copy_mode_only")
1322
+ s.module = ["contains", " subroutine dev_kernel(pa, pb, m, hf)",
1323
+ " use omp_lib, only: omp_is_initial_device",
1324
+ " use, intrinsic :: iso_c_binding, only: c_ptr, "
1325
+ "c_f_pointer",
1326
+ " type(c_ptr) :: pa, pb",
1327
+ " integer :: m, hf, i",
1328
+ " integer, pointer :: pp(:), qq(:)",
1329
+ " " + omp("target", "is_device_ptr(pa, pb)",
1330
+ "private(pp, qq)", "map(tofrom: hf)"),
1331
+ " hf = merge(1, 0, omp_is_initial_device())",
1332
+ " call c_f_pointer(pa, pp, [m])",
1333
+ " call c_f_pointer(pb, qq, [m])",
1334
+ " do i = 1, m", " qq(i) = 2*pp(i) + 1",
1335
+ " end do", " " + omp("end target"),
1336
+ " end subroutine dev_kernel"]
1337
+ s.region += [omp(sp, STD_MAPS, "use_device_ptr(pa, pb)", data_extra),
1338
+ "call dev_kernel(pa, pb, n, host_flag)", omp("end", sp)]
1339
+ return s
1340
+ s = Src(uses=["use, intrinsic :: iso_c_binding, only: c_ptr, c_loc, "
1341
+ "c_f_pointer"],
1342
+ decls=["type(c_ptr) :: pa, pb", "integer, pointer :: pp(:), qq(:)"],
1343
+ init=["pa = c_loc(a)", "pb = c_loc(b)"],
1344
+ needs=("device_c_f_pointer",), evidence="copy_mode_only")
1345
+ s.region += [omp(sp, STD_MAPS, "use_device_ptr(pa, pb)", data_extra),
1346
+ omp("target", "is_device_ptr(pa, pb)", "private(pp, qq)",
1347
+ FLAG_MAP), FLAG,
1348
+ "call c_f_pointer(pa, pp, [n])",
1349
+ "call c_f_pointer(pb, qq, [n])"] + seq_loop(
1350
+ ("qq(i) = 2*pp(i) + 1",)) + [omp("end target"), omp("end", sp)]
1351
+ return s
1352
+
1353
+
1354
+ def device_addr_prog(version, data_extra=""):
1355
+ """use_device_addr on target data, has_device_addr on the target
1356
+ inside. Inside the data region c_loc(a) must differ from a's host
1357
+ address (checked in copy mode only: under unified memory the device
1358
+ address is the host address). 5.0 has no has_device_addr: an
1359
+ outer target data maps a and b, an inner one with use_device_addr(a, b)
1360
+ holds the address check, and the kernel follows it in the outer region
1361
+ (a and b present)."""
1362
+ s = Src(evidence="copy_mode_only")
1363
+ sp = spell("target data", version)
1364
+ if version == 50:
1365
+ s.region += s.save_addresses(("a", "b")) + [
1366
+ omp(sp, STD_MAPS), omp(sp, "use_device_addr(a, b)", data_extra)] \
1367
+ + s.address_moved(("a", "b")) + [omp("end", sp),
1368
+ omp("target", FLAG_MAP), FLAG] \
1369
+ + seq_loop() + [omp("end target"), omp("end", sp)]
1370
+ return s
1371
+ s.region += s.save_addresses(("a", "b")) + [
1372
+ omp(sp, STD_MAPS, "use_device_addr(a, b)", data_extra)] + \
1373
+ s.address_moved(("a", "b")) + [
1374
+ omp("target", "has_device_addr(a, b)", FLAG_MAP), FLAG] + \
1375
+ seq_loop() + [omp("end target"), omp("end", sp)]
1376
+ return s
1377
+
1378
+
1379
+ def mapper_prog(version):
1380
+ """The mapper maps s%v with map type to; the clause invoking it says
1381
+ tofrom, and the decayed map type is to (5.2 Table 5.3, 6.0 map-type
1382
+ decay): the region's write of s%v stays on the device (copy mode).
1383
+ Without the mapper modifier the default mapper maps s tofrom and the
1384
+ write comes back."""
1385
+ dm = spell("declare mapper", version)
1386
+ c = "map(mapper(mm), tofrom: s)"
1387
+ s = Src(spec=["type :: dt", " integer :: v", "end type dt",
1388
+ omp(f"{dm}(mm: dt :: q)", "map(to: q%v)")],
1389
+ decls=["type(dt) :: s"], init=["s%v = 1"], focus=c)
1390
+ s.region += [omp("target", STD_MAPS, c, FLAG_MAP), FLAG] + seq_loop(
1391
+ ("b(i) = 2*a(i) + s%v",)) + ["s%v = 7", omp("end target")]
1392
+ s.post += s.copy_check("s%v == 1")
1393
+ return s
1394
+
1395
+
1396
+ def ancestor_prog(version, modifier="ancestor: 1"):
1397
+ """A reverse-offload region (device(ancestor: 1)) inside a target
1398
+ region. Its body has no checked effect: no OpenMP construct or API call
1399
+ may appear in it (5.2 §13.8, 6.0 §15.8)."""
1400
+ s = Src(spec=[omp("requires", "reverse_offload")],
1401
+ decls=["integer :: t"], init=["t = 0"], observable=False,
1402
+ note="the reverse-offload region has no checked effect",
1403
+ focus=f"device({modifier})")
1404
+ s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop() + [
1405
+ omp("target", f"device({modifier})", "firstprivate(t)"),
1406
+ "t = t + 1", omp("end target"), omp("end target")]
1407
+ return s
1408
+
1409
+
1410
+ def alloc_prog(version, alloc_clause, wrap="uses_allocators("
1411
+ "omp_default_mem_alloc)", decls=(), shared=("a", "b")):
1412
+ """`shared` names the variables the parallel do shares under the
1413
+ default(none) hygiene; an allocator handle variable named in the
1414
+ allocate clause must be among them (CCE 19 ftn-1510)."""
1415
+ return pdo(version, f"private(t) {alloc_clause}", wrap=wrap,
1416
+ body=("t = 2*a(i)", "b(i) = t + 1"), shared=tuple(shared),
1417
+ decls=["integer :: t"] + list(decls), focus=alloc_clause)
1418
+
1419
+
1420
+ TRAITS = ["integer(omp_allocator_handle_kind) :: my_alloc",
1421
+ "type(omp_alloctrait), parameter :: my_traits(1) = &",
1422
+ " [omp_alloctrait(omp_atk_alignment, 64)]"]
1423
+
1424
+
1425
+ # --- clause atoms -----------------------------------------------------------------
1426
+
1427
+ # message/severity on a compute construct (the
1428
+ # re-audit with AFAR's -fopenmp-version=60 shows the mutant survives).
1429
+ def error_probe(version, sev):
1430
+ """Message and severity (6.0) on an error
1431
+ directive with at(compilation) in a target region (before,
1432
+ the main program's specification part), message PROBE_TOKEN: with
1433
+ severity(warning) the compiler must show the message and go on
1434
+ (expect compile_message; the program then runs the standard kernel),
1435
+ with severity(fatal) it must stop the compilation (expect
1436
+ compile_error) (6.0 §10.1 error directive, §10.3 message, §10.4
1437
+ severity). Mutants: the message removed (the token disappears), the
1438
+ severity removed (fatal by default: the compile stops), fatal replaced
1439
+ by warning (the compile goes on)."""
1440
+ from offload.engine.gen import PROBE_TOKEN
1441
+ c = f'message("{PROBE_TOKEN}")'
1442
+ s = Src(evidence="probe", token=PROBE_TOKEN,
1443
+ expect="compile_error" if sev == "fatal" else "compile_message",
1444
+ focus=f"severity({sev})")
1445
+ s.header_notes.append(
1446
+ "compile-output probe: the error directive's message must appear in "
1447
+ "the compiler output " + ("and stop the compilation (fatal)"
1448
+ if sev == "fatal" else
1449
+ "as a warning, and the compilation go on"))
1450
+ # The error directive is in the target region (device code),
1451
+ # not in the host program's specification part
1452
+ s.region += [omp("target", STD_MAPS, FLAG_MAP),
1453
+ omp("error", "at(compilation)", f"severity({sev})", c),
1454
+ FLAG] + seq_loop() + [omp("end target")]
1455
+ return s
1456
+
1457
+
1458
+ # message/severity on parallel do: the directive-name family members keep
1459
+ # this form (offload.engine.gen._DNM_RECIPE)
1460
+ ERROR_CLAUSE_WHY = ("message and severity on parallel act only when a "
1461
+ "num_threads(strict: n) request cannot be met (6.0 "
1462
+ "§12.1.2: error termination as if by an error directive "
1463
+ "with these clauses); neither AFAR nor CCE 19 accepts "
1464
+ "the strict modifier (omp.clause.num_threads.strict is "
1465
+ "rejected by both), so no program can trigger them here, "
1466
+ "and a passing program raises no error")
1467
+ # The present modifiers as negative probes: an absent item with
1468
+ # present requires runtime error termination (5.2 §5.8.3 p.152 and §5.9
1469
+ # p.165; 6.0 §7.9.6 p.283 and §7.10 p.296); without the modifier the map
1470
+ # maps it and the update skips it, and the program goes on.
1471
+ PRESENT_CITE = {"map": {52: "5.2 §5.8.3 p.152", 60: "6.0 §7.9.6 p.283"},
1472
+ "update": {52: "5.2 §5.9 p.165", 60: "6.0 §7.10 p.296"}}
1473
+
1474
+
1475
+ def present_probe(version, which):
1476
+ """Negative probe for map(present, ...), to(present: ...) or
1477
+ from(present: ...): the standard kernel runs first (device evidence; it
1478
+ maps a and b only for its region), then the construct names an item
1479
+ that is not present with the present modifier. A correct run ends there
1480
+ with runtime error termination (offload.engine.harness.ERROR_TERMINATION);
1481
+ a run that goes on sets `continued` and prints check=fail."""
1482
+ up = spell("target update", version)
1483
+ if which == "map":
1484
+ c = "map(present, to: a)"
1485
+ probe = [omp("target", c, "map(from: b)")] + seq_loop() + [
1486
+ omp("end target")]
1487
+ else:
1488
+ c = "to(present: a)" if which == "to" else "from(present: b)"
1489
+ probe = [omp(up, c)]
1490
+ what = "map" if which == "map" else "update"
1491
+ s = Src(decls=["logical :: continued"], init=["continued = .false."],
1492
+ checks=["(.not. continued)"], focus=c, evidence="probe",
1493
+ expect="error_termination")
1494
+ s.header_notes.append(
1495
+ f"negative probe: {c} names an item that is not present, which "
1496
+ f"requires runtime error termination ({PRESENT_CITE[what][version]}); "
1497
+ "reaching the end of the program prints check=fail")
1498
+ s.region += INNER + probe + ["continued = .true."]
1499
+ return s
1500
+
1501
+
1502
+ SELF_WHY = ("re-categorised (was implementation_defined): a self "
1503
+ "map is 'a mapping operation for which the corresponding storage "
1504
+ "is the same as its original storage' (6.0 Glossary p.95), and a "
1505
+ "map-entering self map of storage the device cannot access must "
1506
+ "end in runtime error termination (6.0 §7.9.6 p.285), so the "
1507
+ "spec fixes the behaviour: in copy mode (HSA_XNACK=0) a device "
1508
+ "write of a self-mapped a must be seen by the host, or the "
1509
+ "run must stop; without self the write stays on the device. That "
1510
+ "check is not written: the program needs zero-copy memory as "
1511
+ "generated, and AFAR at HSA_XNACK=0 copies even with self (probe "
1512
+ "2026-10-18: map(self, to: a) with a device write of a leaves the "
1513
+ "host's a unchanged), which the check would report as a wrong "
1514
+ "answer")
1515
+ CLOSE_WHY = ("close is a hint to allocate the device copy in memory close to "
1516
+ "the device; values and presence are the same without it")
1517
+
1518
+
1519
+ def _feat_map(version, feat):
1520
+ """map clause features."""
1521
+ ent, ext = (spell("target enter data", version),
1522
+ spell("target exit data", version))
1523
+ if feat in (None, "to", "from"):
1524
+ # the device overwrites a after reading it: with map(to: a) the
1525
+ # write stays on the device (copy mode), so the host's a is
1526
+ # unchanged; without the map type (or the clause) a is mapped
1527
+ # tofrom and the write comes back
1528
+ s = _k(version, Region("target", body=BODY + ("a(i) = 0",)),
1529
+ focus="map(to: a)")
1530
+ s.post += s.copy_check("all(2*a + 1 == ref)")
1531
+ return s
1532
+ if feat == "tofrom":
1533
+ return _k(version, Region("target", maps="map(to: a) map(tofrom: b)"),
1534
+ focus="map(tofrom: b)")
1535
+ if feat == "always":
1536
+ # a is present (enter data) when the host changes it: only the
1537
+ # always modifier copies the new values to the present device copy
1538
+ # (copy mode; without it the region reads the stale copy)
1539
+ c = "map(always, to: a)"
1540
+ s = _k(version, Region("target", maps=f"{c} map(from: b)"),
1541
+ focus=c, ref="2*(i + 10) + 1", evidence="copy_mode_only",
1542
+ init=[omp(ent, "map(to: a)"), "do i = 1, n",
1543
+ " a(i) = a(i) + 10", "end do"],
1544
+ post=[omp(ext, "map(release: a)")])
1545
+ return s
1546
+ if feat == "close":
1547
+ c = f"map({feat}, to: a)"
1548
+ return _k(version, Region("target", maps=f"{c} map(from: b)"),
1549
+ focus=c).no_oracle("hint_only", CLOSE_WHY, "close_hint")
1550
+ if feat == "present":
1551
+ return present_probe(version, "map")
1552
+ if feat == "alloc":
1553
+ return enter_exit_prog(version, focus="map(to: a) map(alloc: b)")
1554
+ if feat == "release":
1555
+ return enter_exit_prog(version, twice=True,
1556
+ focus="map(from: b) map(release: a)")
1557
+ if feat == "delete":
1558
+ return enter_exit_prog(version, exit_="map(from: b) map(delete: a)",
1559
+ twice=True, focus="map(delete: a)")
1560
+ if feat == "storage":
1561
+ return enter_exit_prog(version, enter="map(to: a) map(storage: b)",
1562
+ focus="map(storage: b)")
1563
+ if feat == "self":
1564
+ c = "map(self: a)"
1565
+ return _k(version, Region("target", maps=f"{c} map(from: b)"),
1566
+ focus=c, note="self maps the original storage: needs memory "
1567
+ "the device can access (zero-copy)", needs=("zero_copy",)
1568
+ ).unverified("oracle_not_written", SELF_WHY)
1569
+ if feat == "ref_ptee":
1570
+ # ref_ptee maps the pointee only (6.0 §7.9.5): the region, which
1571
+ # dereferences p, maps the pointer itself (ref_ptr), attached to
1572
+ # the pointee that the enter data made present with its values
1573
+ c = "map(ref_ptee, to: p)"
1574
+ return _k(version, Region("target", maps="map(ref_ptr, to: p) "
1575
+ "map(from: b)", body=("b(i) = 2*p(i) + 1",)),
1576
+ decls=["integer, pointer :: p(:)"],
1577
+ init=["p => a", omp(ent, c)],
1578
+ post=[omp(ext, "map(ref_ptee, release: p)")], focus=c)
1579
+ if feat in ("ref_ptr", "ref_ptr_ptee"):
1580
+ c = f"map({feat}, to: p)"
1581
+ maps = f"map(to: a) {c} map(from: b)" if feat == "ref_ptr" \
1582
+ else f"{c} map(from: b)"
1583
+ s = _k(version, Region("target", maps=maps,
1584
+ body=("b(i) = 2*p(i) + 1",)),
1585
+ decls=["integer, pointer :: p(:)"], init=["p => a"], focus=c)
1586
+ if feat == "ref_ptr_ptee":
1587
+ s.no_oracle("equivalent_default", "without a ref-modifier the "
1588
+ "map applies to the referring pointer and its "
1589
+ "pointee (6.0 §7.9.6), i.e. ref_ptr_ptee",
1590
+ "ref_ptr_ptee_default")
1591
+ return s
1592
+ if feat == "iterator":
1593
+ # only the items the iterator generates are mapped: for k = 1 the
1594
+ # section w(2:n/2) is present after enter data, the elements around
1595
+ # it are not (copy mode: under unified memory nothing is reported
1596
+ # present); an empty range maps nothing. One iterator
1597
+ # value, since list items generated from different values must not
1598
+ # have the same containing array (5.2 §5.8.3 p.155, 6.0 §7.9.6
1599
+ # p.286); the form w(k), k = 1:n:2, broke that restriction
1600
+ c = "map(iterator(k = 1:1), to: w(k + 1:n/2))"
1601
+ s = _k(version, Region("target"), focus=c,
1602
+ decls=["integer :: k", "integer, target :: w(n)"],
1603
+ init=["w = 0", omp(ent, c)])
1604
+ s.post += s.presence(("w(2)", "w(n/2)"), True) + s.presence(
1605
+ ("w(1)", "w(n/2 + 1)"), False) + [
1606
+ omp(ext, "map(iterator(k = 1:1), release: w(k + 1:n/2))")]
1607
+ return s
1608
+ if feat == "mapper":
1609
+ return mapper_prog(version)
1610
+ return None
1611
+
1612
+
1613
+ def _motion(version, which, feat):
1614
+ """to / from on target update."""
1615
+ arr = "a" if which == "to" else "b"
1616
+ if feat is None:
1617
+ c = f"{which}({arr})"
1618
+ elif feat == "present":
1619
+ return present_probe(version, which)
1620
+ elif feat == "mapper":
1621
+ return motion_mapper_prog(version, which)
1622
+ elif feat == "iterator":
1623
+ c = f"{which}(iterator(k = 1:1): {arr}(k:n))"
1624
+ else:
1625
+ return None
1626
+ kw = {"to": c} if which == "to" else {"frm": c}
1627
+ s = update_prog(version, focus=c, **kw)
1628
+ if feat == "iterator":
1629
+ s.decls.append("integer :: k")
1630
+ return s
1631
+
1632
+
1633
+ def motion_mapper_prog(version, which):
1634
+ """The mapper mm of type dt maps only the
1635
+ component v; s (v = w = 1) is entered whole. to: the host sets v = w =
1636
+ 5 and `target update to(mapper(mm): s)` copies only v, so the device
1637
+ sees v = 5, w = 1. from: the device sets v = w = 5 and `target update
1638
+ from(mapper(mm): s)` copies only v back, so the host sees v = 5, w = 1.
1639
+ Without the modifier the update copies all of s (w = 5 too). Copy mode
1640
+ only (shared storage hides the copies)."""
1641
+ dm, up = spell("declare mapper", version), spell("target update",
1642
+ version)
1643
+ ent, ext = (spell("target enter data", version),
1644
+ spell("target exit data", version))
1645
+ c = f"{which}(mapper(mm): s)"
1646
+ s = Src(spec=["type :: dt", " integer :: v(8)", " integer :: w(8)",
1647
+ "end type dt", omp(f"{dm}(mm: dt :: q)", "map(q%v)")],
1648
+ decls=["type(dt) :: s", "integer :: rv, rw"],
1649
+ init=["s%v = 1", "s%w = 1", "rv = 0", "rw = 0"], focus=c)
1650
+ s.region += INNER + [omp(ent, "map(to: s)")]
1651
+ if which == "to":
1652
+ s.region += ["s%v = 5", "s%w = 5", omp(up, c),
1653
+ omp("target", "map(from: rv, rw)"), "rv = s%v(1)",
1654
+ "rw = s%w(1)", omp("end target")]
1655
+ cond = "rv == 5 .and. rw == 1"
1656
+ else:
1657
+ s.region += [omp("target"), "s%v = 5", "s%w = 5", omp("end target"),
1658
+ omp(up, c), "rv = s%v(1)", "rw = s%w(1)"]
1659
+ cond = "rv == 5 .and. rw == 1"
1660
+ s.region += [omp(ext, "map(delete: s)")]
1661
+ s.post += s.copy_check(cond)
1662
+ return s
1663
+
1664
+
1665
+ SCHEDULE = {None: "schedule(static)", "static": "schedule(static)",
1666
+ "dynamic": "schedule(dynamic)", "guided": "schedule(guided)",
1667
+ "auto": "schedule(auto)", "runtime": "schedule(runtime)",
1668
+ "chunk_size": "schedule(static, 4)",
1669
+ "monotonic": "schedule(monotonic: static)",
1670
+ "nonmonotonic": "schedule(nonmonotonic: dynamic)",
1671
+ "simd": "schedule(simd: static)"}
1672
+
1673
+ REQUIRES = {"unified_address", "unified_shared_memory", "reverse_offload",
1674
+ "dynamic_allocators", "self_maps", "device_safesync"}
1675
+
1676
+
1677
+ # Triage (probe on Tuolumne, 2026-10-18, HSA_XNACK=0): with a chunk
1678
+ # size the dynamic and guided schedules would be checkable (each chunk is a
1679
+ # contiguous block of chunk_size iterations, at least chunk_size for
1680
+ # guided), but schedule(dynamic, 4) and schedule(guided, 8) on parallel do in
1681
+ # a target region give iteration i to thread mod(i - 1, T) on AFAR (T = 32)
1682
+ # and CCE 19 (T = 256), as schedule(static, 4) does (the unaudited
1683
+ # omp.clause.schedule.chunk_size), so no chunked mutant could be killed.
1684
+ SCHED_CHUNK_PROBE = (
1685
+ "; with a chunk size the chunks would be checkable (a chunk is a "
1686
+ "contiguous set of iterations executed by one thread: 5.2/6.0 glossary, "
1687
+ "§11.5.3 / §13.6.3), but both toolchains split chunks across threads on "
1688
+ "the device (probe 2026-10-18: schedule(dynamic, 4) and schedule(guided, "
1689
+ "8) give iteration i to thread mod(i - 1, T), as schedule(static, 4) "
1690
+ "does), which violates the spec rather than using a freedom it grants; "
1691
+ "that failure is measured by the unaudited schedule.chunk_size and "
1692
+ "schedule.static programs, and no chunked mutant could be killed here")
1693
+ # Re-categorisation (was implementation_defined): with a chunk size
1694
+ # the spec fixes the chunks of dynamic and guided ("Each chunk contains
1695
+ # chunk_size iterations, except for the chunk that contains the
1696
+ # sequentially last iteration", 5.2 §11.5.3 p.253, 6.0 §13.6.3 p.418; for
1697
+ # guided "the chunks do not contain fewer than k iterations"), so a
1698
+ # deterministic check exists (each chunk is one thread's), and run-sched-var
1699
+ # can be set on the device with omp_set_schedule.
1700
+ SCHED_UNWRITTEN_WHY = {
1701
+ "dynamic": "the program uses schedule(dynamic), whose chunk size 1 lets "
1702
+ "any assignment conform, but the kind fixes its chunks once a "
1703
+ "chunk size is given (5.2 §11.5.3 p.253, 6.0 §13.6.3 p.418: "
1704
+ "each chunk is chunk_size contiguous iterations run by one "
1705
+ "thread), so schedule(dynamic, 4) with a check that every "
1706
+ "aligned block of 4 iterations ran on one thread is a "
1707
+ "deterministic oracle; it is not written" + SCHED_CHUNK_PROBE,
1708
+ "guided": "the program uses schedule(guided), whose chunk size 1 lets "
1709
+ "any assignment conform, but with a chunk size k no chunk but "
1710
+ "the last may have fewer than k iterations (5.2 §11.5.3 p.253, "
1711
+ "6.0 §13.6.3 p.419), so schedule(guided, 8) with a check that "
1712
+ "every run of iterations on one thread but the last is at "
1713
+ "least 8 long is a deterministic oracle; it is not written"
1714
+ + SCHED_CHUNK_PROBE,
1715
+ "runtime": "run-sched-var's initial value is implementation defined "
1716
+ "(5.2 §2.2 p.40, 6.0 §3.2 p.118; the harness clears "
1717
+ "OMP_SCHEDULE), but omp_set_schedule in the target region "
1718
+ "fixes it, after which schedule(runtime) is the schedule set "
1719
+ "(5.2 §11.5.3 p.253), a deterministic oracle with the static "
1720
+ "kind; it is not written: CCE 19 does not link "
1721
+ "omp_set_schedule in device code (probe 2026-10-18) and the "
1722
+ "chunked kinds are those the toolchains do not honour on the "
1723
+ "device"}
1724
+ # Schedule(static) and dist_schedule(static) without
1725
+ # a chunk size, one reading for both
1726
+ STATIC_NO_CHUNK_WHY = (
1727
+ "the static kind without a chunk size guarantees only contiguous "
1728
+ "chunks, at most one per thread (team), and the spec's Note calls "
1729
+ "chunks of ceil(n/p) and q / q - 1 splits alike compliant, so the check "
1730
+ "tests contiguity and that every iteration ran on an owner; without the "
1731
+ "clause the schedule is implementation defined by the spec (worksharing "
1732
+ "loop: def-sched-var / auto; distribute: stated in the construct's "
1733
+ "section), so it may be such a split and no single run separates the "
1734
+ "clause from its absence (the label rests on this spec text, not on an "
1735
+ "observed default); the chunk_size forms fix one assignment exactly and "
1736
+ "keep an audited oracle")
1737
+ SCHED_WHY = {
1738
+ "auto": "the auto schedule delegates the assignment of iterations to "
1739
+ "threads to the compiler and runtime, so every assignment is "
1740
+ "conforming (5.2 §11.5.3, 6.0 §13.6.3)",
1741
+ "simd": "the simd modifier only rounds the chunk size up to a multiple "
1742
+ "of the implementation-defined SIMD width (5.2 §11.5.3, 6.0 "
1743
+ "§13.6.3), and both toolchains ignore schedule chunk sizes on "
1744
+ "the device (probe 2026-10-18)"}
1745
+ SCHED_BASIS = {"auto": "schedule_auto", "simd": "schedule_simd_width"}
1746
+ NONMONOTONIC_WHY = ("nonmonotonic is the modifier the dynamic kind the "
1747
+ "program uses has without one (5.0 and later)")
1748
+ MONOTONIC_WHY = (
1749
+ "Schedule(monotonic: dynamic) with a check of the order each "
1750
+ "thread ran its iterations in (a per-thread counter, firstprivate from "
1751
+ "0): monotonic requires increasing logical iteration order per thread, "
1752
+ "and a wrong answer is a real failure; without the modifier the dynamic "
1753
+ "kind is nonmonotonic, under which chunks may run in any order, "
1754
+ "increasing order included, so a conforming run without it can "
1755
+ "coincide")
1756
+ PROC_BIND_WHY = ("if bind-var is false the proc_bind clause has no effect, "
1757
+ "and the initial value of bind-var is implementation "
1758
+ "defined (5.2 §2.5 Table 2.4, 6.0 §3.5), so a run without "
1759
+ "the clause may coincide with one with it; omp_get_proc_bind "
1760
+ "returns the policy for nested regions without a proc_bind "
1761
+ "clause (5.2 §18.3.1), never the clause's own value; on the "
1762
+ "device AFAR reports omp_get_num_places() = 0 and CCE 19 "
1763
+ "does not link the places routines in device code (probe "
1764
+ "2026-10-18), so no place check is available either")
1765
+ SIMD_WHY = {
1766
+ "safelen": "safelen bounds the distance between concurrent iterations; "
1767
+ "the number of iterations run concurrently is implementation "
1768
+ "defined, so an implementation that runs one at a time meets "
1769
+ "the bound vacuously (the program's loop has no dependence "
1770
+ "the bound would protect)",
1771
+ "simdlen": "simdlen is a hint for the preferred number of concurrent "
1772
+ "iterations",
1773
+ "nontemporal": "nontemporal states a property of the accesses (low "
1774
+ "temporal locality), not a behaviour",
1775
+ "aligned": "aligned asserts an alignment the program ensures; it "
1776
+ "requires nothing observable of the implementation"}
1777
+ SIMD_BASIS = {"safelen": "simd_concurrency", "simdlen": "simdlen_hint",
1778
+ "nontemporal": "nontemporal_locality",
1779
+ "aligned": "aligned_assertion"}
1780
+ ORDER_WHY = ("order(concurrent) permits but does not require concurrent "
1781
+ "execution; the region may not call the OpenMP API (5.2 §10.3) "
1782
+ "that could see the threads, so a single run cannot tell it "
1783
+ "from the loop without the clause")
1784
+ # The 5.0 programs cite the 5.0 restriction (the
1785
+ # worksharing-loop with order(concurrent) takes the loop construct's
1786
+ # restrictions, 5.0 §2.9.2 p.108)
1787
+ ORDER_WHY_50 = ("order(concurrent) permits but does not require concurrent "
1788
+ "execution; the region may not call the OpenMP Runtime API "
1789
+ "(5.0 §2.9.5 p.131: \"A loop region corresponding to a loop "
1790
+ "construct may not contain calls to the OpenMP Runtime "
1791
+ "API\", which applies with order(concurrent), 5.0 §2.9.2 "
1792
+ "p.108) that could see the threads, so a single run cannot "
1793
+ "tell it from the loop without the clause")
1794
+ ORDER_REPRODUCIBLE_WHY = (
1795
+ "re-categorised (was implementation_defined): two "
1796
+ "worksharing-loops with the same reproducible schedule have consistent "
1797
+ "schedules, and the implementation guarantees that the memory effects "
1798
+ "of an iteration of the first happen before the same iteration of the "
1799
+ "second (5.2 §4.4.5 p.95, 6.0 §6.4.4 p.205), so a first loop with "
1800
+ "nowait that writes w(i) and a second that reads it is a deterministic "
1801
+ "oracle (no barrier between them); it is not written (AFAR rejects "
1802
+ "order on the worksharing-loop construct)")
1803
+ ORDER_UNCONSTRAINED_WHY = (
1804
+ "corrected: unconstrained is not 'the default order-modifier' "
1805
+ "(with schedule(static) it removes the reproducible schedule the loop "
1806
+ "would otherwise have, 5.2 §11.5 p.248, 6.0 §13.6 p.414), but this "
1807
+ "program has no schedule clause, so its schedule is not reproducible "
1808
+ "with or without the modifier and the forms are the same")
1809
+
1810
+
1811
+ # The loop construct's iterations "may execute in any order,
1812
+ # including concurrently" (5.2 §11.7 p.257, 6.0 §13.8; order(concurrent)
1813
+ # implied), so a per-thread counter `mine = mine + 1` updated by every
1814
+ # iteration is a data race between the iterations one thread runs (AFAR
1815
+ # printed 1 per thread for bind(thread) in Fortran). Each iteration
1816
+ # therefore records itself in its own element, hit(i, owner + 1) (owner:
1817
+ # the thread or team), and the host sums each owner's column into tot.
1818
+ BIND_HIT_DECLS = ["integer :: hit(n, 1024), tot(1024), k"]
1819
+ BIND_HIT_POST = ["do k = 1, 1024", " tot(k) = 0", " do i = 1, n",
1820
+ " tot(k) = tot(k) + hit(i, k)", " end do", "end do"]
1821
+
1822
+
1823
+ def bind_orphan_prog(version, kind):
1824
+ """An orphaned loop bind(parallel|teams)
1825
+ in a declare target subroutine, called from every thread of a parallel
1826
+ region (from every team of a teams region of num_teams(4)) in a target
1827
+ region; each iteration a thread (team) runs is recorded in its own
1828
+ element of the declare target module array hit(i, owner + 1)
1829
+ (64 rows: n, written as a literal for the C port's file-scope array) (BIND_HIT_DECLS: race-free), summed into tot(thread or team + 1) on the
1830
+ host after target update from(hit). With the binding the team (league)
1831
+ shares the iterations: sum(tot) == n. Without bind an orphaned loop
1832
+ binds to the encountering thread (5.2 §11.7.1 p.257, 6.0 §13.8.1), so
1833
+ each thread (team) runs all n and the sum is T*n."""
1834
+ sub = ["integer :: hit(64, 1024)", omp("declare target(hit)"),
1835
+ "contains", "subroutine work(t)", omp("declare target"),
1836
+ "integer, intent(in) :: t",
1837
+ "integer :: i", omp("loop", f"bind({kind})"), "do i = 1, 64",
1838
+ " hit(i, t) = hit(i, t) + 1", "end do", omp("end loop"),
1839
+ "end subroutine work"]
1840
+ s = Src(module=sub, decls=["integer :: tot(1024), k", "integer :: nthr"],
1841
+ init=["hit = 0", omp("target update", "to(hit)"),
1842
+ f"nthr = {SENTINEL}"],
1843
+ post=[omp("target update", "from(hit)")] + [
1844
+ x.replace("do i = 1, n", "do i = 1, 64")
1845
+ for x in BIND_HIT_POST],
1846
+ checks=["(nthr >= 1 .and. nthr <= 1024)", "(sum(tot) == n)"],
1847
+ focus=f"bind({kind})")
1848
+ head = omp("target", STD_MAPS, "map(tofrom: nthr)", FLAG_MAP)
1849
+ if kind == "parallel":
1850
+ s.region += [head, FLAG] + seq_loop() + [
1851
+ omp("parallel"),
1852
+ "if (omp_get_thread_num() < 1024) call work("
1853
+ "omp_get_thread_num() + 1)",
1854
+ "if (omp_get_thread_num() == 0) nthr = omp_get_num_threads()",
1855
+ omp("end parallel"), omp("end target")]
1856
+ else:
1857
+ s.region += [head, omp("teams", "num_teams(4)")] + \
1858
+ guarded("omp_get_team_num() == 0",
1859
+ TEAMS_FLAG + ["nthr = omp_get_num_teams()"]
1860
+ + seq_loop()) + [
1861
+ "if (omp_get_team_num() < 1024) call work("
1862
+ "omp_get_team_num() + 1)",
1863
+ omp("end teams"), omp("end target")]
1864
+ return s
1865
+
1866
+
1867
+ def bind_thread_prog(version):
1868
+ """loop bind(thread) in a parallel region in a target region: every
1869
+ thread of the team runs all n iterations (the binding region is the
1870
+ thread's own), each recorded in the thread's own column of hit
1871
+ (BIND_HIT_DECLS), summed into tot(thread + 1) on the host; thread 0
1872
+ alone writes b. Without bind the loop binds to the parallel region and
1873
+ the team shares the iterations, so with more than one thread some
1874
+ thread counts fewer than n (and b is only partly written). A
1875
+ one-thread team cannot tell them apart. Fix: the team size is implementation defined (NVHPC's nvc makes
1876
+ 110,560 threads), so the check no longer assumes nthr <= 1024: the
1877
+ first min(nthr, 1024) threads record their iterations and are
1878
+ checked."""
1879
+ s = Src(decls=BIND_HIT_DECLS + ["integer :: tid, nthr"],
1880
+ init=["hit = 0", f"nthr = {SENTINEL}"], post=list(BIND_HIT_POST),
1881
+ checks=["(nthr >= 1)",
1882
+ "all(tot(1:min(nthr, 1024)) == n)"],
1883
+ focus="bind(thread)")
1884
+ s.region += [omp("target", STD_MAPS, "map(tofrom: hit, nthr)", FLAG_MAP),
1885
+ FLAG,
1886
+ omp("parallel", "default(none) private(tid) "
1887
+ "shared(a, b, hit, nthr)"),
1888
+ "tid = omp_get_thread_num()",
1889
+ "if (tid == 0) nthr = omp_get_num_threads()",
1890
+ omp("loop", "bind(thread)"),
1891
+ "do i = 1, n", " if (tid == 0) b(i) = 2*a(i) + 1",
1892
+ " if (tid < 1024) hit(i, tid + 1) = hit(i, tid + 1) + 1",
1893
+ "end do", omp("end loop"),
1894
+ omp("end parallel"), omp("end target")]
1895
+ return s
1896
+
1897
+
1898
+ def bind_teams_thread_prog(version):
1899
+ """omp.clause.bind: loop bind(thread) closely nested in a
1900
+ teams region of num_teams(4) teams (exactly 4: 5.2 §10.2.1, 6.0
1901
+ §12.2.1) in a target region. With binding thread the binding region is
1902
+ not defined and the binding thread set is the encountering thread (5.2
1903
+ §11.7.1 p.258, 6.0 §13.8.1), so every team's initial thread runs all n
1904
+ iterations, each recorded in the team's own column of hit
1905
+ (BIND_HIT_DECLS), summed into tot(team + 1) on the host; team 0 alone
1906
+ writes b. Without the clause the loop, closely nested in teams, binds
1907
+ to the teams region and the league shares the iterations, so some team
1908
+ counts fewer than n. Unlike the orphaned forms (bind.parallel,
1909
+ bind.teams), deleting the clause leaves a conforming program (an
1910
+ orphaned loop must have bind, 5.2 §11.7 p.257); unlike bind.thread
1911
+ (closely nested in parallel), the binding it replaces is the
1912
+ league's."""
1913
+ s = Src(decls=BIND_HIT_DECLS + ["integer :: tm, nt"],
1914
+ init=["hit = 0", f"nt = {SENTINEL}"], post=list(BIND_HIT_POST),
1915
+ checks=["(nt >= 1 .and. nt <= 1024)",
1916
+ "all(tot(1:min(nt, 1024)) == n)"],
1917
+ focus="bind(thread)")
1918
+ s.region += [omp("target", STD_MAPS, "map(tofrom: hit, nt)", FLAG_MAP),
1919
+ omp("teams", "num_teams(4) default(none) private(tm) "
1920
+ "shared(a, b, hit, nt, host_flag)"),
1921
+ "tm = omp_get_team_num()",
1922
+ "if (tm == 0) nt = omp_get_num_teams()",
1923
+ *guarded("tm == 0", TEAMS_FLAG),
1924
+ omp("loop", "bind(thread)"),
1925
+ "do i = 1, n", " if (tm == 0) b(i) = 2*a(i) + 1",
1926
+ " if (tm < 1024) hit(i, tm + 1) = hit(i, tm + 1) + 1",
1927
+ "end do", omp("end loop"),
1928
+ omp("end teams"), omp("end target")]
1929
+ return s
1930
+
1931
+
1932
+ def task_default_prog(version, clause):
1933
+ """default(shared) on a task: in a parallel region in the
1934
+ target region, each thread's c is private (c = 7); a task with
1935
+ `clause` sets c = 42 and, after taskwait, thread 0 reports its c in r.
1936
+ With default(shared) the task shares the generating thread's c, so r
1937
+ is 42. Without a default clause, c, which is not shared by all implicit
1938
+ tasks of the team in the enclosing context, is firstprivate in the task
1939
+ (5.2 §5.1.1 p.100, 6.0 §7.1.1 p.214): the write is lost and r is 7. In a
1940
+ parallel construct default(shared) would be the default and change
1941
+ nothing; on a task it does not."""
1942
+ s = Src(decls=["integer :: c, r"], init=["r = 0"],
1943
+ checks=["(r == 42)"], focus=clause)
1944
+ s.region += [omp("target", STD_MAPS, "map(from: r)", FLAG_MAP), FLAG] + \
1945
+ seq_loop() + [omp("parallel", "private(c)"), "c = 7",
1946
+ omp("task", clause), "c = 42", omp("end task"),
1947
+ omp("taskwait"),
1948
+ "if (omp_get_thread_num() == 0) r = c",
1949
+ omp("end parallel"), omp("end target")]
1950
+ return s
1951
+
1952
+
1953
+ def monotonic_prog(version):
1954
+ """schedule(monotonic: dynamic) on parallel do in a target region (Task
1955
+ 15): every iteration records its thread (tid) and its place in that
1956
+ thread's execution order (ord, from a per-thread counter firstprivate
1957
+ from 0). Monotonic requires each thread to run the chunks it is
1958
+ assigned in increasing logical iteration order (5.2 §11.5.3 p.254, 6.0
1959
+ §13.6.3 p.419): for two iterations of one thread, the later iteration
1960
+ has the larger ord. Without the modifier the dynamic kind is
1961
+ nonmonotonic, which permits that order too (see MONOTONIC_WHY)."""
1962
+ def loop(flag):
1963
+ return ["do i = 1, m", " if (i <= n) b(i) = 2*a(i) + 1",
1964
+ " mine = mine + 1", " ord(i) = mine",
1965
+ " tid(i) = omp_get_thread_num()", "end do"]
1966
+ post = ["mok = .true.", "do i = 2, m", " do k = 1, i - 1",
1967
+ " if (tid(k) == tid(i) .and. ord(k) >= ord(i)) mok = .false.",
1968
+ " end do", "end do"]
1969
+ s = pdo(version, "schedule(monotonic: dynamic) firstprivate(mine)",
1970
+ loop=loop, maps=f"{STD_MAPS} map(from: tid, ord)",
1971
+ shared=("a", "b", "tid", "ord"),
1972
+ decls=[f"integer, parameter :: m = {SCHED_M}",
1973
+ "integer :: tid(m), ord(m), mine, k", "logical :: mok"],
1974
+ init=["mine = 0", f"tid = {SENTINEL}", f"ord = {SENTINEL}"],
1975
+ post=post, checks=["mok"])
1976
+ s.focus = "schedule(monotonic: dynamic)"
1977
+ return s.no_oracle("implementation_defined", MONOTONIC_WHY,
1978
+ "schedule_nonmonotonic_order", checked=True)
1979
+
1980
+
1981
+ def if_prog(version):
1982
+ """The if clause judged on the device. if(use_par) with use_par
1983
+ false on a parallel do nested in a target region: the parallel region
1984
+ is inactive, a team of one thread (iteration 1 records the team size),
1985
+ while the region runs on the device. Without the clause the team has
1986
+ more than one thread on both toolchains. (The if clause on a target
1987
+ construct with a false condition runs the region on the host, which is
1988
+ not device offload; the clause is the same atom.)"""
1989
+ s = pdo(version, "if(use_par)",
1990
+ maps=f"{STD_MAPS} map(from: nthr)",
1991
+ body=BODY + ("if (i == 1) nthr = omp_get_num_threads()",),
1992
+ shared=("a", "b", "nthr"),
1993
+ decls=["integer :: nthr", "logical :: use_par"],
1994
+ init=["use_par = .false.", "nthr = 0"], checks=["(nthr == 1)"])
1995
+ s.focus = "if(use_par)"
1996
+ return s
1997
+
1998
+
1999
+ def if_dnm_prog(version):
2000
+ """if(parallel: use_dev) with use_dev false on the combined construct:
2001
+ the parallel constituent is inactive, a team of one thread, while the
2002
+ region still runs on the device. Without the modifier the if clause
2003
+ applies to the target constituent too and the region runs on the host
2004
+ (the audit's kill is a host fallback)."""
2005
+ return team_param(version, "if(parallel: use_dev)",
2006
+ ["if (i == 1) nthr = omp_get_num_threads()"],
2007
+ ["integer :: nthr"], ["(nthr == 1)"],
2008
+ extra_decls=["logical :: use_dev"],
2009
+ # If(parallel: ...) is evaluated inside the
2010
+ # teams region (5.2 §17.2 p.340) and default(none)
2011
+ # applies to teams (5.2 §5.4.1 p.109): use_dev is listed
2012
+ extra_shared=("use_dev",),
2013
+ init=["use_dev = .false."])
2014
+
2015
+
2016
+ def _UNCHECKED_CLAUSE(clause):
2017
+ """Src fields for a clause whose own effect the program cannot
2018
+ observe (the check covers the enclosing construct only)."""
2019
+ return {"observable": False,
2020
+ "note": f"the check covers the enclosing target data, not "
2021
+ f"{clause} itself",
2022
+ "header_notes": [f"clause not checked: {clause}"]}
2023
+
2024
+
2025
+ def clause_recipe(clause: str, feat: str | None, version: int) -> Src | None:
2026
+ """The program for omp.clause.<clause>[.<feat>] (directive_name is
2027
+ built by offload.engine.gen from the clause's base program)."""
2028
+ v = version
2029
+ if clause in REQUIRES:
2030
+ if feat in (None, "required"):
2031
+ return requires_prog(v, clause if feat is None
2032
+ else f"{clause}(.true.)")
2033
+ return None
2034
+ if clause == "atomic_default_mem_order":
2035
+ return requires_prog(v, f"{clause}({feat or 'seq_cst'})")
2036
+ if clause == "map":
2037
+ return _feat_map(v, feat)
2038
+ if clause in ("to", "from") and feat in (None, "present", "mapper",
2039
+ "iterator"):
2040
+ if clause == "to" and feat is None:
2041
+ # the declare target clause and the motion clause share the
2042
+ # atom; the motion clause is the one with a device effect
2043
+ return _motion(v, "to", None)
2044
+ return _motion(v, clause, feat)
2045
+ if clause == "reduction":
2046
+ if feat in RED:
2047
+ return reduction_prog(v, feat)
2048
+ if feat is None:
2049
+ return reduction_prog(v)
2050
+ if feat == "default":
2051
+ return reduction_prog(v, modifier="default").no_oracle(
2052
+ "equivalent_default", "default is the default "
2053
+ "reduction-modifier", "reduction_default_modifier")
2054
+ if feat == "original":
2055
+ return reduction_prog(v, modifier="original(shared)")
2056
+ if feat == "task":
2057
+ return task_reduction_prog(v)
2058
+ if feat == "inscan":
2059
+ return inscan_prog(v)
2060
+ return None
2061
+ if clause == "in_reduction":
2062
+ return in_reduction_prog(v, feat or "plus") \
2063
+ if feat is None or feat in RED else None
2064
+ if clause == "schedule":
2065
+ c = SCHEDULE.get(feat)
2066
+ if c is None:
2067
+ return None
2068
+ if feat in (None, "static"):
2069
+ return schedule_prog(v, c)
2070
+ if feat == "chunk_size":
2071
+ return schedule_prog(v, c, chunk=4)
2072
+ if feat == "monotonic":
2073
+ return monotonic_prog(v)
2074
+ s = (pdo_simd if feat == "simd" else pdo)(v, c)
2075
+ if feat == "nonmonotonic":
2076
+ return s.no_oracle("equivalent_default", NONMONOTONIC_WHY,
2077
+ "schedule_default_modifier")
2078
+ if feat in SCHED_UNWRITTEN_WHY:
2079
+ return s.unverified("oracle_not_written",
2080
+ SCHED_UNWRITTEN_WHY[feat])
2081
+ return s.no_oracle("implementation_defined", SCHED_WHY[feat],
2082
+ SCHED_BASIS[feat])
2083
+ if clause == "proc_bind":
2084
+ return pdo(v, f"proc_bind({feat or 'close'})").no_oracle(
2085
+ "implementation_defined", PROC_BIND_WHY, "proc_bind_bind_var")
2086
+ if clause == "num_threads":
2087
+ if feat not in (None, "strict"):
2088
+ return None
2089
+ c = "num_threads(strict: 4)" if feat == "strict" else "num_threads(4)"
2090
+ # at most 4 threads (exactly 4 with strict); both toolchains give
2091
+ # more than 4 without the clause
2092
+ want = "nthr == 4" if feat == "strict" else "nthr >= 1 .and. nthr <= 4"
2093
+ return pdo(v, c, body=BODY + ("if (i == 1) nthr = "
2094
+ "omp_get_num_threads()",),
2095
+ maps=f"{STD_MAPS} map(from: nthr)",
2096
+ shared=("a", "b", "nthr"), decls=["integer :: nthr"],
2097
+ init=[f"nthr = {SENTINEL}"], checks=[f"({want})"])
2098
+ if clause == "message" and feat is None:
2099
+ return error_probe(v, "warning")
2100
+ if clause == "severity" and feat in (None, "warning", "fatal"):
2101
+ return error_probe(v, feat or "warning")
2102
+ if clause in ("safelen", "simdlen") and feat is None:
2103
+ return pdo_simd(v, f"{clause}({8 if clause == 'safelen' else 4})"
2104
+ ).no_oracle("hint_only", SIMD_WHY[clause],
2105
+ SIMD_BASIS[clause])
2106
+ if clause == "nontemporal" and feat is None:
2107
+ return pdo_simd(v, "nontemporal(b)").no_oracle(
2108
+ "hint_only", SIMD_WHY[clause], SIMD_BASIS[clause])
2109
+ if clause == "aligned" and feat in (None, "alignment"):
2110
+ return pdo_simd(v, "aligned(a: 8)").no_oracle(
2111
+ "hint_only", SIMD_WHY[clause], SIMD_BASIS[clause])
2112
+ if clause == "order":
2113
+ c = {None: "order(concurrent)", "concurrent": "order(concurrent)",
2114
+ "reproducible": "order(reproducible: concurrent)",
2115
+ "unconstrained": "order(unconstrained: concurrent)"}.get(feat)
2116
+ if c is None:
2117
+ return None
2118
+ if feat == "unconstrained":
2119
+ return pdo(v, c).no_oracle(
2120
+ "equivalent_default", ORDER_UNCONSTRAINED_WHY,
2121
+ "order_unconstrained_reproducible")
2122
+ if feat == "reproducible":
2123
+ return pdo(v, c).unverified("oracle_not_written",
2124
+ ORDER_REPRODUCIBLE_WHY)
2125
+ return pdo(v, c).no_oracle("implementation_defined",
2126
+ ORDER_WHY_50 if v == 50 else ORDER_WHY,
2127
+ "order_concurrent")
2128
+ if clause == "bind":
2129
+ if feat == "thread":
2130
+ return bind_thread_prog(v)
2131
+ if feat is None:
2132
+ return bind_teams_thread_prog(v)
2133
+ return bind_orphan_prog(v, feat)
2134
+ if clause == "collapse" and feat is None:
2135
+ # with collapse(2) the 64 iterations of the 2 x 32 nest are shared
2136
+ # by the team (schedule(static): one chunk per thread, min(T, 64)
2137
+ # threads busy); without it only the 2 outer iterations are, so at
2138
+ # most 2 threads run the loop
2139
+ s = pdo(v, "collapse(2) schedule(static)", loop=collapse_loop,
2140
+ maps=f"{STD_MAPS} map(from: tid, nthr)",
2141
+ shared=("a", "b", "tid", "nthr"),
2142
+ decls=["integer :: j, k", "integer :: tid(n), nthr, nd"],
2143
+ init=[f"tid = {SENTINEL}", f"nthr = {SENTINEL}"],
2144
+ post=count_owners("tid", "nd", "nthr"),
2145
+ checks=["(nd == min(nthr, n))"])
2146
+ s.focus = "collapse(2)"
2147
+ return s
2148
+ if clause == "ordered":
2149
+ if feat is None:
2150
+ # the ordered regions run in iteration order: each takes the
2151
+ # next ticket; without the ordered clause (and regions) the
2152
+ # team races for the counter
2153
+ def loop(flag):
2154
+ return ["do i = 1, n", " b(i) = 2*a(i) + 1",
2155
+ " " + omp("ordered"), " cnt = cnt + 1",
2156
+ " seq(i) = cnt", " " + omp("end ordered"),
2157
+ "end do"]
2158
+ s = pdo(v, "ordered", loop=loop,
2159
+ maps=f"{STD_MAPS} map(tofrom: cnt) map(from: seq)",
2160
+ shared=("a", "b", "cnt", "seq"),
2161
+ decls=["integer :: cnt, seq(n)"],
2162
+ init=["cnt = 0", f"seq = {SENTINEL}"],
2163
+ checks=["all(2*seq + 1 == ref)"])
2164
+ s.focus = "ordered"
2165
+ return s
2166
+ if feat == "n":
2167
+ return pdo(v, "ordered(1)").no_oracle(
2168
+ "equivalent_default", "re-categorised (was "
2169
+ "implementation_defined): without n the ordered clause "
2170
+ "behaves as if n were the collapse value, here 1, so "
2171
+ "ordered(1) on this one-loop nest is the ordered clause "
2172
+ "without its argument, with or without doacross constructs",
2173
+ "ordered_n_default")
2174
+ return None
2175
+ if clause == "private" and feat is None:
2176
+ # t is written by every iteration; the target's t (mapped tofrom,
2177
+ # 7) must not change: without private(t) the team shares it
2178
+ return pdo(v, "private(t)", body=("t = 2*a(i)", "b(i) = t + 1"),
2179
+ maps=f"{STD_MAPS} map(tofrom: t)", decls=["integer :: t"],
2180
+ init=["t = 7"], checks=["(t == 7)"])
2181
+ if clause == "firstprivate":
2182
+ c = {None: "firstprivate(c)", "saved": "firstprivate(saved: c)"}.get(
2183
+ feat)
2184
+ if c is None:
2185
+ return None
2186
+ # each thread's c starts at 1 (the body reads mod(c, 100) = 1 and
2187
+ # adds 100); the target's c (mapped tofrom) must stay 1: without
2188
+ # the clause the team shares c and the additions come back
2189
+ return pdo(v, c, body=("b(i) = 2*a(i) + mod(c, 100)", "c = c + 100"),
2190
+ maps=f"{STD_MAPS} map(tofrom: c)", decls=["integer :: c"],
2191
+ init=["c = 1"], checks=["(c == 1)"])
2192
+ if clause == "lastprivate":
2193
+ if feat is None:
2194
+ c, body, want = "lastprivate(t)", ("t = a(i)",), "n"
2195
+ elif feat == "conditional":
2196
+ c, body, want = ("lastprivate(conditional: t)",
2197
+ ("if (a(i) <= 10) t = a(i)",), "10")
2198
+ else:
2199
+ return None
2200
+ return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: t)",
2201
+ body=BODY + body, decls=["integer :: t"], init=["t = 0"],
2202
+ checks=[f"(t == {want})"])
2203
+ if clause == "shared" and feat is None:
2204
+ # under default(private) only shared(a, b) lets the team write the
2205
+ # target's b
2206
+ s = pdo(v, "default(private) shared(a, b)", hyg=False)
2207
+ s.focus = "shared(a, b)"
2208
+ return s
2209
+ if clause == "default":
2210
+ if feat in (None, "none", "shared", "private", "firstprivate"):
2211
+ # the base atom uses the observable private form
2212
+ feat = feat or "private"
2213
+ c = f"default({feat}) shared(a, b)"
2214
+ elif feat in ("aggregate", "all", "allocatable", "pointer", "scalar"):
2215
+ c = f"default(shared: {feat}) shared(a, b)"
2216
+ else:
2217
+ return None
2218
+ if feat in ("private", "firstprivate"):
2219
+ # c is not listed: private (firstprivate) by default, written by
2220
+ # every iteration; the target's c (mapped tofrom, 7) must not
2221
+ # change, as it would if the team shared c
2222
+ return pdo(v, c, focus=c.split(" shared")[0],
2223
+ body=("c = 2*a(i)", "b(i) = c + 1"),
2224
+ maps=f"{STD_MAPS} map(tofrom: c)",
2225
+ decls=["integer :: c"], init=["c = 7"],
2226
+ checks=["(c == 7)"])
2227
+ if feat == "shared":
2228
+ return task_default_prog(v, "default(shared)")
2229
+ if feat == "all":
2230
+ return task_default_prog(v, "default(shared: all)").no_oracle(
2231
+ "equivalent_default", "default(shared: all) is "
2232
+ "default(shared) (all is the variable category without one); "
2233
+ "the program checks what both require: a task in a target "
2234
+ "region shares the generating thread's private c",
2235
+ "default_category_all", checked=True)
2236
+ s = pdo(v, c, focus=c.split(" shared")[0])
2237
+ if feat == "none":
2238
+ s.evidence = "legality"
2239
+ s.legality_rule = "default-dsa-001"
2240
+ s.observable = False
2241
+ s.note = ("default(none) has no run-time effect; its oracle is "
2242
+ "the illegal variant without a listed variable")
2243
+ return s
2244
+ return s
2245
+ if clause == "linear":
2246
+ # the step forms use step 2, which the mutation (step removed: 1)
2247
+ # changes
2248
+ c = {None: "linear(x: 1)", "step_simple_modifier": "linear(x: 2)",
2249
+ "step": "linear(x: step(2))", "val": "linear(x: val, step(1))"
2250
+ }.get(feat)
2251
+ if v == 50 and feat == "val":
2252
+ return linear_simd_decl_prog(v, feat)
2253
+ if c:
2254
+ s = linear_prog(v, c, step=2 if feat in (
2255
+ "step", "step_simple_modifier") else 1)
2256
+ if feat == "val":
2257
+ s.no_oracle("equivalent_default", "on a worksharing-loop the "
2258
+ "only linear-modifier allowed is val, and the "
2259
+ "clause's loop semantics do not depend on it, so "
2260
+ "linear(x: val, step(1)) is linear(x: step(1))",
2261
+ "linear_modifier_val")
2262
+ return s
2263
+ if feat in ("ref", "uval"):
2264
+ return linear_simd_decl_prog(v, feat)
2265
+ return None
2266
+ if clause == "induction":
2267
+ c = {None: "induction(step(1), +: x)", "plus":
2268
+ "induction(step(1), +: x)", "step": "induction(step(1), +: x)",
2269
+ "strict": "induction(step(1), strict, +: x)",
2270
+ "relaxed": "induction(step(1), relaxed, +: x)",
2271
+ "times": "induction(step(-1), *: y)"}.get(feat)
2272
+ if c is None:
2273
+ return None
2274
+ # the body applies the inductor, so the value at the end of each
2275
+ # iteration is the closed form for the next (relaxed, 6.0 §7.6.13)
2276
+ if feat == "times":
2277
+ # y = (-1)**(i-1) at the start of iteration i
2278
+ return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: y)",
2279
+ body=("b(i) = 2*a(i) + 1 + (y - merge(1, -1, "
2280
+ "mod(i, 2) == 1))", "y = y * (-1)"),
2281
+ decls=["integer :: y"], init=["y = 1"],
2282
+ checks=["(y == 1)"])
2283
+ return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: x)",
2284
+ body=("b(i) = 2*a(i) + 1 + (x - (i + 2))", "x = x + 1"),
2285
+ decls=["integer :: x"], init=["x = 3"],
2286
+ checks=["(x == n + 3)"])
2287
+ if clause == "allocate":
2288
+ c = {None: "allocate(t)",
2289
+ "allocator": "allocate(allocator(omp_default_mem_alloc): t)",
2290
+ "allocator_simple_modifier": "allocate(omp_default_mem_alloc: t)",
2291
+ # the allocator-complex-modifier with the align modifier (5.2
2292
+ # §6.6 p.179): names an allocator, so no requires is needed
2293
+ "align": "allocate(allocator(omp_default_mem_alloc), "
2294
+ "align(8): t)"}.get(feat)
2295
+ if c and feat is None:
2296
+ # An allocate clause in a target region must name an
2297
+ # allocator unless requires dynamic_allocators is present (5.2
2298
+ # §6.6 p.179, 6.0 §8.6 p.313); the form without one (the bare list) gets the
2299
+ # requirement, which also makes uses_allocators unnecessary
2300
+ # (5.2 §13.8 p.286, 6.0 §15.8 p.463)
2301
+ s = alloc_prog(v, c, wrap="")
2302
+ s.spec = [omp("requires", "dynamic_allocators")] + s.spec
2303
+ return s
2304
+ return alloc_prog(v, c) if c else None
2305
+ if clause == "uses_allocators":
2306
+ if feat is None:
2307
+ s = alloc_prog(v, "allocate(omp_default_mem_alloc: t)")
2308
+ s.focus = "uses_allocators(omp_default_mem_alloc)"
2309
+ return s
2310
+ mod = {"memspace": "memspace(omp_default_mem_space)",
2311
+ "traits": "traits(my_traits)"}.get(feat)
2312
+ if mod is None:
2313
+ return None
2314
+ c = f"uses_allocators({mod}: my_alloc)"
2315
+ if v == 50: # 5.0 §2.12.5 p.170: allocator(allocator-traits-array)
2316
+ c = "uses_allocators(my_alloc(my_traits))"
2317
+ s = alloc_prog(v, "allocate(my_alloc: t)", wrap=c, decls=TRAITS,
2318
+ shared=("a", "b", "my_alloc"))
2319
+ s.focus = c
2320
+ if feat == "memspace":
2321
+ s.no_oracle("equivalent_default", "the program names "
2322
+ "omp_default_mem_space, the memory space used "
2323
+ "without the modifier", "memspace_default")
2324
+ else: # CCE passes, the mutant ICEs
2325
+ s.unverified("oracle_not_written", "re-categorised "
2326
+ "(was implementation_defined): memory from the "
2327
+ "allocator 'will be byte aligned to at least the "
2328
+ "value specified for the alignment trait' (5.2 §6.2 "
2329
+ "p.173, 6.0 §8.2 p.306" + (", 5.0 §2.11.2 p.153"
2330
+ if v == 50 else "")
2331
+ + "), so a check that t's "
2332
+ "address is a multiple of 64 is a deterministic "
2333
+ "oracle of the original; it is not written. A "
2334
+ "mutant without the trait could still be 64-byte "
2335
+ "aligned (any alignment conforms then), AFAR "
2336
+ "rejects uses_allocators and CCE 19's mutant without "
2337
+ "the trait is an internal compiler error "
2338
+ "(final-review re-audit)")
2339
+ return s
2340
+ if clause == "defaultmap":
2341
+ if feat == "character":
2342
+ return defaultmap_character_prog(v)
2343
+ if feat in (None, "scalar"):
2344
+ return defaultmap_prog(v, "tofrom", "scalar")
2345
+ if feat in ("aggregate", "allocatable", "pointer"):
2346
+ # to: an array of the category is not copied back; the
2347
+ # mutation's other category leaves it tofrom
2348
+ return defaultmap_prog(v, "to", feat)
2349
+ if feat == "all":
2350
+ # to: all: the array w written on
2351
+ # the device is not copied back (copy mode); the mutation all ->
2352
+ # scalar leaves arrays tofrom
2353
+ return defaultmap_prog(v, "to", feat)
2354
+ if feat == "default":
2355
+ return defaultmap_prog(v, feat).no_oracle(
2356
+ "equivalent_default", "defaultmap(default) is the behaviour "
2357
+ "without the clause", "defaultmap_default")
2358
+ return defaultmap_prog(v, feat)
2359
+ if clause == "depend":
2360
+ if feat is None:
2361
+ return depend_prog(v, "out", "in")
2362
+ if feat == "in":
2363
+ s = depend_prog(v, "out", "in")
2364
+ s.focus = "depend(in: w)"
2365
+ return s
2366
+ if feat in ("out", "inout", "inoutset", "mutexinoutset"):
2367
+ s = depend_prog(v, feat, "in", writer=True)
2368
+ s.focus = f"depend({feat}: w)"
2369
+ return s
2370
+ if feat in ("iterator", "depobj"):
2371
+ return depend_prog(v, feat, "in")
2372
+ return None
2373
+ if clause == "device":
2374
+ if feat == "ancestor":
2375
+ return ancestor_prog(v)
2376
+ c = {None: "device(dev)", "device_num": "device(device_num: dev)"
2377
+ }.get(feat)
2378
+ if c is None:
2379
+ return None
2380
+ # the last device, not the default one (0): the region must report
2381
+ # it (omp_get_device_num); with one device both are device 0
2382
+ s = team_param(v, c, ["if (i == 1) dn = omp_get_device_num()"],
2383
+ ["integer :: dn"], ["(dn == dev)"],
2384
+ extra_decls=["integer :: dev"],
2385
+ init=["dev = max(0, omp_get_num_devices() - 1)"])
2386
+ if feat == "device_num":
2387
+ s.no_oracle("equivalent_default", "device_num is the default "
2388
+ "device-modifier: device(n) is device(device_num: "
2389
+ "n)", "device_num_default")
2390
+ return s
2391
+ if clause == "if" and feat is None:
2392
+ return if_prog(v)
2393
+ if clause == "nowait":
2394
+ c = {None: "nowait", "do_not_synchronize": "nowait(.true.)"}.get(feat)
2395
+ if c is None:
2396
+ return None
2397
+ s = combined(v, c, post=[omp("taskwait")])
2398
+ return s.no_oracle("implementation_defined", NOWAIT_WHY,
2399
+ "target_nowait") \
2400
+ if feat is None else s.no_oracle(
2401
+ "equivalent_default", "nowait(.true.) is nowait (the "
2402
+ "argument defaults to true), and nowait itself has no "
2403
+ "separating oracle (an implementation may run the target "
2404
+ "task synchronously)", "nowait_true_default")
2405
+ if clause == "thread_limit" and feat is None:
2406
+ # at most 16 threads per team (both toolchains use more without)
2407
+ return team_param(v, "thread_limit(16)",
2408
+ ["if (i == 1) nthr = omp_get_num_threads()"],
2409
+ ["integer :: nthr"],
2410
+ ["(nthr >= 1 .and. nthr <= 16)"])
2411
+ if clause == "num_teams":
2412
+ c = {None: "num_teams(4)", "lower_bound": "num_teams(2: 4)"}.get(feat)
2413
+ if c is None:
2414
+ return None
2415
+ # without a lower bound it equals the upper bound: exactly 4 teams
2416
+ # (5.2 §10.2.1, 6.0 §12.2.1)
2417
+ want = "(nt == 4)" if feat is None else "(nt >= 2 .and. nt <= 4)"
2418
+ if v == 50:
2419
+ # 5.0: the clause is an upper bound ('less than or
2420
+ # equal to the value specified in the num_teams clause', 5.0
2421
+ # §2.7 p.83), so the program asks for one team, which leaves
2422
+ # exactly one: AFAR creates two teams for this loop without the
2423
+ # clause (CCE 19 one), so the check can fail (a bound of 4 was
2424
+ # met without the clause on both, audit)
2425
+ c, want = "num_teams(1)", "(nt == 1)"
2426
+ s = team_param(v, c, ["if (i == 1) nt = omp_get_num_teams()"],
2427
+ ["integer :: nt"], [want])
2428
+ if feat == "lower_bound":
2429
+ s.no_oracle("implementation_defined", "the number of teams is "
2430
+ "an implementation-defined value between the lower "
2431
+ "and the upper bound (5.2 §10.2.1, 6.0 §12.2.1); "
2432
+ "without the lower bound exactly the upper bound "
2433
+ "(4) teams are created, which is also a conforming "
2434
+ "execution of the form with the lower bound 2",
2435
+ "num_teams_bounds", checked=True)
2436
+ return s
2437
+ if clause == "dist_schedule":
2438
+ c = {None: "dist_schedule(static)", "static": "dist_schedule(static)",
2439
+ "chunk_size": "dist_schedule(static, 4)"}.get(feat)
2440
+ if c is None:
2441
+ return None
2442
+ # 4 teams (num_teams(4)); each iteration records its team: one
2443
+ # chunk of about n/4 iterations per team, or chunks of 4
2444
+ # round-robin
2445
+ check = blocked("team", "n", "nt", "dok") if feat != "chunk_size" \
2446
+ else round_robin("team", "n", 4, "nt", "dok")
2447
+ s = team_param(v, f"num_teams(4) {c}",
2448
+ ["team(i) = omp_get_team_num()",
2449
+ "if (i == 1) nt = omp_get_num_teams()"],
2450
+ ["integer :: team(n), nt"], ["(nt >= 1)", "dok"],
2451
+ extra_decls=["logical :: dok"], post=check)
2452
+ s.focus = c
2453
+ if feat != "chunk_size":
2454
+ # With the corrected oracle (idle teams allowed) the
2455
+ # implementation-defined default distribution of AFAR and CCE
2456
+ # also passes, so the clause cannot be told apart from its
2457
+ # absence in one run; the check still tests what the clause
2458
+ # requires (checked: a failure is a real one)
2459
+ s.no_oracle("implementation_defined", STATIC_NO_CHUNK_WHY,
2460
+ "dist_schedule_default", checked=True)
2461
+ return s
2462
+ if clause == "replayable":
2463
+ c = {None: "replayable", "replayable_expression": "replayable(.true.)"
2464
+ }.get(feat)
2465
+ if c is None:
2466
+ return None
2467
+ s = combined(v, c, key="target")
2468
+ return s.no_oracle("equivalent_default", "replayable(.true.) is "
2469
+ "replayable (the argument defaults to true)",
2470
+ "replayable_true_default") if feat else s
2471
+ if clause == "transparent":
2472
+ c = {None: "transparent", "impex_type": "transparent(omp_impex)"
2473
+ }.get(feat)
2474
+ return data_prog(v, c, **_UNCHECKED_CLAUSE("transparent")) \
2475
+ if c else None
2476
+ if clause == "nogroup":
2477
+ c = {None: "nogroup", "do_not_synchronize": "nogroup(.true.)"}.get(
2478
+ feat)
2479
+ return data_prog(v, c, **_UNCHECKED_CLAUSE("nogroup")) if c else None
2480
+ if clause in ("use_device_ptr", "is_device_ptr") and feat is None:
2481
+ s = device_ptr_prog(v)
2482
+ s.focus = "use_device_ptr(pa, pb)" if clause == "use_device_ptr" \
2483
+ else "is_device_ptr(pa, pb)"
2484
+ return s
2485
+ if clause in ("use_device_addr", "has_device_addr") and feat is None:
2486
+ s = device_addr_prog(v)
2487
+ s.focus = f"{clause}(a, b)"
2488
+ if clause == "has_device_addr":
2489
+ # Removing has_device_addr leaves the result unchanged on AFAR
2490
+ # and CCE 19 in both memory modes (they pass the device address
2491
+ # through an implicit map), and a device-side c_loc comparison
2492
+ # did not separate the two either: no check found.
2493
+ s.observable = False
2494
+ s.note = "has_device_addr itself is not checked"
2495
+ s.header_notes.append(
2496
+ "clause not checked: has_device_addr (removing it leaves the "
2497
+ "result unchanged on AFAR and CCE 19)")
2498
+ s.unverified("oracle_not_written", "re-categorised "
2499
+ "(was implementation_defined): the spec fixes what "
2500
+ "has_device_addr does (its list items 'may be "
2501
+ "directly accessed from a target device', 5.2 "
2502
+ "§5.4.9 p.122; their storage location is the same "
2503
+ "inside the construct as for the original list "
2504
+ "item, 6.0 §7.5.9 p.238), so a device-side check "
2505
+ "that c_loc(a) in the region equals a's address in "
2506
+ "the use_device_addr region (copy mode) is a "
2507
+ "deterministic oracle of the original; it is not "
2508
+ "written. Without the clause the program maps "
2509
+ "storage that 'may not be accessible from the host "
2510
+ "device' (5.2 §5.4.10 p.123), so no conforming "
2511
+ "mutant exists: AFAR and CCE 19 translate it back "
2512
+ "to the same device storage (9c probes)")
2513
+ return s
2514
+ return declare_clause(clause, feat, v)
2515
+
2516
+
2517
+ def declare_clause(clause, feat, v) -> Src | None:
2518
+ """Clauses of declare target."""
2519
+ if clause == "enter":
2520
+ if feat is None:
2521
+ return declare_var_prog(v, omp("declare target", "enter(dv)"),
2522
+ focus="enter(dv)")
2523
+ if feat == "automap":
2524
+ # pv is an array: without the directive it is implicitly mapped
2525
+ # tofrom, so the device write of the first region comes back
2526
+ # to the host; with it pv is present and the host copy stays
2527
+ # unchanged. Copy mode only (shared storage hides the copy);
2528
+ # mutation-checked on AFAR (CCE 19 rejects enter(automap:)).
2529
+ s = declare_prog(v, focus="enter(automap: pv)",
2530
+ body=("b(i) = 2*(pv(i) - 4) + 1",))
2531
+ s.module[2:2] = ["integer, allocatable :: pv(:)",
2532
+ omp("declare target", "enter(automap: pv)")]
2533
+ s.shared_setup()
2534
+ s.init += ["allocate(pv(n))", "pv = a",
2535
+ omp(spell("target update", v), "to(pv)"),
2536
+ omp("target")] + seq_loop(("pv(i) = pv(i) + 4",)) + [
2537
+ omp("end target")]
2538
+ s.post += ["if (.not. shared_mem) pres = pres .and. all(pv == a)",
2539
+ "deallocate(pv)"]
2540
+ return s
2541
+ return None
2542
+ if clause == "to" and feat is None:
2543
+ return declare_prog(v, var=omp("declare target", "to(dv)"),
2544
+ body=("b(i) = 2*a(i) + dv",), focus="to(dv)")
2545
+ if clause == "link" and feat is None:
2546
+ return declare_var_prog(v, omp("declare target", "link(dv)"),
2547
+ focus="link(dv)", data=True)
2548
+ if clause == "local" and feat is None:
2549
+ return declare_var_prog(v, omp("declare target", "local(dv)"),
2550
+ focus="local(dv)")
2551
+ if clause == "device_type":
2552
+ kind = feat or "any"
2553
+ if kind == "host":
2554
+ s = declare_prog(v, focus="device_type(host)", observable=False,
2555
+ note="device_type(host): the function is only "
2556
+ "called on the host")
2557
+ s.module += ["contains"] if "contains" not in s.module else []
2558
+ s.module += [" integer function g(x)",
2559
+ " " + omp("declare target", proc_list(v, "g"),
2560
+ "device_type(host)"),
2561
+ " integer, intent(in) :: x", " g = 2*x + 1",
2562
+ " end function g"]
2563
+ s.init += ["do i = 1, n", " ref(i) = g(i)", "end do"]
2564
+ return s
2565
+ if kind not in ("any", "nohost"):
2566
+ return None
2567
+ return declare_prog(v, func=omp("declare target", proc_list(v),
2568
+ f"device_type({kind})"),
2569
+ body=("b(i) = f(a(i))",),
2570
+ focus=f"device_type({kind})", observable=False,
2571
+ note=IMPLICIT_PROCEDURE_NOTE)
2572
+ if clause == "indirect":
2573
+ c = {None: "indirect", "invoked_by_fptr": "indirect(.true.)"}.get(feat)
2574
+ if c is None:
2575
+ return None
2576
+ # enter at both versions: AFAR rejects indirect with to ("The
2577
+ # INDIRECT clause cannot be used without the ENTER clause"), CCE 19
2578
+ # rejects enter (a listed confound in offload.engine.report)
2579
+ return declare_prog(v, func=omp("declare target", "enter(f)", c),
2580
+ body=("b(i) = f(a(i))",), focus=c,
2581
+ observable=False, note="f is called directly; "
2582
+ "no indirect call is checked")
2583
+ return None