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,2559 @@
1
+ """Independent sources of expected behaviour, and the rule for trusting one.
2
+
3
+ A program's expected result (a fuzz checksum, a catalog ``check=pass``, a
4
+ negative probe's error termination) is trusted only when two independent
5
+ sources agree. Each source yields an ``Evidence`` tagged with one of ``TAGS``:
6
+ ``elision`` (run with the directives ignored, on the host), ``metamorphic``
7
+ (spec-equivalent rewrites), ``model`` (the fuzzer's Python model or the
8
+ program's own oracle), ``probe`` (a variant asserting spec-mandated device
9
+ state) and ``consensus_only`` (toolchains agreeing; a hint, never counted).
10
+ ``decide`` returns ``trusted`` when two distinct sources in ``INDEPENDENT``
11
+ give the same value and none differs, ``disputed`` when they differ, else
12
+ ``provisional``. Witnesses from one compiler family count as one source. All
13
+ runs behind a verdict share one memory mode; ``assess`` refuses a mix.
14
+ ``disposition_note`` fits a verdict next to a mutation-audit disposition.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import atexit
19
+ import dataclasses
20
+ import re
21
+ import shutil
22
+ import subprocess
23
+ import tempfile
24
+ from dataclasses import dataclass, field
25
+ from functools import lru_cache
26
+ from pathlib import Path
27
+
28
+ from offload.engine import fsource as F
29
+ from offload.engine.toolchains import AFAR_BIN
30
+
31
+ TAGS = {
32
+ "elision": "sequential elision: the program built without OpenMP "
33
+ "(gfortran and amdflang, no -fopenmp) against a sequential "
34
+ "omp_lib and run on the host, where ELISION_RULE applies",
35
+ "metamorphic": "spec-equivalent rewrites (offload.engine.transforms) run on "
36
+ "the toolchains; the value of the toolchains whose "
37
+ "outputs coincide on the original and its rewrites",
38
+ "model": "the fuzzer's Python model (counted only with elision or "
39
+ "metamorphic), or a catalog program's own oracle (counted only "
40
+ "with elision)",
41
+ "probe": "a self-checking variant asserting spec-mandated device state "
42
+ "(presence by reference count, device execution); the value "
43
+ "printed by toolchains on which every assertion holds",
44
+ "consensus_only": "the toolchains' own results agreeing: a hint, never "
45
+ "validation (CCE 19/20/21 share bugs)",
46
+ "derivation": "The expected value derived by hand from the "
47
+ "spec text, written in the catalog program's header "
48
+ "(`derivation (<value>):` lines citing section and page): "
49
+ "computed by no toolchain and not by the generator's "
50
+ "check code",
51
+ }
52
+ INDEPENDENT = ("elision", "metamorphic", "model", "probe", "derivation")
53
+ # model counts only next to one of these (the brief: the model is trusted
54
+ # only where it agrees with elision or a metamorphic variant)
55
+ MODEL_PARTNERS = ("elision", "metamorphic")
56
+ # a catalog program's oracle is the claim under question: only elision, a
57
+ # value computed without any OpenMP toolchain, makes it a second source
58
+ # Or a hand derivation from the spec (the brief's "independent
59
+ # hand-derived expected value with the derivation written in the program
60
+ # header")
61
+ CATALOG_MODEL_PARTNERS = ("elision", "derivation")
62
+ # sources whose value is printed by OpenMP toolchains: one source when a
63
+ # compiler family backs both
64
+ TOOLCHAIN_SOURCES = ("metamorphic", "probe")
65
+ VERDICTS = {
66
+ "trusted": "two independent sources agree and none disagrees",
67
+ "provisional": "fewer than two independent sources give a value",
68
+ "disputed": "independent sources give different values (recorded, not "
69
+ "resolved by majority)",
70
+ "derived": "Two sources agree (the program's own "
71
+ "oracle and a hand derivation) but no execution shows the "
72
+ "value: no device run, elision or matrix run (program or C "
73
+ "port) printed it",
74
+ "implementation_defined": "Triage: the spec leaves the "
75
+ "observed behaviour unspecified or "
76
+ "implementation defined (quoted); not a "
77
+ "support verdict",
78
+ }
79
+
80
+ ELISION_RULE = """\
81
+ Sequential elision applies to a program only if all of these hold
82
+ (offload.engine.expected.elision_applicability checks each one; the program
83
+ is assumed race-free with deterministic reductions, the precondition of
84
+ the method, which the fuzzer guarantees by construction and the catalog
85
+ programs are written to meet):
86
+
87
+ 1. No floating-point list item in a reduction, in_reduction or
88
+ task_reduction clause or updated in an atomic construct (reduction
89
+ order), unless the program is a fuzz-3 program, whose values are
90
+ exact in every order by construction (offload.engine.fuzz_base).
91
+ 2. No OpenMP runtime routine whose value can reach the result. Allowed:
92
+ the ran_on_host flag idiom `x = merge(1, 0, omp_is_initial_device())`
93
+ when x only feeds the ran_on_host field; `x = omp_get_default_device()`
94
+ when x is only used in device clauses; and the guards
95
+ `if (omp_get_thread_num() == 0)` / `if (omp_get_team_num() == 0)` where
96
+ the guarded code runs exactly once in OpenMP execution (a thread guard
97
+ with no loop construct between it and its binding parallel region and a
98
+ team guard inside every enclosing teams region; a team guard directly
99
+ in a teams region). single and masked (filter 0) obey the same
100
+ once-only condition. No write to shared or reduction storage runs once
101
+ per team or thread (redundant_execution: directly in a teams or
102
+ parallel region, with no worksharing construct, single, masked,
103
+ sections or once-only guard between the write and the region, and no
104
+ private copy of the variable there; sequential execution runs it once).
105
+ 3. No device-only semantics: no present modifier or defaultmap(present),
106
+ no declare target variable or module variable, no device pointers or
107
+ addresses (is_device_ptr, has_device_addr, use_device_ptr,
108
+ use_device_addr), no device(ancestor), no mapper or iterator, no
109
+ requires directive. And the result does not depend on the device data
110
+ environment: a version analysis that runs the program with separate
111
+ host and device copies (map entry/exit, reference counts, always,
112
+ target update, firstprivate/private/lastprivate/reduction copies,
113
+ implicit data-sharing and mapping rules of OpenMP 5.2 §5.8.1, §5.1.1 and
114
+ the combined-construct rules of §17.2) must find that every read sees
115
+ the copy sequential execution would see, both with the reference-count
116
+ rules and with every map copying unconditionally (so the result depends
117
+ neither on copies nor on reference counts).
118
+ 4. No construct whose sequential execution is not one of its conforming
119
+ executions or whose effect is not on values: linear, scan/inscan,
120
+ ordered depend/doacross, user-defined reductions, declare mapper,
121
+ declare variant, metadirective, dispatch, interop, threadprivate,
122
+ copyin, cancel, error, detach, default(private|firstprivate), masked
123
+ with a nonzero filter, conditional compilation (!$) lines.
124
+ 5. Base language (the version analysis of rule 3 tracks storage, not
125
+ names): a structure is tracked component by component; the parts of a
126
+ structure mapped on one construct are one storage block whose
127
+ reference count is the structure's, and a part that is not on the
128
+ device may not be mapped while other parts are present (5.2 §5.8.3);
129
+ a referenced element of a structure with mapped elements must itself
130
+ be mapped (5.2 §13.8); a structure referenced without any clause is
131
+ mapped whole (aggregate category), its pointer components excluded
132
+ (their association status is undefined, §5.8.3); a pointer referenced
133
+ in a target region must be a map list item of that construct whose
134
+ pointer or target is created there (attachment), and `p => t` makes p
135
+ an alias of t; an allocatable scalar or a pointer referenced in a
136
+ target region needs an explicit clause; a call of an internal
137
+ subroutine runs its body on the actual arguments (whole variables;
138
+ argument association shares storage except for a pointer actual of a
139
+ non-pointer dummy, 5.2 §5.1.1, which may then not be present on the
140
+ device at the call nor be referenced by another name in the body),
141
+ VALUE dummies are copies, explicit-shape dummies match the actual's
142
+ extents, and an absent optional dummy is ignored in a clause and may
143
+ not be referenced (5.2 §3.2.1); an array section in a map or target
144
+ update clause copies only its part (a later read of the rest counts as
145
+ a dependence), except `:` or `1:extent` in every dimension and an
146
+ update of the section a host loop wrote since the copies agreed.
147
+ The ran_on_host field is never compared (sequential elision runs on the
148
+ host by definition).
149
+ """
150
+
151
+ # The result line of a run: a fuzz program prints a checksum, a catalog
152
+ # program check=pass/fail. The value compared is "checksum=<n>" or
153
+ # "check=<pass|fail>"; "error_termination" for a run that ends in error.
154
+ FUZZ_LINE = re.compile(r"^RESULT checksum=(-?\d+) ran_on_host=([01])\s*$",
155
+ re.M)
156
+ CAT_LINE = re.compile(r"^RESULT ran_on_host=([01]) check=(pass|fail)\s*$",
157
+ re.M)
158
+
159
+
160
+ def value_of(stdout: str) -> str | None:
161
+ """The compared value of one run's output, or None (no or several
162
+ result lines)."""
163
+ f = FUZZ_LINE.findall(stdout)
164
+ c = CAT_LINE.findall(stdout)
165
+ if len(f) + len(c) != 1:
166
+ return None
167
+ return f"checksum={f[0][0]}" if f else f"check={c[0][1]}"
168
+
169
+
170
+ # --- evidence and the agreement rule ------------------------------------------
171
+
172
+ @dataclass(frozen=True)
173
+ class Evidence:
174
+ tag: str
175
+ status: str # value | inapplicable | no_value | conflict
176
+ value: str | None = None
177
+ detail: str = ""
178
+ observations: tuple = () # ((who, value), ...)
179
+ witnesses: tuple = () # toolchains behind the value (metamorphic,
180
+ # probe)
181
+
182
+ def __post_init__(self):
183
+ if self.tag not in TAGS:
184
+ raise ValueError(f"unknown evidence tag {self.tag!r}")
185
+ if self.status not in ("value", "inapplicable", "no_value",
186
+ "conflict"):
187
+ raise ValueError(f"unknown evidence status {self.status!r}")
188
+ if (self.status == "value") != (self.value is not None):
189
+ raise ValueError("a value exactly when status is 'value'")
190
+
191
+ def to_dict(self) -> dict:
192
+ d = dataclasses.asdict(self)
193
+ d["observations"] = [list(o) for o in self.observations]
194
+ d["witnesses"] = list(self.witnesses)
195
+ return d
196
+
197
+ @staticmethod
198
+ def from_dict(d: dict) -> "Evidence":
199
+ d = dict(d)
200
+ d["observations"] = tuple(tuple(o) for o in d.get("observations",
201
+ ()))
202
+ d["witnesses"] = tuple(d.get("witnesses", ()))
203
+ return Evidence(**d)
204
+
205
+
206
+ @dataclass(frozen=True)
207
+ class Verdict:
208
+ status: str # trusted | provisional | disputed
209
+ value: str | None # the agreed value (trusted only)
210
+ agreeing: tuple = () # tags behind the value
211
+ values: tuple = () # ((tag, value), ...) of independent sources
212
+ hints: tuple = () # consensus_only values
213
+ reason: str = ""
214
+
215
+
216
+ def _families(e: Evidence) -> set[str]:
217
+ from offload.engine.toolchains import family
218
+ return {family(w) for w in e.witnesses}
219
+
220
+
221
+ def decide(evidence: list[Evidence],
222
+ model_partners: tuple = MODEL_PARTNERS) -> Verdict:
223
+ """The agreement rule (module docstring). `model_partners`: the tags
224
+ next to which `model` counts (CATALOG_MODEL_PARTNERS for a catalog
225
+ program)."""
226
+ vals = [(e.tag, e.value) for e in evidence
227
+ if e.tag in INDEPENDENT and e.status == "value"]
228
+ hints = tuple((e.tag, e.value) for e in evidence
229
+ if e.tag == "consensus_only" and e.status == "value")
230
+ tags = [t for t, _ in vals]
231
+ if len(tags) != len(set(tags)):
232
+ raise ValueError("one evidence per independent tag")
233
+ distinct = sorted({v for _, v in vals})
234
+ if len(distinct) > 1:
235
+ by = {v: sorted(t for t, w in vals if w == v) for v in distinct}
236
+ parts = "; ".join(f"{'+'.join(ts)}: {v}" for v, ts in by.items())
237
+ why = f"sources disagree ({parts})"
238
+ el = dict(vals).get("elision")
239
+ mo = dict(vals).get("model")
240
+ if el is not None and mo is not None and el != mo:
241
+ why += "; model disagrees with elision: suspected model bug"
242
+ return Verdict("disputed", None, (), tuple(sorted(vals)), hints, why)
243
+ if not distinct:
244
+ return Verdict("provisional", None, (), (), hints,
245
+ "no independent source gives a value")
246
+ v = distinct[0]
247
+ agreeing = tuple(sorted(tags))
248
+ counted = set(agreeing)
249
+ notes = []
250
+ if set(TOOLCHAIN_SOURCES) <= counted:
251
+ by = {e.tag: e for e in evidence if e.tag in TOOLCHAIN_SOURCES}
252
+ shared = _families(by["metamorphic"]) & _families(by["probe"])
253
+ if shared:
254
+ counted.discard("probe")
255
+ notes.append("metamorphic and probe are one source: both on "
256
+ f"{', '.join(sorted(shared))}")
257
+ if "model" in counted and not counted & set(model_partners):
258
+ counted.discard("model")
259
+ if len(agreeing) > 1:
260
+ notes.append(f"the model counts only with "
261
+ f"{' or '.join(model_partners)}")
262
+ tail = "".join(f"; {n}" for n in notes)
263
+ if len(counted) >= 2:
264
+ return Verdict("trusted", v, agreeing, tuple(sorted(vals)), hints,
265
+ f"{' + '.join(agreeing)} agree{tail}")
266
+ return Verdict("provisional", None, agreeing, tuple(sorted(vals)), hints,
267
+ f"only {' + '.join(agreeing)}{tail}")
268
+
269
+
270
+ def disposition_note(disposition: str, verdict: Verdict) -> str:
271
+ """The audit disposition with the expected-value verdict next to it.
272
+ The disposition itself is never changed (a trusted expected value does
273
+ not make a weak or unaudited oracle valid: the audit asks whether the
274
+ check depends on the atom, this asks whether the expected value is
275
+ right)."""
276
+ tail = verdict.status
277
+ if verdict.status == "trusted":
278
+ tail += f" ({'+'.join(verdict.agreeing)})"
279
+ return f"{disposition}; expected value {tail}"
280
+
281
+
282
+ # --- evidence from recorded runs ------------------------------------------------
283
+
284
+ _DERIVATION = re.compile(r"^(?:!|//) derivation(?: \(([^)]*)\))?: (.*)$")
285
+
286
+
287
+ def derivation_evidence(source: str) -> Evidence:
288
+ """The hand derivation in a catalog program's header comment
289
+ block: a first line ``! derivation (<value>): <text>`` (C ``//``) and
290
+ continuation lines ``! derivation: <text>``. No value when the header
291
+ has none."""
292
+ value, text = None, []
293
+ for ln in source.splitlines():
294
+ if not ln.startswith(("!", "//")):
295
+ break
296
+ m = _DERIVATION.match(ln)
297
+ if m:
298
+ if m.group(1):
299
+ value = m.group(1)
300
+ text.append(m.group(2).strip())
301
+ if value is None:
302
+ return Evidence("derivation", "no_value",
303
+ detail="no derivation in the program header")
304
+ return Evidence("derivation", "value", value, " ".join(text))
305
+
306
+
307
+ def model_evidence(value: str | None, detail: str = "") -> Evidence:
308
+ if value is None:
309
+ return Evidence("model", "no_value", detail=detail)
310
+ return Evidence("model", "value", value, detail)
311
+
312
+
313
+ def consensus_evidence(observations: list[tuple[str, str | None]]
314
+ ) -> Evidence:
315
+ """The toolchains' results of the original program: the value two or
316
+ more toolchains print (a hint)."""
317
+ obs = tuple((w, v) for w, v in observations if v is not None)
318
+ count: dict[str, list[str]] = {}
319
+ for w, v in obs:
320
+ count.setdefault(v, []).append(w)
321
+ shared = {v: ws for v, ws in count.items() if len(ws) >= 2}
322
+ if len(shared) == 1:
323
+ v, ws = next(iter(shared.items()))
324
+ return Evidence("consensus_only", "value", v,
325
+ f"{', '.join(ws)} agree", obs)
326
+ if len(shared) > 1:
327
+ return Evidence("consensus_only", "conflict", None,
328
+ "groups of toolchains agree on different values", obs)
329
+ return Evidence("consensus_only", "no_value", None,
330
+ "no two toolchains agree", obs)
331
+
332
+
333
+ def metamorphic_evidence(runs: list[tuple[str, str, str | None]]
334
+ ) -> Evidence:
335
+ """Runs (toolchain, variant, value or None); variant "original" is the
336
+ program itself. A toolchain with at least two values that all coincide
337
+ is a stable witness; the source's value is the stable witnesses'
338
+ common value."""
339
+ by: dict[str, list[tuple[str, str]]] = {}
340
+ for tc, var, v in runs:
341
+ if v is not None:
342
+ by.setdefault(tc, []).append((var, v))
343
+ stable, violations = {}, []
344
+ for tc, vs in sorted(by.items()):
345
+ if len({v for _, v in vs}) == 1 and len(vs) >= 2:
346
+ stable[tc] = vs[0][1]
347
+ elif len({v for _, v in vs}) > 1:
348
+ violations.append(f"{tc}: " + ", ".join(f"{var}={v}"
349
+ for var, v in vs))
350
+ obs = tuple((f"{tc}/{var}", v) for tc, var, v in runs)
351
+ detail = ""
352
+ if violations:
353
+ detail = "relation violated on " + "; ".join(violations)
354
+ vals = sorted(set(stable.values()))
355
+ if len(vals) == 1:
356
+ return Evidence("metamorphic", "value", vals[0],
357
+ (f"stable on {', '.join(sorted(stable))}" +
358
+ (f"; {detail}" if detail else "")), obs,
359
+ tuple(sorted(stable)))
360
+ if len(vals) > 1:
361
+ return Evidence("metamorphic", "conflict", None,
362
+ "stable witnesses disagree: " + ", ".join(
363
+ f"{tc}={v}" for tc, v in sorted(stable.items()))
364
+ + (f"; {detail}" if detail else ""), obs)
365
+ return Evidence("metamorphic", "no_value", None,
366
+ detail or "no toolchain ran the original and a variant",
367
+ obs)
368
+
369
+
370
+ def probe_evidence(runs: list[tuple[str, bool | None, str | None]],
371
+ applicable: str | None = None) -> Evidence:
372
+ """Probe runs (toolchain, all assertions held?, value). `applicable`:
373
+ None, or why no probe could be built."""
374
+ if applicable is not None:
375
+ return Evidence("probe", "inapplicable", detail=applicable)
376
+ tcs = [tc for tc, _, _ in runs]
377
+ if len(tcs) != len(set(tcs)):
378
+ raise ValueError("one probe run per toolchain (a copy-mode probe "
379
+ "run belongs to its own verdict)")
380
+ ok = {tc: v for tc, held, v in runs if held and v is not None}
381
+ obs = tuple((tc, f"{'held' if held else 'failed' if held is False else 'no result'}"
382
+ f": {v}") for tc, held, v in runs)
383
+ vals = sorted(set(ok.values()))
384
+ if len(vals) == 1:
385
+ return Evidence("probe", "value", vals[0],
386
+ f"state verified on {', '.join(sorted(ok))}", obs,
387
+ tuple(sorted(ok)))
388
+ if len(vals) > 1:
389
+ return Evidence("probe", "conflict", None,
390
+ "state-verified toolchains disagree: " +
391
+ ", ".join(f"{t}={v}" for t, v in sorted(ok.items())),
392
+ obs)
393
+ return Evidence("probe", "no_value", None,
394
+ "no toolchain held every assertion", obs)
395
+
396
+
397
+ # --- elision applicability -------------------------------------------------------
398
+
399
+ EXCLUDED_DIRECTIVES = {
400
+ "declare mapper": "declare mapper", "declare reduction":
401
+ "user-defined reduction", "declare variant": "declare variant",
402
+ "metadirective": "metadirective", "dispatch": "dispatch",
403
+ "interop": "interop", "threadprivate": "threadprivate",
404
+ "cancel": "cancel", "cancellation point": "cancel",
405
+ "error": "error directive", "requires": "requires directive",
406
+ "scan": "scan", "declare induction": "induction",
407
+ "begin declare variant": "declare variant",
408
+ }
409
+ EXCLUDED_CLAUSES = {
410
+ "is_device_ptr": "device pointers", "has_device_addr": "device addresses",
411
+ "use_device_ptr": "device pointers", "use_device_addr":
412
+ "device addresses", "linear": "linear", "detach": "detach",
413
+ "copyin": "threadprivate", "induction": "induction",
414
+ "doacross": "doacross", "uses_allocators": None, "allocate": None,
415
+ }
416
+ INTRINSIC_RED = {"+", "*", "-", "max", "min", "iand", "ior", "ieor",
417
+ ".and.", ".or.", ".eqv.", ".neqv."}
418
+ LOOP_LEAVES = {"do", "simd", "distribute", "loop", "taskloop"}
419
+ STANDALONE = {"target enter data", "target exit data", "target update",
420
+ "barrier", "taskwait", "taskyield", "flush", "depobj",
421
+ "nothing", "section", "declare target", "declare simd",
422
+ "allocate", "assumes", "requires", "error", "scan",
423
+ "cancel", "cancellation point", "interop", "threadprivate",
424
+ "declare reduction", "declare mapper", "declare variant",
425
+ "allocators"}
426
+ FLAG_IDIOM = re.compile(r"^(\w+)\s*=\s*merge\(\s*1\s*,\s*0\s*,\s*"
427
+ r"omp_is_initial_device\(\s*\)\s*\)$", re.I)
428
+ DEV_IDIOM = re.compile(r"^(\w+)\s*=\s*omp_get_default_device\(\s*\)$", re.I)
429
+ THREAD_GUARD = re.compile(r"^if\s*\(\s*omp_get_thread_num\(\s*\)\s*==\s*0"
430
+ r"\s*\)\s*(.*)$", re.I)
431
+ TEAM_GUARD = re.compile(r"^if\s*\(\s*omp_get_team_num\(\s*\)\s*==\s*0"
432
+ r"\s*\)\s*(.*)$", re.I)
433
+
434
+
435
+ @dataclass
436
+ class Applicability:
437
+ ok: bool
438
+ reasons: list = field(default_factory=list)
439
+ flags: frozenset = frozenset()
440
+
441
+ @property
442
+ def why(self) -> str:
443
+ return "; ".join(self.reasons)
444
+
445
+
446
+ @dataclass
447
+ class Node:
448
+ kind: str # code | do | if | ifstmt | dir | loopdir |
449
+ # standalone | atomic
450
+ stmt: F.Stmt | None = None
451
+ dir: F.Dir | None = None
452
+ children: list = field(default_factory=list)
453
+ inner: "Node | None" = None # ifstmt: the statement
454
+ cond: str = "" # if / ifstmt condition
455
+ guard: str = "" # "thread" | "team" | ""
456
+ once: bool = False # if (i == <first or last i>) in a loop
457
+ loop_var: str = ""
458
+ end_stmt: F.Stmt | None = None # dir/loopdir/do: the closing stmt
459
+ lo: str = ""
460
+ hi: str = ""
461
+ step: str = ""
462
+
463
+
464
+ _DO = re.compile(r"^(?:\w+\s*:\s*)?do\s+(\w+)\s*=\s*(.+)$", re.I)
465
+ _DO_OTHER = re.compile(r"^(?:\w+\s*:\s*)?do(\s+while\b.*|\s*)$", re.I)
466
+ _END_DO = re.compile(r"^end\s*do\b", re.I)
467
+ _IF_THEN = re.compile(r"^(?:else\s*)?if\s*\((.*)\)\s*then$", re.I)
468
+ _ELSE = re.compile(r"^else$", re.I)
469
+ _END_IF = re.compile(r"^end\s*if\b", re.I)
470
+
471
+
472
+ def _paren_split(text: str) -> tuple[str, str] | None:
473
+ """`(cond) rest` -> (cond, rest)."""
474
+ if not text.startswith("("):
475
+ return None
476
+ depth = 0
477
+ for k, ch in enumerate(text):
478
+ depth += ch == "("
479
+ depth -= ch == ")"
480
+ if depth == 0:
481
+ return text[1:k], text[k + 1:].strip()
482
+ return None
483
+
484
+
485
+ def _code_node(s: F.Stmt) -> Node:
486
+ low = s.lower
487
+ if low.startswith("if") and re.match(r"if\s*\(", low) and \
488
+ not _IF_THEN.match(s.text):
489
+ ps = _paren_split(s.text[2:].strip())
490
+ if ps:
491
+ cond, rest = ps
492
+ inner = _code_node(F.Stmt(s.first, s.last, rest, "code",
493
+ s.indent))
494
+ guard = "thread" if THREAD_GUARD.match(s.text) else \
495
+ "team" if TEAM_GUARD.match(s.text) else ""
496
+ return Node("ifstmt", s, inner=inner, cond=cond, guard=guard)
497
+ return Node("code", s)
498
+
499
+
500
+ def build_tree(stmts: list[F.Stmt], start: int, stop: int,
501
+ version: int = 52) -> list[Node]:
502
+ """The construct tree of statements [start, stop)."""
503
+ root = Node("root")
504
+ stack = [root]
505
+ i = start
506
+
507
+ def close_loopdirs():
508
+ # a loop directive closes after its loop; its end directive is
509
+ # optional (consumed by the caller's look-ahead)
510
+ nonlocal i
511
+ while stack[-1].kind == "loopdir" and stack[-1].children and \
512
+ stack[-1].children[-1].kind == "do" and \
513
+ getattr(stack[-1].children[-1], "_closed", False):
514
+ ld = stack.pop()
515
+ ld.end_stmt = ld.children[-1].end_stmt
516
+ nxt = stmts[i + 1] if i + 1 < stop else None
517
+ if nxt is not None and nxt.kind == "dir":
518
+ d = F.parse_dir(nxt, version)
519
+ if d.end and d.base == ld.dir.base:
520
+ i += 1
521
+ ld.end_stmt = nxt
522
+ stack[-1].children.append(ld)
523
+
524
+ while i < stop:
525
+ s = stmts[i]
526
+ if s.kind == "condcomp":
527
+ raise F.SourceError("conditional compilation line")
528
+ if s.kind == "acc":
529
+ raise F.SourceError("OpenACC directive")
530
+ if s.kind == "dir":
531
+ d = F.parse_dir(s, version)
532
+ if d.end:
533
+ top = stack[-1]
534
+ if top.kind != "dir" or top.dir.base != d.base:
535
+ raise F.SourceError(f"line {s.first + 1}: unexpected "
536
+ f"{d.name}")
537
+ stack.pop()
538
+ top.end_stmt = s
539
+ stack[-1].children.append(top)
540
+ elif d.base in STANDALONE or d.base.startswith("declare"):
541
+ stack[-1].children.append(Node("standalone", s, d))
542
+ elif d.base == "atomic":
543
+ n = Node("atomic", s, d)
544
+ i += 1
545
+ if i >= stop or stmts[i].kind != "code":
546
+ raise F.SourceError("atomic without a statement")
547
+ n.children.append(_code_node(stmts[i]))
548
+ if i + 1 < stop and stmts[i + 1].kind == "dir":
549
+ e = F.parse_dir(stmts[i + 1], version)
550
+ if e.end and e.base == "atomic":
551
+ i += 1
552
+ stack[-1].children.append(n)
553
+ elif set(d.leaves) & LOOP_LEAVES or d.base in ("tile", "unroll"):
554
+ stack.append(Node("loopdir", s, d))
555
+ else:
556
+ stack.append(Node("dir", s, d))
557
+ i += 1
558
+ continue
559
+ m = _DO.match(s.text)
560
+ if m and not _DO_OTHER.match(s.text):
561
+ var = m.group(1).lower()
562
+ bounds = F.split_top(m.group(2), ",")
563
+ stack.append(Node("do", s, loop_var=var, lo=bounds[0].lower(),
564
+ hi=bounds[1].lower() if len(bounds) > 1
565
+ else "",
566
+ step=bounds[2].lower() if len(bounds) > 2
567
+ else "1"))
568
+ elif _DO_OTHER.match(s.text):
569
+ stack.append(Node("do", s))
570
+ elif _END_DO.match(s.text):
571
+ top = stack.pop()
572
+ if top.kind != "do":
573
+ raise F.SourceError(f"line {s.first + 1}: end do mismatch")
574
+ top._closed = True
575
+ top.end_stmt = s
576
+ stack[-1].children.append(top)
577
+ close_loopdirs()
578
+ elif _IF_THEN.match(s.text) and not s.lower.startswith("else"):
579
+ cond = _IF_THEN.match(s.text).group(1)
580
+ guard = "thread" if THREAD_GUARD.match(s.text) else \
581
+ "team" if TEAM_GUARD.match(s.text) else ""
582
+ stack.append(Node("if", s, cond=cond, guard=guard))
583
+ elif s.lower.startswith("else"):
584
+ top = stack[-1]
585
+ if top.kind != "if":
586
+ raise F.SourceError(f"line {s.first + 1}: else outside if")
587
+ top.guard = "" # a guard with an else branch is no guard
588
+ m2 = _IF_THEN.match(s.text)
589
+ top.children.append(Node("code", F.Stmt(
590
+ s.first, s.last, f"!else ({m2.group(1) if m2 else ''})",
591
+ "code")))
592
+ elif _END_IF.match(s.text):
593
+ top = stack.pop()
594
+ if top.kind != "if":
595
+ raise F.SourceError(f"line {s.first + 1}: end if mismatch")
596
+ stack[-1].children.append(top)
597
+ else:
598
+ stack[-1].children.append(_code_node(s))
599
+ i += 1
600
+ if len(stack) != 1:
601
+ raise F.SourceError(f"unclosed construct: {stack[-1].kind}")
602
+ return root.children
603
+
604
+
605
+ # The sequential copy model: versions are ints; a partial or conditional
606
+ # write makes an interned (previous, event) version, so two copies agree
607
+ # after it only if they agreed before.
608
+
609
+ class _Versions:
610
+ def __init__(self):
611
+ self.n = 0
612
+ self.table: dict = {}
613
+
614
+ def new(self) -> int:
615
+ self.n += 1
616
+ return self.n
617
+
618
+ def upd(self, prev: int | None, event: int) -> int:
619
+ key = (prev, event)
620
+ if key not in self.table:
621
+ self.table[key] = self.new()
622
+ return self.table[key]
623
+
624
+
625
+ class _Violation(Exception):
626
+ pass
627
+
628
+
629
+ @dataclass
630
+ class _Frame:
631
+ copies: dict # leaf -> version (private copies)
632
+ kind: str = "" # target | construct
633
+ reductions: frozenset = frozenset()
634
+ attached: frozenset = frozenset() # target: pointers attached on entry
635
+
636
+
637
+ # --- storage: variables, structure components, pointers ---------------------------
638
+
639
+ @dataclass
640
+ class Leaf:
641
+ """A unit of storage the version analysis tracks: a variable, or a
642
+ component of a structure (``dr%a``, ``dr%nd%k``; ``ds%u`` for the
643
+ component of an array of structures). ``levels`` has one entry per
644
+ part of the path: (rank, extents), extents None when deferred (an
645
+ allocatable or pointer array before its ALLOCATE)."""
646
+ path: str
647
+ root: str
648
+ levels: tuple
649
+ allocatable: bool = False
650
+ pointer: bool = False
651
+ target: bool = False
652
+
653
+
654
+ def _parse_ref(text: str):
655
+ """(path, subscripts per part (None where a part has none), rest) of
656
+ the data reference at the start of `text` (``ds(i)%u`` gives
657
+ ``("ds%u", ["i", None], rest)``), or None."""
658
+ m = re.match(r"\s*([A-Za-z_]\w*)\s*", text)
659
+ if not m:
660
+ return None
661
+ parts, subs = [m.group(1).lower()], [None]
662
+ rest = text[m.end():]
663
+ while True:
664
+ if rest.startswith("("):
665
+ ps = _paren_split(rest)
666
+ if ps is None or subs[-1] is not None:
667
+ return None
668
+ subs[-1], rest = ps[0], ps[1]
669
+ continue
670
+ mm = re.match(r"%\s*([A-Za-z_]\w*)\s*", rest)
671
+ if mm:
672
+ parts.append(mm.group(1).lower())
673
+ subs.append(None)
674
+ rest = rest[mm.end():]
675
+ continue
676
+ return "%".join(parts), subs, rest.strip()
677
+
678
+
679
+ def _assignment(t: str):
680
+ """(path, subscripts per part, rhs) of `ref = rhs`, or None."""
681
+ r = _parse_ref(t)
682
+ if r is None:
683
+ return None
684
+ path, subs, rest = r
685
+ if not rest.startswith("=") or rest.startswith("=="):
686
+ return None
687
+ if path.split("%")[0] in F.KEYWORDS:
688
+ return None
689
+ return path, subs, rest[1:].strip()
690
+
691
+
692
+ def _item_name(item: str) -> str | None:
693
+ m = re.match(r"\s*([A-Za-z_]\w*)", item)
694
+ return m.group(1).lower() if m else None
695
+
696
+
697
+ def _item_ref(item: str):
698
+ """(path, subscripts per part) of a clause list item, or None."""
699
+ r = _parse_ref(item)
700
+ if r is None or r[2]:
701
+ return None
702
+ return r[0], r[1]
703
+
704
+
705
+ def _map_parts(c: F.RawClause) -> tuple[str, list[str], list[str]]:
706
+ """(map type, modifiers, item texts) of a map clause."""
707
+ mods = c.modifiers()
708
+ typ = "tofrom"
709
+ for t in ("tofrom", "to", "from", "alloc", "release", "delete",
710
+ "storage"):
711
+ if t in mods:
712
+ typ = t
713
+ typ = "alloc" if typ == "storage" else typ
714
+ return typ, [m for m in mods if m != typ], list(c.items())
715
+
716
+
717
+ def _red_items(c: F.RawClause) -> tuple[str, list[str]]:
718
+ mods = c.modifiers()
719
+ op = mods[-1] if mods else ""
720
+ return op, [n for n in (_item_name(x) for x in c.items()) if n]
721
+
722
+
723
+ class _Walk:
724
+ """One run of the version analysis (refcounts: the spec's reference
725
+ count rules, or every map copying unconditionally). Values live in
726
+ leaves (``_Analysis.leaf``); a reference to a structure or a structure
727
+ component that is itself a structure is a reference to each leaf under
728
+ it (``_Analysis.under``: pointer components are not part of it). A
729
+ pointer associated with a target (``p => t``) is an alias of the
730
+ target's leaf. Reference counts belong to the root variable: map list
731
+ items with the same containing structure are one storage block (5.2
732
+ §5.8.3)."""
733
+
734
+ def __init__(self, an: "_Analysis", flags: set, devvars: set,
735
+ refcounts: bool, check_reads: bool = True):
736
+ self.check_reads = check_reads
737
+ self.an = an
738
+ self.decls = an.decls
739
+ self.leaf = an.leaf
740
+ self.under = an.under
741
+ self.absent = an.absent
742
+ self.vars = set(an.leaf)
743
+ self.flags, self.devvars = flags, devvars
744
+ self.refcounts = refcounts
745
+ self.V = _Versions()
746
+ self.host: dict = {}
747
+ self.dev: dict = {}
748
+ self.ref: dict = {} # root -> reference count
749
+ self.devleaves: dict = {} # root -> leaves of its device block
750
+ self.ptr_ref: dict = {} # pointer variable -> map count
751
+ self.elis: dict = {}
752
+ self.alias: dict = {} # pointer path -> target leaf
753
+ self.frames: list[_Frame] = []
754
+ self.ctx: list[Node] = [] # enclosing nodes (dirs, dos, ifs)
755
+ self.levels = {p: list(lf.levels) for p, lf in an.leaf.items()}
756
+ # leaf -> (device version, (lo, hi, step)): host writes since the
757
+ # copies agreed, all in that section (``write``)
758
+ self.sections: dict = {}
759
+ # (names, leaves) of the actual arguments the inlined calls being
760
+ # executed may pass by copy
761
+ self.copied: list[tuple] = []
762
+ for v in self.vars:
763
+ x = self.V.new() # undefined initial value, shared
764
+ self.host[v] = self.elis[v] = x
765
+
766
+ # -- names --
767
+
768
+ @staticmethod
769
+ def root(path: str) -> str:
770
+ return path.split("%")[0]
771
+
772
+ def skip(self, path: str) -> bool:
773
+ r = self.root(path)
774
+ return r in self.flags or r in self.devvars
775
+
776
+ def canon(self, path: str) -> str:
777
+ parts = path.split("%")
778
+ for k in range(len(parts), 0, -1):
779
+ pre = "%".join(parts[:k])
780
+ if pre in self.alias:
781
+ return "%".join([self.alias[pre]] + parts[k:])
782
+ return path
783
+
784
+ def is_ptr(self, path: str) -> bool:
785
+ lf = self.leaf.get(path)
786
+ return lf is not None and lf.pointer
787
+
788
+ def expand(self, path: str) -> list[str]:
789
+ """The leaves a reference to `path` reads or writes."""
790
+ if self.root(path) in self.absent:
791
+ raise _Violation(f"absent optional dummy argument "
792
+ f"{self.root(path)} is referenced")
793
+ c = self.canon(path)
794
+ if c != path and any(c in leaves and self.root(path) not in names
795
+ for names, leaves in self.copied):
796
+ raise _Violation(f"{path} refers to {c}, an actual argument of "
797
+ "the procedure being executed that may be a "
798
+ "copy")
799
+ if c in self.leaf:
800
+ return [c]
801
+ if c in self.under:
802
+ return list(self.under[c])
803
+ if "%" in c and self.root(c) in self.leaf:
804
+ raise _Violation(f"component {c} of a structure whose type the "
805
+ "analysis does not see")
806
+ return []
807
+
808
+ def levels_of(self, path: str) -> list:
809
+ """(rank, extents) per part of `path` (its own parts: for a pointer
810
+ alias, the target's)."""
811
+ c = self.canon(path)
812
+ if c in self.levels:
813
+ return self.levels[c]
814
+ leaves = self.under.get(c)
815
+ if leaves:
816
+ return self.levels[leaves[0]][:len(c.split("%"))]
817
+ return []
818
+
819
+ def whole(self, path: str, subs: list) -> bool:
820
+ """The reference covers all of `path`'s storage: no subscripts, or
821
+ sections `:` or `1:extent` in every dimension."""
822
+ if all(s is None for s in subs):
823
+ return True
824
+ if path != self.canon(path):
825
+ if any(s is not None for s in subs[:-1]):
826
+ return False
827
+ subs = subs[-1:]
828
+ lv = self.levels_of(path)
829
+ if len(lv) != len(subs):
830
+ return False
831
+ for (rank, ext), s in zip(lv, subs):
832
+ if s is None:
833
+ continue
834
+ dims = [x.strip().lower() for x in F.split_top(s, ",")]
835
+ if ext is None or len(dims) != rank or len(ext) != rank:
836
+ return False
837
+ for d, e in zip(dims, ext):
838
+ if d == ":":
839
+ continue
840
+ p = [x.strip() for x in F.split_top(d, ":")]
841
+ if len(p) != 2 or p[0] not in ("", "1") or \
842
+ p[1] not in ("", e):
843
+ return False
844
+ return True
845
+
846
+ # -- copies --
847
+
848
+ @property
849
+ def on_device(self) -> bool:
850
+ return any(f.kind == "target" for f in self.frames)
851
+
852
+ def _where(self, v: str):
853
+ for f in reversed(self.frames):
854
+ if v in f.copies:
855
+ return ("frame", f)
856
+ if f.kind == "target":
857
+ break
858
+ if self.on_device:
859
+ if v in self.dev:
860
+ return ("dev", None)
861
+ return ("none", None)
862
+ return ("host", None)
863
+
864
+ def seen(self, v: str):
865
+ w, f = self._where(v)
866
+ if w == "frame":
867
+ return f.copies[v]
868
+ if w == "dev":
869
+ return self.dev[v]
870
+ if w == "host":
871
+ return self.host[v]
872
+ return None
873
+
874
+ def _set(self, v: str, ver: int):
875
+ if not self.check_reads:
876
+ return
877
+ w, f = self._where(v)
878
+ if w == "frame":
879
+ f.copies[v] = ver
880
+ elif w == "dev":
881
+ self.dev[v] = ver
882
+ elif w == "host":
883
+ self.host[v] = ver
884
+ else:
885
+ raise _Violation(f"{v} is written in a target region without a "
886
+ "device copy")
887
+
888
+ def _red_var(self, v: str) -> bool:
889
+ for f in reversed(self.frames):
890
+ if v in f.copies:
891
+ return v in f.reductions
892
+ return False
893
+
894
+ def _device_pointer(self, path: str, line: int):
895
+ """A pointer referenced in a target region must have been attached
896
+ on entry to it (5.2 §5.8.3: mapped explicitly on the construct, with
897
+ its pointer or its target newly created)."""
898
+ if not (self.on_device and self.is_ptr(path)):
899
+ return
900
+ for f in reversed(self.frames):
901
+ if f.kind == "target":
902
+ if path not in f.attached:
903
+ raise _Violation(f"line {line}: pointer {path} is "
904
+ "referenced in a target region without "
905
+ "being attached on entry")
906
+ return
907
+
908
+ def read(self, v: str, line: int, in_update_of: str | None = None):
909
+ if not self.check_reads or self.skip(v):
910
+ return
911
+ self._device_pointer(v, line)
912
+ mine = set(self.expand(in_update_of)) if in_update_of else set()
913
+ for leaf in self.expand(v):
914
+ if self._red_var(leaf):
915
+ if leaf in mine:
916
+ continue
917
+ raise _Violation(f"line {line}: {leaf} is read inside its "
918
+ "reduction region outside its own update")
919
+ if self.seen(leaf) != self.elis[leaf]:
920
+ raise _Violation(
921
+ f"line {line}: {leaf} is read "
922
+ f"{'on the device' if self.on_device else 'on the host'} "
923
+ "from a copy that differs from sequential execution")
924
+
925
+ def write(self, v: str, full: bool, line: int, section=None):
926
+ """`section`: the (lo, hi, step) of the loop whose variable is the
927
+ only subscript of a partial write of a rank-1 array on the host
928
+ (``loop_section``)."""
929
+ if not self.check_reads or self.skip(v):
930
+ return
931
+ self._device_pointer(v, line)
932
+ for leaf in self.expand(v):
933
+ e = self.V.new()
934
+ if self._red_var(leaf):
935
+ self.elis[leaf] = self.V.upd(self.elis[leaf], e)
936
+ continue
937
+ where = self._where(leaf)[0]
938
+ if full:
939
+ self._set(leaf, e)
940
+ self.elis[leaf] = e
941
+ else:
942
+ cur = self.seen(leaf)
943
+ self._set(leaf, self.V.upd(cur, e))
944
+ self.elis[leaf] = self.V.upd(self.elis[leaf], e)
945
+ if where == "host":
946
+ # host writes since the copies last agreed, all inside one
947
+ # section: a target update of that section makes them
948
+ # agree again
949
+ old = self.sections.get(leaf)
950
+ if not full and section is not None and (
951
+ old is None and self.dev.get(leaf) == cur or
952
+ old is not None and old[1] == section):
953
+ self.sections[leaf] = (old[0] if old else cur, section)
954
+ else:
955
+ self.sections.pop(leaf, None)
956
+
957
+ def loop_section(self, path: str, subs: list):
958
+ """(lo, hi, step) when the only subscript of `path` (a rank-1
959
+ array) is the variable of the innermost enclosing loop with that
960
+ variable, else None."""
961
+ if any(x is not None for x in subs[:-1]) or subs[-1] is None:
962
+ return None
963
+ idx = subs[-1].strip().lower()
964
+ for n in reversed(self.ctx):
965
+ if n.kind == "do" and n.loop_var == idx:
966
+ return (n.lo, n.hi, n.step)
967
+ return None
968
+
969
+ # -- maps --
970
+
971
+ def _groups(self, items) -> dict:
972
+ """root -> [(leaves, map type, modifiers, whole)] of map list items
973
+ (path, map type, modifiers, subscripts per part)."""
974
+ groups: dict = {}
975
+ for path, typ, mods, subs in items:
976
+ if self.root(path) in self.absent:
977
+ continue # an absent optional dummy: no effect
978
+ if self.skip(path):
979
+ continue
980
+ leaves = self.expand(path)
981
+ if not leaves:
982
+ continue
983
+ groups.setdefault(self.root(self.canon(path)), []).append(
984
+ (leaves, typ, mods, self.whole(path, subs)))
985
+ return groups
986
+
987
+ def _copy(self, dst: dict, src: dict, leaf: str, whole: bool):
988
+ if whole or dst.get(leaf) == src[leaf]:
989
+ dst[leaf] = src[leaf]
990
+ else:
991
+ # a section: the copies agree only where it was copied
992
+ dst[leaf] = self.V.upd(dst.get(leaf), self.V.new())
993
+
994
+ def map_enter(self, items) -> dict:
995
+ """Map entry of a construct's list items; returns the reference
996
+ counts before it."""
997
+ groups = self._groups(items)
998
+ before = {r: self.ref.get(r, 0) for r in groups}
999
+ for r, its in groups.items():
1000
+ cnt = before[r]
1001
+ block = set().union(*(set(lv) for lv, _, _, _ in its))
1002
+ if cnt == 0 or not self.refcounts:
1003
+ for leaves, typ, mods, whole in its:
1004
+ for leaf in leaves:
1005
+ if cnt == 0:
1006
+ self.dev.pop(leaf, None) # new storage
1007
+ if typ in ("to", "tofrom"):
1008
+ self._copy(self.dev, self.host, leaf, whole)
1009
+ elif leaf not in self.dev:
1010
+ self.dev[leaf] = self.V.new() # uninitialised
1011
+ self.devleaves[r] = block if cnt == 0 else \
1012
+ self.devleaves.get(r, set()) | block
1013
+ else:
1014
+ if not block <= self.devleaves.get(r, set()):
1015
+ raise _Violation(
1016
+ f"{r}: a part of the structure that is not on the device "
1017
+ "is mapped while other parts are present (5.2 §5.8.3: "
1018
+ "one storage block)")
1019
+ for leaves, typ, mods, whole in its:
1020
+ if "always" in mods and typ in ("to", "tofrom"):
1021
+ for leaf in leaves:
1022
+ self._copy(self.dev, self.host, leaf, whole)
1023
+ self.ref[r] = cnt + 1
1024
+ return before
1025
+
1026
+ def map_exit(self, items):
1027
+ for r, its in self._groups(items).items():
1028
+ cnt = self.ref.get(r, 0)
1029
+ if cnt == 0:
1030
+ continue # not present: no effect
1031
+ cnt = 0 if any(t == "delete" for _, t, _, _ in its) else cnt - 1
1032
+ for leaves, typ, mods, whole in its:
1033
+ if typ in ("from", "tofrom") and (
1034
+ cnt == 0 or "always" in mods or not self.refcounts):
1035
+ for leaf in leaves:
1036
+ if leaf in self.dev:
1037
+ self._copy(self.host, self.dev, leaf, whole)
1038
+ self.ref[r] = cnt
1039
+ if cnt == 0:
1040
+ for leaf in self.devleaves.pop(r, ()):
1041
+ self.dev.pop(leaf, None)
1042
+
1043
+ def update(self, path: str, subs: list, to: bool):
1044
+ """target update to/from of one list item."""
1045
+ if self.skip(path):
1046
+ return
1047
+ whole = self.whole(path, subs)
1048
+ sec = None
1049
+ if not whole and all(x is None for x in subs[:-1]):
1050
+ p = [x.strip().lower() for x in F.split_top(subs[-1], ":")]
1051
+ if len(p) in (2, 3) and "," not in subs[-1]:
1052
+ sec = (p[0], p[1], p[2] if len(p) == 3 else "1")
1053
+ for leaf in self.expand(path):
1054
+ if leaf not in self.dev:
1055
+ continue
1056
+ pend = self.sections.get(leaf)
1057
+ if to and sec is not None and pend is not None and \
1058
+ pend[1] == sec and self.dev[leaf] == pend[0]:
1059
+ self.dev[leaf] = self.host[leaf]
1060
+ self.sections.pop(leaf)
1061
+ elif to:
1062
+ self._copy(self.dev, self.host, leaf, whole)
1063
+ else:
1064
+ self._copy(self.host, self.dev, leaf, whole)
1065
+
1066
+ # -- statements --
1067
+
1068
+ def full_write(self, path: str, subs: list) -> bool:
1069
+ """A write `ref = ...` covers all of `path`: no subscripts, or
1070
+ subscripts that are the variables of enclosing unconditional loops
1071
+ over the declared extents (or `:`), each loop variable once."""
1072
+ if self._conditional():
1073
+ return False
1074
+ if all(s is None for s in subs):
1075
+ return True
1076
+ if path != self.canon(path):
1077
+ if any(s is not None for s in subs[:-1]):
1078
+ return False
1079
+ subs = subs[-1:]
1080
+ lv = self.levels_of(path)
1081
+ if len(lv) != len(subs):
1082
+ return False
1083
+ loops = {n.loop_var: n for n in self.ctx if n.kind == "do"}
1084
+ used = []
1085
+ for (rank, ext), s in zip(lv, subs):
1086
+ if s is None:
1087
+ continue
1088
+ idx = [x.strip().lower() for x in F.split_top(s, ",")]
1089
+ if ext is None or len(ext) != len(idx):
1090
+ return False
1091
+ for x, e in zip(idx, ext):
1092
+ if x == ":":
1093
+ continue
1094
+ n = loops.get(x)
1095
+ if n is None:
1096
+ return False
1097
+ if not ((n.lo == "1" and n.hi == e and n.step == "1") or
1098
+ (n.lo == e and n.hi == "1" and n.step == "-1")):
1099
+ return False
1100
+ used.append(x)
1101
+ return len(set(used)) == len(used)
1102
+
1103
+ def _conditional(self) -> bool:
1104
+ return any(n.kind in ("if", "ifstmt") and not n.guard and
1105
+ not n.once for n in self.ctx)
1106
+
1107
+ def once_in_loop(self, cond: str) -> bool:
1108
+ """`i == c` where i is the variable of an enclosing unconditional
1109
+ loop that takes the value c exactly once (c its first value, or
1110
+ its last with step 1 or -1)."""
1111
+ m = re.match(r"^\s*(\w+)\s*==\s*(\w+)\s*$", cond)
1112
+ if not m:
1113
+ return False
1114
+ var, c = m.group(1).lower(), m.group(2).lower()
1115
+ for k in range(len(self.ctx) - 1, -1, -1):
1116
+ n = self.ctx[k]
1117
+ if n.kind == "do" and n.loop_var == var:
1118
+ if any(x.kind in ("if", "ifstmt") and not x.guard and
1119
+ not x.once for x in self.ctx[k:]):
1120
+ return False
1121
+ return c == n.lo or (c == n.hi and n.step in ("1", "-1"))
1122
+ return False
1123
+
1124
+ def code(self, s: F.Stmt):
1125
+ t, line = s.text, s.first + 1
1126
+ low = s.lower
1127
+ if low.startswith(CALL_MARK):
1128
+ parts = t.split()
1129
+ name = parts[1]
1130
+ if self.on_device:
1131
+ raise _Violation(f"line {line}: internal procedure {name} "
1132
+ "called in a target region")
1133
+ copied = [] if parts[2] == "-" else parts[2].split(",")
1134
+ for a in copied:
1135
+ if self.ref.get(self.root(self.canon(a)), 0) > 0:
1136
+ raise _Violation(
1137
+ f"line {line}: actual argument {a} of {name} is "
1138
+ "present on the device at the call (a copy-in/"
1139
+ "copy-out argument would not be)")
1140
+ for req in parts[3:]:
1141
+ a, ext = req.split("=")
1142
+ lv = self.levels_of(a)
1143
+ if not lv or lv[-1][1] != tuple(ext.split("|")):
1144
+ raise _Violation(
1145
+ f"line {line}: explicit-shape dummy of {name}: the "
1146
+ f"extents of actual {a} are not known to be "
1147
+ f"({', '.join(ext.split('|'))})")
1148
+ self.copied.append((set(copied), {leaf for a in copied
1149
+ for leaf in self.expand(a)}))
1150
+ return
1151
+ if low.startswith(END_CALL_MARK):
1152
+ self.copied.pop()
1153
+ return
1154
+ if low.startswith("!else"):
1155
+ for v in F.designators(t[5:]):
1156
+ self.read(v, line)
1157
+ return
1158
+ if re.match(r"(print|write)\b", low):
1159
+ for v in F.designators(t):
1160
+ if v not in F.KEYWORDS:
1161
+ self.read(v, line)
1162
+ return
1163
+ m = re.match(r"allocate\s*\((.*)\)$", t, re.I)
1164
+ if m:
1165
+ if self.on_device:
1166
+ raise _Violation(f"line {line}: allocate in a target region")
1167
+ for item in F.split_top(m.group(1), ","):
1168
+ r = _item_ref(item)
1169
+ if r is None:
1170
+ raise _Violation(f"line {line}: allocate item {item!r}")
1171
+ path, subs = r
1172
+ self.alias.pop(path, None) # a pointer gets its own target
1173
+ for v in F.designators(subs[-1] or ""):
1174
+ self.read(v, line)
1175
+ for leaf in self.expand(path):
1176
+ lv = self.levels[leaf]
1177
+ dims = [x.strip().lower() for x in
1178
+ F.split_top(subs[-1], ",")] if subs[-1] else []
1179
+ lv[-1] = (len(dims), tuple(dims) if all(
1180
+ ":" not in d for d in dims) else None)
1181
+ self.write(path, True, line)
1182
+ return
1183
+ if re.match(r"deallocate\b", low) or low in (
1184
+ "continue", "exit", "cycle", "stop") or \
1185
+ re.match(r"(stop|return)\b", low):
1186
+ return
1187
+ m = re.match(r"(.+?)\s*=>\s*(.+)$", t)
1188
+ if m and not m.group(1).strip().startswith("("):
1189
+ if self.on_device:
1190
+ raise _Violation(f"line {line}: pointer assignment in a "
1191
+ "target region")
1192
+ lhs, rhs = _item_ref(m.group(1)), _item_ref(m.group(2))
1193
+ if lhs is None or not self.is_ptr(lhs[0]) or \
1194
+ any(x is not None for x in lhs[1]):
1195
+ raise _Violation(f"line {line}: pointer assignment not "
1196
+ f"understood: {t!r}")
1197
+ if rhs is None or any(x is not None for x in rhs[1]):
1198
+ raise _Violation(f"line {line}: pointer target is not a "
1199
+ f"whole variable: {t!r}")
1200
+ tgt = self.canon(rhs[0])
1201
+ if tgt not in self.leaf or not (self.leaf[tgt].target or
1202
+ self.leaf[tgt].pointer):
1203
+ raise _Violation(f"line {line}: pointer target {rhs[0]} is "
1204
+ "not a TARGET variable")
1205
+ self.alias[lhs[0]] = tgt
1206
+ return
1207
+ m = re.match(r"call\s+(\w+)\s*(\((.*)\))?$", t, re.I)
1208
+ if m:
1209
+ for v in F.designators(m.group(3) or ""):
1210
+ self.read(v, line)
1211
+ for a in F.split_top(m.group(3) or "", ","):
1212
+ r = _parse_ref(a.strip())
1213
+ if r is not None and not r[2]:
1214
+ self.write(r[0], False, line)
1215
+ return
1216
+ m = _assignment(t)
1217
+ if m is None:
1218
+ raise _Violation(f"line {line}: statement not understood: {t!r}")
1219
+ path, subs, rhs = m
1220
+ if self.skip(path):
1221
+ return
1222
+ for v in F.designators(rhs) + [d for x in subs if x
1223
+ for d in F.designators(x)]:
1224
+ self.read(v, line, in_update_of=path)
1225
+ self.write(path, self.full_write(path, subs), line,
1226
+ self.loop_section(path, subs))
1227
+
1228
+
1229
+ CALL_MARK = "!call "
1230
+ END_CALL_MARK = "!endcall "
1231
+
1232
+
1233
+ class _Analysis:
1234
+ """A source split for the analysis: the main program's declarations and
1235
+ the derived types it defines, its storage leaves, and its execution
1236
+ part with every call of an internal subroutine replaced by the
1237
+ subroutine's body (``inline``)."""
1238
+
1239
+ def __init__(self, source: str, version: int):
1240
+ self.source, self.version = source, version
1241
+ self.stmts = F.statements(source)
1242
+ start, stop = F.main_program(self.stmts)
1243
+ prog = next(i for i, s in enumerate(self.stmts)
1244
+ if s.kind == "code" and
1245
+ re.match(r"program\s+\w+", s.lower))
1246
+ self.decls = F.declarations(self.stmts, prog + 1, start)
1247
+ self.all_decls = F.declarations(self.stmts)
1248
+ self.types = F.type_defs(self.stmts)
1249
+ self.absent: set = set()
1250
+ self.procs = F.internal_procedures(self.stmts) \
1251
+ if stop < len(self.stmts) and self.stmts[stop].lower == \
1252
+ "contains" else {}
1253
+ self.has_procs = bool(self.procs)
1254
+ self.body = self.inline(self.stmts[start:stop], 0)
1255
+ self.leaf: dict[str, Leaf] = {}
1256
+ self.under: dict[str, list[str]] = {}
1257
+ for name, v in self.decls.items():
1258
+ if not v.parameter:
1259
+ self._add_leaf(name, name, v, ())
1260
+
1261
+ # -- leaves --
1262
+
1263
+ def _add_leaf(self, path: str, root: str, v: F.Var, levels: tuple,
1264
+ depth: int = 0):
1265
+ lv = levels + ((v.rank, v.extents if (v.extents or v.rank == 0)
1266
+ else None),)
1267
+ m = re.match(r"type\s*\(\s*(\w+)\s*\)", v.type)
1268
+ t = self.types.get(m.group(1).lower()) if m else None
1269
+ if t is None:
1270
+ self.leaf[path] = Leaf(path, root, lv, v.allocatable, v.pointer,
1271
+ v.target)
1272
+ return
1273
+ if v.pointer or v.allocatable or depth > 8:
1274
+ raise F.SourceError(f"{path}: pointer or allocatable structure")
1275
+ for cn, cv in t.items():
1276
+ self._add_leaf(f"{path}%{cn}", root, cv, lv, depth + 1)
1277
+ self.under[path] = [p for p, lf in self.leaf.items()
1278
+ if p.startswith(path + "%") and not lf.pointer]
1279
+
1280
+ # -- internal procedures --
1281
+
1282
+ def inline(self, stmts: list, depth: int) -> list:
1283
+ """`stmts` with each `call p(...)` of an internal subroutine p
1284
+ replaced by: a marker statement (CALL_MARK, naming the actual
1285
+ arguments that may be passed by copy-in/copy-out), an assignment of
1286
+ each VALUE dummy's copy, and p's body with its dummies renamed to
1287
+ the actual arguments and its locals to names of their own. The
1288
+ call executes p's body on the caller's variables (argument
1289
+ association by reference) in OpenMP and in sequential execution
1290
+ alike; ``_Walk.code`` checks that no actual argument that could be
1291
+ copied is present on the device at the call, so the presence the
1292
+ body's maps find is the caller's."""
1293
+ out = []
1294
+ for s in stmts:
1295
+ m = re.match(r"call\s+(\w+)\s*(?:\((.*)\))?\s*$", s.text, re.I) \
1296
+ if s.kind == "code" else None
1297
+ if m and m.group(1).lower() in self.procs:
1298
+ out += self._expand_call(s, m.group(1).lower(),
1299
+ m.group(2) or "", depth)
1300
+ continue
1301
+ names = F.designators(s.text if s.kind == "code" else
1302
+ " ".join(c.arg or "" for c in
1303
+ F.parse_dir(s, self.version)
1304
+ .clauses)
1305
+ if s.kind == "dir" else "")
1306
+ if set(names) & set(self.procs):
1307
+ raise F.SourceError(f"line {s.first + 1}: internal procedure "
1308
+ "referenced other than by a CALL "
1309
+ "statement")
1310
+ out.append(s)
1311
+ return out
1312
+
1313
+ def _expand_call(self, s: F.Stmt, name: str, args: str,
1314
+ depth: int) -> list:
1315
+ if depth > 4:
1316
+ raise F.SourceError(f"internal procedure {name}: calls nested too "
1317
+ "deeply")
1318
+ p = self.procs[name]
1319
+ actuals = [a.strip() for a in F.split_top(args, ",") if a.strip()]
1320
+ if len(actuals) > len(p.dummies) or \
1321
+ any(re.match(r"\w+\s*=[^=]", a) for a in actuals):
1322
+ raise F.SourceError(f"line {s.first + 1}: call of {name} not "
1323
+ "understood (keyword or extra arguments)")
1324
+ mapping, pre, copied, shapes = {}, [], [], []
1325
+ for k, d in enumerate(p.dummies):
1326
+ dv = p.decls.get(d)
1327
+ if dv is None:
1328
+ raise F.SourceError(f"{name}: dummy {d} not declared")
1329
+ if k >= len(actuals):
1330
+ if not dv.optional:
1331
+ raise F.SourceError(f"line {s.first + 1}: {name}: "
1332
+ f"missing actual for {d}")
1333
+ new = f"{name}__absent__{d}"
1334
+ mapping[d] = new
1335
+ self.absent.add(new)
1336
+ continue
1337
+ a = actuals[k]
1338
+ if dv.value:
1339
+ new = f"{name}__{d}"
1340
+ mapping[d] = new
1341
+ self.decls[new] = dataclasses.replace(dv, name=new,
1342
+ value=False,
1343
+ intent="")
1344
+ pre.append(F.Stmt(s.first, s.last, f"{new} = {a}", "code",
1345
+ s.indent))
1346
+ continue
1347
+ if not re.fullmatch(r"[A-Za-z_]\w*", a):
1348
+ raise F.SourceError(f"line {s.first + 1}: {name}: actual "
1349
+ f"argument {a!r} is not a variable")
1350
+ av = self.decls.get(a.lower())
1351
+ if av is None or av.parameter:
1352
+ raise F.SourceError(f"line {s.first + 1}: {name}: actual "
1353
+ f"argument {a} is not a variable")
1354
+ if (dv.allocatable and not av.allocatable) or \
1355
+ (dv.pointer and not av.pointer) or \
1356
+ (dv.type.startswith("type") and dv.type != av.type) or \
1357
+ dv.rank != av.rank or \
1358
+ (dv.extents and av.extents and
1359
+ dv.extents != av.extents) or \
1360
+ (dv.rank and not dv.extents and
1361
+ not (dv.allocatable or dv.pointer) and
1362
+ re.search(r"\(\s*\*", dv.type)) or \
1363
+ (dv.intent == "out" and dv.allocatable):
1364
+ raise F.SourceError(f"line {s.first + 1}: {name}: dummy {d} "
1365
+ f"and actual {a} are not associated "
1366
+ "element by element")
1367
+ if dv.extents and not av.extents:
1368
+ # explicit shape against a deferred-shape actual: checked
1369
+ # against the actual's allocated extents at the call
1370
+ shapes.append((a.lower(), dv.extents))
1371
+ mapping[d] = a.lower()
1372
+ if av.pointer and not dv.pointer:
1373
+ # a pointer's target may be discontiguous: the dummy may be
1374
+ # a copy (5.2 §5.1.1 calls a dummy argument's storage the
1375
+ # actual's only for a scalar, a structure or an array that
1376
+ # is not a pointer or assumed-shape array)
1377
+ copied.append(a.lower())
1378
+ for local, lv in p.decls.items():
1379
+ if local in p.dummies:
1380
+ continue
1381
+ new = f"{name}__{local}"
1382
+ mapping[local] = new
1383
+ self.decls[new] = dataclasses.replace(lv, name=new)
1384
+ body = [F.Stmt(t.first, t.last, F.rename(t.text, mapping), t.kind,
1385
+ t.indent) for t in p.body]
1386
+ mark = F.Stmt(s.first, s.last,
1387
+ f"{CALL_MARK}{name} {','.join(copied) or '-'} " +
1388
+ " ".join(f"{a}={'|'.join(e)}" for a, e in shapes),
1389
+ "code", s.indent)
1390
+ end = F.Stmt(s.last, s.last, f"{END_CALL_MARK}{name}", "code",
1391
+ s.indent)
1392
+ return [mark] + pre + self.inline(body, depth + 1) + [end]
1393
+
1394
+ def static_exclusions(self) -> tuple[list[str], set, set]:
1395
+ """Reasons the program is out of scope before any walk, the
1396
+ ran_on_host flag variables, and device-number variables."""
1397
+ why: list[str] = []
1398
+ st = self.stmts
1399
+ if any(s.kind == "condcomp" for s in st):
1400
+ why.append("conditional compilation (!$) line")
1401
+ # module variables
1402
+ in_mod, in_type, mod_vars = False, False, []
1403
+ for s in st:
1404
+ low = s.lower if s.kind == "code" else ""
1405
+ if in_type:
1406
+ in_type = not F._END_TYPE.match(low)
1407
+ elif re.match(r"module\s+\w+$", low):
1408
+ in_mod = True
1409
+ elif low.startswith("contains") and in_mod:
1410
+ in_mod = False
1411
+ elif in_mod and F._TYPEDEF.match(low):
1412
+ in_type = True
1413
+ elif in_mod and F._TYPE.match(s.text) and \
1414
+ "parameter" not in low:
1415
+ mod_vars.append(s.text)
1416
+ if mod_vars:
1417
+ why.append("module variables (declare target visibility)")
1418
+ fp = {n for n, v in self.decls.items()
1419
+ if v.type.startswith(("real", "double", "complex"))}
1420
+ flags, devvars = set(), set()
1421
+ body = self.body
1422
+ for s in body:
1423
+ if s.kind != "code":
1424
+ continue
1425
+ m = FLAG_IDIOM.match(_strip_if(s.text))
1426
+ if m:
1427
+ flags.add(m.group(1).lower())
1428
+ m = DEV_IDIOM.match(s.text)
1429
+ if m:
1430
+ devvars.add(m.group(1).lower())
1431
+ # flags may only be initialised, set by the idiom, listed in
1432
+ # clauses and printed in the ran_on_host field
1433
+ for s in body:
1434
+ if s.kind != "code":
1435
+ continue
1436
+ ids = set(F.identifiers(s.text))
1437
+ bad = ids & (flags | devvars)
1438
+ if not bad:
1439
+ continue
1440
+ txt = _strip_if(s.text)
1441
+ if FLAG_IDIOM.match(txt) or DEV_IDIOM.match(txt):
1442
+ continue
1443
+ if re.match(r"(\w+)\s*=\s*-?\d+$", txt):
1444
+ continue
1445
+ if re.match(r"print\b", s.lower) and "ran_on_host" in s.lower:
1446
+ continue # the ran_on_host field (never compared)
1447
+ why.append(f"ran_on_host flag or device number "
1448
+ f"{', '.join(sorted(bad))} used in a value "
1449
+ f"(line {s.first + 1})")
1450
+ for s in body:
1451
+ if s.kind != "code":
1452
+ continue
1453
+ for name in re.findall(r"\b(omp_\w+)\s*\(", s.text, re.I):
1454
+ txt = _strip_if(s.text)
1455
+ if name.lower() == "omp_is_initial_device" and \
1456
+ FLAG_IDIOM.match(txt):
1457
+ continue
1458
+ if name.lower() == "omp_get_default_device" and \
1459
+ DEV_IDIOM.match(txt):
1460
+ continue
1461
+ if THREAD_GUARD.match(s.text) or TEAM_GUARD.match(s.text):
1462
+ continue
1463
+ why.append(f"runtime routine {name.lower()} "
1464
+ f"(line {s.first + 1})")
1465
+ for s in st:
1466
+ if s.kind != "dir":
1467
+ continue
1468
+ d = F.parse_dir(s, self.version)
1469
+ if d.end:
1470
+ continue
1471
+ if d.base in EXCLUDED_DIRECTIVES:
1472
+ why.append(EXCLUDED_DIRECTIVES[d.base])
1473
+ if d.base == "declare target":
1474
+ items = [_item_name(x) for x in
1475
+ F.split_top(d.args or "", ",") if x]
1476
+ for c in d.clauses:
1477
+ items += [_item_name(x) for x in c.items()]
1478
+ if any(n in self.all_decls for n in items if n):
1479
+ why.append("declare target variable")
1480
+ if d.base == "ordered" and (d.clause("depend") or
1481
+ d.clause("doacross")):
1482
+ why.append("ordered depend/doacross")
1483
+ if d.base == "masked":
1484
+ for c in d.clause("filter"):
1485
+ if (c.arg or "").strip() != "0":
1486
+ why.append("masked with a nonzero filter")
1487
+ for c in d.clauses:
1488
+ if c.name in EXCLUDED_CLAUSES and EXCLUDED_CLAUSES[c.name]:
1489
+ why.append(EXCLUDED_CLAUSES[c.name])
1490
+ mods = c.modifiers()
1491
+ if c.name in ("map", "to", "from", "defaultmap") and (
1492
+ "present" in mods or (c.name == "defaultmap" and
1493
+ (c.arg or "").lower()
1494
+ .startswith("present"))):
1495
+ why.append("present modifier")
1496
+ if c.name in ("map", "to", "from", "depend") and any(
1497
+ m.startswith(("iterator", "mapper")) for m in mods):
1498
+ why.append("iterator or mapper modifier")
1499
+ if c.name == "map" and any(m in ("self", "ref_ptr",
1500
+ "ref_ptee", "ref_ptr_ptee")
1501
+ for m in mods):
1502
+ why.append("6.0 map modifier")
1503
+ if c.name == "defaultmap" and (c.arg or "").lower().split(
1504
+ ":")[0].strip() in ("self", "private", "present"):
1505
+ why.append(f"defaultmap({c.arg})")
1506
+ if c.name == "device" and "ancestor" in mods:
1507
+ why.append("device(ancestor)")
1508
+ if c.name == "default" and (c.arg or "").lower().strip() in (
1509
+ "private", "firstprivate"):
1510
+ why.append(f"default({c.arg})")
1511
+ if c.name in ("reduction", "in_reduction",
1512
+ "task_reduction"):
1513
+ op, items = _red_items(c)
1514
+ if "inscan" in mods:
1515
+ why.append("inscan reduction")
1516
+ if op and op not in INTRINSIC_RED:
1517
+ why.append(f"reduction identifier {op}")
1518
+ if set(items) & fp and not exact_fp(self.source):
1519
+ why.append("floating-point reduction")
1520
+ return sorted(set(why)), flags, devvars
1521
+
1522
+
1523
+ _GEN = re.compile(r"^! generator: fuzz-(\d+)\s*$", re.M)
1524
+
1525
+
1526
+ def exact_fp(source: str) -> bool:
1527
+ """A fuzz-3 program (offload.engine.fuzz_base): every floating-point
1528
+ value, and every partial result of any reduction order, is exactly
1529
+ representable by construction, so rule 1 (reduction order) does not
1530
+ exclude its floating-point reductions."""
1531
+ m = _GEN.search(source)
1532
+ return m is not None and int(m.group(1)) >= 3
1533
+
1534
+
1535
+ def _strip_if(text: str) -> str:
1536
+ """The statement of a one-line if (or the text itself)."""
1537
+ if re.match(r"if\s*\(", text, re.I) and not _IF_THEN.match(text):
1538
+ ps = _paren_split(text[2:].strip())
1539
+ if ps:
1540
+ return ps[1]
1541
+ return text
1542
+
1543
+
1544
+ # clauses whose list items name variables without accessing them
1545
+ SHARING_CLAUSES = ("shared", "private", "firstprivate", "lastprivate",
1546
+ "reduction", "in_reduction", "task_reduction", "depend",
1547
+ "default", "allocate")
1548
+
1549
+
1550
+ def _refs(nodes: list[Node], sharing: bool = True) -> set[str]:
1551
+ """Data references (``F.designators``) in a list of nodes (statements
1552
+ and the clause arguments of nested directives; without the list items
1553
+ of data-sharing and depend clauses unless `sharing`)."""
1554
+ out: set[str] = set()
1555
+ for n in nodes:
1556
+ if n.stmt is not None:
1557
+ if n.kind in ("dir", "loopdir", "standalone", "atomic"):
1558
+ for c in (n.dir.clauses if n.dir else []):
1559
+ if sharing or c.name not in SHARING_CLAUSES:
1560
+ out |= set(F.designators(c.arg or ""))
1561
+ else:
1562
+ out |= set(F.designators(n.stmt.text))
1563
+ if n.inner is not None:
1564
+ out |= _refs([n.inner], sharing)
1565
+ out |= _refs(n.children, sharing)
1566
+ return out
1567
+
1568
+
1569
+ def _assoc_loop_vars(node: Node) -> list[str]:
1570
+ """Iteration variables of the loops associated with a loop directive
1571
+ (collapse(k) takes k levels; a nested loop directive is not one)."""
1572
+ k = 1
1573
+ for c in node.dir.clauses:
1574
+ if c.name == "collapse":
1575
+ try:
1576
+ k = int((c.arg or "1").strip())
1577
+ except ValueError:
1578
+ k = 1
1579
+ out, cur = [], node
1580
+ for _ in range(k):
1581
+ loops = [c for c in cur.children if c.kind == "do"]
1582
+ if not loops:
1583
+ break
1584
+ out.append(loops[0].loop_var)
1585
+ cur = loops[0]
1586
+ return [v for v in out if v]
1587
+
1588
+
1589
+ # --- multiplicity: code executed once per team or thread ---------
1590
+
1591
+ # leaves that share out the work of the innermost enclosing region of a
1592
+ # kind among its teams or threads (a loop construct: the iterations; the
1593
+ # others: their block runs once)
1594
+ _SHARES = {"do": "parallel", "for": "parallel", "workshare": "parallel",
1595
+ "sections": "parallel", "single": "parallel",
1596
+ "masked": "parallel", "master": "parallel",
1597
+ "distribute": "teams", "workdistribute": "teams"}
1598
+ _PRIVATE_CLAUSES = ("private", "firstprivate", "lastprivate", "linear")
1599
+
1600
+
1601
+ def _redundant_region(ctx: list[Node]) -> tuple[int, str] | None:
1602
+ """(index in ctx, "teams" | "parallel") of the innermost enclosing
1603
+ teams or parallel region whose body the innermost statement runs once
1604
+ per team or thread: no worksharing construct (do, distribute, loop with
1605
+ its binding), single, masked, sections or once-only guard
1606
+ (``if (omp_get_team_num() == 0)`` / ``omp_get_thread_num()``) between
1607
+ the statement and the region; None when it runs once. Detection only:
1608
+ num_teams(1), num_threads(1) or if(.false.) on the region are not
1609
+ read."""
1610
+ pending: set[str] = set()
1611
+ for k in range(len(ctx) - 1, -1, -1):
1612
+ x = ctx[k]
1613
+ if x.kind in ("if", "ifstmt"):
1614
+ if x.guard == "thread":
1615
+ pending.add("parallel")
1616
+ elif x.guard == "team":
1617
+ pending.add("teams")
1618
+ continue
1619
+ if x.kind not in ("dir", "loopdir"):
1620
+ continue
1621
+ leaves = list(x.dir.leaves)
1622
+ for leaf in reversed(leaves):
1623
+ if leaf in ("teams", "parallel"):
1624
+ if leaf in pending or "nearest" in pending:
1625
+ pending -= {leaf, "nearest"}
1626
+ else:
1627
+ return k, leaf
1628
+ elif leaf in _SHARES and (x.kind == "loopdir" or
1629
+ leaf not in ("do", "for",
1630
+ "distribute")):
1631
+ pending.add(_SHARES[leaf])
1632
+ elif leaf == "loop" and x.kind == "loopdir":
1633
+ bind = [c.arg.strip().lower() for c in x.dir.clause("bind")
1634
+ if c.arg]
1635
+ if bind:
1636
+ if bind[0] in ("teams", "parallel"):
1637
+ pending.add(bind[0])
1638
+ elif "parallel" in leaves:
1639
+ pending.add("parallel")
1640
+ elif "teams" in leaves:
1641
+ pending.add("teams")
1642
+ else:
1643
+ pending.add("nearest")
1644
+ return None
1645
+
1646
+
1647
+ def _written(s: F.Stmt) -> str | None:
1648
+ """The variable an assignment statement writes (its root name)."""
1649
+ low = s.lower
1650
+ if low.startswith((CALL_MARK, END_CALL_MARK, "!else")):
1651
+ return None
1652
+ m = _assignment(s.text)
1653
+ if m is None:
1654
+ return None
1655
+ return re.split(r"[%(]", m[0], maxsplit=1)[0].strip().lower()
1656
+
1657
+
1658
+ def _private_at(ctx: list[Node], start: int, var: str) -> bool:
1659
+ """Whether `var` has a copy per team or thread at or inside ctx[start]:
1660
+ the innermost data-sharing clause naming it there is private-like (a
1661
+ reduction copy is combined back, so it is not), or it is the iteration
1662
+ variable of a loop there."""
1663
+ for x in reversed(ctx[start:]):
1664
+ if x.kind == "do" and x.loop_var == var:
1665
+ return True
1666
+ if x.kind not in ("dir", "loopdir"):
1667
+ continue
1668
+ if x.kind == "loopdir" and var in _assoc_loop_vars(x):
1669
+ return True
1670
+ for c in x.dir.clauses:
1671
+ names = {(_item_name(i) or "").lower() for i in c.items()}
1672
+ if var not in names:
1673
+ continue
1674
+ if c.name in _PRIVATE_CLAUSES:
1675
+ return True
1676
+ if c.name in ("reduction", "in_reduction", "task_reduction"):
1677
+ return False
1678
+ return False
1679
+
1680
+
1681
+ def _redundant_writes(nodes: list[Node], flags: set,
1682
+ ctx: list[Node] | None = None,
1683
+ out: list | None = None) -> list[str]:
1684
+ ctx = [] if ctx is None else ctx
1685
+ out = [] if out is None else out
1686
+ for n in nodes:
1687
+ if n.kind == "code":
1688
+ var = _written(n.stmt)
1689
+ if var is None or var in flags:
1690
+ continue
1691
+ r = _redundant_region(ctx)
1692
+ if r is None or _private_at(ctx, r[0], var):
1693
+ continue
1694
+ k, kind = r
1695
+ out.append(f"line {n.stmt.first + 1}: {var} is written once per "
1696
+ f"{'team' if kind == 'teams' else 'thread'} (the "
1697
+ f"{ctx[k].dir.base} region at line "
1698
+ f"{ctx[k].stmt.first + 1}: no worksharing construct "
1699
+ "or once-only guard around the write)")
1700
+ elif n.kind == "ifstmt":
1701
+ ctx.append(n)
1702
+ _redundant_writes([n.inner], flags, ctx, out)
1703
+ ctx.pop()
1704
+ elif n.kind in ("if", "do", "dir", "loopdir"):
1705
+ ctx.append(n)
1706
+ _redundant_writes(n.children, flags, ctx, out)
1707
+ ctx.pop()
1708
+ elif n.kind == "atomic":
1709
+ _redundant_writes(n.children, flags, ctx, out)
1710
+ return out
1711
+
1712
+
1713
+ def redundant_execution(source: str, version: int = 52) -> list[str]:
1714
+ """Writes to shared or reduction storage that OpenMP execution runs
1715
+ once per team or thread and sequential elision runs once: elision does not apply and the rewrites that change the
1716
+ number of teams or threads (num_teams_*, thread_limit_32, if_false)
1717
+ change the result. The ran_on_host flag variables are left out (every
1718
+ instance writes the same value, and the flag is never compared).
1719
+ Raises F.SourceError when the source is not understood."""
1720
+ an, _, flags, _, tree = _analysed(source, version)
1721
+ return _redundant_writes(tree, set(flags))
1722
+
1723
+
1724
+ class _Runner:
1725
+ """Walks the tree with one _Walk."""
1726
+
1727
+ def __init__(self, an: _Analysis, w: _Walk, check_guards: bool = True,
1728
+ snapshots: list | None = None):
1729
+ self.an, self.w = an, w
1730
+ self.check_guards = check_guards
1731
+ # presence snapshots for the probe: (where, physical line,
1732
+ # {variable: present}) with where "before" or "after"
1733
+ self.snapshots = snapshots
1734
+
1735
+ def snap(self, where: str, line: int, extra: tuple = ()):
1736
+ if self.snapshots is None:
1737
+ return
1738
+ state = {v: c > 0 for v, c in self.w.ref.items()
1739
+ if v not in self.w.flags}
1740
+ for v in extra:
1741
+ if v in self.w.decls and v not in self.w.flags:
1742
+ state.setdefault(v, self.w.ref.get(v, 0) > 0)
1743
+ self.snapshots.append((where, line, state))
1744
+
1745
+ def nodes(self, nodes: list[Node]):
1746
+ for n in nodes:
1747
+ self.node(n)
1748
+
1749
+ def node(self, n: Node):
1750
+ w = self.w
1751
+ if n.kind == "code":
1752
+ w.code(n.stmt)
1753
+ elif n.kind == "ifstmt":
1754
+ for v in F.designators(n.cond):
1755
+ if not v.startswith("omp_"):
1756
+ w.read(v, n.stmt.first + 1)
1757
+ if n.guard and self.check_guards:
1758
+ self.check_guard(n)
1759
+ n.once = w.once_in_loop(n.cond)
1760
+ w.ctx.append(n)
1761
+ self.node(n.inner)
1762
+ w.ctx.pop()
1763
+ elif n.kind == "if":
1764
+ for v in F.designators(n.cond):
1765
+ if not v.startswith("omp_"):
1766
+ w.read(v, n.stmt.first + 1)
1767
+ if n.guard and self.check_guards:
1768
+ self.check_guard(n)
1769
+ w.ctx.append(n)
1770
+ self.nodes(n.children)
1771
+ w.ctx.pop()
1772
+ elif n.kind == "do":
1773
+ line = n.stmt.first + 1
1774
+ for part in (n.lo, n.hi, n.step):
1775
+ for v in F.designators(part):
1776
+ w.read(v, line)
1777
+ if n.loop_var:
1778
+ w.write(n.loop_var, True, line)
1779
+ w.ctx.append(n)
1780
+ self.nodes(n.children)
1781
+ w.ctx.pop()
1782
+ elif n.kind == "atomic":
1783
+ self.nodes(n.children)
1784
+ elif n.kind == "standalone":
1785
+ self.standalone(n)
1786
+ elif n.kind in ("dir", "loopdir"):
1787
+ self.construct(n)
1788
+
1789
+ def check_guard(self, n: Node):
1790
+ """The guarded code must run exactly once (ELISION_RULE 2)."""
1791
+ dirs = [x for x in self.w.ctx if x.kind in ("dir", "loopdir") or
1792
+ (x.kind in ("if", "ifstmt") and x.guard)]
1793
+ line = n.stmt.first + 1
1794
+ if n.guard == "thread":
1795
+ for x in reversed(dirs):
1796
+ if x.kind in ("if", "ifstmt"):
1797
+ continue
1798
+ lv = set(x.dir.leaves)
1799
+ if lv & LOOP_LEAVES:
1800
+ raise _Violation(f"line {line}: thread guard inside a "
1801
+ "loop construct")
1802
+ if "parallel" in lv or "target" in lv:
1803
+ break
1804
+ self._teams_guarded(dirs, line, "thread guard")
1805
+ else:
1806
+ inner = None
1807
+ for x in reversed(dirs):
1808
+ if x.kind in ("if", "ifstmt"):
1809
+ continue
1810
+ lv = set(x.dir.leaves)
1811
+ if "teams" in lv:
1812
+ inner = x
1813
+ break
1814
+ if lv & (LOOP_LEAVES | {"parallel"}):
1815
+ raise _Violation(f"line {line}: team guard inside a "
1816
+ "parallel or loop construct")
1817
+ if inner is None:
1818
+ raise _Violation(f"line {line}: team guard outside teams")
1819
+
1820
+ def _teams_guarded(self, dirs, line, what):
1821
+ """Every enclosing teams region has a team guard inside it."""
1822
+ seen_guard = False
1823
+ for x in reversed(dirs):
1824
+ if x.kind in ("if", "ifstmt") and x.guard == "team":
1825
+ seen_guard = True
1826
+ elif x.kind in ("dir", "loopdir") and "teams" in x.dir.leaves:
1827
+ if not seen_guard:
1828
+ raise _Violation(f"line {line}: {what} runs once per "
1829
+ "team")
1830
+ seen_guard = False
1831
+
1832
+ def _map_items(self, d: F.Dir) -> list:
1833
+ """(path, map type, modifiers, subscripts per part) of the map
1834
+ clauses of `d`."""
1835
+ out = []
1836
+ for c in d.clause("map"):
1837
+ typ, mods, items = _map_parts(c)
1838
+ for it in items:
1839
+ r = _item_ref(it)
1840
+ if r is None:
1841
+ raise _Violation(f"line {d.stmt.first + 1}: map list item "
1842
+ f"{it!r} not understood")
1843
+ out.append((r[0], typ, mods, r[1]))
1844
+ return out
1845
+
1846
+ def _names(self, items: list) -> set:
1847
+ """Leaves of data-sharing list items (variables)."""
1848
+ out = set()
1849
+ for v in items:
1850
+ if self.w.skip(v):
1851
+ continue
1852
+ out |= set(self.w.expand(v))
1853
+ return out
1854
+
1855
+ def standalone(self, n: Node):
1856
+ d, w = n.dir, self.w
1857
+ if d.base == "target enter data":
1858
+ w.map_enter(self._map_items(d))
1859
+ self.snap("after", n.stmt.last)
1860
+ elif d.base == "target exit data":
1861
+ w.map_exit(self._map_items(d))
1862
+ self.snap("after", n.stmt.last)
1863
+ elif d.base == "target update":
1864
+ for c in d.clauses:
1865
+ if c.name in ("to", "from"):
1866
+ for it in c.items():
1867
+ r = _item_ref(it)
1868
+ if r is None:
1869
+ raise _Violation(f"line {n.stmt.first + 1}: "
1870
+ f"update item {it!r}")
1871
+ w.update(r[0], r[1], c.name == "to")
1872
+
1873
+ def construct(self, n: Node):
1874
+ d, w = n.dir, self.w
1875
+ line = n.stmt.first + 1
1876
+ leaves = d.leaves
1877
+ own = tuple(w.root(p) for p, _, _, _ in self._map_items(d))
1878
+ if d.base == "target data":
1879
+ maps = self._map_items(d)
1880
+ self.snap("before", n.stmt.first, own)
1881
+ w.map_enter(maps)
1882
+ self.snap("after", n.stmt.last)
1883
+ w.ctx.append(n)
1884
+ self.nodes(n.children)
1885
+ w.ctx.pop()
1886
+ w.map_exit(maps)
1887
+ self.snap("after", n.end_stmt.last)
1888
+ return
1889
+ if d.base in ("single", "masked", "master") and self.check_guards:
1890
+ dirs = [x for x in w.ctx if x.kind in ("dir", "loopdir") or
1891
+ (x.kind in ("if", "ifstmt") and x.guard)]
1892
+ self._teams_guarded(dirs, line, d.base)
1893
+ exits = None
1894
+ if "target" in leaves:
1895
+ self.snap("before", n.stmt.first, own)
1896
+ exits = self.target_entry(n)
1897
+ # data-sharing of the other leaves
1898
+ inner = _Frame({}, "construct")
1899
+ priv, fpriv, lpriv, reds = set(), set(), {}, set()
1900
+ is_target = "target" in leaves
1901
+ for c in d.clauses:
1902
+ items = [x for x in (_item_name(i) for i in c.items()) if x]
1903
+ if c.name == "private":
1904
+ priv |= set(items)
1905
+ elif c.name == "firstprivate" and (not is_target or len(leaves)
1906
+ > 1):
1907
+ fpriv |= set(items)
1908
+ elif c.name == "lastprivate":
1909
+ cond = "conditional" in c.modifiers()
1910
+ for v in items:
1911
+ lpriv[v] = cond
1912
+ elif c.name in ("reduction", "in_reduction"):
1913
+ if c.name == "in_reduction" and is_target and \
1914
+ len(leaves) == 1:
1915
+ reds |= set(items)
1916
+ continue
1917
+ reds |= set(items)
1918
+ if n.kind == "loopdir":
1919
+ priv |= set(_assoc_loop_vars(n))
1920
+ # implicit: variables private in an enclosing frame become
1921
+ # firstprivate in a task
1922
+ if "task" in leaves or "taskloop" in leaves:
1923
+ for v in _refs(n.children):
1924
+ if w.skip(v) or w.root(v) in w.absent:
1925
+ continue
1926
+ for leaf in w.expand(v):
1927
+ for f in reversed(w.frames):
1928
+ if leaf in f.copies:
1929
+ fpriv.add(leaf)
1930
+ break
1931
+ priv, fpriv, reds = (self._names(priv), self._names(fpriv),
1932
+ self._names(reds))
1933
+ lpriv = {leaf: cond for v, cond in lpriv.items()
1934
+ for leaf in self._names([v])}
1935
+ for v in sorted(reds):
1936
+ # nested in a reduction region of the same item, the original
1937
+ # is the enclosing region's private copy (combined there)
1938
+ if not w._red_var(v) and w.check_reads:
1939
+ if w.seen(v) != w.elis[v]:
1940
+ raise _Violation(f"line {line}: the original value of "
1941
+ f"reduction item {v} differs from "
1942
+ "sequential execution")
1943
+ for v in priv | set(lpriv) | fpriv | reds:
1944
+ if v in reds:
1945
+ inner.copies[v] = w.V.new()
1946
+ elif v in fpriv or lpriv.get(v):
1947
+ inner.copies[v] = w.seen(v)
1948
+ else:
1949
+ inner.copies[v] = w.V.new() # undefined
1950
+ inner.reductions = frozenset(v for v in reds if v in inner.copies)
1951
+ w.frames.append(inner)
1952
+ w.ctx.append(n)
1953
+ self.nodes(n.children)
1954
+ w.ctx.pop()
1955
+ w.frames.pop()
1956
+ for v in lpriv:
1957
+ if v in inner.copies:
1958
+ w._set(v, inner.copies[v])
1959
+ for v in inner.reductions:
1960
+ w._set(v, w.elis[v])
1961
+ if exits is not None:
1962
+ self.target_exit(exits)
1963
+ self.snap("after", n.end_stmt.last)
1964
+
1965
+ def target_entry(self, n: Node) -> tuple:
1966
+ """Maps and the firstprivate/private copies of a target construct
1967
+ (explicit, implied by the combined-construct rules, implicit);
1968
+ returns the map exits. A structure is mapped implicitly as a whole
1969
+ when no part of it appears in a clause (5.2 §5.8.1: a reference
1970
+ to a structure element is a reference to the structure; aggregate
1971
+ category); when parts of it are mapped, every referenced part must
1972
+ be (5.2 §13.8, a restriction on the program). A pointer referenced
1973
+ in the region must be a list item of a map clause of the construct
1974
+ (``_Walk._device_pointer``); an allocatable scalar must have an
1975
+ explicit clause (whether 5.2's scalar category holds it is not
1976
+ settled)."""
1977
+ d, w = n.dir, self.w
1978
+ line = n.stmt.first + 1
1979
+ maps = self._map_items(d)
1980
+ mapped_paths = [p for p, _, _, _ in maps]
1981
+ mapped = {p for p in mapped_paths if "%" not in p}
1982
+ fp, pv, inred = set(), set(), set()
1983
+ multi = len(d.leaves) > 1
1984
+ lp_items = set()
1985
+ shared_names = set()
1986
+ for c in d.clauses:
1987
+ items = [x for x in (_item_name(i) for i in c.items()) if x]
1988
+ if c.name == "firstprivate":
1989
+ fp |= set(items) - mapped
1990
+ elif c.name == "private" and not multi:
1991
+ pv |= set(items)
1992
+ elif c.name == "in_reduction":
1993
+ inred |= set(items)
1994
+ elif c.name in ("lastprivate", "reduction") and multi:
1995
+ lp_items |= set(items)
1996
+ elif c.name == "shared":
1997
+ shared_names |= set(items)
1998
+ for v in sorted(lp_items - mapped):
1999
+ maps.append((v, "tofrom", [], [None])) # 5.2 §17.2
2000
+ mapped_paths.append(v)
2001
+ named = fp | pv | inred | lp_items
2002
+ if multi:
2003
+ # private on a combined construct goes to the innermost leaf;
2004
+ # the target region still references the variable
2005
+ for c in d.clause("private"):
2006
+ named |= {x for x in (_item_name(i) for i in c.items())
2007
+ if x}
2008
+ explicit_roots = named | {w.root(p) for p in mapped_paths}
2009
+ dm: dict[str, str] = {}
2010
+ for c in d.clause("defaultmap"):
2011
+ beh = (c.arg or "").split(":")[0].strip().lower()
2012
+ cat = (c.arg or "").split(":")[1].strip().lower() if ":" in (
2013
+ c.arg or "") else "all"
2014
+ dm[cat] = beh
2015
+ refs = set(_refs(n.children, sharing=False))
2016
+ # variables named only in data-sharing clauses of nested
2017
+ # constructs: referenced (implicitly mapped) unless a part of them
2018
+ # is in a clause of this construct
2019
+ named_inside = {w.root(p) for p in _refs(n.children)} - \
2020
+ {w.root(p) for p in refs}
2021
+ for c in d.clauses:
2022
+ if c.name == "shared":
2023
+ named_inside |= {w.root(p) for p in
2024
+ F.designators(c.arg or "")}
2025
+ elif c.name not in SHARING_CLAUSES + ("map", "defaultmap",
2026
+ "nowait"):
2027
+ refs |= set(F.designators(c.arg or ""))
2028
+ named_inside -= {w.root(p) for p in refs}
2029
+ implicit = {r for r in named_inside if r in w.decls and
2030
+ not w.decls[r].parameter and not w.skip(r) and
2031
+ r not in explicit_roots and r not in w.absent and
2032
+ not w.decls[r].pointer}
2033
+ for p in sorted(refs):
2034
+ r = w.root(p)
2035
+ if r not in w.decls or w.decls[r].parameter or w.skip(p):
2036
+ continue
2037
+ if r in w.absent:
2038
+ raise _Violation(f"line {line}: absent optional dummy "
2039
+ f"argument {r} referenced in a target "
2040
+ "region")
2041
+ if w.is_ptr(p):
2042
+ continue # checked where it is referenced
2043
+ if r in explicit_roots:
2044
+ if r in named or r in mapped or any(
2045
+ p == q or p.startswith(q + "%")
2046
+ for q in mapped_paths):
2047
+ continue
2048
+ raise _Violation(f"line {line}: {p} is referenced but only "
2049
+ f"other parts of {r} are mapped (5.2 §13.8)")
2050
+ implicit.add(r)
2051
+ for v in sorted(implicit):
2052
+ var = w.decls[v]
2053
+ if var.pointer:
2054
+ raise _Violation(f"line {line}: pointer {v} is referenced "
2055
+ "without a map clause")
2056
+ struct = v in w.under
2057
+ if var.rank == 0 and var.allocatable:
2058
+ raise _Violation(f"line {line}: allocatable scalar {v} "
2059
+ "without an explicit data-sharing or map "
2060
+ "clause")
2061
+ if var.rank == 0 and not struct:
2062
+ cats = ["scalar", "all"]
2063
+ else:
2064
+ cats = ["aggregate", "all"] + (["allocatable"]
2065
+ if var.allocatable else [])
2066
+ behs = {dm[c] for c in cats if c in dm and dm[c] != "default"}
2067
+ if len(behs) > 1:
2068
+ raise _Violation(f"line {line}: defaultmap categories give "
2069
+ f"{v} different behaviours")
2070
+ beh = behs.pop() if behs else "default"
2071
+ if beh == "default":
2072
+ beh = "firstprivate" if cats[0] == "scalar" else "tofrom"
2073
+ if beh == "firstprivate":
2074
+ fp.add(v)
2075
+ elif beh == "none":
2076
+ raise _Violation(f"line {line}: defaultmap(none) and "
2077
+ f"{v} has no explicit data-sharing")
2078
+ else:
2079
+ maps.append((v, "alloc" if beh == "storage" else beh, [],
2080
+ [None]))
2081
+ ptrs = {p for p in mapped_paths if w.is_ptr(p)}
2082
+ holders = {p: (p if "%" not in p else w.root(p)) for p in ptrs}
2083
+ ptr_before = {p: w.ptr_ref.get(p, 0) for p in ptrs}
2084
+ before = w.map_enter(maps)
2085
+ attached = set()
2086
+ for p in ptrs:
2087
+ tgt = w.root(w.canon(p))
2088
+ h = holders[p]
2089
+ new_ptr = ptr_before[p] == 0 if h == p else \
2090
+ before.get(h, w.ref.get(h, 0)) == 0
2091
+ if before.get(tgt, 0) == 0 or new_ptr:
2092
+ attached.add(p)
2093
+ w.ptr_ref[p] = w.ptr_ref.get(p, 0) + 1
2094
+ frame = _Frame({}, "target", attached=frozenset(attached))
2095
+ for v in self._names(fp):
2096
+ if v in w.dev and w.dev[v] != w.host[v] and w.check_reads:
2097
+ raise _Violation(f"line {line}: firstprivate {v} has "
2098
+ "host and device copies that differ")
2099
+ frame.copies[v] = w.host[v]
2100
+ for v in self._names(pv):
2101
+ frame.copies[v] = w.V.new()
2102
+ for v in self._names(inred):
2103
+ if w.host[v] != w.elis[v] and w.check_reads:
2104
+ raise _Violation(f"line {line}: in_reduction item {v}: "
2105
+ "the original differs from sequential")
2106
+ frame.copies[v] = w.V.new()
2107
+ frame.reductions = frozenset(self._names(inred) & set(frame.copies))
2108
+ w.frames.append(frame)
2109
+ return maps, frame, ptrs
2110
+
2111
+ def target_exit(self, exits):
2112
+ w = self.w
2113
+ maps, frame, ptrs = exits
2114
+ # the target frame is below the construct frame, already popped
2115
+ w.frames.pop()
2116
+ for v in frame.reductions:
2117
+ w.host[v] = w.elis[v]
2118
+ w.map_exit(maps)
2119
+ for p in ptrs:
2120
+ w.ptr_ref[p] -= 1
2121
+
2122
+
2123
+ def _analysed(source: str, version: int):
2124
+ """(analysis, exclusions, flags, devvars, tree), or SourceError."""
2125
+ an = _Analysis(source, version)
2126
+ why, flags, devvars = an.static_exclusions()
2127
+ tree = build_tree(an.body, 0, len(an.body), version)
2128
+ return an, why, flags, devvars, tree
2129
+
2130
+
2131
+ def elision_applicability(source: str, version: int = 52) -> Applicability:
2132
+ """Whether sequential elision gives the OpenMP result of `source`
2133
+ (ELISION_RULE), with the reasons when it does not."""
2134
+ try:
2135
+ an, why, flags, devvars, tree = _analysed(source, version)
2136
+ except F.SourceError as e:
2137
+ return Applicability(False, [f"source not understood: {e}"])
2138
+ if why:
2139
+ return Applicability(False, why, frozenset(flags))
2140
+ red = _redundant_writes(tree, set(flags))
2141
+ if red:
2142
+ return Applicability(False, red, frozenset(flags))
2143
+ reasons = []
2144
+ for refcounts in (True, False):
2145
+ w = _Walk(an, flags, devvars, refcounts)
2146
+ try:
2147
+ _Runner(an, w).nodes(tree)
2148
+ except _Violation as e:
2149
+ label = "copy semantics" if refcounts else \
2150
+ "copies ignoring reference counts"
2151
+ reasons.append(f"{e} [{label}]")
2152
+ except F.SourceError as e:
2153
+ reasons.append(f"source not understood: {e}")
2154
+ return Applicability(not reasons, reasons, frozenset(flags))
2155
+
2156
+
2157
+ # Exclusions that concern the device data environment (the precondition
2158
+ # of the host-execution rewrite, offload.engine.transforms if_false, as well as
2159
+ # of elision)
2160
+ DATA_REASONS = ("module variables", "declare target variable",
2161
+ "present modifier", "iterator or mapper", "6.0 map modifier",
2162
+ "defaultmap(", "device(ancestor)", "device pointers",
2163
+ "device addresses", "requires directive", "declare mapper",
2164
+ "runtime routine omp_target", "runtime routine "
2165
+ "omp_get_mapped", "conditional compilation",
2166
+ "source not understood")
2167
+
2168
+
2169
+ def copy_independent(source: str, version: int = 52) -> Applicability:
2170
+ """Whether the result of `source` is the same with separate host and
2171
+ device copies (reference counts or unconditional copies) as with one
2172
+ shared copy: the data-environment half of ELISION_RULE (rule 3), without
2173
+ the rules about runtime routines, guards and reduction order. The
2174
+ precondition of rewrites that move execution or data motion
2175
+ (offload.engine.transforms)."""
2176
+ try:
2177
+ an, why, flags, devvars, tree = _analysed(source, version)
2178
+ except F.SourceError as e:
2179
+ return Applicability(False, [f"source not understood: {e}"])
2180
+ why = [r for r in why if r.startswith(DATA_REASONS)]
2181
+ if why:
2182
+ return Applicability(False, why, frozenset(flags))
2183
+ reasons = []
2184
+ for refcounts in (True, False):
2185
+ w = _Walk(an, flags, devvars, refcounts)
2186
+ try:
2187
+ _Runner(an, w, check_guards=False).nodes(tree)
2188
+ except _Violation as e:
2189
+ reasons.append(f"{e} [{'copy semantics' if refcounts else 'copies ignoring reference counts'}]")
2190
+ except F.SourceError as e:
2191
+ reasons.append(f"source not understood: {e}")
2192
+ return Applicability(not reasons, reasons, frozenset(flags))
2193
+
2194
+
2195
+ def presence_snapshots(source: str, version: int = 52) -> list[tuple]:
2196
+ """The presence of every mapped variable the reference-count rules fix
2197
+ (OpenMP 5.2 §5.8.3, §13.6-13.8) at the host points around each data
2198
+ construct and target construct: (where, physical line, {var: present})
2199
+ with where "before" (insert before that line) or "after". Raises
2200
+ SourceError for a source the model does not handle."""
2201
+ an, _, flags, devvars, tree = _analysed(source, version)
2202
+ snaps: list = []
2203
+ w = _Walk(an, flags, devvars, True, check_reads=False)
2204
+ _Runner(an, w, check_guards=False, snapshots=snaps).nodes(tree)
2205
+ return snaps
2206
+
2207
+
2208
+ # --- running the elided program ---------------------------------------------------
2209
+
2210
+ GFORTRAN = shutil.which("gfortran") or "/usr/tce/bin/gfortran"
2211
+ AMDFLANG = f"{AFAR_BIN}/amdflang"
2212
+ # amdflang's runtime links libquadmath from the system GCC (the AFAR
2213
+ # Fortran recipe passes the same -L)
2214
+ AMDFLANG_LINK = "-L/usr/lib/gcc/x86_64-redhat-linux/8"
2215
+ ELISION_COMPILERS = ("gfortran", "amdflang")
2216
+ ELISION_TIMEOUT_S = 60
2217
+
2218
+ ELISION_OMP_LIB = """\
2219
+ ! Sequential omp_lib for sequential elision (offload.engine.expected): the
2220
+ ! values of the runtime routines for one initial thread on the host with
2221
+ ! no devices (OpenMP 5.2 §18: the initial device is the host; a team of
2222
+ ! one thread, one team; omp_get_num_devices() = 0 and the initial device
2223
+ ! number equals it). Built without -fopenmp; the programs' directives are
2224
+ ! comments.
2225
+ module omp_lib
2226
+ use, intrinsic :: iso_c_binding, only: c_ptr, c_int
2227
+ implicit none
2228
+ integer, parameter :: omp_allocator_handle_kind = 8
2229
+ integer, parameter :: omp_memspace_handle_kind = 8
2230
+ integer, parameter :: omp_alloctrait_key_kind = 8
2231
+ integer, parameter :: omp_alloctrait_val_kind = 8
2232
+ integer, parameter :: omp_depend_kind = 16
2233
+ integer, parameter :: omp_impex_kind = 4
2234
+ type omp_alloctrait
2235
+ integer(omp_alloctrait_key_kind) :: key
2236
+ integer(omp_alloctrait_val_kind) :: value
2237
+ end type omp_alloctrait
2238
+ integer(omp_alloctrait_key_kind), parameter :: omp_atk_alignment = 3
2239
+ integer(omp_allocator_handle_kind), parameter :: omp_default_mem_alloc = 1
2240
+ integer(omp_memspace_handle_kind), parameter :: omp_default_mem_space = 0
2241
+ integer(omp_impex_kind), parameter :: omp_not_impex = 0, omp_import = 1, &
2242
+ omp_export = 2, omp_impex = 3
2243
+ contains
2244
+ logical function omp_is_initial_device()
2245
+ omp_is_initial_device = .true.
2246
+ end function omp_is_initial_device
2247
+ integer function omp_get_team_num()
2248
+ omp_get_team_num = 0
2249
+ end function omp_get_team_num
2250
+ integer function omp_get_thread_num()
2251
+ omp_get_thread_num = 0
2252
+ end function omp_get_thread_num
2253
+ integer function omp_get_num_threads()
2254
+ omp_get_num_threads = 1
2255
+ end function omp_get_num_threads
2256
+ integer function omp_get_num_teams()
2257
+ omp_get_num_teams = 1
2258
+ end function omp_get_num_teams
2259
+ integer function omp_get_num_devices()
2260
+ omp_get_num_devices = 0
2261
+ end function omp_get_num_devices
2262
+ integer function omp_get_default_device()
2263
+ omp_get_default_device = 0
2264
+ end function omp_get_default_device
2265
+ integer function omp_get_device_num()
2266
+ omp_get_device_num = 0
2267
+ end function omp_get_device_num
2268
+ end module omp_lib
2269
+ """
2270
+
2271
+
2272
+ _ELISION_LIBS: dict = {}
2273
+
2274
+
2275
+ def _elision_lib(compiler: str) -> tuple[str, str]:
2276
+ """(module dir, object) of ELISION_OMP_LIB built by `compiler`; rebuilt if its directory was removed."""
2277
+ have = _ELISION_LIBS.get(compiler)
2278
+ if have and Path(have[1]).is_file():
2279
+ return have
2280
+ _ELISION_LIBS[compiler] = _build_elision_lib(compiler)
2281
+ return _ELISION_LIBS[compiler]
2282
+
2283
+
2284
+ def _build_elision_lib(compiler: str) -> tuple[str, str]:
2285
+ d = tempfile.mkdtemp(prefix=f"elision_{compiler}_")
2286
+ atexit.register(shutil.rmtree, d, True)
2287
+ src = Path(d) / "omp_lib_elision.f90"
2288
+ src.write_text(ELISION_OMP_LIB)
2289
+ obj = str(Path(d) / "omp_lib_elision.o")
2290
+ if compiler == "gfortran":
2291
+ cmd = [GFORTRAN, "-c", str(src), "-o", obj, f"-J{d}"]
2292
+ else:
2293
+ cmd = [AMDFLANG, "-c", str(src), "-o", obj, "-module-dir", d]
2294
+ subprocess.run(cmd, cwd=d, check=True, capture_output=True, timeout=120)
2295
+ return d, obj
2296
+
2297
+
2298
+ def run_elided(source: str, compiler: str, extra: tuple = (),
2299
+ env: dict | None = None, value=None
2300
+ ) -> tuple[str | None, str]:
2301
+ """(value or None, note) of the source built by `compiler` without
2302
+ OpenMP against ELISION_OMP_LIB, run on the host. `extra`: more
2303
+ compiler flags (fuzz_min's range guard: -ftrapv); `env`: more
2304
+ environment variables of the run (the fuzz definedness check:
2305
+ MALLOC_PERTURB_); `value`: how the output gives the value (default
2306
+ ``value_of``: one RESULT line; passes its own for a finding's
2307
+ free-form output)."""
2308
+ mdir, obj = _elision_lib(compiler)
2309
+ with tempfile.TemporaryDirectory(prefix="elided_") as d:
2310
+ src = Path(d) / "p.f90"
2311
+ src.write_text(source)
2312
+ exe = str(Path(d) / "p.out")
2313
+ if compiler == "gfortran":
2314
+ cmd = [GFORTRAN, "-O0", *extra, f"-I{mdir}", "-J", d, str(src),
2315
+ obj, "-o", exe]
2316
+ else:
2317
+ cmd = [AMDFLANG, "-O0", *extra, "-I", mdir, "-module-dir", d,
2318
+ str(src), obj, AMDFLANG_LINK, "-o", exe]
2319
+ b = subprocess.run(cmd, cwd=d, capture_output=True, text=True,
2320
+ timeout=300)
2321
+ if b.returncode != 0:
2322
+ err = [ln for ln in (b.stderr + b.stdout).splitlines()
2323
+ if "rror" in ln]
2324
+ return None, f"{compiler} build failed: " + (
2325
+ err[0] if err else f"rc={b.returncode}")[:200]
2326
+ try:
2327
+ # no core dumps, set by the shell: a preexec_fn runs Python in
2328
+ # the forked child, which can deadlock in a threaded parent
2329
+ # (it hung the test suite)
2330
+ r = subprocess.run(["bash", "-c", 'ulimit -c 0; exec "$0"', exe],
2331
+ cwd=d, capture_output=True, text=True,
2332
+ timeout=ELISION_TIMEOUT_S,
2333
+ env={"PATH": "/usr/bin:/bin", **(env or {})})
2334
+ except subprocess.TimeoutExpired:
2335
+ return None, f"{compiler} run timed out"
2336
+ if r.returncode != 0:
2337
+ return None, f"{compiler} run failed: rc={r.returncode}"
2338
+ v = (value or value_of)(r.stdout)
2339
+ if v is None:
2340
+ return None, f"{compiler}: no single RESULT line"
2341
+ return v, ""
2342
+
2343
+
2344
+ def elided_source(source: str) -> str:
2345
+ """The program sequential elision builds, as a reader sees it.
2346
+ run_elided compiles `source` unchanged without OpenMP, where every
2347
+ line whose first non-blank characters are the sentinel ``!$``
2348
+ (directives, their continuations, conditional compilation) is a
2349
+ comment; this leaves those lines out and keeps every other line as
2350
+ it is. The build also links ELISION_OMP_LIB."""
2351
+ return "".join(ln for ln in source.splitlines(keepends=True)
2352
+ if not ln.lstrip().startswith("!$"))
2353
+
2354
+
2355
+ def elision_evidence(source: str, version: int = 52,
2356
+ compilers=ELISION_COMPILERS,
2357
+ applicability: Applicability | None = None,
2358
+ value=None) -> Evidence:
2359
+ """Sequential elision of `source`: inapplicable with the reasons, or
2360
+ the value every compiler prints (conflict if they differ; no value
2361
+ unless every build printed one)."""
2362
+ ap = applicability or elision_applicability(source, version)
2363
+ if not ap.ok:
2364
+ return Evidence("elision", "inapplicable", detail=ap.why)
2365
+ obs, notes = [], []
2366
+ for c in compilers:
2367
+ v, note = run_elided(source, c) if value is None else \
2368
+ run_elided(source, c, value=value)
2369
+ obs.append((c, v))
2370
+ if note:
2371
+ notes.append(note)
2372
+ vals = sorted({v for _, v in obs if v is not None})
2373
+ detail = "; ".join(notes)
2374
+ if len(vals) == 1 and all(v is not None for _, v in obs):
2375
+ return Evidence("elision", "value", vals[0], detail, tuple(obs))
2376
+ if len(vals) > 1:
2377
+ return Evidence("elision", "conflict", None,
2378
+ "the elided builds disagree" +
2379
+ (f"; {detail}" if detail else ""), tuple(obs))
2380
+ return Evidence("elision", "no_value", None, detail or "no value",
2381
+ tuple(obs))
2382
+
2383
+
2384
+ # --- legality of rewritten programs ----------------------------------------------
2385
+
2386
+ def variant_legality(source: str, version: int, kind: str) -> str | None:
2387
+ """None if a rewritten program is legal, else the first failure. A
2388
+ fuzz program (kind "fuzz") gets the fuzzer's three checks
2389
+ (offload.engine.fuzz.check: rules, gfortran, AFAR flang's frontend); a
2390
+ catalog program the rules at its version and gfortran at its std
2391
+ level (the generator's own checks of its programs)."""
2392
+ from offload.engine import fuzz
2393
+ if kind == "fuzz":
2394
+ # a rewrite may run on the host (if(.false.)): it is a reference,
2395
+ # not an offload test, so the device-offload scope check
2396
+ # does not apply
2397
+ res = fuzz.check(source, scope=False)
2398
+ return None if res.ok else f"{res.stage}: {res.detail}"[:300]
2399
+ from offload.engine.rules import ProgramError, parse_program, violations
2400
+ try:
2401
+ bad = violations(parse_program(source, version), version)
2402
+ except ProgramError as e:
2403
+ return f"rules: {e}"[:300]
2404
+ if bad:
2405
+ return f"rules: {', '.join(bad)}"
2406
+ std = re.search(r"^! std: (\S+)$", source, re.M)
2407
+ err = _gfortran_check(source, std.group(1) if std else "f2008")
2408
+ return f"gfortran: {err}"[:300] if err else None
2409
+
2410
+
2411
+ def _gfortran_check(source: str, std: str) -> str | None:
2412
+ from offload.engine.fuzz import _stub_dir
2413
+ with tempfile.TemporaryDirectory(prefix="expected_gf_") as d:
2414
+ (Path(d) / "t.f90").write_text(source)
2415
+ r = subprocess.run([GFORTRAN, f"-std={std}", "-fsyntax-only",
2416
+ f"-I{_stub_dir()}", "-J", d, "t.f90"], cwd=d,
2417
+ capture_output=True, text=True, timeout=120)
2418
+ if r.returncode == 0:
2419
+ return None
2420
+ lines = [ln for ln in r.stderr.splitlines() if "Error" in ln]
2421
+ return lines[0] if lines else r.stderr.strip()[:200]
2422
+
2423
+
2424
+ # --- running a program or variant on a toolchain ------------------------------------
2425
+
2426
+ RUN_TIMEOUT_S = 120
2427
+ COMPILE_TIMEOUT_S = 600
2428
+
2429
+
2430
+ @dataclass
2431
+ class ToolRun:
2432
+ """One compile and run of a (rewritten) program on a toolchain under
2433
+ HSA_XNACK=`xnack` and OMP_TARGET_OFFLOAD=mandatory. `value`: the
2434
+ compared value, "error_termination" (the toolchain's runtime error
2435
+ message, offload.engine.harness.ERROR_TERMINATION), or None (`why`).
2436
+ `probe`: whether every PROBE line said pass (None: no PROBE line)."""
2437
+ toolchain: str
2438
+ variant: str
2439
+ value: str | None
2440
+ ran_on_host: int | None = None
2441
+ probe: bool | None = None
2442
+ why: str = ""
2443
+ xnack: int = 1
2444
+
2445
+ def to_dict(self) -> dict:
2446
+ return dataclasses.asdict(self)
2447
+
2448
+
2449
+ def terminated(toolchain: str, source: str, text: str) -> bool:
2450
+ """Whether a run's output (`text`) shows the negative probe's error
2451
+ termination on `toolchain`: the harness's check
2452
+ (``harness.probe_termination``: the toolchain family's message and, for
2453
+ CCE, a present-table miss at a directive of `source` that carries the
2454
+ present modifier), so the matrix and this module read a probe run
2455
+ alike."""
2456
+ from offload.engine import harness
2457
+ return harness.probe_termination(toolchain, "f90", source,
2458
+ text) is not None
2459
+
2460
+
2461
+ def run_on(toolchain: str, source: str, version: int, kind: str,
2462
+ variant: str = "original", xnack: int = 1,
2463
+ timeout_s: int = RUN_TIMEOUT_S) -> ToolRun:
2464
+ """Compile `source` with the toolchain's recipe (a catalog program
2465
+ with the matrix's version flags, a fuzz program as the campaign) and
2466
+ run it once."""
2467
+ from offload.engine import harness
2468
+ from offload.engine.run_matrix import for_version
2469
+ from offload.engine.toolchains import TOOLCHAINS
2470
+ from offload.engine.transforms import PROBE_LINE
2471
+ tc = TOOLCHAINS[toolchain]
2472
+ if kind != "fuzz":
2473
+ tc = for_version(tc, version)
2474
+ tc = tc.for_mode(xnack) # NVHPC builds per memory mode
2475
+ with tempfile.TemporaryDirectory(prefix="expected_run_") as d:
2476
+ tmp = Path(d)
2477
+ src, exe = tmp / "p.f90", tmp / "p.out"
2478
+ src.write_text(source)
2479
+ b = harness._execute(harness.compile_script(tc, str(src), str(exe),
2480
+ "-O2", str(tmp)),
2481
+ tmp, COMPILE_TIMEOUT_S, merge=True)
2482
+ if b.timed_out:
2483
+ return ToolRun(toolchain, variant, None, why="compile timeout",
2484
+ xnack=xnack)
2485
+ if b.rc != 0 or not exe.is_file():
2486
+ found = harness.classify_message(tc.name, b.stdout,
2487
+ harness.CATEGORY_ORDER[:-1],
2488
+ "c" if tc.lang == "c" else
2489
+ "f90")
2490
+ why = f"{found[0]}: {found[1]}" if found else \
2491
+ f"compile failed: rc={b.rc}"
2492
+ return ToolRun(toolchain, variant, None,
2493
+ why=why.replace(str(tmp), "<tmpdir>")[:300],
2494
+ xnack=xnack)
2495
+ ex = harness._execute(harness.run_script(tc, str(exe), xnack, True,
2496
+ str(tmp)), tmp, timeout_s)
2497
+ out = ex.stdout + "\n" + ex.stderr
2498
+ probes = PROBE_LINE.findall(ex.stdout)
2499
+ probe = None if not probes else all(p == "pass" for p in probes)
2500
+ if ex.timed_out:
2501
+ return ToolRun(toolchain, variant, None, probe=probe, why="timeout",
2502
+ xnack=xnack)
2503
+ v = value_of(ex.stdout) if ex.rc == 0 else None
2504
+ host = None
2505
+ m = re.search(r"ran_on_host=([01])", ex.stdout)
2506
+ if m:
2507
+ host = int(m.group(1))
2508
+ if v is not None:
2509
+ return ToolRun(toolchain, variant, v, host, probe, xnack=xnack)
2510
+ line = harness.probe_termination(toolchain, "f90", source, out)
2511
+ if line is not None:
2512
+ return ToolRun(toolchain, variant, "error_termination", host,
2513
+ probe, why=f"error termination ({line.strip()[:60]})",
2514
+ xnack=xnack)
2515
+ return ToolRun(toolchain, variant, None, host, probe,
2516
+ why=f"run failed: rc={ex.rc}", xnack=xnack)
2517
+
2518
+
2519
+ # --- one program's evidence ---------------------------------------------------------
2520
+
2521
+ def assess(model: Evidence, elision: Evidence, runs: list[ToolRun],
2522
+ probe_na: str | None = None,
2523
+ model_partners: tuple = MODEL_PARTNERS,
2524
+ derivation: Evidence | None = None
2525
+ ) -> tuple[list[Evidence], Verdict]:
2526
+ """The evidence of a program from its model value, its elision and
2527
+ its runs (variant "original", the rewrites, "probe"), and the
2528
+ verdict. Metamorphic uses the original and the rewrites; consensus
2529
+ the original; probe the probe runs (state verified: every PROBE line
2530
+ passed, and the run reached the device or ended in error
2531
+ termination, by a toolchain that does not violate the metamorphic
2532
+ relation). Every run must be in one memory mode (HSA_XNACK): a
2533
+ copy-mode run is no rewrite of a zero-copy run."""
2534
+ modes = {r.xnack for r in runs}
2535
+ if len(modes) > 1:
2536
+ raise ValueError(f"runs of more than one memory mode (HSA_XNACK "
2537
+ f"{sorted(modes)}): assess each mode separately")
2538
+ meta = [(r.toolchain, r.variant, r.value) for r in runs
2539
+ if r.variant != "probe"]
2540
+ has_variant = any(r.variant not in ("original", "probe") for r in runs)
2541
+ ev = [model, elision]
2542
+ ev.append(metamorphic_evidence(meta) if has_variant else
2543
+ Evidence("metamorphic", "inapplicable",
2544
+ detail="no rewrite applies"))
2545
+ # a toolchain the rewrites show inconsistent (it is wrong on at least
2546
+ # one of them) is no probe witness, however its state checks went
2547
+ unstable = {tc for tc in {r.toolchain for r in runs}
2548
+ if len({r.value for r in runs if r.toolchain == tc and
2549
+ r.variant != "probe" and r.value is not None}) > 1}
2550
+ pr = [(r.toolchain,
2551
+ bool(r.probe) and r.toolchain not in unstable and
2552
+ (r.ran_on_host == 0 or r.value == "error_termination"),
2553
+ r.value) for r in runs if r.variant == "probe"]
2554
+ ev.append(probe_evidence(pr, probe_na if not pr else None))
2555
+ ev.append(consensus_evidence([(r.toolchain, r.value) for r in runs
2556
+ if r.variant == "original"]))
2557
+ if derivation is not None:
2558
+ ev.append(derivation)
2559
+ return ev, decide(ev, model_partners)