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,1933 @@
1
+ """Oracle mutation audit: does a program's check depend on its atom?
2
+
3
+ ``neutralise(prog)`` builds the mutant: the program with the construct,
4
+ clause or modifier named by ``prog.primary_atom`` removed, under the id
5
+ ``<id>-mutant``. ``audit`` runs the original and the mutant on each
6
+ toolchain in copy mode (``HSA_XNACK=0``; 1 for programs needing zero-copy)
7
+ through ``harness.run_program`` with ``repeats=1``. ``summarise`` gives a
8
+ per-atom verdict.
9
+ Directives are commented out with their end directive and clauses are
10
+ deleted. For modifiers, the removal that still parses and loses the fewest
11
+ other atoms is used. ``REPLACEMENTS`` lists forms swapped for a weaker one,
12
+ ``NO_MUTATION`` the atoms without a mutant. Probe and detector code
13
+ (``INFRA_VARS``) is never mutated. OpenACC and C programs use the same rules.
14
+ ``MutantResult.status`` is ``killed``, ``survived``, ``inconclusive`` or
15
+ ``not_applicable``; a mutant that breaks a rule is not run."""
16
+ from __future__ import annotations
17
+
18
+ import dataclasses
19
+ import re
20
+ from concurrent.futures import ThreadPoolExecutor
21
+ from dataclasses import dataclass
22
+ from functools import lru_cache
23
+ from typing import Callable, Sequence
24
+
25
+ from offload.engine import grammar as G
26
+ from offload.engine.catalog import (base_name, default_grammar, default_skeleton,
27
+ directive_atom, model_of, slice_atoms)
28
+ from offload.engine.gen import Program, generate
29
+ from offload.engine.grammar import GrammarError, Directive, parse_directive
30
+ from offload.engine.harness import Outcome, Result, run_program
31
+ from offload.engine.rules import ProgramError, parse_program
32
+ from offload.engine.run_matrix import for_version
33
+ from offload.engine.toolchains import TOOLCHAINS
34
+
35
+
36
+ class MutationError(Exception):
37
+ """A program whose primary atom cannot be removed as the rules say."""
38
+
39
+
40
+ # --- atoms without a mutation ------------------------------------------------------
41
+
42
+ REQUIRES_CLAUSES = ("atomic_default_mem_order", "reverse_offload",
43
+ "unified_address", "unified_shared_memory",
44
+ "dynamic_allocators", "self_maps", "device_safesync")
45
+
46
+ _REQUIRES_WHY = ("declarative requires clause: removing it leaves a program "
47
+ "with nothing to test (the requirement only constrains the "
48
+ "compilation unit; the program is observable=False)")
49
+
50
+
51
+ # Forms whose deletion leaves a default that is the same program, or one
52
+ # that still does what the form does, and for which no weaker form exists
53
+ # (the generated program uses the default value): no mutation.
54
+ EQUIVALENT_DEFAULTS: dict[str, str] = {
55
+ "omp.clause.schedule.nonmonotonic":
56
+ "nonmonotonic is the default modifier of the dynamic kind the "
57
+ "program uses (5.0 and later)",
58
+ "omp.clause.order.unconstrained":
59
+ "the program has no schedule clause, so its loop schedule is not "
60
+ "reproducible with or without the modifier (with "
61
+ "schedule(static) unconstrained would remove reproducibility, a "
62
+ "relaxation no run can show)",
63
+ "omp.clause.ordered.n":
64
+ "without n the ordered clause behaves as if n were the collapse "
65
+ "value, 1 on the program's one-loop nest",
66
+ "omp.clause.reduction.default":
67
+ "default is the default reduction-modifier",
68
+ "omp.clause.linear.val":
69
+ "val is the default linear-modifier of a non-reference list item",
70
+ "omp.clause.default.all": "all is the default variable category",
71
+ "omp.clause.defaultmap.default":
72
+ "defaultmap(default) is the behaviour without the clause",
73
+ "omp.clause.nowait.do_not_synchronize":
74
+ "nowait(.true.) is nowait (the argument defaults to true)",
75
+ "omp.clause.nogroup.do_not_synchronize":
76
+ "nogroup(.true.) is nogroup (the argument defaults to true)",
77
+ "omp.clause.replayable.replayable_expression":
78
+ "replayable(.true.) is replayable (the argument defaults to true)",
79
+ "omp.clause.indirect.invoked_by_fptr":
80
+ "indirect(.true.) is indirect (the argument defaults to true)",
81
+ "omp.clause.transparent.impex_type":
82
+ "transparent(omp_impex) is transparent (the default impex type)",
83
+ "omp.clause.uses_allocators.memspace":
84
+ "the program names omp_default_mem_space, the default memory space",
85
+ "omp.clause.default.none":
86
+ "default(none) only requires every variable to be listed: a "
87
+ "compile-time restriction with no run-time effect; its oracle is "
88
+ "the illegal variant of rule default-dsa-001",
89
+ "omp.clause.map.ref_ptr_ptee":
90
+ "without a ref-modifier the map applies to the referring pointer "
91
+ "and its referenced pointee (6.0 §7.9.6), i.e. ref_ptr_ptee",
92
+ "omp.clause.device.device_num":
93
+ "device_num is the default device-modifier (device(n) is "
94
+ "device(device_num: n)); deleting the clause instead tests the "
95
+ "device clause, audited as omp.clause.device",
96
+ }
97
+
98
+ _DNM_WHY = ("a directive-name modifier restricts the clause to the named "
99
+ "constituent; deleting it applies the clause to every "
100
+ "constituent that accepts it, which contains the modifier's "
101
+ "effect (the generated program names the constituent that takes "
102
+ "the clause), and no weaker form exists")
103
+
104
+
105
+ # Directive-name modifiers whose deletion is a mutation: if(parallel: x)
106
+ # with x false leaves the region on the device; if(x) sends it to the host.
107
+ DNM_MUTATED = frozenset({"omp.clause.if.directive_name"})
108
+
109
+
110
+ def _no_mutation() -> dict[str, str]:
111
+ out = {"omp.directive.requires": (
112
+ "declarative directive: removing requires leaves a program with "
113
+ "nothing to test (its clauses are the atoms; observable=False)")}
114
+ for v in (50, 52, 60):
115
+ for atom in slice_atoms(v):
116
+ parts = atom.split(".")
117
+ if parts[1] == "clause" and parts[2] in REQUIRES_CLAUSES:
118
+ out[atom] = _REQUIRES_WHY
119
+ elif parts[-1] == "directive_name" and atom not in DNM_MUTATED:
120
+ out[atom] = _DNM_WHY
121
+ for atom, why in EQUIVALENT_DEFAULTS.items():
122
+ out[atom] = "equivalent default: " + why
123
+ return dict(sorted(out.items()))
124
+
125
+
126
+ NO_MUTATION: dict[str, str] = _no_mutation()
127
+
128
+ # Forms whose deletion leaves a default that still does what the form does
129
+ # (map(to: a) -> map(a) is tofrom): the form is REPLACED, per directive
130
+ # ("*" = any other directive), by one without the behaviour; None deletes
131
+ # the clause carrying the form, KEEP leaves it (no weaker form exists there).
132
+ KEEP = "keep"
133
+ # The iterator's range made empty ("If the iterator value set is empty then
134
+ # the effect is as if the list item was not specified", 5.2 §3.2.6, 6.0
135
+ # §5.2.6): the clause stays, the items it generates go, k is never unset.
136
+ EMPTY_RANGE = "empty range"
137
+ _CATEGORY = ("deleting the variable category applies the behaviour to all "
138
+ "categories, which include it; a disjoint category moves it off "
139
+ "the tested variables")
140
+ REPLACEMENTS: dict[str, tuple[dict, str]] = {
141
+ "omp.clause.map.to": (
142
+ {"target": "delete", "*": "alloc"},
143
+ "on target the map type is deleted (tofrom: the program checks that "
144
+ "the region's write of the to-mapped array does not come back, copy "
145
+ "mode); elsewhere map(to: x) without its transfer is map(alloc: x) "
146
+ "(deleting the type gives tofrom on target data or to on enter "
147
+ "data, which still copies to the device)"),
148
+ "omp.clause.map.from": (
149
+ {"target exit data": "release", "*": "alloc"},
150
+ "map(from: x) without its transfer is alloc (target, target data) or "
151
+ "release (exit data); deleting the type gives tofrom or from"),
152
+ "omp.clause.map.tofrom": (
153
+ {"*": "alloc"},
154
+ "tofrom is the default map type, so deleting it changes nothing; "
155
+ "alloc keeps the mapping without either transfer"),
156
+ "omp.clause.map.delete": (
157
+ {"*": "release"},
158
+ "delete forces the reference count to zero, release only decrements "
159
+ "it; deleting the type gives from, which also releases"),
160
+ "omp.clause.map.alloc": (
161
+ {"target enter data": None, "target data": None, "*": KEEP},
162
+ "alloc is the weakest map type: its clause is deleted where it "
163
+ "allocates storage for later regions (target enter data, target "
164
+ "data) and kept on target regions (deleting it there gives tofrom, "
165
+ "which still allocates)"),
166
+ "omp.clause.map.storage": (
167
+ {"target enter data": None, "target data": None, "*": KEEP},
168
+ "storage is the weakest map type (alloc/release): as for alloc"),
169
+ "omp.clause.map.release": (
170
+ {"*": None},
171
+ "release is the weakest exit map type: its clause is deleted "
172
+ "(deleting the type gives from, which also releases)"),
173
+ "omp.clause.defaultmap.scalar": ({"*": "aggregate"}, _CATEGORY),
174
+ "omp.clause.defaultmap.aggregate": ({"*": "scalar"}, _CATEGORY),
175
+ "omp.clause.defaultmap.allocatable": ({"*": "scalar"}, _CATEGORY),
176
+ "omp.clause.defaultmap.pointer": ({"*": "scalar"}, _CATEGORY),
177
+ "omp.clause.default.scalar": ({"*": "aggregate"}, _CATEGORY),
178
+ "omp.clause.default.aggregate": ({"*": "scalar"}, _CATEGORY),
179
+ "omp.clause.default.allocatable": ({"*": "scalar"}, _CATEGORY),
180
+ "omp.clause.default.pointer": ({"*": "scalar"}, _CATEGORY),
181
+ # Defaultmap(to: all) -> to: scalar leaves the arrays
182
+ # tofrom
183
+ "omp.clause.defaultmap.all": ({"*": "scalar"}, _CATEGORY),
184
+ # an orphaned loop must carry bind (5.2 §11.7 p.257, 6.0 §13.8 p.424:
185
+ # loop-nesting-001), so removing it is illegal; bind(thread) is the
186
+ # conforming form without the team's or league's sharing
187
+ "omp.clause.bind.parallel": (
188
+ {"*": "thread"},
189
+ "an orphaned loop must have a bind clause; thread binds it to the "
190
+ "encountering thread, so each thread runs every iteration"),
191
+ "omp.clause.bind.teams": (
192
+ {"*": "thread"},
193
+ "an orphaned loop must have a bind clause; thread binds it to the "
194
+ "encountering thread, so each team runs every iteration"),
195
+ # severity(fatal) is the default, so deleting it is the same program;
196
+ # warning lets the compilation go on
197
+ "omp.clause.severity.fatal": (
198
+ {"*": "warning"},
199
+ "fatal is the default sev-level, so deleting it changes nothing; "
200
+ "warning shows the message and lets the compilation go on"),
201
+ }
202
+ REPLACEMENTS["omp.clause.map.ref_ptee"] = (
203
+ {"*": "ref_ptr"},
204
+ "ref_ptee maps only the pointee; ref_ptr maps only the pointer, so the "
205
+ "pointee's data is not mapped (deleting the modifier maps both, a "
206
+ "superset)")
207
+ REPLACEMENTS["omp.clause.map.ref_ptr"] = (
208
+ {"*": "ref_ptee"},
209
+ "ref_ptr maps only the pointer; ref_ptee maps only the pointee, so no "
210
+ "device pointer is attached (deleting the modifier maps both, a "
211
+ "superset)")
212
+ for _c in ("depend", "to", "from", "map"):
213
+ REPLACEMENTS[f"omp.clause.{_c}.iterator"] = (
214
+ {"*": EMPTY_RANGE},
215
+ "an empty range (k = 1:0) removes exactly the list items the "
216
+ "iterator generates and keeps the clause; deleting the iterator "
217
+ "alone would leave k unset")
218
+ for _t in ("in", "out", "inout", "inoutset", "mutexinoutset", "depobj"):
219
+ REPLACEMENTS[f"omp.clause.depend.{_t}"] = (
220
+ {"*": None},
221
+ "the dependence is deleted with its clause: 6.0's depend(x) without "
222
+ "a type is inout, which still orders the tasks, and no type orders "
223
+ "less than the one tested without adding another dependence")
224
+
225
+ # Semantic atoms (catalog/omp/semantic_atoms.yaml): no directive
226
+ # carries them, so nothing can be deleted. The mutant is the program with
227
+ # the behaviour the atom specifies replaced by the competing one: `clause`
228
+ # is added to every (unprotected) directive named `directive`.
229
+ SEMANTIC_MUTATIONS: dict[str, tuple[str, str, str]] = {
230
+ "omp.clause.defaultmap.character": (
231
+ "target", "firstprivate(ch, s)",
232
+ "the character variables are made firstprivate, as an integer "
233
+ "scalar is implicitly (what treating Fortran character as a scalar "
234
+ "variable would give): the device's writes to ch and s no longer "
235
+ "come back"),
236
+ }
237
+
238
+ # Clauses whose removal makes other constructs non-conforming: those are
239
+ # commented out too (atom -> directive atom).
240
+ COMPANIONS: dict[str, str] = {
241
+ "omp.clause.ordered": "omp.directive.ordered",
242
+ "omp.clause.reduction.inscan": "omp.directive.scan",
243
+ }
244
+
245
+ # Variables that report where the program ran (not the atom): never removed
246
+ # from a clause.
247
+ INFRA_VARS = frozenset({"host_flag", "on_dev", "zc"})
248
+
249
+ # Data-sharing clauses: deleted under default(none), their list items are
250
+ # listed in shared(...) instead (the closest conforming program without
251
+ # the privatisation).
252
+ DSA_CLAUSES = frozenset({"private", "firstprivate", "lastprivate",
253
+ "reduction", "linear",
254
+ # Induction is a data-sharing clause too;
255
+ # deleted under default(none) it left x unlisted,
256
+ # an illegal mutant (default-dsa-001)
257
+ "induction"})
258
+
259
+ # Directives that need a clause (a motion clause, an extended list): when
260
+ # the mutation removes the last one, the directive is commented out.
261
+ NEEDS_A_CLAUSE = frozenset({"target update", "declare target",
262
+ "declare"}) # declare: OpenACC
263
+
264
+ # OpenACC: data clauses whose deletion leaves the implicit copy
265
+ # of the array (3.4 §2.6.2: an aggregate in no data clause is copy), which
266
+ # still transfers it; the clause is renamed to create, which keeps the
267
+ # device copy and drops the transfers (3.4 §2.7.10)
268
+ ACC_RENAMES: dict[str, tuple[str, str]] = {
269
+ f"acc.clause.{c}": ("create", f"{c} without its transfers is create: "
270
+ "deleting it leaves the implicit copy of the array "
271
+ "(3.4 §2.6.2), which transfers it as well")
272
+ for c in ("copy", "copyin", "copyout")}
273
+ ACC_RENAMES["acc.clause.link"] = (
274
+ "create", "declare link allocates the device copy only when a data "
275
+ "clause names the variable (3.4 §2.13.3), so the compute construct's "
276
+ "copyin transfers it; declare create allocates it for the whole "
277
+ "program, so the copyin finds it present and transfers nothing "
278
+ "(deleting link leaves a declare without a clause, and the routine's "
279
+ "use of the global then needs a declare)")
280
+ # feature atoms whose argument is replaced by the value its absence means
281
+ ACC_ARG_REPLACE: dict[str, tuple[str, str]] = {
282
+ "acc.clause.self.condition": (
283
+ "cond_true", "self without a condition is self(.true.) (3.4 "
284
+ "§2.5.7); CCE 19 rejects self without an argument and with a "
285
+ "literal argument, so the condition is replaced by the program's "
286
+ "variable cond_true, which is .true.")}
287
+ # atomic-clauses removed with their construct (see the module docstring)
288
+ ACC_CONSTRUCT_CLAUSES = frozenset({"acc.clause.read", "acc.clause.write",
289
+ "acc.clause.capture"})
290
+ # OpenACC semantic atoms (catalog/acc/semantic_atoms.yaml): the clause that
291
+ # gives the variable the competing attribute
292
+ SEMANTIC_MUTATIONS_ACC: dict[str, tuple[str, str, str]] = {
293
+ "acc.clause.firstprivate.implicit_scalar": (
294
+ "serial", "copy(x)",
295
+ "x is made copy, so the region's write reaches the host"),
296
+ "acc.clause.copy.implicit_kernels_scalar": (
297
+ "kernels", "create(x)",
298
+ "x is given a device-only copy (create), so the device's write "
299
+ "does not reach the host (copy mode)"),
300
+ }
301
+
302
+
303
+ # --- the directives of a source, with positions -----------------------------------
304
+
305
+ _SENT = re.compile(r"^(\s*)!\$omp", re.I)
306
+ _SENTS = {"omp": _SENT, "acc": re.compile(r"^(\s*)!\$acc", re.I)}
307
+ # The OpenACC C programs' sentinel too (#pragma acc)
308
+ _C_PRAGMA = re.compile(r"^(\s*)#\s*pragma\s+(?:omp|acc)\b")
309
+
310
+
311
+ def _sent(version: int, lang: str = "f90") -> re.Pattern:
312
+ return _C_PRAGMA if lang == "c" else _SENTS[model_of(version)]
313
+
314
+
315
+ def _glang(lang: str) -> str:
316
+ """The parse_directive language name."""
317
+ return "c" if lang == "c" else "fortran"
318
+
319
+
320
+ @dataclass
321
+ class _Line:
322
+ idx: int # physical line index (0-based)
323
+ cstart: int # content start column
324
+ cend: int # content end column (before a trailing '&' / comment)
325
+
326
+
327
+ @dataclass
328
+ class _Dir:
329
+ first: int
330
+ last: int
331
+ lines: list[_Line]
332
+ text: str # the directive's physical text (for parse_directive)
333
+ logical: str # contents joined with one blank
334
+ pos: list # logical index -> (line idx, col) or None (the blank)
335
+ d: Directive
336
+ end: "_Dir | None" = None
337
+ protected: bool = False
338
+ lang: str = "f90"
339
+
340
+
341
+ def _scan(lines: list[str], version: int, lang: str = "f90") -> list[_Dir]:
342
+ if lang == "c":
343
+ return _scan_c(lines, version)
344
+ out: list[_Dir] = []
345
+ k = 0
346
+ _SENT = _sent(version)
347
+ while k < len(lines):
348
+ if not _SENT.match(lines[k]):
349
+ k += 1
350
+ continue
351
+ first, parts = k, []
352
+ while True:
353
+ raw = lines[k]
354
+ m = _SENT.match(raw)
355
+ if m is None:
356
+ raise MutationError(f"line {k + 1}: continuation is not a "
357
+ "directive line")
358
+ c = m.end()
359
+ if parts: # continuation: optional '&' after the sentinel
360
+ while c < len(raw) and raw[c] in " \t":
361
+ c += 1
362
+ if raw[c:c + 1] == "&":
363
+ c += 1
364
+ rest = G._strip_bang_comment(raw[c:])
365
+ end = c + len(rest.rstrip())
366
+ cont = raw[:end].endswith("&")
367
+ if cont:
368
+ end -= 1
369
+ while c < end and raw[c] in " \t":
370
+ c += 1
371
+ while end > c and raw[end - 1] in " \t":
372
+ end -= 1
373
+ parts.append(_Line(k, c, end))
374
+ if not cont:
375
+ break
376
+ k += 1
377
+ text = "\n".join(lines[first:k + 1])
378
+ logical, pos = "", []
379
+ for n, ln in enumerate(parts):
380
+ if n:
381
+ logical += " "
382
+ pos.append(None)
383
+ seg = lines[ln.idx][ln.cstart:ln.cend]
384
+ logical += seg
385
+ pos += [(ln.idx, ln.cstart + i) for i in range(len(seg))]
386
+ out.append(_Dir(first, k, parts, text, logical, pos,
387
+ parse_directive(text, "fortran", version)))
388
+ k += 1
389
+ return out
390
+
391
+
392
+ def _scan_c(lines: list[str], version: int) -> list[_Dir]:
393
+ """The ``#pragma omp`` directives of a C source, with ``\\``
394
+ continuations. An ``end declare target`` is a directive of
395
+ its own here (``d.end`` is set on it, so it is handled as an end)."""
396
+ out: list[_Dir] = []
397
+ k = 0
398
+ while k < len(lines):
399
+ m = _C_PRAGMA.match(lines[k])
400
+ if not m:
401
+ k += 1
402
+ continue
403
+ first, parts = k, []
404
+ while True:
405
+ raw = lines[k]
406
+ c = m.end() if k == first else 0
407
+ body = raw.rstrip()
408
+ cont = body.endswith("\\")
409
+ end = len(body) - (1 if cont else 0)
410
+ while c < end and raw[c] in " \t":
411
+ c += 1
412
+ while end > c and raw[end - 1] in " \t":
413
+ end -= 1
414
+ parts.append(_Line(k, c, end))
415
+ if not cont or k + 1 >= len(lines):
416
+ break
417
+ k += 1
418
+ text = "\n".join(lines[first:k + 1])
419
+ logical, pos = "", []
420
+ for n, ln in enumerate(parts):
421
+ if n:
422
+ logical += " "
423
+ pos.append(None)
424
+ seg = lines[ln.idx][ln.cstart:ln.cend]
425
+ logical += seg
426
+ pos += [(ln.idx, ln.cstart + i) for i in range(len(seg))]
427
+ d = parse_directive(text, "c", version)
428
+ if d.name == "end declare target":
429
+ d = dataclasses.replace(d, end=True)
430
+ out.append(_Dir(first, k, parts, text, logical, pos, d, lang="c"))
431
+ k += 1
432
+ return out
433
+
434
+
435
+ def _pair(dirs: list[_Dir], source: str, version: int) -> None:
436
+ """Attach end directives (from parse_program's pairing) and mark the
437
+ probe and shared-memory detector as protected."""
438
+ lang = dirs[0].lang if dirs else "f90"
439
+ tree = parse_program(source, version, lang="c" if lang == "c" else None)
440
+ begins = [x for x in dirs if not x.d.end]
441
+ by_line = {x.first + 1: x for x in dirs}
442
+ if len(begins) != len(tree.directives):
443
+ raise MutationError("directive count differs from parse_program")
444
+ for x, a in zip(begins, tree.assoc):
445
+ if x.first + 1 != a.line:
446
+ raise MutationError(f"directive at line {x.first + 1} does not "
447
+ f"match parse_program's line {a.line}")
448
+ if a.end_line is not None and a.end_line != a.line:
449
+ # a loop construct without its end directive ends at its END DO
450
+ # (OpenACC programs write none), which is no directive
451
+ x.end = by_line.get(a.end_line)
452
+ if x.end is not None and not x.end.d.end:
453
+ x.end = None
454
+ lines = source.splitlines()
455
+ for x, a in zip(begins, tree.assoc):
456
+ if x.d.name == "declare target" and x.d.args == ["on_dev"]:
457
+ x.protected = True
458
+ elif x.d.name in ("target", "serial") and x.end is not None:
459
+ inner = {s.strip() for s in lines[x.last + 1:x.end.first]}
460
+ if inner & {"on_dev = 1", "zc(1) = 1"}:
461
+ x.protected = True
462
+ elif x.lang == "c" and x.d.name == "target" and a.end_line:
463
+ # C: the block statement of the target region
464
+ inner = {s.strip() for s in lines[x.last + 1:a.end_line]}
465
+ if inner & {"on_dev = 1;", "zc[0] = 1;"}:
466
+ x.protected = True
467
+
468
+
469
+ # --- clause positions --------------------------------------------------------------
470
+
471
+ @dataclass
472
+ class _Clause:
473
+ name: str
474
+ start: int # logical index of the name
475
+ stop: int # logical index after ')' (or the name)
476
+ arg: tuple | None # (start, stop) of the argument text
477
+
478
+
479
+ @lru_cache(maxsize=None)
480
+ def _fparser(version: int, lang: str = "f90"):
481
+ m = model_of(version)
482
+ return G._parser(default_skeleton(m), default_grammar(m),
483
+ "C" if lang == "c" else "Fortran", version)
484
+
485
+
486
+ def _clauses(x: _Dir, version: int) -> list[_Clause]:
487
+ """The clauses of a directive with their logical positions (the
488
+ walk of grammar._Parser.parse, keeping the token positions)."""
489
+ if x.lang == "c":
490
+ toks = G._tokenize(x.logical)
491
+ _, _, toks = _fparser(version, "c").c_name(toks)
492
+ else:
493
+ toks = G._tokenize(x.logical.lower())
494
+ _, _, toks = _fparser(version).fortran_name(toks)
495
+ i, out = 0, []
496
+ if toks and toks[0].text == "(":
497
+ i = G._close_paren(toks, 0, x.d.name) + 1
498
+ while i < len(toks):
499
+ t = toks[i]
500
+ if t.text == ",":
501
+ i += 1
502
+ continue
503
+ if i + 1 < len(toks) and toks[i + 1].text == "(":
504
+ j = G._close_paren(toks, i + 1, t.text)
505
+ out.append(_Clause(t.text, t.start, toks[j].end,
506
+ (toks[i + 1].end, toks[j].start)))
507
+ i = j + 1
508
+ else:
509
+ out.append(_Clause(t.text, t.start, t.end, None))
510
+ i += 1
511
+ if [c.name for c in out] != [c.name for c in x.d.clauses]:
512
+ raise MutationError(f"clause walk differs from the parse of "
513
+ f"{x.text!r}")
514
+ return out
515
+
516
+
517
+ def _pieces(text: str, base: int) -> tuple[list[tuple], list[tuple]]:
518
+ """Top-level pieces (start, stop) of a clause argument and the
519
+ separators (pos, char) between them, at ',' and ':' (the split of
520
+ grammar._split, with positions)."""
521
+ pieces, seps = [], []
522
+ depth, quote, start = 0, None, 0
523
+ for i, ch in enumerate(text):
524
+ if quote:
525
+ quote = None if ch == quote else quote
526
+ elif ch in "'\"":
527
+ quote = ch
528
+ elif ch in "([":
529
+ depth += 1
530
+ elif ch in ")]":
531
+ depth -= 1
532
+ elif depth == 0 and ch in ",:":
533
+ pieces.append((start, i))
534
+ seps.append((i, ch))
535
+ start = i + 1
536
+ pieces.append((start, len(text)))
537
+
538
+ def trim(a, b):
539
+ while a < b and text[a] in " \t":
540
+ a += 1
541
+ while b > a and text[b - 1] in " \t":
542
+ b -= 1
543
+ return a + base, b + base
544
+ return [trim(a, b) for a, b in pieces], [(p + base, c) for p, c in seps]
545
+
546
+
547
+ def _piece_span(c: _Clause, i: int, pieces, seps, logical: str) -> tuple:
548
+ """Logical span deleting piece i with one adjacent separator."""
549
+ n = len(pieces)
550
+ if n == 1:
551
+ return c.arg[0] - 1, c.arg[1] + 1
552
+ ps, pe = pieces[i]
553
+ before = seps[i - 1] if i > 0 else None
554
+ after = seps[i] if i < n - 1 else None
555
+ if after is None or (before is not None and before[1] == ","
556
+ and after[1] == ":"):
557
+ return before[0], pe
558
+ return ps, pieces[i + 1][0]
559
+
560
+
561
+ def _clause_span(c: _Clause, x: _Dir) -> tuple:
562
+ """Logical span deleting a whole clause with its separating blank (or
563
+ comma): the blank before it, or, at the start of a line, after it."""
564
+ s, e = c.start, c.stop
565
+ line = x.pos[s][0] if x.pos[s] else None
566
+ j = s
567
+ while j > 0 and x.pos[j - 1] is not None and x.logical[j - 1] in " \t,":
568
+ j -= 1
569
+ prev_same = j > 0 and x.pos[j - 1] is not None \
570
+ and x.pos[j - 1][0] == line
571
+ if prev_same:
572
+ return j, e
573
+ while e < len(x.logical) and x.pos[e] is not None \
574
+ and x.logical[e] in " \t,":
575
+ e += 1
576
+ return s, e
577
+
578
+
579
+ # --- applying edits ----------------------------------------------------------------
580
+
581
+ def _apply(lines: list[str], x: _Dir, edits: list[tuple]) -> list[str]:
582
+ """The directive's physical lines with the logical edits applied: each
583
+ edit is a span (s, e) or (s, e, text), the span deleted and `text`
584
+ put in its place. A continuation line left empty is dropped with the
585
+ '&' before it."""
586
+ kill: dict[int, set] = {}
587
+ put: dict[tuple, str] = {}
588
+ for ed in edits:
589
+ s, e = ed[0], ed[1]
590
+ text = ed[2] if len(ed) > 2 else ""
591
+ cols = [x.pos[i] for i in range(s, e) if x.pos[i] is not None]
592
+ if len({ln for ln, _ in cols}) > 1:
593
+ raise MutationError(f"an edit spans lines in {x.text!r}")
594
+ if cols:
595
+ kill.setdefault(cols[0][0], set()).update(c for _, c in cols)
596
+ if text:
597
+ at = x.pos[s] if s < len(x.pos) else None
598
+ if at is None:
599
+ raise MutationError(f"no position for an insertion in "
600
+ f"{x.text!r}")
601
+ put[at] = put.get(at, "") + text
602
+ out: list[tuple[_Line, str]] = []
603
+ for ln in x.lines:
604
+ raw = lines[ln.idx]
605
+ dead = kill.get(ln.idx, set())
606
+ new, content = [], []
607
+ for col, ch in enumerate(raw):
608
+ ins = put.get((ln.idx, col), "")
609
+ new.append(ins)
610
+ if ln.cstart <= col < ln.cend:
611
+ content.append(ins)
612
+ if col not in dead:
613
+ new.append(ch)
614
+ if ln.cstart <= col < ln.cend:
615
+ content.append(ch)
616
+ if out and not "".join(content).strip():
617
+ continue # an emptied continuation line
618
+ out.append((ln, "".join(new)))
619
+ if out:
620
+ ln, new = out[-1]
621
+ if ln is not x.lines[-1]: # the last line was dropped: no '&'
622
+ out[-1] = (ln, re.sub(r"\s*\\\s*$", "", new) if x.lang == "c"
623
+ else re.sub(r"\s*&\s*$", "", new))
624
+ return [new for _, new in out]
625
+
626
+
627
+ def _rebuild(lines: list[str], edits: dict[int, list[str]]) -> list[str]:
628
+ """Replace each directive (keyed by its first line) by its new lines;
629
+ `edits` values are (number of original lines, new lines)."""
630
+ out, k = [], 0
631
+ while k < len(lines):
632
+ if k in edits:
633
+ n, new = edits[k]
634
+ out += new
635
+ k += n
636
+ continue
637
+ out.append(lines[k])
638
+ k += 1
639
+ return out
640
+
641
+
642
+ def _lang_of(new: list[str]) -> str:
643
+ return "c" if new and _C_PRAGMA.match(new[0]) else "fortran"
644
+
645
+
646
+ def _parse_lines(new: list[str], version: int) -> Directive | None:
647
+ try:
648
+ return parse_directive("\n".join(new), _lang_of(new), version)
649
+ except GrammarError:
650
+ return None
651
+
652
+
653
+ def _parse_error(new: list[str], version: int) -> str | None:
654
+ try:
655
+ parse_directive("\n".join(new), _lang_of(new), version)
656
+ except GrammarError as err:
657
+ return err.code
658
+ return None
659
+
660
+
661
+ # --- choosing the edits -------------------------------------------------------------
662
+
663
+ def _base(item: str) -> str:
664
+ m = re.match(r"\s*([A-Za-z_]\w*)", item)
665
+ return m.group(1).lower() if m else ""
666
+
667
+
668
+ def _infra(parsed) -> tuple[bool, bool]:
669
+ """(all list items are infrastructure, some are)."""
670
+ items = [_base(a) for a in parsed.args]
671
+ hit = [i in INFRA_VARS for i in items]
672
+ return bool(items) and all(hit), any(hit)
673
+
674
+
675
+ def _default_none(x: _Dir) -> bool:
676
+ return any(c.name == "default" and [a.lower() for a in c.args] ==
677
+ ["none"] for c in x.d.clauses)
678
+
679
+
680
+ def _drop_clause(x: _Dir, c: _Clause, parsed) -> tuple:
681
+ """Edit deleting clause `c`; under default(none) a data-sharing
682
+ clause's list items are listed in shared(...) in its place."""
683
+ s, e = _clause_span(c, x)
684
+ if parsed.name in DSA_CLAUSES and _default_none(x) and parsed.args \
685
+ and (x.text.lstrip().lower().startswith("!$omp")
686
+ or _C_PRAGMA.match(x.text)):
687
+ rep = f"shared({', '.join(parsed.args)})"
688
+ return (s, e, " " + rep) if s < c.start else (s, e, rep + " ")
689
+ return s, e
690
+
691
+
692
+ def _only_instance(x: _Dir, cl: list[_Clause], keep: int, name: str,
693
+ lines: list[str]) -> list[str]:
694
+ spans = [_clause_span(c, x) for j, c in enumerate(cl)
695
+ if c.name == name and j != keep]
696
+ return _apply(lines, x, spans)
697
+
698
+
699
+ INFRA_HOW = "the reporting variable's clause left as is"
700
+ _INFRA_CLAUSE = re.compile(
701
+ r"\s*\b[a-z_]+\((?:[^():]*:\s*)?(?:" + "|".join(sorted(INFRA_VARS))
702
+ + r")\s*\)", re.I)
703
+
704
+
705
+ def _strip_infra(text: str) -> str:
706
+ """`text` with every clause whose only list item is a reporting
707
+ variable removed from its directive lines."""
708
+ return "\n".join(_INFRA_CLAUSE.sub("", ln)
709
+ if any(p.match(ln) for p in _SENTS.values())
710
+ or _C_PRAGMA.match(ln) else ln
711
+ for ln in text.split("\n"))
712
+
713
+
714
+ def atoms_outside_infra(source: str, version: int) -> frozenset:
715
+ """The atoms of `source` ignoring the reporting variables' clauses
716
+ (e.g. map(tofrom: host_flag) carries omp.clause.map.tofrom)."""
717
+ tree = parse_program(_strip_infra(source), version)
718
+ return frozenset().union(*(d.atoms for d in tree.directives),
719
+ *(d.atoms for d in tree.ends.values()))
720
+ INFRA_KEPT = ("infra",) # marker in an edit list: an infrastructure clause
721
+
722
+
723
+ def _clause_edits(x: _Dir, name: str, version: int) -> list[tuple]:
724
+ """Every instance of clause `name` deleted, except infrastructure list
725
+ items (a clause of only those is left; from a mixed one only the tested
726
+ items are deleted)."""
727
+ cl = _clauses(x, version)
728
+ edits = []
729
+ for c, parsed in zip(cl, x.d.clauses):
730
+ if c.name != name:
731
+ continue
732
+ everything, some = _infra(parsed)
733
+ if everything:
734
+ edits.append(INFRA_KEPT)
735
+ continue
736
+ if not some:
737
+ edits.append(_drop_clause(x, c, parsed))
738
+ continue
739
+ text = x.logical[c.arg[0]:c.arg[1]]
740
+ pieces, seps = _pieces(text, c.arg[0])
741
+ for i, (ps, pe) in enumerate(pieces):
742
+ item = x.logical[ps:pe]
743
+ if item in parsed.args and _base(item) not in INFRA_VARS:
744
+ edits.append(_piece_span(c, i, pieces, seps, x.logical))
745
+ return edits
746
+
747
+
748
+ def _rule(atom: str, directive: str):
749
+ table = REPLACEMENTS.get(atom)
750
+ if table is None:
751
+ return "delete"
752
+ rules = table[0]
753
+ return rules.get(directive, rules.get("*"))
754
+
755
+
756
+ def _feature_edits(x: _Dir, atom: str, name: str, lines: list[str],
757
+ version: int) -> tuple[list[tuple], set]:
758
+ cl = _clauses(x, version)
759
+ edits, how = [], set()
760
+ rule = _rule(atom, base_name(x.d.name))
761
+ for j, (c, parsed) in enumerate(zip(cl, x.d.clauses)):
762
+ if c.name != name:
763
+ continue
764
+ alone = _parse_lines(_only_instance(x, cl, j, name, lines), version)
765
+ if alone is None:
766
+ raise MutationError(f"cannot tell whether {name} instance {j} "
767
+ f"carries {atom} in {x.text!r}")
768
+ if atom not in alone.atoms:
769
+ continue
770
+ everything, some = _infra(parsed)
771
+ if everything:
772
+ how.add(INFRA_HOW)
773
+ continue
774
+ if some:
775
+ raise MutationError(f"{atom} on a clause mixing infrastructure "
776
+ f"and tested items in {x.text!r}")
777
+ if rule == KEEP:
778
+ how.add(f"kept on {base_name(x.d.name)}: no weaker form")
779
+ continue
780
+ if rule is None:
781
+ edits.append(_drop_clause(x, c, parsed))
782
+ how.add("clause carrying it")
783
+ continue
784
+ best = None
785
+ if c.arg is not None:
786
+ text = x.logical[c.arg[0]:c.arg[1]]
787
+ pieces, seps = _pieces(text, c.arg[0])
788
+ args = next(o.args for o in alone.clauses if o.name == name)
789
+ base = [_clause_span(o, x) for k, o in enumerate(cl)
790
+ if o.name == name and k != j]
791
+ for i in range(len(pieces)):
792
+ span = _piece_span(c, i, pieces, seps, x.logical)
793
+ new = _parse_lines(_apply(lines, x, base + [span]), version)
794
+ if new is None or atom in new.atoms:
795
+ continue
796
+ kept = next(o.args for o in new.clauses if o.name == name)
797
+ key = (len(alone.atoms - new.atoms), kept != args, i)
798
+ if best is None or key < best[0]:
799
+ best = (key, span, pieces[i])
800
+ if best is not None and rule == "delete":
801
+ edits.append(best[1])
802
+ how.add("modifier")
803
+ elif best is not None and rule == EMPTY_RANGE:
804
+ ps, pe = best[2]
805
+ piece = x.logical[ps:pe]
806
+ new_piece, k = re.subn(r"(=\s*)1\s*:\s*[^:,()]+", r"\g<1>1:0",
807
+ piece, count=1)
808
+ if k != 1 or not piece.lower().startswith("iterator("):
809
+ raise MutationError(f"no 1:<end> iterator range in {piece!r}")
810
+ edits.append((ps, pe, new_piece))
811
+ how.add(f"iterator range made empty: {new_piece}")
812
+ elif best is not None:
813
+ ps, pe = best[2]
814
+ edits.append((ps, pe, rule))
815
+ how.add(f"{x.logical[ps:pe]} replaced by {rule}")
816
+ elif isinstance(rule, str) and rule not in ("delete", EMPTY_RANGE) \
817
+ and c.arg is not None:
818
+ # a lone argument (severity(fatal)): replace the piece whose
819
+ # replacement no longer carries the atom
820
+ text = x.logical[c.arg[0]:c.arg[1]]
821
+ pieces, _ = _pieces(text, c.arg[0])
822
+ base = [_clause_span(o, x) for k, o in enumerate(cl)
823
+ if o.name == name and k != j]
824
+ for ps, pe in pieces:
825
+ new = _parse_lines(_apply(lines, x, base + [(ps, pe, rule)]),
826
+ version)
827
+ if new is not None and atom not in new.atoms:
828
+ edits.append((ps, pe, rule))
829
+ how.add(f"{x.logical[ps:pe]} replaced by {rule}")
830
+ break
831
+ else:
832
+ raise MutationError(f"no piece of {name} can be replaced by "
833
+ f"{rule} in {x.text!r}")
834
+ else:
835
+ edits.append(_drop_clause(x, c, parsed))
836
+ how.add("clause carrying it")
837
+ return edits, how
838
+
839
+
840
+ def _rename_edits(x: _Dir, name: str, new: str, version: int) -> list:
841
+ """Edits renaming every instance of clause `name` (except clauses of
842
+ reporting variables only) to `new`, its arguments kept."""
843
+ out = []
844
+ for c, parsed in zip(_clauses(x, version), x.d.clauses):
845
+ if c.name != name or _infra(parsed)[0]:
846
+ continue
847
+ if _infra(parsed)[1]:
848
+ raise MutationError(f"{name} mixes reporting and tested items "
849
+ f"in {x.text!r}")
850
+ out.append((c.start, c.start + len(c.name), new))
851
+ return out
852
+
853
+
854
+ def _comment(lines: list[str], x: _Dir) -> list[str]:
855
+ out = []
856
+ lead = "// MUTANT " if x.lang == "c" else "! MUTANT "
857
+ for raw in lines[x.first:x.last + 1]:
858
+ body = raw.lstrip()
859
+ if x.lang == "c": # no line splice into the next line
860
+ body = re.sub(r"\s*\\\s*$", "", body)
861
+ out.append(raw[:len(raw) - len(raw.lstrip())] + lead + body)
862
+ return out
863
+
864
+
865
+ def _comment_pair(edits: dict, lines: list[str], x: _Dir) -> None:
866
+ edits[x.first] = (x.last - x.first + 1, _comment(lines, x))
867
+ if x.end is not None:
868
+ e = x.end
869
+ edits[e.first] = (e.last - e.first + 1, _comment(lines, e))
870
+
871
+
872
+ def neutralise(prog: Program) -> Program | None:
873
+ """The mutant of `prog` without its primary atom (see the module
874
+ docstring), or None for an atom in NO_MUTATION."""
875
+ atom = prog.primary_atom
876
+ if atom in NO_MUTATION:
877
+ return None
878
+ version = int(re.match(r"(?:omp|acc)(\d\d)-", prog.id).group(1))
879
+ model = model_of(version)
880
+ lang = "c" if prog.lang == "c" else "f90"
881
+ lines = prog.source.split("\n")
882
+ try:
883
+ dirs = _scan(lines, version, lang)
884
+ _pair(dirs, prog.source, version)
885
+ except (GrammarError, ProgramError) as err:
886
+ raise MutationError(f"{prog.id}: {err}") from err
887
+ edits: dict[int, tuple] = {}
888
+ hows: set = set()
889
+ kept = False
890
+ parts = atom.split(".")
891
+ if atom in SEMANTIC_MUTATIONS or atom in SEMANTIC_MUTATIONS_ACC:
892
+ return _semantic_mutant(prog, atom, lines, dirs)
893
+ begins = [x for x in dirs if not x.d.end]
894
+ for x in dirs:
895
+ if x.protected or (x.d.end and parts[1] == "directive"):
896
+ continue
897
+ if parts[1] == "directive":
898
+ if directive_atom(x.d.name, model) != atom:
899
+ continue
900
+ _comment_pair(edits, lines, x)
901
+ hows.add("directive and its end" if x.end else "directive")
902
+ continue
903
+ if atom not in x.d.atoms:
904
+ continue
905
+ name = parts[2]
906
+ if atom in ACC_CONSTRUCT_CLAUSES:
907
+ _comment_pair(edits, lines, x)
908
+ hows.add("its atomic construct commented out (the statements "
909
+ "stay)")
910
+ continue
911
+ if atom in ACC_ARG_REPLACE:
912
+ new_arg = ACC_ARG_REPLACE[atom][0]
913
+ dedits = [(c.arg[0], c.arg[1], new_arg)
914
+ for c in _clauses(x, version)
915
+ if c.name == name and c.arg is not None]
916
+ edits[x.first] = (x.last - x.first + 1,
917
+ _apply(lines, x, dedits))
918
+ hows.add(f"its argument replaced by {new_arg}")
919
+ kept = True
920
+ continue
921
+ if atom in ACC_RENAMES:
922
+ dedits = _rename_edits(x, name, ACC_RENAMES[atom][0], version)
923
+ if not dedits:
924
+ continue
925
+ new = _apply(lines, x, dedits)
926
+ edits[x.first] = (x.last - x.first + 1, new)
927
+ hows.add(f"renamed {name} -> {ACC_RENAMES[atom][0]}")
928
+ continue
929
+ if len(parts) == 3:
930
+ dedits = _clause_edits(x, name, version)
931
+ how = {"clause"}
932
+ if INFRA_KEPT in dedits:
933
+ how.add(INFRA_HOW)
934
+ dedits = [e for e in dedits if e != INFRA_KEPT]
935
+ else:
936
+ dedits, how = _feature_edits(x, atom, name, lines, version)
937
+ # the atom stays where the table keeps the form (no weaker one) or
938
+ # where the iterator stays with an empty range
939
+ kept = kept or any(h.startswith(("kept on", "iterator range made "
940
+ "empty")) for h in how)
941
+ if not dedits:
942
+ hows |= how
943
+ continue
944
+ new = _apply(lines, x, dedits)
945
+ after = _parse_lines(new, version)
946
+ needs = _parse_error(new, version) == "missing_required" or (
947
+ after is not None and not after.clauses and x.d.clauses
948
+ and not x.d.args and base_name(x.d.name) in NEEDS_A_CLAUSE)
949
+ if needs and not x.d.end:
950
+ _comment_pair(edits, lines, x)
951
+ hows.add("directive commented out: its required clause was the "
952
+ "one removed")
953
+ continue
954
+ bare = _parse_lines(_strip_infra("\n".join(new)).split("\n"),
955
+ version)
956
+ if bare is not None and atom in bare.atoms and not kept:
957
+ raise MutationError(f"{prog.id}: {atom} still in {new}")
958
+ hows |= how
959
+ if any(len(e) > 2 and e[2].lstrip().startswith("shared(")
960
+ for e in dedits):
961
+ hows.add("its list items made shared under default(none)")
962
+ edits[x.first] = (x.last - x.first + 1, new)
963
+ comp = COMPANIONS.get(atom)
964
+ if comp is not None and edits:
965
+ for x in begins:
966
+ if not x.protected and directive_atom(x.d.name, model) == comp:
967
+ _comment_pair(edits, lines, x)
968
+ hows.add(f"with the {comp.split('.')[-1]} constructs that "
969
+ "need it")
970
+ if not edits:
971
+ raise MutationError(f"{prog.id}: no directive carries {atom}")
972
+ out = _rebuild(lines, edits)
973
+ if _needs_dynamic_allocators(out, version):
974
+ out = _add_dynamic_allocators(out)
975
+ hows.add("requires dynamic_allocators added: an allocate clause in a "
976
+ "target region without an allocator needs it")
977
+ how = "; ".join(sorted(hows))
978
+ lead = "//" if lang == "c" else "!"
979
+ out.insert(1, f"{lead} MUTANT of {prog.id}: removed {atom} ({how})")
980
+ source = "\n".join(out)
981
+ try:
982
+ tree = parse_program(source, version)
983
+ atoms = frozenset().union(*(d.atoms for d in tree.directives),
984
+ *(d.atoms for d in tree.ends.values()))
985
+ except (GrammarError, ProgramError):
986
+ atoms = frozenset(prog.atoms - {atom})
987
+ if atom in atoms and not kept:
988
+ try:
989
+ outside = atoms_outside_infra(source, version)
990
+ except (GrammarError, ProgramError):
991
+ outside = atoms
992
+ if atom in outside:
993
+ raise MutationError(f"{prog.id}: {atom} survives the mutation")
994
+ return dataclasses.replace(prog, id=f"{prog.id}-mutant", source=source,
995
+ atoms=atoms)
996
+
997
+
998
+ def _needs_dynamic_allocators(lines: list[str], version: int) -> bool:
999
+ """Removing the allocator modifier of an allocate clause in a
1000
+ target region leaves a non-conforming program (rule allocate-clause-002:
1001
+ 5.2 §6.6 p.179, 6.0 §8.6 p.313) unless a requires dynamic_allocators
1002
+ directive is present; the conforming mutant adds it."""
1003
+ from offload.engine.rules import violations
1004
+ try:
1005
+ found = violations(parse_program("\n".join(lines), version), version)
1006
+ except (GrammarError, ProgramError):
1007
+ return False
1008
+ return f"OMP{version}-allocate-clause-002" in found
1009
+
1010
+
1011
+ def _add_dynamic_allocators(lines: list[str]) -> list[str]:
1012
+ """`lines` with requires dynamic_allocators after the main program's
1013
+ IMPLICIT statement (C: at file scope after the #include lines)."""
1014
+ if any(_C_PRAGMA.match(ln) for ln in lines):
1015
+ k = max(k for k, ln in enumerate(lines)
1016
+ if ln.startswith("#include"))
1017
+ return lines[:k + 1] + ["#pragma omp requires dynamic_allocators"] \
1018
+ + lines[k + 1:]
1019
+ start = next(k for k, ln in enumerate(lines)
1020
+ if ln.strip().startswith("program "))
1021
+ k = next(k for k in range(start, len(lines))
1022
+ if lines[k].strip().lower() == "implicit none")
1023
+ indent = lines[k][:len(lines[k]) - len(lines[k].lstrip())]
1024
+ return lines[:k + 1] + [f"{indent}!$omp requires dynamic_allocators"] \
1025
+ + lines[k + 1:]
1026
+
1027
+
1028
+ def _semantic_mutant(prog: Program, atom: str, lines: list[str],
1029
+ dirs: list) -> Program:
1030
+ """The mutant of a semantic atom (SEMANTIC_MUTATIONS): its clause added
1031
+ at the end of every unprotected begin directive of its name."""
1032
+ directive, clause, _ = {**SEMANTIC_MUTATIONS,
1033
+ **SEMANTIC_MUTATIONS_ACC}[atom]
1034
+ version = int(re.match(r"(?:omp|acc)(\d\d)-", prog.id).group(1))
1035
+ out = list(lines)
1036
+ hit = False
1037
+ for x in dirs:
1038
+ if x.protected or x.d.end or base_name(x.d.name) != directive:
1039
+ continue
1040
+ out[x.last] = out[x.last].rstrip() + " " + clause
1041
+ hit = True
1042
+ if not hit:
1043
+ raise MutationError(f"{prog.id}: no {directive} directive for {atom}")
1044
+ mark = "//" if prog.lang == "c" else "!" # A C program
1045
+ out.insert(1, f"{mark} MUTANT of {prog.id}: removed {atom} (added "
1046
+ f"{clause} to {directive})")
1047
+ source = "\n".join(out)
1048
+ try:
1049
+ tree = parse_program(source, version,
1050
+ lang="c" if prog.lang == "c" else None)
1051
+ except (GrammarError, ProgramError) as err:
1052
+ raise MutationError(f"{prog.id}: {err}") from err
1053
+ atoms = frozenset().union(*(d.atoms for d in tree.directives),
1054
+ *(d.atoms for d in tree.ends.values()))
1055
+ return dataclasses.replace(prog, id=f"{prog.id}-mutant", source=source,
1056
+ atoms=atoms)
1057
+
1058
+
1059
+ def mutation_of(mutant: Program) -> str:
1060
+ """The `removed <atom> (<how>)` text of a mutant's header."""
1061
+ m = re.search(r"^(?:!|//) MUTANT of \S+: (.*)$", mutant.source, re.M)
1062
+ return m.group(1) if m else ""
1063
+
1064
+
1065
+ # --- the audit ---------------------------------------------------------------------
1066
+
1067
+ @dataclass(frozen=True)
1068
+ class MutantResult:
1069
+ program_id: str
1070
+ primary_atom: str
1071
+ version: int
1072
+ toolchain: str
1073
+ status: str # killed | survived | inconclusive | not_applicable
1074
+ original: str # the original's Outcome ("" if not run)
1075
+ mutant: str # the mutant's Outcome ("" if not run)
1076
+ reason: str
1077
+ xnack: int = 0
1078
+ mutation: str = "" # what the mutant removed
1079
+ original_reason: str = ""
1080
+ mutant_reason: str = ""
1081
+ kill_kind: str = "" # killed only: check_fail | fell_back_to_host | crash
1082
+ body_sha: str = "" # body_sha of the audited original (staleness check)
1083
+ # The result was copied from this program's audit (same body,
1084
+ # other version: a 5.0 program identical to its 5.2 program)
1085
+ reused_from: str = ""
1086
+
1087
+
1088
+ STATUSES = ("killed", "survived", "inconclusive", "not_applicable")
1089
+
1090
+
1091
+ KILL_KINDS = ("check_fail", "fell_back_to_host", "crash")
1092
+
1093
+
1094
+ def kill_kind(mut: Result) -> str:
1095
+ """How a failing mutant failed: fell_back_to_host (ran_on_host=1),
1096
+ check_fail (the check failed on a completed run), crash (abnormal end:
1097
+ a signal, a non-zero exit, no RESULT line), timeout."""
1098
+ if mut.outcome == Outcome.fell_back_to_host:
1099
+ return "fell_back_to_host"
1100
+ if "timeout" in mut.reason:
1101
+ return "timeout"
1102
+ if mut.reason.startswith("check_fail") or "check=fail" in mut.reason:
1103
+ return "check_fail"
1104
+ return "crash"
1105
+
1106
+
1107
+ def classify(orig: Result, mut: Result) -> tuple[str, str, str]:
1108
+ """(status, reason, kill kind) from the original's and the mutant's
1109
+ results. A mutant that times out (also when re-run) is inconclusive:
1110
+ a hang may be GPU contention as well as the missing construct."""
1111
+ C = Outcome.correct_on_device
1112
+ if orig.outcome != C:
1113
+ return "inconclusive", (f"original_{orig.outcome}: {orig.reason}; "
1114
+ f"mutant {mut.outcome}: {mut.reason}"), ""
1115
+ if mut.outcome == C:
1116
+ return "survived", "mutant correct_on_device", ""
1117
+ if mut.outcome == Outcome.rejected:
1118
+ return "inconclusive", f"mutant_compile_failure: {mut.reason}", ""
1119
+ if mut.outcome in (Outcome.wrong_answer, Outcome.fell_back_to_host):
1120
+ kind = kill_kind(mut)
1121
+ if kind == "timeout":
1122
+ return "inconclusive", f"mutant_timeout: {mut.reason}", ""
1123
+ return "killed", f"mutant {mut.outcome}: {mut.reason}", kind
1124
+ return "inconclusive", f"mutant_{mut.outcome}: {mut.reason}", ""
1125
+
1126
+
1127
+ def illegal(mutant: Program, version: int) -> list[str]:
1128
+ """The rules (offload.engine.rules.violations) a mutant breaks; a mutant
1129
+ the rule scanner cannot parse counts as illegal too."""
1130
+ from offload.engine.rules import violations
1131
+ try:
1132
+ return violations(parse_program(mutant.source, version), version)
1133
+ except (GrammarError, ProgramError) as err:
1134
+ return [f"unparsable ({err})"]
1135
+
1136
+
1137
+ def audit_programs(version: int, only: Callable[[Program], bool] | None = None,
1138
+ lang: str = "f90") -> list[Program]:
1139
+ """The programs the audit covers at `version`: legal, observable, with
1140
+ an audited evidence level (offload.engine.gen.AUDITED: construct,
1141
+ copy_mode_only, probe; not the no-oracle categories, legality or
1142
+ unverified), device scope (not a host+device pattern),
1143
+ accepted by `only`. `lang` "c": the C programs (Task
1144
+ 20)."""
1145
+ from offload.engine.gen import AUDITED
1146
+ if lang == "c" and model_of(version) == "acc":
1147
+ from offload.engine.acc_c_gen import all_acc_c_programs
1148
+ # One program per atom; the -binop companions
1149
+ # share their compound twin's oracle and atom, and nvc does not
1150
+ # compile them (data/findings/nvhpc-c-acc-atomic-update-sum-ice.c)
1151
+ progs = [p for p in all_acc_c_programs()
1152
+ if not p.id.endswith("-binop")]
1153
+ elif lang == "c":
1154
+ from offload.engine.c_gen import all_c_programs
1155
+ progs = all_c_programs(version)
1156
+ else:
1157
+ progs = generate(slice_atoms(version), version)
1158
+ # A host+device pattern is not its atom's oracle (the
1159
+ # device-only headline program is)
1160
+ return [p for p in progs
1161
+ if p.legal and p.observable and p.evidence in AUDITED
1162
+ and p.scope == "device" and (only is None or only(p))]
1163
+
1164
+
1165
+ def audit(version: int, toolchains: Sequence[str], xnack: int = 0,
1166
+ opt: str = "-O2", workers: int = 8,
1167
+ only: Callable[[Program], bool] | None = None, *,
1168
+ runner: Callable[..., Result] = run_program, timeout_s: int = 120,
1169
+ progress: Callable[[str], None] | None = None,
1170
+ lang: str = "f90") -> list[MutantResult]:
1171
+ """Run original and mutant of every audited program on each toolchain
1172
+ (see the module docstring); `runner` is run_program (tests pass a
1173
+ fake). `lang` "c" audits the C programs."""
1174
+ progs = audit_programs(version, only, lang)
1175
+ # the version flags of the matrix (AFAR 6.0: -fopenmp-version=60), so
1176
+ # the audit builds each program as the matrix does
1177
+ tcs = [for_version(TOOLCHAINS[t] if isinstance(t, str) else t, version,
1178
+ lang)
1179
+ for t in toolchains]
1180
+ out: list[MutantResult] = []
1181
+ jobs = [] # (prog, mutant, tc, mode)
1182
+ for p in progs:
1183
+ mut = neutralise(p)
1184
+ for tc in tcs:
1185
+ if mut is None:
1186
+ out.append(MutantResult(p.id, p.primary_atom, version,
1187
+ tc.name, "not_applicable", "", "",
1188
+ NO_MUTATION[p.primary_atom], xnack,
1189
+ body_sha=body_sha(p.source)))
1190
+ continue
1191
+ # A machine without unified memory (NVHPC on wingtip)
1192
+ # runs it in the requested mode
1193
+ mode = 1 if "zero_copy" in p.needs and 1 in tc.xnack_modes \
1194
+ else xnack
1195
+ jobs.append((p, mut, tc, mode))
1196
+ cache: dict[tuple, Result] = {} # originals, within this audit
1197
+
1198
+ def run(prog: Program, tc, mode: int) -> Result:
1199
+ r = runner(prog, tc, mode, opt, timeout_s=timeout_s, repeats=1)
1200
+ if "timeout" in r.reason: # once more (GPU contention)
1201
+ r = runner(prog, tc, mode, opt, timeout_s=timeout_s, repeats=1)
1202
+ return r
1203
+
1204
+ def original(prog, tc, mode):
1205
+ key = (prog.id, tc.name, mode)
1206
+ if key not in cache:
1207
+ cache[key] = run(prog, tc, mode)
1208
+ return cache[key]
1209
+
1210
+ def job(item) -> MutantResult:
1211
+ p, mut, tc, mode = item
1212
+ o = original(p, tc, mode)
1213
+ bad = illegal(mut, version)
1214
+ if bad:
1215
+ # a mutant that breaks a restriction is no evidence:
1216
+ # inconclusive, and it is not run
1217
+ return MutantResult(p.id, p.primary_atom, version, tc.name,
1218
+ "inconclusive", str(o.outcome), "",
1219
+ f"mutant is illegal: {', '.join(bad)}", mode,
1220
+ mutation_of(mut), o.reason, "", "",
1221
+ body_sha(p.source))
1222
+ m = run(mut, tc, mode)
1223
+ status, reason, kind = classify(o, m)
1224
+ if mode != xnack:
1225
+ reason = f"needs_zero_copy: run under HSA_XNACK={mode}; {reason}"
1226
+ if progress:
1227
+ progress(f"{p.id} {tc.name}: {status}")
1228
+ return MutantResult(p.id, p.primary_atom, version, tc.name, status,
1229
+ str(o.outcome), str(m.outcome), reason, mode,
1230
+ mutation_of(mut), o.reason, m.reason, kind,
1231
+ body_sha(p.source))
1232
+
1233
+ with ThreadPoolExecutor(max_workers=max(1, workers)) as pool:
1234
+ out += list(pool.map(job, jobs))
1235
+ return sorted(out, key=lambda r: (r.version, r.program_id, r.toolchain))
1236
+
1237
+
1238
+ def summarise(results: Sequence[MutantResult]) -> dict[str, dict]:
1239
+ """Per atom: status "valid" (killed on at least one toolchain), "weak"
1240
+ (survived on every toolchain where it was conclusive, at least one),
1241
+ "unknown" otherwise; the per-toolchain statuses; whether the atom has
1242
+ no mutation."""
1243
+ by: dict[str, list[MutantResult]] = {}
1244
+ for r in results:
1245
+ by.setdefault(r.primary_atom, []).append(r)
1246
+ out = {}
1247
+ for atom in sorted(by):
1248
+ rs = by[atom]
1249
+ sts = [r.status for r in rs]
1250
+ if "killed" in sts:
1251
+ status = "valid"
1252
+ elif "survived" in sts:
1253
+ status = "weak"
1254
+ else:
1255
+ status = "unknown"
1256
+ out[atom] = dict(
1257
+ status=status,
1258
+ toolchains={r.toolchain: r.status for r in rs},
1259
+ programs=sorted({r.program_id for r in rs}),
1260
+ not_applicable=all(s == "not_applicable" for s in sts))
1261
+ return out
1262
+
1263
+
1264
+ # --- the committed per-atom summary (tests/data/oracle_audit_summary.json) ----------
1265
+
1266
+ # Dispositions of a legal program's atom (``audit_summary``). Only "valid"
1267
+ # and "unaudited_no_passing_original" are acceptable for an observable
1268
+ # program whose evidence is construct, copy_mode_only or probe; the
1269
+ # no-oracle ones belong to programs whose evidence is that
1270
+ # category (header lines ``! no oracle (<kind>): <reason>`` and ``! basis:
1271
+ # ...``), the unverified ones to programs marked evidence="unverified"
1272
+ # (``! unverified (<kind>): <reason>``, offload.engine.gen.UNVERIFIED), legality
1273
+ # to a program whose oracle is an illegal variant.
1274
+ DISPOSITIONS = {
1275
+ "valid": "the mutant is killed on at least one toolchain (copy mode)",
1276
+ "unaudited_no_passing_original": "unaudited: no toolchain passes the "
1277
+ "original, so nothing can be credited "
1278
+ "to a toolchain",
1279
+ "hint_only": "no oracle can exist: the spec lets an implementation "
1280
+ "ignore the form (accepted; conforms vacuously)",
1281
+ "implementation_defined": "no oracle can exist: the spec leaves the "
1282
+ "behaviour to the implementation "
1283
+ "(implementation-defined)",
1284
+ "equivalent_default": "no oracle can exist: by the spec the form is the "
1285
+ "behaviour without it (equivalent to the default)",
1286
+ "legality": "the atom's only effect is a compile-time restriction; its "
1287
+ "oracle is the illegal variant named in the reason",
1288
+ "unverified_oracle_not_written": "a deterministic oracle for behaviour "
1289
+ "the spec fixes is possible but the "
1290
+ "program does not have it",
1291
+ "unverified_needs_other_infrastructure": "observable only outside the "
1292
+ "program's result (a tool "
1293
+ "interface, or a feature no "
1294
+ "toolchain provides; evidence "
1295
+ "unverified)",
1296
+ "not_observable": "the program checks no effect of the atom "
1297
+ "(observable=False)",
1298
+ "weak": "the mutant survived on every toolchain where the result was "
1299
+ "conclusive",
1300
+ "no_mutation": "the atom has no mutation (NO_MUTATION) but its program "
1301
+ "claims evidence",
1302
+ "unknown": "no conclusive result although an original passed",
1303
+ "missing": "no audit result for an audited program",
1304
+ }
1305
+
1306
+
1307
+ def body_sha(source: str) -> str:
1308
+ """sha256 of a program source without its leading header comment block
1309
+ (the audited text: a summary is stale when this changes)."""
1310
+ import hashlib
1311
+ lines = source.splitlines(keepends=True)
1312
+ k = 0
1313
+ while k < len(lines) and lines[k].startswith(("!", "//")):
1314
+ k += 1
1315
+ return hashlib.sha256("".join(lines[k:]).encode()).hexdigest()
1316
+
1317
+
1318
+ def audit_summary(results: Sequence[MutantResult], programs: Sequence[Program],
1319
+ version: int) -> dict[str, dict]:
1320
+ """Per legal program at `version` (keyed by primary atom): the audit
1321
+ status (summarise; "not_audited" for programs the audit skips), the
1322
+ per-toolchain statuses, originals and kill kinds, the program's
1323
+ evidence and observability, the sha of its body, and the disposition
1324
+ (DISPOSITIONS) with its reason."""
1325
+ from offload.engine.gen import NO_ORACLE, legality_of, no_oracle_of, \
1326
+ unverified_of
1327
+ rs = [r for r in results if r.version == version]
1328
+ sums = summarise(rs)
1329
+ by: dict[str, list[MutantResult]] = {}
1330
+ for r in rs:
1331
+ by.setdefault(r.primary_atom, []).append(r)
1332
+ out = {}
1333
+ for p in sorted((p for p in programs
1334
+ if p.legal and p.scope == "device"),
1335
+ key=lambda p: p.primary_atom):
1336
+ atom = p.primary_atom
1337
+ mine = sorted(by.get(atom, []), key=lambda r: r.toolchain)
1338
+ s = sums.get(atom)
1339
+ # an audited atom carries the sha of the program the audit ran (a
1340
+ # summary rebuilt from results of an older program is stale)
1341
+ shas = {r.body_sha for r in mine}
1342
+ entry = dict(
1343
+ program=p.id, evidence=p.evidence, observable=p.observable,
1344
+ body_sha=(shas.pop() if len(shas) == 1 else "mixed") if mine
1345
+ else body_sha(p.source),
1346
+ status=s["status"] if s else "not_audited",
1347
+ toolchains={r.toolchain: r.status for r in mine},
1348
+ originals={r.toolchain: r.original for r in mine},
1349
+ kill_kinds=sorted({r.kill_kind for r in mine
1350
+ if r.status == "killed"}),
1351
+ xnack=max((r.xnack for r in mine), default=0))
1352
+ unv = unverified_of(p.source)
1353
+ if p.evidence == "unverified":
1354
+ kind, why = unv if unv else ("missing_reason", "")
1355
+ disp, reason = f"unverified_{kind}", why
1356
+ elif p.evidence in NO_ORACLE:
1357
+ noo = no_oracle_of(p.source)
1358
+ disp = p.evidence
1359
+ reason = f"{noo[1]} [basis: {noo[2]}]" if noo else ""
1360
+ elif p.evidence == "legality":
1361
+ disp = "legality"
1362
+ reason = (f"its oracle is the illegal variant of "
1363
+ f"{legality_of(p.source)}")
1364
+ elif not p.observable:
1365
+ disp = "not_observable"
1366
+ m = re.search(r"^(?:!|//) observable: no(?: \((.*)\))?$",
1367
+ p.source, re.M)
1368
+ reason = (m.group(1) or "") if m else ""
1369
+ elif not mine:
1370
+ disp, reason = "missing", ""
1371
+ elif s["status"] == "valid":
1372
+ disp = "valid"
1373
+ reason = "" if "check_fail" in entry["kill_kinds"] else (
1374
+ "valid only via " + ", ".join(entry["kill_kinds"]))
1375
+ elif s["not_applicable"]:
1376
+ disp, reason = "no_mutation", NO_MUTATION.get(atom, "")
1377
+ elif s["status"] == "unknown" and all(
1378
+ r.original != str(Outcome.correct_on_device) for r in mine):
1379
+ disp = "unaudited_no_passing_original"
1380
+ reason = "; ".join(f"{r.toolchain}: {r.original}" for r in mine)
1381
+ else:
1382
+ disp, reason = s["status"], ""
1383
+ entry.update(disposition=disp, reason=reason)
1384
+ out[atom] = entry
1385
+ return out
1386
+
1387
+
1388
+ # --- Map mutants, judged in both memory modes --------------------------
1389
+
1390
+ MAP_KINDS = ("weaken_map", "swap_to_from", "drop_map", "drop_update")
1391
+ MAP_KIND_TEXT = {
1392
+ "weaken_map": "every map type of a tested item made alloc (release on "
1393
+ "target exit data; OpenACC: copy, copyin, copyout -> "
1394
+ "create): the mapping stays, its transfers go",
1395
+ "swap_to_from": "to <-> from on the map types of target and target data "
1396
+ "and on the motion clauses of target update (OpenACC: "
1397
+ "copyin <-> copyout, update host <-> device)",
1398
+ "drop_map": "every map clause of a tested item deleted (OpenACC: copy, "
1399
+ "copyin, copyout, create); a data directive left without "
1400
+ "one is commented out with its end",
1401
+ "drop_update": "every target update (OpenACC: update) directive "
1402
+ "commented out",
1403
+ }
1404
+ _MAP_TYPES = frozenset({"to", "from", "tofrom", "alloc", "release", "delete",
1405
+ "storage"})
1406
+ _DATA_DIRS = frozenset({"target enter data", "target exit data",
1407
+ "target data", "enter data", "exit data", "data"})
1408
+ _ACC_DATA = ("copy", "copyin", "copyout", "create")
1409
+
1410
+
1411
+ def _map_type_piece(x: _Dir, c: _Clause):
1412
+ """(start, stop) of the map-type piece of map clause `c` (a piece
1413
+ before the colon that is a map type), or None; and the colon's
1414
+ position (None: no modifiers, the whole argument is the list)."""
1415
+ text = x.logical[c.arg[0]:c.arg[1]]
1416
+ pieces, seps = _pieces(text, c.arg[0])
1417
+ colons = [k for k, (_, ch) in enumerate(seps) if ch == ":"]
1418
+ if not colons:
1419
+ return None, None
1420
+ last = colons[-1]
1421
+ for ps, pe in pieces[:last + 1]:
1422
+ if x.logical[ps:pe].strip().lower() in _MAP_TYPES:
1423
+ return (ps, pe), seps[last][0]
1424
+ return None, seps[last][0]
1425
+
1426
+
1427
+ def _map_edits(x: _Dir, kind: str, version: int) -> list[tuple]:
1428
+ """The logical edits of one directive for an OpenMP map mutant."""
1429
+ name = base_name(x.d.name)
1430
+ out = []
1431
+ for c, parsed in zip(_clauses(x, version), x.d.clauses):
1432
+ everything, some = _infra(parsed)
1433
+ if kind == "swap_to_from" and name == "target update" \
1434
+ and c.name in ("to", "from") and not some:
1435
+ out.append((c.start, c.start + len(c.name),
1436
+ "from" if c.name == "to" else "to"))
1437
+ continue
1438
+ if c.name != "map" or c.arg is None or everything or some:
1439
+ continue
1440
+ piece, _colon = _map_type_piece(x, c)
1441
+ typ = x.logical[piece[0]:piece[1]].lower() if piece else None
1442
+ if kind == "weaken_map":
1443
+ weak = "release" if name == "target exit data" else (
1444
+ "storage" if version >= 60 and "storage" in x.logical.lower()
1445
+ else "alloc")
1446
+ if typ in ("to", "from", "tofrom"):
1447
+ out.append((piece[0], piece[1], weak))
1448
+ elif typ is None and _colon is None:
1449
+ out.append((c.arg[0], c.arg[0], f"{weak}: "))
1450
+ elif kind == "swap_to_from" and name in ("target", "target data") \
1451
+ and typ in ("to", "from"):
1452
+ out.append((piece[0], piece[1], "from" if typ == "to" else "to"))
1453
+ return out
1454
+
1455
+
1456
+ def _acc_edits(x: _Dir, kind: str, version: int) -> list[tuple]:
1457
+ """The logical edits of one directive for an OpenACC map mutant."""
1458
+ out = []
1459
+ for c, parsed in zip(_clauses(x, version), x.d.clauses):
1460
+ everything, some = _infra(parsed)
1461
+ if everything or some:
1462
+ continue
1463
+ if kind == "weaken_map" and c.name in ("copy", "copyin", "copyout"):
1464
+ out.append((c.start, c.start + len(c.name), "create"))
1465
+ elif kind == "swap_to_from" and c.name in ("copyin", "copyout"):
1466
+ out.append((c.start, c.start + len(c.name),
1467
+ "copyout" if c.name == "copyin" else "copyin"))
1468
+ elif kind == "swap_to_from" and base_name(x.d.name) == "update" \
1469
+ and c.name in ("host", "self", "device"):
1470
+ out.append((c.start, c.start + len(c.name),
1471
+ "host" if c.name == "device" else "device"))
1472
+ return out
1473
+
1474
+
1475
+ def map_mutants(prog: Program) -> dict[str, Program]:
1476
+ """The map mutants of `prog` (MAP_KINDS: weaken the map types,
1477
+ swap to and from, drop the map clauses, drop target update), each the
1478
+ program with that change on every directive outside the
1479
+ infrastructure (the probe, the shared-memory detector, the reporting
1480
+ variables' clauses). A kind that changes nothing, or whose edit the
1481
+ mutation machinery cannot place, is left out. Ids
1482
+ ``<id>-map-<kind>``; header ``! MAP MUTANT of <id>: <kind> (...)``."""
1483
+ version = int(re.match(r"(?:omp|acc)(\d\d)-", prog.id).group(1))
1484
+ acc = model_of(version) == "acc"
1485
+ lang = "c" if prog.lang == "c" else "f90"
1486
+ lines = prog.source.split("\n")
1487
+ dirs = _scan(lines, version, lang)
1488
+ _pair(dirs, prog.source, version)
1489
+ update = "update" if acc else "target update"
1490
+ out = {}
1491
+ for kind in MAP_KINDS:
1492
+ edits: dict[int, tuple] = {}
1493
+ try:
1494
+ for x in dirs:
1495
+ if x.protected or x.d.end:
1496
+ continue
1497
+ name = base_name(x.d.name)
1498
+ if kind == "drop_update":
1499
+ if name == update:
1500
+ _comment_pair(edits, lines, x)
1501
+ continue
1502
+ if kind == "drop_map":
1503
+ dedits = []
1504
+ for cname in (_ACC_DATA if acc else ("map",)):
1505
+ dedits += [e for e in _clause_edits(x, cname,
1506
+ version)
1507
+ if e != INFRA_KEPT]
1508
+ else:
1509
+ dedits = (_acc_edits if acc else _map_edits)(
1510
+ x, kind, version)
1511
+ if not dedits:
1512
+ continue
1513
+ new = _apply(lines, x, dedits)
1514
+ after = _parse_lines(new, version)
1515
+ left = after is not None and any(
1516
+ c.name in (_ACC_DATA + ("present", "no_create",
1517
+ "attach", "delete",
1518
+ "detach", "deviceptr")
1519
+ if acc else ("map", "use_device_ptr",
1520
+ "use_device_addr"))
1521
+ for c in after.clauses)
1522
+ if after is None or (name in _DATA_DIRS and not left):
1523
+ _comment_pair(edits, lines, x)
1524
+ else:
1525
+ edits[x.first] = (x.last - x.first + 1, new)
1526
+ except MutationError:
1527
+ continue
1528
+ if not edits:
1529
+ continue
1530
+ new_lines = _rebuild(lines, edits)
1531
+ lead = "//" if lang == "c" else "!"
1532
+ new_lines.insert(1, f"{lead} MAP MUTANT of {prog.id}: {kind} "
1533
+ f"({MAP_KIND_TEXT[kind]})")
1534
+ source = "\n".join(new_lines)
1535
+ try:
1536
+ tree = parse_program(source, version,
1537
+ lang="c" if lang == "c" else None)
1538
+ atoms = frozenset().union(
1539
+ *(d.atoms for d in tree.directives),
1540
+ *(d.atoms for d in tree.ends.values()))
1541
+ except (GrammarError, ProgramError):
1542
+ atoms = prog.atoms
1543
+ out[kind] = dataclasses.replace(prog, id=f"{prog.id}-map-{kind}",
1544
+ source=source, atoms=atoms)
1545
+ return out
1546
+
1547
+
1548
+ @dataclass(frozen=True)
1549
+ class MapMutantResult:
1550
+ program_id: str
1551
+ primary_atom: str
1552
+ version: int
1553
+ lang: str
1554
+ toolchain: str
1555
+ kind: str # MAP_KINDS
1556
+ xnack: int
1557
+ status: str # killed | survived | inconclusive
1558
+ original: str
1559
+ mutant: str
1560
+ reason: str
1561
+ kill_kind: str = ""
1562
+ body_sha: str = ""
1563
+
1564
+
1565
+ def map_audit(version: int, toolchains: Sequence[str],
1566
+ modes: Sequence[int] = (0, 1), opt: str = "-O2",
1567
+ workers: int = 8,
1568
+ only: Callable[[Program], bool] | None = None, *,
1569
+ runner: Callable[..., Result] = run_program,
1570
+ timeout_s: int = 120,
1571
+ progress: Callable[[str], None] | None = None,
1572
+ lang: str = "f90") -> list[MapMutantResult]:
1573
+ """Run the original and every map mutant of each audited
1574
+ program (audit_programs) on each toolchain in each memory mode
1575
+ (HSA_XNACK=0 and 1), one repeat (a timeout once more), and classify
1576
+ as the primary-atom audit does (classify). A program that needs
1577
+ zero-copy memory is not run at HSA_XNACK=0 (inconclusive,
1578
+ needs_zero_copy); an illegal mutant is not run (inconclusive)."""
1579
+ progs = audit_programs(version, only, lang)
1580
+ tcs = [for_version(TOOLCHAINS[t] if isinstance(t, str) else t, version,
1581
+ lang) for t in toolchains]
1582
+ jobs = []
1583
+ for p in progs:
1584
+ muts = map_mutants(p)
1585
+ for kind, m in muts.items():
1586
+ bad = illegal(m, version)
1587
+ for tc in tcs:
1588
+ for x in modes:
1589
+ jobs.append((p, kind, m, tc, x, bad))
1590
+ cache: dict[tuple, Result] = {}
1591
+ lock = __import__("threading").Lock()
1592
+
1593
+ def run(prog, tc, x):
1594
+ r = runner(prog, tc, x, opt, timeout_s=timeout_s, repeats=1)
1595
+ if "timeout" in r.reason:
1596
+ r = runner(prog, tc, x, opt, timeout_s=timeout_s, repeats=1)
1597
+ return r
1598
+
1599
+ def original(p, tc, x):
1600
+ key = (p.id, tc.name, x)
1601
+ with lock:
1602
+ have = cache.get(key)
1603
+ if have is None:
1604
+ have = run(p, tc, x)
1605
+ with lock:
1606
+ cache.setdefault(key, have)
1607
+ return have
1608
+
1609
+ def job(item) -> MapMutantResult:
1610
+ p, kind, m, tc, x, bad = item
1611
+ base = dict(program_id=p.id, primary_atom=p.primary_atom,
1612
+ version=version, lang=lang, toolchain=tc.name, kind=kind,
1613
+ xnack=x, body_sha=body_sha(p.source))
1614
+ if x == 0 and "zero_copy" in p.needs:
1615
+ return MapMutantResult(**base, status="inconclusive",
1616
+ original="", mutant="",
1617
+ reason="needs_zero_copy: not run at "
1618
+ "HSA_XNACK=0")
1619
+ o = original(p, tc, x)
1620
+ if bad:
1621
+ return MapMutantResult(**base, status="inconclusive",
1622
+ original=str(o.outcome), mutant="",
1623
+ reason="mutant is illegal: "
1624
+ + ", ".join(bad))
1625
+ r = run(m, tc, x)
1626
+ status, reason, kk = classify(o, r)
1627
+ if progress:
1628
+ progress(f"{p.id} {kind} {tc.name} x{x}: {status}")
1629
+ return MapMutantResult(**base, status=status, original=str(o.outcome),
1630
+ mutant=str(r.outcome), reason=reason,
1631
+ kill_kind=kk)
1632
+
1633
+ with ThreadPoolExecutor(max_workers=max(1, workers)) as pool:
1634
+ out = list(pool.map(job, jobs))
1635
+ return sorted(out, key=lambda r: (r.version, r.program_id, r.kind,
1636
+ r.toolchain, r.xnack))
1637
+
1638
+
1639
+ def _caught(x0: bool, x1: bool) -> str:
1640
+ return {(True, True): "both", (True, False): "x0_only",
1641
+ (False, True): "x1_only"}.get((x0, x1), "neither")
1642
+
1643
+
1644
+ def map_summary(results: Sequence[MapMutantResult]) -> dict[str, dict]:
1645
+ """Per primary atom (one version and language at a time): for each map
1646
+ kind the mode that catches it (``both``, ``x0_only``, ``x1_only``,
1647
+ ``neither``: killed on some toolchain in that mode; ``inconclusive``:
1648
+ no run of the kind was conclusive), ``caught`` over
1649
+ all kinds, ``copy_mode_only`` (the kinds caught at HSA_XNACK=0 only: a
1650
+ valid oracle of the mapping in copy mode, blind to it under unified
1651
+ memory) and the per-kind, per-toolchain, per-mode statuses."""
1652
+ by: dict[str, list[MapMutantResult]] = {}
1653
+ for r in results:
1654
+ by.setdefault(r.primary_atom, []).append(r)
1655
+ out = {}
1656
+ for atom in sorted(by):
1657
+ rs = by[atom]
1658
+ kinds, detail = {}, {}
1659
+ for k in MAP_KINDS:
1660
+ mine = [r for r in rs if r.kind == k]
1661
+ if not mine:
1662
+ continue
1663
+ kill = {x: any(r.status == "killed" for r in mine
1664
+ if r.xnack == x) for x in (0, 1)}
1665
+ kinds[k] = _caught(kill[0], kill[1])
1666
+ if kinds[k] == "neither" and not any(
1667
+ r.status == "survived" for r in mine):
1668
+ kinds[k] = "inconclusive" # no conclusive run at all
1669
+ detail[k] = {f"{r.toolchain}_x{r.xnack}": r.status
1670
+ for r in mine}
1671
+ x0 = any(v in ("both", "x0_only") for v in kinds.values())
1672
+ x1 = any(v in ("both", "x1_only") for v in kinds.values())
1673
+ caught = _caught(x0, x1)
1674
+ if caught == "neither" and all(v == "inconclusive"
1675
+ for v in kinds.values()):
1676
+ caught = "inconclusive"
1677
+ out[atom] = dict(program=rs[0].program_id, kinds=kinds,
1678
+ caught=caught,
1679
+ copy_mode_only=[k for k, v in kinds.items()
1680
+ if v == "x0_only"],
1681
+ detail=detail)
1682
+ return out
1683
+
1684
+
1685
+ # --- Checks that read uninitialized from-mapped items --
1686
+ #
1687
+ # A check that reads an item the program maps back (map from or tofrom)
1688
+ # but the host never assigns before the region judges a mutant that loses
1689
+ # the copy-back on uninitialized memory: the mutant may survive by chance.
1690
+ # Detected from the text (Fortran and C, the catalog's generated forms).
1691
+
1692
+ _MUTANT_LINE = re.compile(r"^\s*(?:!|//) MUTANT\b")
1693
+ _FROM_MAP = re.compile(
1694
+ r"\bmap\s*\(\s*((?:[\w-]+\s*,\s*)*)(from|tofrom)\s*:\s*([^)]*(?:\([^)]*\)[^)]*)*)\)",
1695
+ re.I)
1696
+ _DIRECTIVE = re.compile(r"^\s*(?:!\$omp|!\$acc|#\s*pragma\s+omp)\b", re.I)
1697
+ # OpenACC: copyout and copy move data back to the host
1698
+ _ACC_FROM = re.compile(r"\b(?:copyout|copy)\s*\(([^)]*(?:\([^)]*\)[^)]*)*)\)",
1699
+ re.I)
1700
+
1701
+
1702
+ def _live(source: str) -> list[str]:
1703
+ return [ln for ln in source.split("\n") if not _MUTANT_LINE.match(ln)]
1704
+
1705
+
1706
+ def _split_items(text: str) -> list[str]:
1707
+ out, depth, cur = [], 0, ""
1708
+ for ch in text:
1709
+ if ch in "([":
1710
+ depth += 1
1711
+ elif ch in ")]":
1712
+ depth -= 1
1713
+ if ch == "," and depth == 0:
1714
+ out.append(cur)
1715
+ cur = ""
1716
+ else:
1717
+ cur += ch
1718
+ out.append(cur)
1719
+ return [re.split(r"[(\[%.]", x.strip(), maxsplit=1)[0].strip()
1720
+ for x in out if x.strip()]
1721
+
1722
+
1723
+ def _directive_maps(lines: list[str]) -> list[tuple[int, list[str]]]:
1724
+ """(index of the directive's first line, from/tofrom item names) per
1725
+ directive, continuation lines joined (Fortran &, C backslash)."""
1726
+ return [(i, names) for i, names, _ in _directive_items(lines) if names]
1727
+
1728
+
1729
+ def _directive_text(lines: list[str], i: int) -> tuple[str, int]:
1730
+ """The directive starting at line i with its continuation lines joined
1731
+ (Fortran &, C backslash), and the index of its last line."""
1732
+ j, text = i, lines[i]
1733
+ while re.search(r"(&|\\)\s*$", lines[j]) and j + 1 < len(lines):
1734
+ j += 1
1735
+ text = re.sub(r"(&|\\)\s*$", " ", text) + " " + re.sub(
1736
+ r"^\s*(?:!\$omp&|!\$acc&|&)", "", lines[j])
1737
+ return text, j
1738
+
1739
+
1740
+ def _directive_items(lines: list[str]) -> list[tuple[int, list[str],
1741
+ set[str]]]:
1742
+ """(first line, from/tofrom item names, the names mapped as a C array
1743
+ section) of every directive."""
1744
+ out, i = [], 0
1745
+ while i < len(lines):
1746
+ if _DIRECTIVE.match(lines[i]):
1747
+ text, j = _directive_text(lines, i)
1748
+ names, raw = [], []
1749
+ for m in _FROM_MAP.finditer(text):
1750
+ raw.append(m.group(3))
1751
+ if re.match(r"^\s*!\$acc", lines[i], re.I):
1752
+ for m in _ACC_FROM.finditer(text):
1753
+ raw.append(m.group(1))
1754
+ sections = set()
1755
+ for r in raw:
1756
+ names += _split_items(r)
1757
+ if lines[i].lstrip().startswith("#"):
1758
+ sections |= {re.split(r"\[", x.strip(), maxsplit=1)[0]
1759
+ .strip() for x in _split_raw(r)
1760
+ if "[" in x}
1761
+ out.append((i, names, sections))
1762
+ i = j + 1
1763
+ else:
1764
+ i += 1
1765
+ return out
1766
+
1767
+
1768
+ def _split_raw(text: str) -> list[str]:
1769
+ out, depth, cur = [], 0, ""
1770
+ for ch in text:
1771
+ if ch in "([":
1772
+ depth += 1
1773
+ elif ch in ")]":
1774
+ depth -= 1
1775
+ if ch == "," and depth == 0:
1776
+ out.append(cur)
1777
+ cur = ""
1778
+ else:
1779
+ cur += ch
1780
+ out.append(cur)
1781
+ return [x for x in out if x.strip()]
1782
+
1783
+
1784
+ # C directives without an associated statement: the line after them is
1785
+ # host code
1786
+ _C_STANDALONE = re.compile(
1787
+ r"#\s*pragma\s+omp\s+(?:target\s+(?:enter|exit)\s+data|target\s+update|"
1788
+ r"taskwait|barrier|flush|taskyield|depobj|cancel|cancellation|"
1789
+ r"interop|error|scan|assume\b|nothing|requires|declare)\b", re.I)
1790
+ # directives that open a device region (the code inside runs on the device)
1791
+ _F_DEVICE = re.compile(r"^\s*!\$(?:omp\s+target(?!\s+(?:data|enter|exit|"
1792
+ r"update)\b)|acc\s+(?:parallel|kernels|serial)\b)",
1793
+ re.I)
1794
+ _F_DEVICE_END = re.compile(r"^\s*!\$(?:omp\s+end\s+target(?!\s+data\b)|"
1795
+ r"acc\s+end\s+(?:parallel|kernels|serial)\b)",
1796
+ re.I)
1797
+ _C_DEVICE = re.compile(r"^\s*#\s*pragma\s+omp\s+target(?!\s+(?:data|enter|"
1798
+ r"exit|update)\b)", re.I)
1799
+
1800
+
1801
+ def _c_statement_end(lines: list[str], k: int) -> int:
1802
+ """Index of the last line of the C statement (a block or a loop)
1803
+ starting after line k."""
1804
+ depth, k = 0, k + 1
1805
+ while k < len(lines):
1806
+ depth += lines[k].count("{") - lines[k].count("}")
1807
+ if depth <= 0 and (lines[k].rstrip().endswith((";", "}"))):
1808
+ break
1809
+ k += 1
1810
+ return k
1811
+
1812
+
1813
+ def _host_lines(lines: list[str]) -> list[bool]:
1814
+ """Per line: whether it is host code of the main program (not a
1815
+ module, a function before main or an internal procedure, not inside a
1816
+ device region: a target construct, an OpenACC compute construct)."""
1817
+ n = len(lines)
1818
+ host = [True] * n
1819
+ start = next((k for k, ln in enumerate(lines)
1820
+ if re.match(r"^\s*program\b", ln, re.I)
1821
+ or re.match(r"^\s*int\s+main\s*\(", ln)), 0)
1822
+ for k in range(start):
1823
+ host[k] = False
1824
+ contains = next((k for k in range(start, n)
1825
+ if re.match(r"^\s*contains\s*$", lines[k], re.I)), None)
1826
+ if contains is not None:
1827
+ for k in range(contains, n):
1828
+ host[k] = False
1829
+ k = start
1830
+ while k < n:
1831
+ ln = lines[k]
1832
+ if _F_DEVICE.match(ln):
1833
+ _, j = _directive_text(lines, k)
1834
+ e = next((q for q in range(j + 1, n) if _F_DEVICE_END.match(
1835
+ lines[q])), None)
1836
+ if e is None: # a combined loop construct without end directive
1837
+ e, depth = j + 1, 0
1838
+ while e < n:
1839
+ low = lines[e].strip().lower()
1840
+ if re.match(r"do\b", low):
1841
+ depth += 1
1842
+ elif re.match(r"end\s*do\b", low):
1843
+ depth -= 1
1844
+ if depth <= 0:
1845
+ break
1846
+ e += 1
1847
+ for q in range(k, min(e, n - 1) + 1):
1848
+ host[q] = False
1849
+ k = e + 1
1850
+ elif _C_DEVICE.match(ln):
1851
+ _, j = _directive_text(lines, k)
1852
+ e = _c_statement_end(lines, j)
1853
+ for q in range(k, min(e, n - 1) + 1):
1854
+ host[q] = False
1855
+ k = e + 1
1856
+ else:
1857
+ k += 1
1858
+ return host
1859
+
1860
+
1861
+ def _check_text(lines: list[str]) -> str:
1862
+ """The host code that computes the check: every line after the last
1863
+ OpenMP directive and the region it opens (Fortran: the directive's
1864
+ continuation lines; C: the statement or block after the pragma, none
1865
+ after a standalone directive such as target exit data), the RESULT
1866
+ print excluded."""
1867
+ last = max((k for k, ln in enumerate(lines) if _DIRECTIVE.match(ln)),
1868
+ default=-1)
1869
+ if last < 0:
1870
+ return ""
1871
+ _, k = _directive_text(lines, last)
1872
+ if lines[last].lstrip().startswith("#") and not _C_STANDALONE.match(
1873
+ lines[last].lstrip()):
1874
+ # skip the statement the pragma applies to (a block or a loop)
1875
+ k = _c_statement_end(lines, k)
1876
+ return " ".join(ln for ln in lines[k + 1:] if "RESULT" not in ln)
1877
+
1878
+
1879
+ def _assigned_before(lines: list[str], k: int, name: str,
1880
+ section: bool = False, host=None) -> bool:
1881
+ """Whether host code of the main program before line k sets `name`:
1882
+ an assignment to it, an element or a component of it (Fortran
1883
+ ``x(..)``, ``s%c``; C ``x[..][..]``, ``s.c``), a declaration or DATA
1884
+ initializer, a call that receives it, memset. A C array `section`
1885
+ (``p[0:n]``) needs its elements set: assigning the pointer is not
1886
+ enough."""
1887
+ n = re.escape(name)
1888
+ host = host if host is not None else _host_lines(lines)
1889
+ sub = r"(?:\s*(?:\([^=]*?\)|\[[^\]=]*\]|%\s*\w+|\.\s*[A-Za-z_]\w*))"
1890
+ elem = rf"{sub}+" if section else rf"{sub}*"
1891
+ assign = re.compile(
1892
+ rf"(?:^\s*|[;{{)]\s*|\b(?:int|long|double|float|bool|char|unsigned|"
1893
+ rf"short|struct\s+\w+)\s+(?:[\w\s,*=\[\]]*,\s*)?\**\s*){n}{elem}"
1894
+ rf"\s*=(?!=)", re.M)
1895
+ decl = re.compile(rf"::.*\b{n}\s*(?:\([^)]*\))?\s*=(?!=)")
1896
+ data = re.compile(rf"^\s*data\b.*\b{n}\b", re.I)
1897
+ call = re.compile(rf"^\s*call\s+\w+\s*\(.*\b{n}\b", re.I)
1898
+ cfun = re.compile(rf"\b\w+\s*\([^;]*(?<!&)&(?!&)\s*{n}\b|"
1899
+ rf"\bmemset\s*\(\s*{n}\b")
1900
+ for q, ln in enumerate(lines[:k]):
1901
+ if not host[q]:
1902
+ continue
1903
+ if assign.search(ln) or data.search(ln) or call.search(ln) \
1904
+ or cfun.search(ln):
1905
+ return True
1906
+ if not section and decl.search(ln):
1907
+ return True
1908
+ return False
1909
+
1910
+
1911
+ def uninit_from_items(source: str) -> list[str]:
1912
+ """Items mapped from or tofrom whose value the check reads and that the
1913
+ host never sets before the directive that maps them (sorted; host code
1914
+ of the main program only, see _assigned_before)."""
1915
+ lines = _live(source)
1916
+ check = _check_text(lines)
1917
+ host = _host_lines(lines)
1918
+ out = set()
1919
+ for k, names, sections in _directive_items(lines):
1920
+ for name in names:
1921
+ if (re.search(rf"\b{re.escape(name)}\b", check)
1922
+ and not _assigned_before(lines, k, name,
1923
+ name in sections, host)):
1924
+ out.add(name)
1925
+ return sorted(out)
1926
+
1927
+
1928
+ def uninit_affected(original: str, mutant: str) -> list[str]:
1929
+ """The uninitialized from-mapped items of `original` (uninit_from_items)
1930
+ that the mutant no longer maps from or tofrom: its check reads them
1931
+ without a copy-back."""
1932
+ still = {n for _, names in _directive_maps(_live(mutant)) for n in names}
1933
+ return [n for n in uninit_from_items(original) if n not in still]