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,2098 @@
1
+ """A generic reducer for failing device-offload programs (tools/reduce.py).
2
+
3
+ A failing program (C or Fortran, OpenMP or OpenACC, one file or a
4
+ ``!@file``/``//@file`` bundle) shrinks by delta debugging. An accepted
5
+ candidate must pass the legality witness (``offload.engine.prerun.rules_witness``,
6
+ never the compiler under test), fail on every target with the recorded
7
+ ``Signature`` (outcome class and normalized message) and still compile.
8
+ Candidates come from registered steps (``STEPS``, added with ``step``) at four
9
+ levels: statements and blocks, clauses, expressions, cleanup. A fuzz-format
10
+ Fortran program uses the steps of ``offload.engine.fuzz_min``. ``greedy`` takes
11
+ the first candidate that keeps the failure and restarts the level. A
12
+ ``Judge`` caches by text and logs rows, a ``Budget`` limits the work and a
13
+ ``checkpoint`` resumes a stopped run. ``controls`` writes one-change edits
14
+ of the result that pass on every target. ``FuzzContract`` rewrites a fuzz
15
+ checksum line into a ``check=pass|fail`` line."""
16
+ from __future__ import annotations
17
+
18
+ import dataclasses
19
+ import hashlib
20
+ import json
21
+ import random
22
+ import re
23
+ import time
24
+ from collections import Counter
25
+ from concurrent.futures import ThreadPoolExecutor
26
+ from dataclasses import dataclass, field
27
+ from pathlib import Path
28
+ from typing import Callable, Iterable
29
+
30
+ from offload.engine import finding_rows as R
31
+ from offload.engine import gate as G
32
+ from offload.engine.site import matrix as M
33
+
34
+ from offload.engine.root import ROOT # noqa: E402
35
+
36
+
37
+ class NoFailure(Exception):
38
+ """The program does not fail (with the wanted signature) on the target."""
39
+
40
+
41
+ class BudgetExhausted(Exception):
42
+ """The candidate or time budget is spent."""
43
+
44
+
45
+ class RunnerUnavailable(Exception):
46
+ """This node cannot run the target (no GPU, another machine)."""
47
+
48
+
49
+ class NoReference(Exception):
50
+ """A wrong-answer reduction of a self-checking program needs an output
51
+ oracle (``--reference elision`` or ``host:TOOLCHAIN``)."""
52
+
53
+
54
+ class CheckpointMismatch(Exception):
55
+ """The checkpoint was written for other targets, levels, witness or
56
+ signature."""
57
+
58
+
59
+ # --- targets and signatures ---------------------------------------------------------
60
+
61
+ @dataclass(frozen=True)
62
+ class Target:
63
+ """A toolchain, its memory-mode row key (HSA_XNACK on AMD; 0 copy and
64
+ 2 managed on wingtip, as the campaigns record them) and the
65
+ optimisation level."""
66
+ toolchain: str
67
+ mode: int
68
+ opt: str = "-O2"
69
+
70
+ @property
71
+ def label(self) -> str:
72
+ return f"{self.toolchain}/{self.mode}"
73
+
74
+ @staticmethod
75
+ def parse(text: str, opt: str = "-O2") -> "Target":
76
+ """``nvhpc/0``; ``nvhpc`` alone takes the toolchain's first mode."""
77
+ name, _, mode = text.partition("/")
78
+ if mode == "":
79
+ from offload.engine.toolchains import TOOLCHAINS
80
+ if name not in TOOLCHAINS:
81
+ raise ValueError(f"unknown toolchain {name!r}")
82
+ mode = str(TOOLCHAINS[name].xnack_modes[0])
83
+ return Target(name, int(mode), opt)
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class Signature:
88
+ cls: str # the gate's outcome class: ice, rejected, crash, run_error, wrong_answer, timeout, ...
89
+ key: str # the site matrix's normalized line (or the gate's failure key)
90
+
91
+ def __str__(self) -> str:
92
+ return f"{self.cls}: {self.key}" if self.key else self.cls
93
+
94
+
95
+ _IGNORED_RUN = re.compile(r"; run: (\w+): ?(.*)$", re.S)
96
+
97
+
98
+ def run_class(row: dict) -> str:
99
+ """The gate's outcome class of a row, except that an ``accepted_ignored``
100
+ row (the compiler ignored a clause with a note and the program ran) is
101
+ classed by its run (the ``run: <outcome>: <reason>`` tail of the
102
+ reason): the note is not the failure, the run's result is."""
103
+ d = R.normalize_row(dict(row))
104
+ cls = G.outcome_class(d)
105
+ if cls == "accepted_ignored":
106
+ m = _IGNORED_RUN.search(str(d.get("reason") or ""))
107
+ if m:
108
+ from offload.engine.confirm.model import outcome_of_result
109
+ try:
110
+ return outcome_of_result(m.group(1), m.group(2))
111
+ except ValueError:
112
+ return cls
113
+ return cls
114
+
115
+
116
+ def signature(row: dict) -> Signature:
117
+ """The signature of a recorded row of any format (a harness row, a gate
118
+ rerun, a fuzz or stress result): the gate's class (``run_class``) and,
119
+ for a compile failure or a wrong answer, the normalized line
120
+ tools/repo/check_findings.py compares (offload.engine.site.matrix.signature); a
121
+ crash keeps its signal, a run error its exit kind, a timeout its
122
+ stage."""
123
+ d = R.normalize_row(dict(row))
124
+ cls = run_class(d)
125
+ if cls in ("crash", "run_error", "timeout") and G.outcome_class(d) == "accepted_ignored":
126
+ m = _IGNORED_RUN.search(str(d.get("reason") or ""))
127
+ d = dict(d, outcome="wrong_answer", reason=m.group(2) if m else d.get("reason"))
128
+ if cls in ("rejected", "ice", "accepted_ignored", "wrong_answer"):
129
+ return Signature(cls, M.signature(R._stage_text(d, cls))[1])
130
+ if cls in ("crash", "run_error"):
131
+ # finer than the gate's key (the exit kind or signal alone): the normalized first error
132
+ # line of the run, so another run-time failure of the same kind is another failure
133
+ line = _run_line(d)
134
+ return Signature(cls, G.failure_key(d, cls) + (f" | {line}" if line else ""))
135
+ if cls == "timeout":
136
+ return Signature(cls, G.failure_key(d, cls))
137
+ return Signature(cls, "")
138
+
139
+
140
+ _RUN_MARK = re.compile(r"^--- run\b.*$", re.M)
141
+ _RUN_DIAG = re.compile(r"error|fatal|fault|abort|illegal|exception|signal|terminat|assert", re.I)
142
+
143
+
144
+ def _run_line(d: dict) -> str:
145
+ """The normalized first error line of a run's output (the text after the
146
+ harness's ``--- run`` marker, else the recorded output), '' when none."""
147
+ text = "\n".join(str(d.get(k) or "") for k in ("stderr", "output", "stdout"))
148
+ m = _RUN_MARK.search(text)
149
+ if m:
150
+ text = text[m.end():]
151
+ lines = [x.strip() for x in text.splitlines() if x.strip() and not x.startswith("---")
152
+ and not x.strip().startswith("RESULT")]
153
+ pick = next((x for x in lines if _RUN_DIAG.search(x)), lines[0] if lines else "")
154
+ return M._norm(pick)
155
+
156
+
157
+ def is_failure(row: dict) -> bool:
158
+ return G.is_failure(run_class(row))
159
+
160
+
161
+ def failures_of_record(rec: dict, mode: int) -> list[tuple[Target, Signature]]:
162
+ """(target, signature) of each failing toolchain of a fuzz campaign
163
+ record (results/fuzz/<run>/programs.jsonl: ``results`` per toolchain).
164
+ A wrong checksum is the ``check=fail`` signature the reduced,
165
+ self-checking program will show (FuzzContract)."""
166
+ out = []
167
+ for tc, r in (rec.get("results") or {}).items():
168
+ d = dict(r, toolchain=tc, program_id=str(rec.get("seed", "")), xnack=mode)
169
+ cls = G.outcome_class(d)
170
+ if not G.is_failure(cls):
171
+ continue
172
+ if cls == "wrong_answer":
173
+ host = r.get("ran_on_host") or 0
174
+ sig = Signature(cls, f"result ran_on_host={host} check=fail")
175
+ else:
176
+ sig = signature(d)
177
+ out.append((Target(tc, mode), sig))
178
+ return out
179
+
180
+
181
+ # --- candidates and the step table ------------------------------------------------------
182
+
183
+ @dataclass(frozen=True)
184
+ class Candidate:
185
+ what: str
186
+ text: str
187
+
188
+
189
+ @dataclass(frozen=True)
190
+ class Step:
191
+ name: str
192
+ level: int
193
+ langs: tuple
194
+ fn: Callable
195
+ controls: bool = False
196
+ only: Callable | None = None # applies(text, lang); None: every program
197
+
198
+ def applies(self, text: str, lang: str) -> bool:
199
+ return lang in self.langs and (self.only is None or self.only(text, lang))
200
+
201
+
202
+ STEPS: list[Step] = []
203
+ LEVELS = (1, 2, 3, 4)
204
+
205
+
206
+ def step(name: str, level: int, langs=("f90", "c"), controls: bool = False, only=None):
207
+ """Register a candidate generator ``fn(text, lang) -> Iterable[Candidate]``
208
+ at a level (1 blocks and statements, 2 clauses, 3 expressions, 4
209
+ cleanup). ``controls``: its edits are one-change controls too.
210
+ ``only``: a predicate on (text, lang) that selects the programs it
211
+ applies to (the fuzz-format steps)."""
212
+ def deco(fn):
213
+ unregister(name)
214
+ STEPS.append(Step(name, level, tuple(langs), fn, controls, only))
215
+ return fn
216
+ return deco
217
+
218
+
219
+ def unregister(name: str) -> None:
220
+ STEPS[:] = [s for s in STEPS if s.name != name]
221
+
222
+
223
+ def is_fuzz_format(text: str, lang: str = "f90") -> bool:
224
+ return lang == "f90" and "! fuzz: kernels" in text and "! fuzz: checksum" in text
225
+
226
+
227
+ def not_fuzz_format(text: str, lang: str) -> bool:
228
+ return not is_fuzz_format(text, lang)
229
+
230
+
231
+ def steps_for(lang: str, text: str = "", levels=LEVELS) -> list[Step]:
232
+ """The steps that apply to a program, by level then registration order."""
233
+ out = [s for s in STEPS if s.level in levels and s.applies(text, lang)]
234
+ return sorted(out, key=lambda s: (s.level, STEPS.index(s)))
235
+
236
+
237
+ def candidates(text: str, lang: str, levels=LEVELS, steps=None) -> list[Candidate]:
238
+ """Every candidate of the steps at these levels, distinct and different
239
+ from the program, in step order."""
240
+ out, seen = [], {text}
241
+ for s in steps if steps is not None else steps_for(lang, text, levels):
242
+ for c in s.fn(text, lang):
243
+ if c.text not in seen:
244
+ seen.add(c.text)
245
+ out.append(c)
246
+ return out
247
+
248
+
249
+ # --- directives: a small model shared by the steps --------------------------------------
250
+
251
+ _F_DIR = re.compile(r"^(\s*)!\$(omp|acc)&?\s+(.*?)\s*$", re.I)
252
+ _C_DIR = re.compile(r"^(\s*)#\s*pragma\s+(omp|acc)\s+(.*?)\s*$", re.I)
253
+ DIRECTIVE_WORDS = {
254
+ "target", "teams", "distribute", "parallel", "do", "for", "simd", "loop", "sections", "section",
255
+ "single", "masked", "master", "task", "taskloop", "taskgroup", "taskwait", "taskyield", "barrier",
256
+ "critical", "atomic", "flush", "ordered", "workshare", "data", "enter", "exit", "update", "declare",
257
+ "mapper", "metadirective", "dispatch", "interop", "scan", "tile", "unroll", "nothing", "error",
258
+ "requires", "assume", "assumes", "allocate", "allocators", "cancel", "cancellation", "point",
259
+ "kernels", "serial", "host_data", "wait", "init", "shutdown", "set", "routine", "cache", "end",
260
+ "variant", "reduction", "simd", "read", "write", "capture", "compare"}
261
+ # words that are directive names only when first (atomic update/read/write/capture are clauses of atomic)
262
+ _ATOMIC_CLAUSES = {"update", "read", "write", "capture", "compare"}
263
+ BLOCK_LAST = {"target", "teams", "parallel", "task", "masked", "master", "single", "critical",
264
+ "taskgroup", "data", "kernels", "serial", "host_data", "ordered", "sections", "workshare"}
265
+ LOOP_LAST = {"do", "for", "simd", "loop", "distribute", "taskloop", "tile", "unroll"}
266
+ STANDALONE = {("target", "update"), ("target", "enter", "data"), ("target", "exit", "data"),
267
+ ("taskwait",), ("taskyield",), ("barrier",), ("flush",), ("cancel",),
268
+ ("cancellation", "point"), ("update",), ("wait",), ("enter", "data"), ("exit", "data"),
269
+ ("init",), ("shutdown",), ("set",), ("declare", "target"), ("requires",), ("error",),
270
+ ("nothing",), ("interop",), ("scan",), ("depobj",)}
271
+
272
+
273
+ @dataclass
274
+ class Directive:
275
+ indent: str
276
+ model: str # omp | acc
277
+ name: list # the directive's words
278
+ clauses: list # the clause tokens, in order
279
+ lang: str
280
+
281
+ def render(self) -> str:
282
+ head = f"{self.indent}!${self.model} " if self.lang == "f90" else f"{self.indent}#pragma {self.model} "
283
+ return (head + " ".join(self.name + self.clauses)).rstrip()
284
+
285
+ @property
286
+ def is_end(self) -> bool:
287
+ return bool(self.name) and self.name[0] == "end"
288
+
289
+ @property
290
+ def last(self) -> str:
291
+ return self.name[-1] if self.name else ""
292
+
293
+ @property
294
+ def standalone(self) -> bool:
295
+ return tuple(self.name) in STANDALONE
296
+
297
+
298
+ def _tokens(body: str) -> list[str]:
299
+ """The directive's words and clause tokens: split at depth 0 on blanks
300
+ and commas; a token that opens with "(" joins the word before it."""
301
+ toks, depth, cur = [], 0, ""
302
+ for ch in body:
303
+ if ch in " \t," and depth == 0:
304
+ if cur:
305
+ toks.append(cur)
306
+ cur = ""
307
+ continue
308
+ depth += {"(": 1, ")": -1}.get(ch, 0)
309
+ cur += ch
310
+ if cur:
311
+ toks.append(cur)
312
+ out: list[str] = []
313
+ for t in toks:
314
+ if t.startswith("(") and out and "(" not in out[-1]:
315
+ out[-1] += t
316
+ else:
317
+ out.append(t)
318
+ return out
319
+
320
+
321
+ def parse_directive(line: str, lang: str) -> Directive | None:
322
+ m = (_F_DIR if lang == "f90" else _C_DIR).match(line)
323
+ if not m:
324
+ return None
325
+ toks = _tokens(m.group(3))
326
+ name: list[str] = []
327
+ for i, t in enumerate(toks):
328
+ w = t.lower()
329
+ if "(" in t or w not in DIRECTIVE_WORDS:
330
+ break
331
+ if i > 0 and w in _ATOMIC_CLAUSES and name[0] == "atomic":
332
+ break
333
+ if i > 0 and w == "reduction":
334
+ break
335
+ name.append(w)
336
+ return Directive(m.group(1), m.group(2).lower(), name, toks[len(name):], lang)
337
+
338
+
339
+ def is_directive(line: str, lang: str) -> bool:
340
+ return parse_directive(line, lang) is not None
341
+
342
+
343
+ @dataclass
344
+ class Clause:
345
+ word: str
346
+ mods: list # the modifiers before the last top-level ":" (None: no ":")
347
+ items: list
348
+ raw: str
349
+
350
+ def render(self) -> str:
351
+ if not self.items and self.mods is None:
352
+ return self.word
353
+ inner = ", ".join(self.items)
354
+ if self.mods is not None:
355
+ inner = ", ".join(self.mods) + (": " if inner else ":") + inner
356
+ return f"{self.word}({inner})"
357
+
358
+
359
+ def _split_top(s: str, seps=(",",)) -> list[str]:
360
+ parts, depth, cur = [], 0, ""
361
+ for ch in s:
362
+ if ch in seps and depth == 0:
363
+ parts.append(cur.strip())
364
+ cur = ""
365
+ continue
366
+ depth += {"(": 1, "[": 1, ")": -1, "]": -1}.get(ch, 0)
367
+ cur += ch
368
+ parts.append(cur.strip())
369
+ return [p for p in parts if p != ""]
370
+
371
+
372
+ def parse_clause(tok: str) -> Clause:
373
+ m = re.match(r"([A-Za-z_]\w*)\s*\((.*)\)$", tok, re.S)
374
+ if not m:
375
+ return Clause(tok, None, [], tok)
376
+ word, inner = m.group(1).lower(), m.group(2)
377
+ depth, cut = 0, None
378
+ for i, ch in enumerate(inner):
379
+ depth += {"(": 1, "[": 1, ")": -1, "]": -1}.get(ch, 0)
380
+ if ch == ":" and depth == 0 and not (i + 1 < len(inner) and inner[i + 1] == ":"):
381
+ cut = i
382
+ if cut is None:
383
+ return Clause(word, None, _split_top(inner), tok)
384
+ return Clause(word, _split_top(inner[:cut]), _split_top(inner[cut + 1:]), tok)
385
+
386
+
387
+ def _set_clauses(d: Directive, clauses: list) -> str:
388
+ return Directive(d.indent, d.model, list(d.name), list(clauses), d.lang).render()
389
+
390
+
391
+ # --- logical lines ------------------------------------------------------------------------
392
+
393
+ def logical_lines(text: str, lang: str) -> list[str]:
394
+ """Continued lines joined (Fortran ``&`` and ``!$omp&``; C ``\\``)."""
395
+ if lang == "f90":
396
+ from offload.engine.fuzz_min import join_lines
397
+ return join_lines(text)
398
+ out, raw, k = [], text.splitlines(), 0
399
+ while k < len(raw):
400
+ line = raw[k].rstrip()
401
+ while line.endswith("\\") and k + 1 < len(raw):
402
+ k += 1
403
+ line = line[:-1].rstrip() + " " + raw[k].strip()
404
+ out.append(line)
405
+ k += 1
406
+ return out
407
+
408
+
409
+ def render_lines(lines: list[str], lang: str) -> str:
410
+ if lang == "f90":
411
+ from offload.engine.fuzz_min import render
412
+ return render(list(lines))
413
+ return "\n".join(lines) + "\n"
414
+
415
+
416
+ def _indent(line: str) -> int:
417
+ return len(line) - len(line.lstrip())
418
+
419
+
420
+ # --- extents of constructs -----------------------------------------------------------------
421
+
422
+ def _f_end_of_loop(lines, k) -> int | None:
423
+ """``end do`` of the do statement at k (nesting by do/end do)."""
424
+ depth = 0
425
+ for j in range(k, len(lines)):
426
+ s = lines[j].strip().lower()
427
+ s = re.sub(r"^\w+\s*:\s*", "", s) # a construct name
428
+ if re.match(r"do\b", s) and not re.match(r"do\s*while\b.*\bend\b", s):
429
+ depth += 1
430
+ elif re.match(r"end\s*do\b", s):
431
+ depth -= 1
432
+ if depth == 0:
433
+ return j
434
+ return None
435
+
436
+
437
+ def _f_end_of_if(lines, k) -> int | None:
438
+ depth = 0
439
+ for j in range(k, len(lines)):
440
+ s = lines[j].strip().lower()
441
+ if re.match(r"(\w+\s*:\s*)?if\s*\(.*\)\s*then$", s):
442
+ depth += 1
443
+ elif re.match(r"end\s*if\b", s):
444
+ depth -= 1
445
+ if depth == 0:
446
+ return j
447
+ return None
448
+
449
+
450
+ def _f_end_directive(lines, k, d: Directive) -> int | None:
451
+ """The ``end <name>`` directive matching the block directive at k
452
+ (nesting by the same name)."""
453
+ depth = 0
454
+ for j in range(k + 1, len(lines)):
455
+ e = parse_directive(lines[j], "f90")
456
+ if e is None or e.model != d.model:
457
+ continue
458
+ if not e.is_end and e.name == d.name:
459
+ depth += 1
460
+ elif e.is_end and e.name[1:] == d.name:
461
+ if depth == 0:
462
+ return j
463
+ depth -= 1
464
+ return None
465
+
466
+
467
+ def f_construct_extent(lines, k) -> tuple[int, int | None] | None:
468
+ """(last line of the construct, its end-directive line or None) for the
469
+ directive at k: a block construct to its end directive; a loop
470
+ construct to its loop's end (plus an optional end directive); a
471
+ standalone directive is one line."""
472
+ d = parse_directive(lines[k], "f90")
473
+ if d is None or d.is_end or not d.name:
474
+ return None
475
+ if tuple(d.name) in STANDALONE:
476
+ return k, None
477
+ if d.last == "atomic" or d.name[0] == "atomic":
478
+ e = _f_end_directive(lines, k, d)
479
+ return (e, e) if e is not None else (k + 1, None)
480
+ if d.last in LOOP_LAST or (d.model == "acc" and d.last in ("loop", "kernels") and
481
+ k + 1 < len(lines) and lines[k + 1].strip().lower().startswith("do")):
482
+ j = k + 1
483
+ while j < len(lines) and not lines[j].strip():
484
+ j += 1
485
+ if j < len(lines) and re.match(r"(\w+\s*:\s*)?do\b", lines[j].strip().lower()):
486
+ e = _f_end_of_loop(lines, j)
487
+ if e is None:
488
+ return None
489
+ if e + 1 < len(lines):
490
+ nxt = parse_directive(lines[e + 1], "f90")
491
+ if nxt is not None and nxt.is_end and nxt.name[1:] == d.name:
492
+ return e + 1, e + 1
493
+ return e, None
494
+ e = _f_end_directive(lines, k, d)
495
+ return (e, e) if e is not None else None
496
+ e = _f_end_directive(lines, k, d)
497
+ return (e, e) if e is not None else None
498
+
499
+
500
+ def _c_block_end(lines, k) -> int:
501
+ """The last line of the C statement that starts at line k: a ``{``
502
+ block to its matching ``}``; else to the first line ending in ``;``
503
+ or a ``}``; a ``for``/``if`` head followed by a block or statement."""
504
+ depth, opened = 0, False
505
+ for j in range(k, len(lines)):
506
+ s = lines[j]
507
+ depth += s.count("{") - s.count("}")
508
+ if "{" in s:
509
+ opened = True
510
+ if opened and depth <= 0:
511
+ return j
512
+ if not opened and s.rstrip().endswith(";") and not re.match(r"\s*(for|if|while)\s*\(", s):
513
+ return j
514
+ if not opened and re.match(r"\s*(for|if|while)\s*\(.*\)\s*$", s):
515
+ continue
516
+ if not opened and re.match(r"\s*(for|if|while)\s*\(.*\).*;\s*$", s):
517
+ return j
518
+ return len(lines) - 1
519
+
520
+
521
+ def c_construct_extent(lines, k) -> tuple[int, int | None] | None:
522
+ d = parse_directive(lines[k], "c")
523
+ if d is None or not d.name:
524
+ return None
525
+ if tuple(d.name) in STANDALONE or d.name[0] in ("declare", "requires"):
526
+ return k, None
527
+ if k + 1 >= len(lines):
528
+ return k, None
529
+ return _c_block_end(lines, k + 1), None
530
+
531
+
532
+ def construct_extent(lines, k, lang):
533
+ return f_construct_extent(lines, k) if lang == "f90" else c_construct_extent(lines, k)
534
+
535
+
536
+ _F_KEEP = re.compile(r"^\s*(program|end\b|module|contains|implicit|use\s|subroutine|function|"
537
+ r"interface|type\b|integer function|do\b|else|block|\w+\s*:\s*do\b)", re.I)
538
+ _F_DECL = re.compile(r"^\s*(integer|real|complex|logical|character|double precision|type\s*\(|class\s*\()"
539
+ r".*::", re.I)
540
+ _C_DECL = re.compile(r"^\s*(?:static\s+|const\s+|unsigned\s+|signed\s+)*(int|long|short|char|float|double|"
541
+ r"size_t|_Bool|bool|int\d+_t|uint\d+_t|struct\s+\w+)\b[^(]*;\s*$")
542
+ _RESULT = re.compile(r"RESULT|check=", re.I)
543
+
544
+
545
+ def _f_statement_deletable(lines, k) -> bool:
546
+ s = lines[k].strip()
547
+ low = s.lower()
548
+ if not s or s.startswith("!") or _F_KEEP.match(s) or _F_DECL.match(s) or "::" in s:
549
+ return False
550
+ if re.match(r"(\w+\s*:\s*)?if\s*\(.*\)\s*then$", low) or re.match(r"end\s*(do|if|block)\b", low):
551
+ return False
552
+ if _RESULT.search(s) and low.startswith(("print", "write")):
553
+ return False
554
+ if k > 0 and (lines[k - 1].strip().lower().startswith(("!$omp atomic", "!$acc atomic"))):
555
+ return False
556
+ return True
557
+
558
+
559
+ def _c_statement_deletable(lines, k) -> bool:
560
+ s = lines[k].strip()
561
+ if not s or s.startswith(("//", "#", "/*", "*")) or s in ("{", "}") or s.endswith("{"):
562
+ return False
563
+ if re.match(r"(for|if|else|while|do|switch|case|default|return|int\s+main)\b", s) or _C_DECL.match(s):
564
+ return False
565
+ if _RESULT.search(s) and "printf" in s:
566
+ return False
567
+ return s.endswith(";")
568
+
569
+
570
+ # --- level 1: blocks, unwrapping, statements ---------------------------------------------
571
+
572
+ @step("block", level=1, only=not_fuzz_format)
573
+ def block_candidates(text: str, lang: str) -> list[Candidate]:
574
+ """Delete a construct with its body (coarsest first), a loop, an if
575
+ block, a standalone directive."""
576
+ L = logical_lines(text, lang)
577
+ spans = []
578
+ for k in range(len(L)):
579
+ if is_directive(L[k], lang):
580
+ ext = construct_extent(L, k, lang)
581
+ if ext is not None:
582
+ spans.append((k, ext[0], f"delete construct {L[k].strip()[:50]} at line {k + 1}"))
583
+ elif lang == "f90":
584
+ s = L[k].strip().lower()
585
+ if re.match(r"(\w+\s*:\s*)?do\b", s) and not (k > 0 and is_directive(L[k - 1], lang)):
586
+ e = _f_end_of_loop(L, k)
587
+ if e is not None:
588
+ spans.append((k, e, f"delete loop at line {k + 1}"))
589
+ elif re.match(r"(\w+\s*:\s*)?if\s*\(.*\)\s*then$", s):
590
+ e = _f_end_of_if(L, k)
591
+ if e is not None:
592
+ spans.append((k, e, f"delete if block at line {k + 1}"))
593
+ else:
594
+ s = L[k].strip()
595
+ if re.match(r"(for|if)\s*\(", s) and not (k > 0 and is_directive(L[k - 1], lang)):
596
+ e = _c_block_end(L, k)
597
+ spans.append((k, e, f"delete {s.split('(')[0].strip()} block at line {k + 1}"))
598
+ out = []
599
+ for k, e, what in sorted(spans, key=lambda t: (t[0] - t[1], t[0])):
600
+ out.append(Candidate(what, render_lines(L[:k] + L[e + 1:], lang)))
601
+ return out
602
+
603
+
604
+ @step("unwrap", level=1, controls=True, only=not_fuzz_format)
605
+ def unwrap_candidates(text: str, lang: str) -> list[Candidate]:
606
+ """Remove a directive (and its end directive), keeping its body."""
607
+ L = logical_lines(text, lang)
608
+ out = []
609
+ for k in range(len(L)):
610
+ d = parse_directive(L[k], lang)
611
+ if d is None or d.is_end or not d.name:
612
+ continue
613
+ ext = construct_extent(L, k, lang)
614
+ if ext is None or ext[0] == k:
615
+ continue
616
+ drop = {k} | ({ext[1]} if ext[1] is not None else set())
617
+ if lang == "f90" and (d.last == "atomic" or d.name[0] == "atomic"):
618
+ continue
619
+ new = [ln for i, ln in enumerate(L) if i not in drop]
620
+ out.append(Candidate(f"unwrap {' '.join(d.name)} at line {k + 1}", render_lines(new, lang)))
621
+ return out
622
+
623
+
624
+ _F_LHS = re.compile(r"^\s*(?:if\s*\(.*\)\s*)?([a-z_]\w*)\s*(?:\([^=]*\))?\s*=(?!=)(.*)$", re.I)
625
+ _C_LHS = re.compile(r"^\s*(?:([A-Za-z_]\w*)\s*(?:\[[^=]*\])*\s*(?:[-+*/%&|^]|<<|>>)?=(?!=)(.*?);|"
626
+ r"(?:\+\+|--)\s*([A-Za-z_]\w*)\s*;|([A-Za-z_]\w*)\s*(?:\+\+|--)\s*;)\s*$")
627
+ _LOOP_COND = re.compile(r"\b(?:while|for)\s*\((.*)\)|\bdo\s+while\s*\((.*)\)", re.I)
628
+
629
+
630
+ def assigned_name(line: str, lang: str) -> tuple[str | None, str]:
631
+ """(the variable a statement assigns or updates, the right-hand side)."""
632
+ if lang == "f90":
633
+ m = _F_LHS.match(line)
634
+ return (m.group(1), m.group(2)) if m else (None, "")
635
+ m = _C_LHS.match(line)
636
+ if not m:
637
+ return None, ""
638
+ return m.group(1) or m.group(2) or m.group(3), m.group(2) or ""
639
+
640
+
641
+ def _loop_condition_names(L, lang) -> set:
642
+ out = set()
643
+ for ln in L:
644
+ for m in _LOOP_COND.finditer(ln):
645
+ cond = m.group(1) or m.group(2) or ""
646
+ out |= set(re.findall(r"[A-Za-z_]\w*", cond))
647
+ return out
648
+
649
+
650
+ def is_read_elsewhere(L, k: int, name: str, lang: str) -> bool:
651
+ """Some line other than k names `name` otherwise than as the target of a
652
+ whole-variable assignment whose right-hand side does not read it (a
653
+ declaration does not count; an element assignment, a print, a
654
+ condition, any expression does)."""
655
+ flags = re.I if lang == "f90" else 0
656
+ pat = re.compile(rf"(?<![\w%]){re.escape(name)}(?!\w)", flags)
657
+ decl = _F_DECL if lang == "f90" else _C_DECL
658
+ for j, ln in enumerate(L):
659
+ s = ln.strip()
660
+ if j == k or not pat.search(ln) or not s or s.startswith(("!", "//", "#")) or decl.match(ln):
661
+ continue
662
+ if lang == "f90" and re.match(r"\s*(use\s|integer|real|logical|character|double)", ln, re.I):
663
+ continue
664
+ tgt, rhs = assigned_name(ln, lang)
665
+ plain = re.match(rf"\s*{re.escape(name)}\s*=(?!=)", ln, flags) is not None
666
+ if tgt and tgt.lower() == name.lower() and plain and not pat.search(rhs):
667
+ continue
668
+ return True
669
+ return False
670
+
671
+
672
+ @step("statement", level=1, only=not_fuzz_format)
673
+ def statement_candidates(text: str, lang: str) -> list[Candidate]:
674
+ """Delete one statement line (not a declaration, a structural line or
675
+ the RESULT line). An assignment stays while another line reads its
676
+ variable (a print, an expression, an element assignment: the read
677
+ would become undefined) or the variable steers a while/for loop."""
678
+ L = logical_lines(text, lang)
679
+ ok = _f_statement_deletable if lang == "f90" else _c_statement_deletable
680
+ loop_vars = _loop_condition_names(L, lang)
681
+ out = []
682
+ for k in range(len(L)):
683
+ if is_directive(L[k], lang) or not ok(L, k):
684
+ continue
685
+ if lang == "f90" and construct_extent(L, k, lang) is not None:
686
+ continue
687
+ name, _ = assigned_name(L[k], lang)
688
+ if name and (name in loop_vars or is_read_elsewhere(L, k, name, lang)):
689
+ continue
690
+ out.append(Candidate(f"delete statement at line {k + 1}: {L[k].strip()[:40]}",
691
+ render_lines(L[:k] + L[k + 1:], lang)))
692
+ return out
693
+
694
+
695
+ # --- level 2: clauses, items, modifiers, prefixes -----------------------------------------
696
+
697
+ def _directive_lines(L, lang):
698
+ for k, ln in enumerate(L):
699
+ d = parse_directive(ln, lang)
700
+ if d is not None and d.name and d.clauses:
701
+ yield k, d
702
+
703
+
704
+ @step("clause", level=2, controls=True, only=not_fuzz_format)
705
+ def clause_candidates(text: str, lang: str) -> list[Candidate]:
706
+ L = logical_lines(text, lang)
707
+ out = []
708
+ for k, d in _directive_lines(L, lang):
709
+ for i, c in enumerate(d.clauses):
710
+ new = list(L)
711
+ new[k] = _set_clauses(d, d.clauses[:i] + d.clauses[i + 1:])
712
+ out.append(Candidate(f"delete clause {c[:40]} at line {k + 1}", render_lines(new, lang)))
713
+ return out
714
+
715
+
716
+ @step("clause-item", level=2, controls=True, only=not_fuzz_format)
717
+ def clause_item_candidates(text: str, lang: str) -> list[Candidate]:
718
+ """Drop one item of a clause's list (two or more items; a schedule's
719
+ chunk only)."""
720
+ L = logical_lines(text, lang)
721
+ out = []
722
+ for k, d in _directive_lines(L, lang):
723
+ for i, tok in enumerate(d.clauses):
724
+ c = parse_clause(tok)
725
+ if len(c.items) < 2:
726
+ continue
727
+ idx = [len(c.items) - 1] if c.word in ("schedule", "dist_schedule") else range(len(c.items))
728
+ for j in idx:
729
+ nc = Clause(c.word, c.mods, c.items[:j] + c.items[j + 1:], tok)
730
+ new = list(L)
731
+ new[k] = _set_clauses(d, d.clauses[:i] + [nc.render()] + d.clauses[i + 1:])
732
+ out.append(Candidate(f"drop {c.items[j][:30]} from {tok[:40]} at line {k + 1}",
733
+ render_lines(new, lang)))
734
+ return out
735
+
736
+
737
+ _REDUCTIONS = ("reduction", "in_reduction", "task_reduction")
738
+
739
+
740
+ @step("modifier", level=2, controls=True, only=not_fuzz_format)
741
+ def modifier_candidates(text: str, lang: str) -> list[Candidate]:
742
+ """Drop one modifier of a clause: a map-type modifier (always, close,
743
+ present), a directive-name modifier of if, the iterator of depend."""
744
+ L = logical_lines(text, lang)
745
+ out = []
746
+ for k, d in _directive_lines(L, lang):
747
+ for i, tok in enumerate(d.clauses):
748
+ c = parse_clause(tok)
749
+ if c.mods is None or c.word in _REDUCTIONS:
750
+ continue
751
+ drop = list(range(len(c.mods) - 1)) if len(c.mods) > 1 else \
752
+ ([0] if c.word in ("if", "defaultmap") and len(c.mods) == 1 and c.word == "if" else [])
753
+ for j in drop:
754
+ rest = c.mods[:j] + c.mods[j + 1:]
755
+ nc = Clause(c.word, rest or None, c.items, tok)
756
+ new = list(L)
757
+ new[k] = _set_clauses(d, d.clauses[:i] + [nc.render()] + d.clauses[i + 1:])
758
+ out.append(Candidate(f"drop modifier {c.mods[j]} from {tok[:40]} at line {k + 1}",
759
+ render_lines(new, lang)))
760
+ return out
761
+
762
+
763
+ @step("prefix", level=2, controls=True, only=not_fuzz_format)
764
+ def prefix_candidates(text: str, lang: str) -> list[Candidate]:
765
+ """Shorten a combined construct to a prefix of its name (``target
766
+ teams distribute parallel do`` -> ``target teams distribute``, ...,
767
+ ``target``); Fortran end directives follow, and a loop construct that
768
+ becomes a block construct gets its end directive after the loop."""
769
+ L = logical_lines(text, lang)
770
+ out = []
771
+ for k in range(len(L)):
772
+ d = parse_directive(L[k], lang)
773
+ if d is None or d.is_end or len(d.name) < 2 or tuple(d.name) in STANDALONE:
774
+ continue
775
+ if d.name[0] in ("end", "declare", "atomic", "cancellation", "target") and \
776
+ tuple(d.name[:2]) in (("target", "update"), ("target", "enter"), ("target", "exit")):
777
+ continue
778
+ ext = construct_extent(L, k, lang)
779
+ if ext is None:
780
+ continue
781
+ last, end_line = ext
782
+ for cut in range(len(d.name) - 1, 0, -1):
783
+ prefix = d.name[:cut]
784
+ if prefix[-1] not in BLOCK_LAST | LOOP_LAST:
785
+ continue
786
+ new = list(L)
787
+ new[k] = Directive(d.indent, d.model, prefix, d.clauses, lang).render()
788
+ if lang == "f90":
789
+ if end_line is not None:
790
+ new[end_line] = f"{d.indent}!${d.model} end {' '.join(prefix)}"
791
+ elif prefix[-1] in BLOCK_LAST:
792
+ new.insert(last + 1, f"{d.indent}!${d.model} end {' '.join(prefix)}")
793
+ out.append(Candidate(f"shorten {' '.join(d.name)} to {' '.join(prefix)} at line {k + 1}",
794
+ render_lines(new, lang)))
795
+ return out
796
+
797
+
798
+ @step("clause-order", level=0, controls=True, only=not_fuzz_format)
799
+ def clause_order_candidates(text: str, lang: str) -> list[Candidate]:
800
+ """Swap two adjacent clauses (a control only: level 0 is never a
801
+ reduction level; the order of clauses carries no meaning)."""
802
+ L = logical_lines(text, lang)
803
+ out = []
804
+ for k, d in _directive_lines(L, lang):
805
+ for i in range(len(d.clauses) - 1):
806
+ cl = list(d.clauses)
807
+ cl[i], cl[i + 1] = cl[i + 1], cl[i]
808
+ new = list(L)
809
+ new[k] = _set_clauses(d, cl)
810
+ out.append(Candidate(f"swap clause order: {d.clauses[i][:25]} and {d.clauses[i + 1][:25]} "
811
+ f"at line {k + 1}", render_lines(new, lang)))
812
+ return out
813
+
814
+
815
+ # --- level 3: expressions, bounds and extents ----------------------------------------------
816
+
817
+ _F_BOUND = re.compile(r"parameter\s*::\s*\w+\s*=\s*(\d+)|\bdo\s+\w+\s*=\s*-?\d+\s*,\s*(\d+)", re.I)
818
+ _C_BOUND = re.compile(r"#\s*define\s+\w+\s+(\d+)|\b\w+\s*<=?\s*(\d+)\s*;")
819
+ _DIGITS_IN_BRACKETS = re.compile(r"[(\[,]\s*(\d+)\s*(?=[)\],])")
820
+
821
+
822
+ def _is_decl(line: str, lang: str) -> bool:
823
+ return bool((_F_DECL if lang == "f90" else _C_DECL).match(line)) or line.lstrip().startswith("#define")
824
+
825
+
826
+ def extent_literals(text: str, lang: str) -> list[int]:
827
+ """The integer extents, bounds and parameters a program can shrink:
828
+ array extents in declarations, parameter and #define values, do and
829
+ for upper bounds (4 and above), largest first."""
830
+ found = set()
831
+ for ln in text.splitlines():
832
+ if _is_decl(ln, lang):
833
+ found |= {int(x) for x in _DIGITS_IN_BRACKETS.findall(ln)}
834
+ for m in (_F_BOUND if lang == "f90" else _C_BOUND).finditer(ln):
835
+ found.add(int(m.group(1) or m.group(2)))
836
+ return sorted((x for x in found if x >= 4), reverse=True)
837
+
838
+
839
+ def index_literals(text: str, lang: str) -> set:
840
+ """The integer literals an executable line reads an element at (a(5),
841
+ a[4]): an extent never shrinks below one of them."""
842
+ out = set()
843
+ for ln in text.splitlines():
844
+ if _is_decl(ln, lang) or is_directive(ln, lang) or ln.strip().startswith(("!", "//")):
845
+ continue
846
+ out |= {int(x) for x in _DIGITS_IN_BRACKETS.findall(ln)}
847
+ return out
848
+
849
+
850
+ def _split_binary(expr: str, lang: str) -> list[str]:
851
+ """Immediate operands of a top-level binary expression; the arguments
852
+ of a one-call expression; the inside of outer parentheses."""
853
+ e = expr.strip()
854
+ while e.startswith("(") and e.endswith(")") and _balanced(e[1:-1]):
855
+ e = e[1:-1].strip()
856
+ seps = (" + ", " - ", " * ", " / ", " .and. ", " .or. ") if lang == "f90" else \
857
+ ("+", "-", "*", "/", "%", "&&", "||", "&", "|", "^")
858
+ parts, depth, cur, k = [], 0, "", 0
859
+ while k < len(e):
860
+ if depth == 0 and k > 0:
861
+ for sep in seps:
862
+ if e.startswith(sep, k) and cur.strip() and not cur.rstrip().endswith(("(", ",", "=")):
863
+ parts.append(cur.strip())
864
+ cur = ""
865
+ k += len(sep)
866
+ break
867
+ else:
868
+ depth += {"(": 1, "[": 1, ")": -1, "]": -1}.get(e[k], 0)
869
+ cur += e[k]
870
+ k += 1
871
+ continue
872
+ depth += {"(": 1, "[": 1, ")": -1, "]": -1}.get(e[k], 0)
873
+ cur += e[k]
874
+ k += 1
875
+ if cur.strip():
876
+ parts.append(cur.strip())
877
+ if len(parts) >= 2:
878
+ return parts
879
+ m = re.match(r"(\w+)\s*\((.*)\)$", e)
880
+ if m and _balanced(m.group(2)) and m.group(1).lower() not in ("merge",):
881
+ args = _split_top(m.group(2))
882
+ return [a for a in args if a]
883
+ if m and m.group(1).lower() == "merge":
884
+ return _split_top(m.group(2))[:2]
885
+ return []
886
+
887
+
888
+ def _balanced(s: str) -> bool:
889
+ d = 0
890
+ for ch in s:
891
+ d += {"(": 1, ")": -1}.get(ch, 0)
892
+ if d < 0:
893
+ return False
894
+ return d == 0
895
+
896
+
897
+ _F_ASSIGN = re.compile(r"^(\s*(?:if\s*\(.*\)\s*)?[a-z]\w*(?:\s*\([^=]*\))?\s*=\s*)(?!=)(.+?)\s*$", re.I)
898
+ _C_ASSIGN = re.compile(r"^(\s*[A-Za-z_]\w*(?:\s*\[[^=]*\])*\s*=\s*)(?![=])(.+?);\s*$")
899
+
900
+
901
+ @step("expression", level=3, only=not_fuzz_format)
902
+ def expression_candidates(text: str, lang: str) -> list[Candidate]:
903
+ """Halve every occurrence of one integer extent, bound or parameter
904
+ (all at once, so declarations and loops stay consistent); replace a
905
+ right-hand side by one of its operands."""
906
+ out = []
907
+ # an extent never shrinks below a literal index the program reads at (a(5) with n = 4)
908
+ indices = index_literals(text, lang)
909
+ for lit in extent_literals(text, lang):
910
+ if any(lit // 2 < i <= lit for i in indices):
911
+ continue
912
+ new = re.sub(rf"(?<![\w.]){lit}(?![\w.])", str(lit // 2), text)
913
+ if new != text:
914
+ out.append(Candidate(f"halve {lit} to {lit // 2}", new))
915
+ L = logical_lines(text, lang)
916
+ rx = _F_ASSIGN if lang == "f90" else _C_ASSIGN
917
+ for k, ln in enumerate(L):
918
+ m = rx.match(ln)
919
+ if not m or is_directive(ln, lang) or re.match(r"\s*(do|for|if|while)\b", ln):
920
+ continue
921
+ head, rhs = m.group(1), m.group(2)
922
+ if "omp_is_initial_device" in rhs or "acc_on_device" in rhs or _RESULT.search(ln):
923
+ continue
924
+ for sub in _split_binary(rhs, lang):
925
+ if sub == rhs.strip():
926
+ continue
927
+ new = list(L)
928
+ new[k] = head + sub + (";" if lang == "c" else "")
929
+ out.append(Candidate(f"simplify line {k + 1}: {sub[:40]}", render_lines(new, lang)))
930
+ return out
931
+
932
+
933
+ # --- level 4: cleanup --------------------------------------------------------------------
934
+
935
+ def _f_entities(decl: str) -> tuple[str, list[str]]:
936
+ head, ents = decl.split("::", 1)
937
+ return head, _split_top(ents)
938
+
939
+
940
+ def _name_of(entity: str) -> str:
941
+ m = re.match(r"\s*([A-Za-z_]\w*)", entity)
942
+ return m.group(1) if m else entity.strip()
943
+
944
+
945
+ @step("cleanup", level=4, only=not_fuzz_format)
946
+ def cleanup_candidates(text: str, lang: str) -> list[Candidate]:
947
+ """Remove a declared variable no other line names (its entity, its
948
+ whole-line initialisation, allocate and deallocate)."""
949
+ L = logical_lines(text, lang)
950
+ out = []
951
+ decl_rx = _F_DECL if lang == "f90" else _C_DECL
952
+ for k, ln in enumerate(L):
953
+ if not decl_rx.match(ln) or ("parameter" in ln.lower() and lang == "f90"):
954
+ continue
955
+ if lang == "f90":
956
+ head, ents = _f_entities(ln)
957
+ sep = ", "
958
+ else:
959
+ m = re.match(r"^(\s*(?:[\w\s]+?)\s+)(.*);\s*$", ln)
960
+ if not m:
961
+ continue
962
+ head, ents = m.group(1), _split_top(m.group(2))
963
+ for ent in ents:
964
+ name = _name_of(ent)
965
+ pat = re.compile(rf"(?<![\w%]){re.escape(name)}(?![\w])", re.I if lang == "f90" else 0)
966
+ others = [j for j, x in enumerate(L) if j != k and pat.search(x)]
967
+ init = [j for j in others if re.match(
968
+ rf"\s*(?:{re.escape(name)}\s*(?:\(.*\))?\s*=[^=]|(?:de)?allocate\s*\(\s*{re.escape(name)}\b)",
969
+ L[j], re.I)]
970
+ if [j for j in others if j not in init]:
971
+ continue
972
+ keep = [e for e in ents if e is not ent]
973
+ new = []
974
+ for j, x in enumerate(L):
975
+ if j == k:
976
+ if keep:
977
+ new.append((head + "::" + " " + sep.join(keep)) if lang == "f90"
978
+ else head + ", ".join(keep) + ";")
979
+ elif j not in init:
980
+ new.append(x)
981
+ out.append(Candidate(f"remove unused {name}", render_lines(new, lang)))
982
+ return out
983
+
984
+
985
+ # --- the fuzz-format steps (offload.engine.fuzz_min) --------------------------------------------
986
+
987
+ def _fuzz_steps(kind: str):
988
+ from offload.engine import fuzz_min as FM
989
+
990
+ def gen(text: str, lang: str) -> list[Candidate]:
991
+ lines = FM.join_lines(text)
992
+ try:
993
+ if kind == "files":
994
+ cands = FM.file_candidates(lines)
995
+ elif kind == "cleanup":
996
+ cands = FM.cleanup_candidates(lines)
997
+ else:
998
+ cands = FM.candidates(lines)
999
+ except (StopIteration, ValueError, IndexError):
1000
+ return []
1001
+ if kind in ("blocks", "clauses", "expressions"):
1002
+ want = {"blocks": ("delete lines", "unwrap", "rename", "delete statement"),
1003
+ "clauses": ("delete clause", "drop "), "expressions": ("simplify",)}[kind]
1004
+ cands = [c for c in cands if c.what.startswith(want)]
1005
+ return [Candidate(c.what, FM.render(list(c.lines))) for c in cands]
1006
+ return gen
1007
+
1008
+
1009
+ step("fuzz-blocks", level=1, langs=("f90",), only=is_fuzz_format)(_fuzz_steps("blocks"))
1010
+ step("fuzz-files", level=1, langs=("f90",), only=is_fuzz_format)(_fuzz_steps("files"))
1011
+ step("fuzz-clauses", level=2, langs=("f90",), only=is_fuzz_format)(_fuzz_steps("clauses"))
1012
+ step("fuzz-expressions", level=3, langs=("f90",), only=is_fuzz_format)(_fuzz_steps("expressions"))
1013
+ step("fuzz-cleanup", level=4, langs=("f90",), only=is_fuzz_format)(_fuzz_steps("cleanup"))
1014
+
1015
+
1016
+ _USE_ONLY = re.compile(r"^(\s*use\s+\w+\s*,\s*only\s*:\s*)(.*)$", re.I)
1017
+
1018
+
1019
+ @step("fuzz-tidy", level=4, langs=("f90",), only=is_fuzz_format)
1020
+ def fuzz_tidy_candidates(text: str, lang: str) -> list[Candidate]:
1021
+ """Remove an empty module (``module``, ``implicit none``, ``contains``,
1022
+ ``end module`` and nothing else) and a name no line uses from a ``use
1023
+ ..., only:`` list (the whole line when none is left)."""
1024
+ L = text.splitlines()
1025
+ out = []
1026
+ for k, ln in enumerate(L):
1027
+ m = re.match(r"^\s*module\s+(\w+)\s*$", ln, re.I)
1028
+ if m:
1029
+ e = next((j for j in range(k + 1, len(L))
1030
+ if re.match(rf"^\s*end\s+module\s+{m.group(1)}\s*$", L[j], re.I)), None)
1031
+ if e is not None and all(re.match(r"^\s*(implicit none|contains|private|public)?\s*$", x, re.I)
1032
+ for x in L[k + 1:e]) and \
1033
+ not any(re.search(rf"\buse\s+{m.group(1)}\b", x, re.I) for x in L):
1034
+ out.append(Candidate(f"remove empty module {m.group(1)}", "\n".join(L[:k] + L[e + 1:]) + "\n"))
1035
+ u = _USE_ONLY.match(ln)
1036
+ if u:
1037
+ names = [x.strip() for x in u.group(2).split(",") if x.strip()]
1038
+ rest = "\n".join(x for j, x in enumerate(L) if j != k)
1039
+ for nm in names:
1040
+ if re.search(rf"\b{re.escape(nm)}\b", rest, re.I):
1041
+ continue
1042
+ keep = [x for x in names if x != nm]
1043
+ new = list(L)
1044
+ if keep:
1045
+ new[k] = u.group(1) + ", ".join(keep)
1046
+ else:
1047
+ del new[k]
1048
+ out.append(Candidate(f"drop unused import {nm}", "\n".join(new) + "\n"))
1049
+ return out
1050
+
1051
+ # the generic directive edits serve as controls of a reduced fuzz program too
1052
+ for _n in ("unwrap", "clause", "clause-item", "modifier", "prefix", "clause-order"):
1053
+ _s = next(s for s in STEPS if s.name == _n)
1054
+ STEPS.append(Step(f"{_n}-fuzz", 0, _s.langs, _s.fn, True, is_fuzz_format))
1055
+
1056
+
1057
+ # --- the output contract ---------------------------------------------------------------------
1058
+
1059
+ FUZZ_PRINT = re.compile(r"^(\s*)print\s*'\([^']*\)'\s*,\s*'RESULT checksum='\s*,\s*(\w+)\s*,"
1060
+ r"\s*' ran_on_host='\s*,\s*(.+?)\s*$", re.M)
1061
+
1062
+
1063
+ class PlainContract:
1064
+ """A self-checking program (``RESULT ran_on_host=. check=pass|fail``)."""
1065
+ why = ""
1066
+
1067
+ def applies(self, text: str) -> bool:
1068
+ return True
1069
+
1070
+ def prepare(self, text: str) -> str | None:
1071
+ return text
1072
+
1073
+
1074
+ def elision_compilers() -> tuple:
1075
+ """The elision's compilers that are installed here (wingtip has gfortran
1076
+ only; offload.engine.expected lists gfortran and AFAR's amdflang)."""
1077
+ from offload.engine import expected as E
1078
+ import shutil
1079
+ have = []
1080
+ for c in E.ELISION_COMPILERS:
1081
+ path = E.GFORTRAN if c == "gfortran" else E.AMDFLANG
1082
+ if Path(path).is_file() or shutil.which(path):
1083
+ have.append(c)
1084
+ return tuple(have)
1085
+
1086
+
1087
+ def elision_reference(text: str):
1088
+ """The sequential elision's checksum (int) of a fuzz program by the
1089
+ installed elision compilers, or why there is none (str)."""
1090
+ from offload.engine import expected as E
1091
+ comps = elision_compilers()
1092
+ if not comps:
1093
+ return "no elision compiler installed here"
1094
+ ev = E.elision_evidence(text, compilers=comps)
1095
+ if ev.status == "value":
1096
+ return int(ev.value.split("=", 1)[1])
1097
+ why = ev.detail if isinstance(ev.detail, str) else "; ".join(map(str, ev.detail or []))
1098
+ return f"{ev.status}: {why}"[:300]
1099
+
1100
+
1101
+ class FuzzContract:
1102
+ """A fuzz program prints ``RESULT checksum=N ran_on_host=H`` and has no
1103
+ self-check; the candidate run gets the harness's form, checked
1104
+ against the reference value of the same text (default: the
1105
+ sequential elision, offload.engine.fuzz_exec.elision_value: an int, or a
1106
+ string saying why there is none, which rejects the candidate)."""
1107
+
1108
+ def __init__(self, reference=None):
1109
+ self.reference = reference or elision_reference
1110
+ self.why = ""
1111
+
1112
+ @staticmethod
1113
+ def applies(text: str) -> bool:
1114
+ return FUZZ_PRINT.search(text) is not None
1115
+
1116
+ def prepare(self, text: str) -> str | None:
1117
+ m = FUZZ_PRINT.search(text)
1118
+ if m is None:
1119
+ return text
1120
+ ref = self.reference(text)
1121
+ if not isinstance(ref, int):
1122
+ self.why = str(ref)
1123
+ return None
1124
+ ind, cs, host = m.group(1), m.group(2), m.group(3)
1125
+ new = (f"{ind}print '(a,i0)', 'checksum= ', {cs}\n"
1126
+ f"{ind}print '(a,i0,a,a)', 'RESULT ran_on_host=', {host}, ' check=', "
1127
+ f"merge('pass', 'fail', {cs} == {ref}_8)")
1128
+ return text[:m.start()] + new + text[m.end():]
1129
+
1130
+
1131
+ def contract_for(text: str, lang: str):
1132
+ return FuzzContract() if lang == "f90" and FuzzContract.applies(text) else PlainContract()
1133
+
1134
+
1135
+ # --- witnesses -------------------------------------------------------------------------------
1136
+
1137
+ def rules_witness(text: str, lang: str) -> tuple[bool, list[str]]:
1138
+ """The repository's legality rules (offload.engine.prerun.rules_witness) on
1139
+ a program text: accepted only on a clear yes (a program the rules
1140
+ cannot read is no candidate)."""
1141
+ from offload.engine import prerun
1142
+ ok, why = prerun.rules_witness((text, lang))
1143
+ return bool(ok), list(why)
1144
+
1145
+
1146
+ DEVICE_FLAG = re.compile(r"omp_is_initial_device|acc_on_device|omp_get_device_num", re.I)
1147
+
1148
+
1149
+ def has_device_flag(text: str) -> bool:
1150
+ """A call of the host-execution flag (not its import in a ``use`` or
1151
+ ``#include`` line)."""
1152
+ return any(DEVICE_FLAG.search(ln) for ln in text.splitlines()
1153
+ if not re.match(r"\s*(use\s|#\s*include|!|//)", ln))
1154
+
1155
+
1156
+ DEVICE_CONSTRUCT = re.compile(r"^\s*(?:!\$omp\s+target\b(?!\s+(?:update|enter|exit|data)\b)|"
1157
+ r"#\s*pragma\s+omp\s+target\b(?!\s+(?:update|enter|exit|data)\b)|"
1158
+ r"!\$acc\s+(?:parallel|kernels|serial)\b|"
1159
+ r"#\s*pragma\s+acc\s+(?:parallel|kernels|serial)\b)", re.I | re.M)
1160
+
1161
+
1162
+ def has_device_construct(text: str) -> bool:
1163
+ """A compute construct that runs on the device (a target region, an
1164
+ OpenACC compute construct); a program without one is no device-offload
1165
+ failure."""
1166
+ return DEVICE_CONSTRUCT.search(text) is not None
1167
+
1168
+
1169
+ def flag_names(text: str) -> set:
1170
+ """The variables assigned from the host-execution flag."""
1171
+ out = set()
1172
+ for ln in text.splitlines():
1173
+ if DEVICE_FLAG.search(ln) and not re.match(r"\s*(use\s|#\s*include|!|//)", ln):
1174
+ m = re.match(r"\s*(?:if\s*\(.*\)\s*)?([A-Za-z_]\w*)\s*=", ln)
1175
+ if m:
1176
+ out.add(m.group(1))
1177
+ return out
1178
+
1179
+
1180
+ def flagless(text: str) -> str:
1181
+ """The program with every use of a host-execution flag variable other
1182
+ than its assignment and declaration replaced by 0 (for the elision's
1183
+ applicability analysis, which otherwise stops at that use)."""
1184
+ names = flag_names(text)
1185
+ if not names:
1186
+ return text
1187
+ out = []
1188
+ assign = re.compile(r"\s*(?:if\s*\(.*\)\s*)?(" + "|".join(map(re.escape, names)) + r")\s*=(?!=)", re.I)
1189
+ for ln in text.splitlines():
1190
+ if DEVICE_FLAG.search(ln) or assign.match(ln) or \
1191
+ re.match(r"\s*(integer|logical|real|use\s|!|//|#)", ln, re.I):
1192
+ out.append(ln) # the flag's declaration and assignments stay; its uses become 0
1193
+ continue
1194
+ for n in names:
1195
+ ln = re.sub(rf"(?<![\w%]){re.escape(n)}(?!\w)", "0", ln)
1196
+ out.append(ln)
1197
+ return "\n".join(out) + ("\n" if text.endswith("\n") else "")
1198
+
1199
+
1200
+ _PAIR = re.compile(r"([A-Za-z_]\w*)\s*=\s*(-?\d[\d.eE+-]*)")
1201
+
1202
+
1203
+ def printed_values(stdout: str, exclude=()) -> dict:
1204
+ """The ``name= value`` pairs a program printed before (not on) its
1205
+ RESULT line, as {name: value}, without the names in `exclude` (the
1206
+ host-execution flags)."""
1207
+ out = {}
1208
+ for ln in (stdout or "").splitlines():
1209
+ if ln.strip().startswith("RESULT"):
1210
+ continue
1211
+ for m in _PAIR.finditer(ln):
1212
+ if m.group(1) not in exclude and m.group(1) not in ("ran_on_host", "check"):
1213
+ out[m.group(1)] = m.group(2)
1214
+ return out
1215
+
1216
+
1217
+ # --- output oracles (references) ------------------------------------------------------------
1218
+
1219
+ class FakeReference:
1220
+ """A reference for tests: ``fn(text) -> (values or None, why)``."""
1221
+ name = "fake"
1222
+
1223
+ def __init__(self, fn):
1224
+ self.fn = fn
1225
+
1226
+ def values(self, text: str, lang: str):
1227
+ return self.fn(text)
1228
+
1229
+
1230
+ class ElisionReference:
1231
+ """The sequential elision (offload.engine.expected: the program built
1232
+ without OpenMP by the installed elision compilers, every directive a
1233
+ comment, the host as the only device): the values it prints. Fortran
1234
+ only; inapplicable programs give no values, with the reason."""
1235
+ name = "elision"
1236
+
1237
+ def values(self, text: str, lang: str):
1238
+ from offload.engine import expected as E
1239
+ if lang != "f90":
1240
+ return None, "the sequential elision reads Fortran only"
1241
+ # the analysis stops at the flag's use in the RESULT line (a value the elision cannot
1242
+ # mirror); the flag-derived values are left out of the comparison, so the analysis sees the
1243
+ # program with those uses neutralized and its copy-semantics walk runs on everything else
1244
+ ap = E.elision_applicability(flagless(text))
1245
+ if not ap.ok:
1246
+ return None, "elision inapplicable: " + ap.why[:200]
1247
+ comps = elision_compilers()
1248
+ if not comps:
1249
+ return None, "no elision compiler installed here"
1250
+ outs = []
1251
+ for c in comps:
1252
+ v, note = E.run_elided(flagless(text), c, value=lambda s: s)
1253
+ if v is None:
1254
+ return None, f"elision ({c}): {note}"
1255
+ bad = reference_check_fails(v)
1256
+ if bad:
1257
+ return None, f"elision ({c}): {bad}"
1258
+ outs.append(printed_values(v, flag_names(text)))
1259
+ if any(o != outs[0] for o in outs):
1260
+ return None, "the elision compilers print different values"
1261
+ return outs[0], ""
1262
+
1263
+
1264
+ _CHECK = re.compile(r"^RESULT .*check=(pass|fail)", re.M)
1265
+
1266
+
1267
+ def reference_check_fails(stdout: str) -> str:
1268
+ """Why a reference run does not pass: its self-check line says fail (the
1269
+ candidate's check is no longer satisfiable by a correct run), or there
1270
+ is no single RESULT line. '' when it passes."""
1271
+ found = _CHECK.findall(stdout or "")
1272
+ if len(found) != 1:
1273
+ return "the reference prints no single RESULT line"
1274
+ if found[0] != "pass":
1275
+ return "the reference's own self-check fails: the candidate is not a correct program any more"
1276
+ return ""
1277
+
1278
+
1279
+ class HostReference:
1280
+ """A host run (OMP_TARGET_OFFLOAD=disabled) of a toolchain from another
1281
+ compiler family than every target, on this node: the values it
1282
+ prints."""
1283
+
1284
+ def __init__(self, toolchain: str, targets):
1285
+ from offload.engine.toolchains import TOOLCHAINS, family
1286
+ if toolchain not in TOOLCHAINS:
1287
+ raise ValueError(f"unknown reference toolchain {toolchain!r}")
1288
+ same = [t.label for t in targets if family(t.toolchain) == family(toolchain)]
1289
+ if same:
1290
+ raise ValueError(f"reference {toolchain} is in the compiler family of the target "
1291
+ f"{', '.join(same)}: a reference is never the compiler under test's family")
1292
+ self.toolchain = toolchain
1293
+ self.name = f"host:{toolchain}"
1294
+
1295
+ def values(self, text: str, lang: str):
1296
+ import shutil
1297
+ import tempfile
1298
+ from offload.engine import harness
1299
+ from offload.engine.toolchains import TOOLCHAINS
1300
+ tc = TOOLCHAINS[self.toolchain]
1301
+ lang_ = "c" if lang == "c" else "f90"
1302
+ if tc.lang != lang_:
1303
+ tc = dataclasses.replace(tc, lang=lang_)
1304
+ tc = dataclasses.replace(tc, model=model_of(text))
1305
+ mode = tc.xnack_modes[0]
1306
+ tc = tc.for_mode(mode)
1307
+ tmp = Path(tempfile.mkdtemp(prefix="reduce_ref_"))
1308
+ try:
1309
+ src, exe = tmp / f"ref.{lang_}", tmp / "ref.out"
1310
+ src.write_text(flagless(text)) # the host run's flag reads 1; its uses become 0
1311
+ build = harness._execute(harness.compile_script(tc, str(src), str(exe), "-O0", str(tmp)), tmp, 600,
1312
+ merge=True)
1313
+ if build.rc != 0 or not exe.is_file():
1314
+ return None, f"reference {self.toolchain} did not build: {harness._clip(build.stdout)[-200:]}"
1315
+ run = harness._execute(harness.run_script(tc, str(exe), mode, False, str(tmp), offload="disabled"),
1316
+ tmp, 120)
1317
+ if run.timed_out or run.rc != 0:
1318
+ return None, f"reference {self.toolchain} run failed: rc={run.rc}"
1319
+ bad = reference_check_fails(run.stdout)
1320
+ if bad:
1321
+ return None, f"reference {self.toolchain}: {bad}"
1322
+ return printed_values(run.stdout, flag_names(text)), ""
1323
+ finally:
1324
+ shutil.rmtree(tmp, ignore_errors=True)
1325
+
1326
+
1327
+ def reference_for(spec: str | None, lang: str, targets):
1328
+ """The output oracle named by ``--reference``: ``elision`` (Fortran),
1329
+ ``host:TOOLCHAIN`` (another family than every target), None for none."""
1330
+ if spec is None:
1331
+ return None
1332
+ if spec == "elision":
1333
+ if lang != "f90":
1334
+ raise ValueError("the sequential elision reads Fortran only; a C program needs --reference host:TC")
1335
+ return ElisionReference()
1336
+ if spec.startswith("host:"):
1337
+ return HostReference(spec[5:], targets)
1338
+ raise ValueError(f"unknown reference {spec!r}: elision or host:TOOLCHAIN")
1339
+
1340
+
1341
+ # --- runners ---------------------------------------------------------------------------------
1342
+
1343
+ def sha(text: str) -> str:
1344
+ return hashlib.sha256(text.encode()).hexdigest()
1345
+
1346
+
1347
+ def fake_row(cls: str, text: str, target: Target, msg: str = "", program_id: str = "fake") -> dict:
1348
+ """A harness-shaped row of class `cls` (correct, wrong_answer, rejected,
1349
+ ice, crash, run_error, timeout, fell_back_to_host) for a program
1350
+ text, as offload.engine.harness would record it."""
1351
+ row = dict(program_id=program_id, primary_atom="reduce", atoms=["reduce"], toolchain=target.toolchain,
1352
+ xnack=target.mode, opt=target.opt, flaky=False, flags="", rocm=None, stdout="", stderr="",
1353
+ needs=[], evidence="construct", observable=True, legal=True, violates=None, version=52,
1354
+ thp="n/a", device="fake", compiler="fake", model="omp", lang="f90", scope="device",
1355
+ machine="", arch="", gpu="", build_dir="", node="", toolchain_build="", env_hash="",
1356
+ git_rev="", gate="", messages="", sha=sha(text), file="")
1357
+ clog = "--- compile (rc=0) ---\n"
1358
+ if cls == "correct":
1359
+ row.update(outcome="correct_on_device", reason="ran_on_host=0 check=pass",
1360
+ stdout="r= 5\nRESULT ran_on_host=0 check=pass\n", stderr=clog)
1361
+ elif cls == "wrong_answer":
1362
+ row.update(outcome="wrong_answer", reason="check_fail: ran_on_host=0 check=fail",
1363
+ stdout="r= 0\nRESULT ran_on_host=0 check=fail\n", stderr=clog)
1364
+ elif cls == "fell_back_to_host":
1365
+ row.update(outcome="fell_back_to_host", reason="ran_on_host=1 check=pass",
1366
+ stdout="RESULT ran_on_host=1 check=pass\n", stderr=clog)
1367
+ elif cls in ("rejected", "ice"):
1368
+ code = "ice" if cls == "ice" else "diagnostic"
1369
+ row.update(outcome="rejected", reason=f"{code}: {msg}", stderr=f"--- compile (rc=2) ---\n{msg}\n",
1370
+ messages=msg)
1371
+ elif cls == "crash":
1372
+ sig = msg or "SIGSEGV" # a crash prints nothing: the signal alone is the key
1373
+ row.update(outcome="wrong_answer", reason=f"crash: {sig}; without mandatory: crash: {sig}",
1374
+ stderr=clog + "--- run (OMP_TARGET_OFFLOAD=mandatory, rc=-11) ---\n")
1375
+ elif cls == "run_error":
1376
+ row.update(outcome="wrong_answer", reason="exit_status: 1; without mandatory: exit_status: 1",
1377
+ stderr=clog + f"--- run (OMP_TARGET_OFFLOAD=mandatory, rc=1) ---\n{msg}\n")
1378
+ elif cls == "timeout":
1379
+ row.update(outcome="wrong_answer", reason="timeout", stderr=clog)
1380
+ else:
1381
+ raise ValueError(f"fake_row: unknown class {cls!r}")
1382
+ return row
1383
+
1384
+
1385
+ class FakeRunner:
1386
+ """A runner for tests: ``classify(text, target)`` gives a class name
1387
+ (see fake_row), a (class, message) pair or a whole row."""
1388
+ machine = "fake"
1389
+
1390
+ def __init__(self, classify: Callable):
1391
+ self.classify = classify
1392
+ self.calls = 0
1393
+
1394
+ def check(self, targets) -> None:
1395
+ pass
1396
+
1397
+ def run(self, text: str, lang: str, target: Target, program_id: str) -> dict:
1398
+ self.calls += 1
1399
+ res = self.classify(text, target)
1400
+ if isinstance(res, dict):
1401
+ row = dict(res)
1402
+ elif isinstance(res, tuple):
1403
+ row = fake_row(res[0], text, target, msg=res[1])
1404
+ else:
1405
+ row = fake_row(res, text, target)
1406
+ row.update(program_id=program_id, lang=lang, sha=sha(text))
1407
+ return row
1408
+
1409
+
1410
+ def model_of(text: str) -> str:
1411
+ acc = re.search(r"^\s*(?:!\$acc|#\s*pragma\s+acc)\b", text, re.M | re.I)
1412
+ omp = re.search(r"^\s*(?:!\$omp|#\s*pragma\s+omp)\b", text, re.M | re.I)
1413
+ return "acc" if acc and not omp else "omp"
1414
+
1415
+
1416
+ class HarnessRunner:
1417
+ """offload.engine.harness.run_program on this node (a bundle: its files
1418
+ compiled as a set with the harness's scripts). ``check`` refuses a
1419
+ toolchain this node cannot run (offload.engine.harness.wrong_arch: no GPU
1420
+ on a login node, another machine's architecture)."""
1421
+
1422
+ def __init__(self, run_timeout_s: int = 60, repeats: int = 2, compile_timeout_s: int = 600):
1423
+ self.run_timeout_s, self.repeats, self.compile_timeout_s = run_timeout_s, repeats, compile_timeout_s
1424
+
1425
+ def check(self, targets) -> None:
1426
+ from offload.engine import harness
1427
+ from offload.engine.toolchains import TOOLCHAINS
1428
+ for t in targets:
1429
+ if t.toolchain not in TOOLCHAINS:
1430
+ raise RunnerUnavailable(f"unknown toolchain {t.toolchain!r}")
1431
+ tc = TOOLCHAINS[t.toolchain]
1432
+ try:
1433
+ tc.for_mode(t.mode)
1434
+ except ValueError as e:
1435
+ raise RunnerUnavailable(str(e))
1436
+ if t.mode not in tc.xnack_modes:
1437
+ raise RunnerUnavailable(f"{t.toolchain} has no mode {t.mode} (its modes: {tc.xnack_modes})")
1438
+ why = harness.wrong_arch(tc)
1439
+ if why:
1440
+ raise RunnerUnavailable(f"{t.label}: {why} (run tools/reduce.py on {tc.machine}, "
1441
+ "on a compute node or in the wingtip cap)")
1442
+
1443
+ def run(self, text: str, lang: str, target: Target, program_id: str) -> dict:
1444
+ from offload.engine import harness
1445
+ from offload.engine.confirm.programs import split_files
1446
+ from offload.engine.gen import Program
1447
+ from offload.engine.toolchains import TOOLCHAINS
1448
+ tc = TOOLCHAINS[target.toolchain]
1449
+ files = split_files(text)
1450
+ if files and len(files) > 1:
1451
+ return self._run_set(files, lang, tc, target, program_id, text)
1452
+ prog = Program(id=program_id, lang="c" if lang == "c" else "fortran", source=text,
1453
+ atoms=frozenset({"reduce"}), primary_atom="reduce", legal=True, violates=None,
1454
+ observable=True, model=model_of(text))
1455
+ res = harness.run_program(prog, tc, target.mode, target.opt, timeout_s=self.run_timeout_s,
1456
+ repeats=self.repeats, compile_timeout_s=self.compile_timeout_s)
1457
+ row = dataclasses.asdict(res)
1458
+ row["outcome"] = str(res.outcome)
1459
+ row.update(sha=sha(text), file="")
1460
+ return row
1461
+
1462
+ def _run_set(self, files, lang, tc, target, program_id, text) -> dict:
1463
+ """A bundle through the harness's compile-set script and trial."""
1464
+ import shutil
1465
+ import tempfile
1466
+ from offload.engine import harness
1467
+ lang_ = "c" if lang == "c" else "f90"
1468
+ if tc.lang != lang_:
1469
+ tc = dataclasses.replace(tc, lang=lang_)
1470
+ tc = dataclasses.replace(tc, model=model_of(text)).for_mode(target.mode)
1471
+ tmp = Path(tempfile.mkdtemp(prefix="harness_"))
1472
+ base = dict(program_id=program_id, primary_atom="reduce", atoms=("reduce",), toolchain=tc.name,
1473
+ xnack=target.mode, opt=target.opt, flags=harness._flags(tc, target.opt),
1474
+ rocm=harness._rocm(tc), device=harness.device_of(tc), compiler=harness.compiler_version(tc),
1475
+ needs=(), evidence="construct", observable=True, legal=True, violates=None, version=52,
1476
+ thp=harness.detect_thp(), model=tc.model, lang=lang_, scope="device",
1477
+ machine=tc.machine, arch=tc.arch, gpu=harness.gpu_text())
1478
+ try:
1479
+ for n, t in files:
1480
+ (tmp / n).write_text(t)
1481
+ exe = tmp / "prog.out"
1482
+ build = harness._execute(harness.compile_set_script(tc, [n for n, _ in files], str(exe),
1483
+ target.opt, str(tmp)),
1484
+ tmp, self.compile_timeout_s, merge=True)
1485
+ clog = f"--- compile (rc={build.rc}) ---\n" + harness._clip(build.stdout)
1486
+
1487
+ def result(outcome, reason, stdout="", stderr="", flaky=False):
1488
+ res = harness.Result(outcome=outcome, reason=reason, flaky=flaky, stdout=harness._clip(stdout),
1489
+ stderr=clog + ("\n" + harness._clip(stderr) if stderr else ""), **base,
1490
+ **harness._provenance(tc, target.mode, str(tmp), base["flags"], outcome))
1491
+ row = dataclasses.asdict(res)
1492
+ row["outcome"] = str(outcome)
1493
+ row.update(sha=sha(text), file="")
1494
+ return row
1495
+ if build.timed_out:
1496
+ return result(harness.Outcome.harness_error, "compile_timeout")
1497
+ if build.rc != 0:
1498
+ found = harness.classify_message(tc.name, build.stdout, harness.CATEGORY_ORDER[:-1], lang_)
1499
+ if found is None:
1500
+ return result(harness.Outcome.harness_error, f"unclassified_compile_failure: rc={build.rc}")
1501
+ cat, line = found
1502
+ if cat == "environment":
1503
+ return result(harness.Outcome.harness_error, f"environment: {line}")
1504
+ return result(harness.Outcome.rejected, f"{harness.CODES[cat]}: {line}")
1505
+ if not exe.is_file():
1506
+ return result(harness.Outcome.harness_error, "no_executable")
1507
+ trials = []
1508
+ for _ in range(max(1, self.repeats)):
1509
+ t = harness._trial(tc, exe, target.mode, tmp, self.run_timeout_s)
1510
+ trials.append(t)
1511
+ if "timeout" in t.reason or t.outcome == harness.Outcome.harness_error:
1512
+ break
1513
+ worst = min(trials, key=lambda t: harness.WORST_FIRST.index(t.outcome))
1514
+ flaky = len({(t.outcome, t.reason) for t in trials}) > 1
1515
+ return result(worst.outcome, worst.reason, worst.stdout, worst.stderr, flaky)
1516
+ finally:
1517
+ shutil.rmtree(tmp, ignore_errors=True)
1518
+
1519
+
1520
+ # --- the judge -----------------------------------------------------------------------------
1521
+
1522
+ @dataclass
1523
+ class Budget:
1524
+ candidates: int | None = None # runner calls (one per candidate and target)
1525
+ seconds: float | None = None # runner time
1526
+
1527
+ def exhausted(self, used: dict) -> bool:
1528
+ return (self.candidates is not None and used["candidates"] >= self.candidates) or \
1529
+ (self.seconds is not None and used["seconds"] >= self.seconds)
1530
+
1531
+
1532
+ def not_evidence(row: dict) -> str:
1533
+ """Why a row is no evidence of the program's behaviour: a harness error
1534
+ or a capacity limit, a compile that hit the harness's time limit, a
1535
+ flaky row (its repeats disagree). '' when it is evidence."""
1536
+ d = R.normalize_row(dict(row))
1537
+ cls = G.outcome_class(d)
1538
+ reason = str(d.get("reason") or "")
1539
+ if cls in G.NOT_EVIDENCE or cls in G.CAPACITY_CLASSES:
1540
+ return f"harness error: {reason[:120]}"
1541
+ if cls == "timeout" and "compile" in reason[:40]:
1542
+ return f"compile timeout: not evidence ({reason[:80]})"
1543
+ if d.get("flaky"):
1544
+ return f"flaky: the repeats disagree ({reason[-120:]})"
1545
+ return ""
1546
+
1547
+
1548
+ class Judge:
1549
+ """Does a candidate keep the failure? In order: the host-execution
1550
+ flag must stay (a program that stops calling omp_is_initial_device
1551
+ could pass as a host run), the rules witness must accept, the output
1552
+ contract must give a reference, then one run per target: the gate's
1553
+ class must be evidence (no harness error) and the signature must be
1554
+ the wanted one on every target. Cached by text; every run is a row."""
1555
+
1556
+ def __init__(self, runner, targets: list, signature: Signature | None, lang: str,
1557
+ witness: Callable = rules_witness, rows_path: Path | None = None, name: str = "reduce",
1558
+ contract=None, budget: Budget | None = None, verify_targets=(), log=None,
1559
+ require_device_flag: bool = True, reference=None, witness_name: str | None = None):
1560
+ import threading
1561
+ self.runner, self.targets, self.signature, self.lang = runner, list(targets), signature, lang
1562
+ self.witness, self.rows_path, self.name = witness, Path(rows_path) if rows_path else None, name
1563
+ self.witness_name = witness_name or ("rules_witness" if witness is rules_witness else witness.__name__)
1564
+ self.contract, self.budget = contract, budget or Budget()
1565
+ self.verify_targets, self.log = list(verify_targets), log or (lambda *_: None)
1566
+ self.require_device_flag = require_device_flag
1567
+ self.reference = reference # the output oracle of a wrong-answer reduction (ElisionReference, ...)
1568
+ self.cache: dict[str, tuple[bool, str]] = {}
1569
+ self.rows: list[dict] = []
1570
+ self.used = {"candidates": 0, "seconds": 0.0}
1571
+ self.n = 0
1572
+ self.device_flag = True # the original calls the host-execution flag
1573
+ self.device_construct = True # the original has a device compute construct
1574
+ self._lock = threading.Lock()
1575
+
1576
+ def __call__(self, text: str) -> tuple[bool, str]:
1577
+ key = sha(text)
1578
+ if key not in self.cache:
1579
+ verdict = self._judge(text)
1580
+ with self._lock:
1581
+ self.cache.setdefault(key, verdict)
1582
+ return self.cache[key]
1583
+
1584
+ def contract_for(self, text: str):
1585
+ if self.contract is None:
1586
+ self.contract = contract_for(text, self.lang)
1587
+ return self.contract
1588
+
1589
+ def prepared(self, text: str) -> str | None:
1590
+ return self.contract_for(text).prepare(text)
1591
+
1592
+ def _check_text(self, text: str) -> tuple[str | None, str]:
1593
+ if self.require_device_flag and self.device_flag and not has_device_flag(text):
1594
+ return None, "the host-execution flag (omp_is_initial_device) must stay"
1595
+ if self.device_construct and not has_device_construct(text):
1596
+ return None, "no device construct left (the failure must stay a device-offload failure)"
1597
+ ok, why = self.witness(text, self.lang)
1598
+ if not ok:
1599
+ return None, "illegal: " + "; ".join(why)[:300]
1600
+ prepared = self.prepared(text)
1601
+ if prepared is None:
1602
+ return None, f"no reference: {self.contract.why}"[:300]
1603
+ return prepared, ""
1604
+
1605
+ def _judge(self, text: str) -> tuple[bool, str]:
1606
+ prepared, why = self._check_text(text)
1607
+ if prepared is None:
1608
+ return False, why
1609
+ with self._lock:
1610
+ self.n += 1
1611
+ n = self.n
1612
+ pid = f"{self.name}~c{n}"
1613
+ for t in self.targets:
1614
+ row = self.run(prepared, t, pid)
1615
+ bad = not_evidence(row)
1616
+ if bad:
1617
+ return False, f"{t.label}: {bad}"
1618
+ cls = run_class(row)
1619
+ got = signature(row)
1620
+ # the output oracle judges a wrong answer only when a wrong answer is what is reduced: a
1621
+ # candidate that turns a compile failure or a crash into a wrong answer is another failure
1622
+ if cls == "wrong_answer" and isinstance(self.contract_for(text), PlainContract) and \
1623
+ (self.signature is None or self.signature.cls == "wrong_answer"):
1624
+ got, why = self._oracle(text, row, got, t)
1625
+ if got is None:
1626
+ return False, why
1627
+ if self.signature is None:
1628
+ if not G.is_failure(cls):
1629
+ return False, f"{t.label}: {got}: not a failure"
1630
+ self.signature = got
1631
+ elif got != self.signature:
1632
+ # the recorded key of a wrong answer names no values; the first failing run (the
1633
+ # original, or on a resume the first candidate that fails) refines it once
1634
+ if got.cls == self.signature.cls and "; wrong:" in got.key and \
1635
+ "; wrong:" not in self.signature.key and got.key.startswith(self.signature.key):
1636
+ self.signature = got
1637
+ else:
1638
+ return False, f"{t.label}: signature {got} (want {self.signature})"
1639
+ return True, str(self.signature)
1640
+
1641
+ def _oracle(self, text: str, row: dict, got: Signature, t: Target):
1642
+ """The output oracle of a self-checking program's wrong answer: the
1643
+ reference's values must be there and the device's must differ; the
1644
+ key names the wrong values."""
1645
+ if self.reference is None:
1646
+ raise NoReference("a wrong-answer reduction of a self-checking program needs an output oracle "
1647
+ "(--reference elision, or host:TOOLCHAIN of another family): without one every "
1648
+ "candidate that breaks the computation would count as the identical failure")
1649
+ ref, why = self.reference.values(text, self.lang)
1650
+ if ref is None:
1651
+ return None, f"no reference: {why}"
1652
+ flags = flag_names(text)
1653
+ shown = printed_values(str(row.get("stdout") or ""))
1654
+ bad_flag = [f"{k}={v}" for k, v in shown.items() if k in flags and v != "0"]
1655
+ if bad_flag:
1656
+ return None, (f"{t.label}: the host-execution flag did not reach the host as a device run "
1657
+ f"({' '.join(bad_flag)}): the flag's map is gone or the region ran on the host")
1658
+ dev = {k: v for k, v in shown.items() if k not in flags}
1659
+ missing = [k for k in ref if k not in dev]
1660
+ if missing:
1661
+ return None, f"{t.label}: the program no longer prints {', '.join(missing)}"
1662
+ wrong = {k: v for k, v in dev.items() if k in ref and ref[k] != v}
1663
+ if not wrong:
1664
+ return None, (f"{t.label}: the device agrees with the reference ({self.reference.name}: "
1665
+ f"{' '.join(f'{k}={v}' for k, v in sorted(ref.items()))}): not a wrong answer")
1666
+ key = got.key + "; wrong: " + " ".join(f"{k}={v}" for k, v in sorted(wrong.items()))
1667
+ return Signature(got.cls, key), ""
1668
+
1669
+ def run(self, text: str, target: Target, program_id: str) -> dict:
1670
+ with self._lock:
1671
+ if self.budget.exhausted(self.used):
1672
+ raise BudgetExhausted(f"budget spent: {self.used}")
1673
+ t0 = time.time()
1674
+ row = self.runner.run(text, self.lang, target, program_id)
1675
+ row.setdefault("seconds", round(time.time() - t0, 2))
1676
+ with self._lock:
1677
+ self.used["candidates"] += 1
1678
+ self.used["seconds"] += time.time() - t0
1679
+ self.rows.append(row)
1680
+ if self.rows_path is not None:
1681
+ self.rows_path.parent.mkdir(parents=True, exist_ok=True)
1682
+ with self.rows_path.open("a") as f:
1683
+ f.write(json.dumps(row) + "\n")
1684
+ return row
1685
+
1686
+ def passes(self, text: str, program_id: str, targets=None) -> tuple[bool, str, list[str], list[dict]]:
1687
+ """(passes everywhere, why not, the targets it passed on, the rows):
1688
+ legal, with a reference, and correct_on_device on every target."""
1689
+ prepared, why = self._check_text(text)
1690
+ if prepared is None:
1691
+ return False, why, [], []
1692
+ passed, rows, bad = [], [], ""
1693
+ for t in (targets if targets is not None else self.targets + self.verify_targets):
1694
+ row = self.run(prepared, t, program_id)
1695
+ rows.append(row)
1696
+ cls = run_class(row)
1697
+ if not_evidence(row):
1698
+ bad = bad or f"{t.label}: {not_evidence(row)}"
1699
+ elif cls in G.PASSING and cls != "unobservable":
1700
+ passed.append(t.label)
1701
+ elif not bad:
1702
+ bad = f"{t.label}: {signature(row)}"
1703
+ return not bad, bad, passed, rows
1704
+
1705
+ def load_cache(self, tried: dict) -> None:
1706
+ for k, v in tried.items():
1707
+ self.cache[k] = (bool(v[0]), str(v[1]))
1708
+
1709
+
1710
+ # --- the driver ----------------------------------------------------------------------------
1711
+
1712
+ def greedy(state, generators, judge, workers: int = 1, max_rounds: int = 1000, log=None, order=None):
1713
+ """Greedy delta debugging over `generators` (each ``state -> [(what,
1714
+ state)]``), coarse first: within a generator the first candidate in
1715
+ order that `judge` accepts (``state -> (ok, why)``) wins and the
1716
+ generator restarts; when none does, the next generator. `order`
1717
+ reorders a round's candidates (a seeded shuffle); batches of
1718
+ `workers` are judged in parallel. Returns (state, steps, verdict)."""
1719
+ steps, verdict = [], ""
1720
+ log = log or (lambda *_: None)
1721
+ pool = ThreadPoolExecutor(workers) if workers > 1 else None
1722
+ try:
1723
+ for gen in generators:
1724
+ for _ in range(max_rounds):
1725
+ cands = list(gen(state))
1726
+ if order is not None:
1727
+ cands = order(cands)
1728
+ hit = None
1729
+ for b in range(0, len(cands), max(1, workers)):
1730
+ batch = cands[b:b + max(1, workers)]
1731
+ if pool is not None:
1732
+ res = list(pool.map(lambda c: judge(c[1]), batch))
1733
+ else:
1734
+ res = [judge(c[1]) for c in batch]
1735
+ for c, (ok, why) in zip(batch, res):
1736
+ if ok:
1737
+ hit = (c, why)
1738
+ break
1739
+ if hit:
1740
+ break
1741
+ if not hit:
1742
+ break
1743
+ state, verdict = hit[0][1], hit[1]
1744
+ steps.append(hit[0][0])
1745
+ log(hit[0][0], state)
1746
+ finally:
1747
+ if pool is not None:
1748
+ pool.shutdown(wait=True)
1749
+ return state, steps, verdict
1750
+
1751
+
1752
+ @dataclass
1753
+ class Reduction:
1754
+ text: str
1755
+ lines_before: int
1756
+ lines_after: int
1757
+ steps: list = field(default_factory=list)
1758
+ verdict: str = ""
1759
+ candidates_judged: int = 0
1760
+ budget_used: dict = field(default_factory=dict)
1761
+ stopped: str = "done" # done | budget
1762
+ signature: Signature | None = None
1763
+
1764
+
1765
+ def _setup(judge: Judge, levels) -> dict:
1766
+ """What a resumed run must have in common with the stopped one."""
1767
+ return {"targets": [t.label for t in judge.targets], "verify": [t.label for t in judge.verify_targets],
1768
+ "opt": sorted({t.opt for t in judge.targets}), "levels": list(levels), "witness": judge.witness_name,
1769
+ "reference": getattr(judge.reference, "name", None)}
1770
+
1771
+
1772
+ def _save_checkpoint(path: Path, origin: str, lang: str, text: str, steps: list, judge: Judge,
1773
+ levels, stopped: str) -> None:
1774
+ path.parent.mkdir(parents=True, exist_ok=True)
1775
+ data = {"version": 2, "origin": origin, "lang": lang, "text": text, "steps": list(steps),
1776
+ "signature": dataclasses.asdict(judge.signature) if judge.signature else None,
1777
+ "tried": {k: [ok, why] for k, (ok, why) in judge.cache.items()},
1778
+ "used": dict(judge.used), "stopped": stopped, "saved": time.strftime("%Y-%m-%dT%H:%M:%S"),
1779
+ **_setup(judge, levels)}
1780
+ tmp = path.with_suffix(".tmp")
1781
+ tmp.write_text(json.dumps(data, indent=1))
1782
+ tmp.replace(path)
1783
+
1784
+
1785
+ def load_checkpoint(path: Path, origin: str, judge: Judge | None = None, levels=LEVELS) -> dict | None:
1786
+ """The checkpoint of this origin program, or None (no file, or another
1787
+ program's). With a judge, the checkpoint must have been written for the
1788
+ same targets, verify targets, levels, witness, reference and signature
1789
+ (CheckpointMismatch otherwise)."""
1790
+ path = Path(path)
1791
+ if not path.is_file():
1792
+ return None
1793
+ data = json.loads(path.read_text())
1794
+ if data.get("origin") != origin:
1795
+ return None
1796
+ if judge is not None:
1797
+ want = _setup(judge, levels)
1798
+ bad = [f"{k}: {data.get(k)} (now {v})" for k, v in want.items() if data.get(k) != v]
1799
+ sig = data.get("signature")
1800
+ if sig and judge.signature is not None:
1801
+ stored = Signature(**sig)
1802
+ if stored.cls != judge.signature.cls or not stored.key.startswith(judge.signature.key):
1803
+ bad.append(f"signature: {stored} (now {judge.signature})")
1804
+ if bad:
1805
+ raise CheckpointMismatch(f"{path} was written for another run: " + "; ".join(bad)
1806
+ + ". Remove it, or rerun with the same targets, levels and witness.")
1807
+ return data
1808
+
1809
+
1810
+ def reduce(text: str, lang: str, judge: Judge, levels=LEVELS, seed: int | None = None,
1811
+ checkpoint: Path | None = None, workers: int = 1, log=None, max_rounds: int = 200) -> Reduction:
1812
+ """Reduce `text` while `judge` accepts (see the module docstring).
1813
+ Raises NoFailure when the original does not fail with the signature.
1814
+ A checkpoint of the same original resumes: its text, steps and
1815
+ judged candidates are reused."""
1816
+ log = log or (lambda *_: None)
1817
+ origin = sha(text)
1818
+ before = len(text.splitlines())
1819
+ judge.device_flag = has_device_flag(text)
1820
+ judge.device_construct = has_device_construct(text)
1821
+ steps: list[str] = []
1822
+ ck = load_checkpoint(checkpoint, origin, judge, levels) if checkpoint else None
1823
+ if ck:
1824
+ judge.load_cache(ck.get("tried") or {})
1825
+ steps = list(ck.get("steps") or [])
1826
+ if ck.get("signature"):
1827
+ stored = Signature(**ck["signature"])
1828
+ # the stored signature is the refined one (load_checkpoint checked it extends the judge's)
1829
+ if judge.signature is None or stored.key.startswith(judge.signature.key):
1830
+ judge.signature = stored
1831
+ current = ck["text"]
1832
+ log(f"resumed from {checkpoint}: {len(steps)} steps, {len(judge.cache)} candidates judged")
1833
+ else:
1834
+ current = text
1835
+ ok, why = judge(current)
1836
+ if not ok:
1837
+ raise NoFailure(why)
1838
+ rnd = random.Random(seed) if seed is not None else None
1839
+
1840
+ def order(cands):
1841
+ if rnd is None:
1842
+ return cands
1843
+ cands = list(cands)
1844
+ rnd.shuffle(cands)
1845
+ return cands
1846
+
1847
+ def accepted(what, state):
1848
+ log(f" {what} -> {len(state.splitlines())} lines")
1849
+ if checkpoint:
1850
+ _save_checkpoint(Path(checkpoint), origin, lang, state, steps, judge, levels, "running")
1851
+
1852
+ def gens_for(level):
1853
+ ss = steps_for(lang, current, (level,))
1854
+ if not ss:
1855
+ return []
1856
+ return [lambda state, ss=ss: [(c.what, c.text) for c in candidates(state, lang, steps=ss)]]
1857
+
1858
+ stopped = "done"
1859
+ # after the last level the first is tried again, then the cleanup (what the finer levels freed)
1860
+ plan = list(levels) + ([levels[0]] + ([4] if 4 in levels and levels[0] != 4 else []) if len(levels) > 1
1861
+ else [])
1862
+ try:
1863
+ for level in plan:
1864
+ gens = gens_for(level)
1865
+ if not gens:
1866
+ continue
1867
+ new, got, verdict = greedy(current, gens, judge, workers, max_rounds, accepted, order)
1868
+ steps += got
1869
+ current = new
1870
+ except BudgetExhausted as e:
1871
+ log(str(e))
1872
+ stopped = "budget"
1873
+ if checkpoint:
1874
+ _save_checkpoint(Path(checkpoint), origin, lang, current, steps, judge, levels, stopped)
1875
+ return Reduction(current, before, len(current.splitlines()), steps, str(judge.signature or ""),
1876
+ judge.used["candidates"], dict(judge.used), stopped, judge.signature)
1877
+
1878
+
1879
+ def witness_sentence(witness_name: str) -> str:
1880
+ """What the header says about legality, by the judge's witness."""
1881
+ if witness_name in ("none", "", None):
1882
+ return ("no legality witness: exploration only (every step was judged by the compiler under test "
1883
+ "alone; the result is not a candidate finding)")
1884
+ return ("Every step passed the legality rules witness (offload.engine.prerun rules); the front-end witness "
1885
+ "(offload.engine.prerun.check) is still to run on Tuolumne")
1886
+
1887
+
1888
+ def reduction_header(name: str, origin: str, targets, res: "Reduction", out: Path, lang: str,
1889
+ witness_name: str) -> str:
1890
+ """The comment header of a written reduction."""
1891
+ lead = "//" if lang == "c" else "!"
1892
+ tgt = ", ".join(t.label for t in targets)
1893
+ lines = [f"{lead} Reduced by tools/reduce.py from {origin} on {tgt}; failure signature: {res.signature}.",
1894
+ f"{lead} {res.lines_before} -> {res.lines_after} lines in {len(res.steps)} steps, "
1895
+ f"{res.candidates_judged} candidate runs; rows {out}/rows.jsonl; controls {out}/controls/.",
1896
+ f"{lead} {witness_sentence(witness_name)}."]
1897
+ return "\n".join(lines) + "\n"
1898
+
1899
+
1900
+ # --- controls --------------------------------------------------------------------------------
1901
+
1902
+ @dataclass
1903
+ class Control:
1904
+ what: str
1905
+ text: str
1906
+ passed: list = field(default_factory=list)
1907
+ rows: list = field(default_factory=list)
1908
+ slug: str = ""
1909
+
1910
+
1911
+ def _any_directive(line: str) -> bool:
1912
+ return is_directive(line, "f90") or is_directive(line, "c")
1913
+
1914
+
1915
+ def one_change(a: str, b: str) -> str | None:
1916
+ """What `b` changes in `a` when that is one thing: one line removed or
1917
+ changed; a block construct's directive and end directive both removed
1918
+ or both changed; a directive changed with an end directive added (a
1919
+ loop construct shortened to a block construct). None otherwise (also
1920
+ when equal)."""
1921
+ import difflib
1922
+ lang = "f90" if re.search(r"^\s*!\$", a, re.M) or not re.search(r"^\s*#\s*pragma", a, re.M) else "c"
1923
+ la, lb = logical_lines(a, lang), logical_lines(b, lang) # a continued directive is one line
1924
+ gone, changed, added = [], [], []
1925
+ for tag, i1, i2, j1, j2 in difflib.SequenceMatcher(None, la, lb, autojunk=False).get_opcodes():
1926
+ if tag == "equal":
1927
+ continue
1928
+ if tag == "delete":
1929
+ gone += la[i1:i2]
1930
+ elif tag == "insert":
1931
+ added += lb[j1:j2]
1932
+ else:
1933
+ n = min(i2 - i1, j2 - j1)
1934
+ changed += la[i1:i1 + n]
1935
+ gone += la[i1 + n:i2]
1936
+ added += lb[j1 + n:j2]
1937
+ touched = gone + changed
1938
+ if not touched or len(touched) > 2 or len(added) > 1:
1939
+ return None
1940
+ if len(touched) == 1 and not added:
1941
+ i = la.index(touched[0]) + 1
1942
+ return f"line {i} removed" if gone else f"line {i} changed"
1943
+ if not all(_any_directive(x) for x in touched + added):
1944
+ return None
1945
+
1946
+ def is_end(line: str) -> bool:
1947
+ d = parse_directive(line, "f90") or parse_directive(line, "c")
1948
+ return d is not None and d.is_end
1949
+ if len(touched) == 2 and not added and not is_end(touched[0]) and is_end(touched[1]):
1950
+ return "directive pair removed" if len(gone) == 2 else "directive pair changed" if len(changed) == 2 \
1951
+ else None
1952
+ if len(changed) == 1 and not gone and len(added) == 1 and not is_end(changed[0]) and is_end(added[0]):
1953
+ return "directive changed and end directive added"
1954
+ return None
1955
+
1956
+
1957
+ def control_candidates(text: str, lang: str) -> list[Candidate]:
1958
+ """One-change edits of the directives of a program (the steps marked
1959
+ ``controls``), distinct and different from it."""
1960
+ L = logical_lines(text, lang)
1961
+ base = render_lines(L, lang)
1962
+ ss = [s for s in STEPS if s.controls and s.applies(text, lang)]
1963
+ out, seen = [], {text, base}
1964
+ for s in sorted(ss, key=lambda s: (s.level, STEPS.index(s))):
1965
+ for c in s.fn(text, lang):
1966
+ if c.text in seen or one_change(base, c.text) is None:
1967
+ continue
1968
+ seen.add(c.text)
1969
+ out.append(c)
1970
+ return out
1971
+
1972
+
1973
+ def _slug(what: str) -> str:
1974
+ w = re.sub(r"\s+at line \d+$", "", what)
1975
+ w = re.sub(r"[^A-Za-z0-9]+", "-", w).strip("-").lower()
1976
+ return w[:40].rstrip("-")
1977
+
1978
+
1979
+ def controls(text: str, lang: str, judge: Judge, limit: int = 4, name: str = "reduce") -> list[Control]:
1980
+ """The one-change controls of a reduced program that are legal and
1981
+ pass on every target and verify target of the judge; at most `limit`,
1982
+ in step order (a clause, an item or a modifier first)."""
1983
+ out, used = [], set()
1984
+ for c in control_candidates(text, lang):
1985
+ slug = _slug(c.what)
1986
+ pid = f"{name}-{slug}-control"
1987
+ try:
1988
+ ok, why, passed, rows = judge.passes(c.text, pid)
1989
+ except BudgetExhausted as e:
1990
+ judge.log(str(e))
1991
+ break
1992
+ if ok:
1993
+ while slug in used:
1994
+ slug += "x"
1995
+ used.add(slug)
1996
+ out.append(Control(c.what, c.text, passed, rows, slug))
1997
+ judge.log(f" control kept: {c.what} (passes on {', '.join(passed)})")
1998
+ if len(out) >= limit:
1999
+ break
2000
+ else:
2001
+ judge.log(f" control rejected: {c.what}: {why}")
2002
+ return out
2003
+
2004
+
2005
+ # --- rows in: a job from a recorded failure --------------------------------------------------
2006
+
2007
+ @dataclass
2008
+ class Job:
2009
+ text: str
2010
+ lang: str
2011
+ targets: list
2012
+ signature: Signature | None
2013
+ origin: str
2014
+ notes: list = field(default_factory=list)
2015
+
2016
+
2017
+ def lang_of(path: str) -> str:
2018
+ return "c" if str(path).endswith((".c", ".h")) else "f90"
2019
+
2020
+
2021
+ def from_rows(paths, ident: str, root: Path = ROOT, program: Path | None = None,
2022
+ mode: int | None = None) -> Job:
2023
+ """The job of a recorded failing row: `ident` is a harness row's
2024
+ program id (or its file's stem), or the seed of a fuzz campaign record
2025
+ (results/fuzz/<run>/programs.jsonl). The program text comes from
2026
+ `program`, the row's ``file`` (relative to `root`), or the fuzz
2027
+ generator; the targets are the failing rows' (toolchain, mode); the
2028
+ signature is the most common one among them."""
2029
+ rows = R.load_jsonl([str(p) for p in paths])
2030
+ recs = [d for d in rows if "seed" in d and "results" in d and str(d["seed"]) == str(ident).split(":")[-1]]
2031
+ if recs:
2032
+ rec = recs[0]
2033
+ from offload.engine.fuzz import generate
2034
+ prog = generate(int(rec["seed"]), rec.get("gen_version") or "fuzz-1", tuple(rec.get("base") or ()))
2035
+ text = program.read_text() if program else prog.source
2036
+ m = mode if mode is not None else _mode_of_run(paths)
2037
+ found = failures_of_record(rec, m)
2038
+ if not found:
2039
+ raise NoFailure(f"seed {rec['seed']}: no failing toolchain in its record")
2040
+ sigs = Counter(s for _, s in found)
2041
+ top = sigs.most_common(1)[0][0]
2042
+ notes = [f"{t.label}: {s}" for t, s in found if s != top]
2043
+ return Job(text, "f90", [t for t, s in found if s == top], top,
2044
+ f"fuzz seed {rec['seed']} ({rec.get('gen_version')}) in {Path(paths[0]).name}",
2045
+ [f"another signature on {n}" for n in notes])
2046
+ mine = []
2047
+ for d in rows:
2048
+ d = R.normalize_row(d)
2049
+ pid = str(d.get("program_id") or d.get("ident") or "")
2050
+ stem = Path(str(d.get("file") or "")).stem
2051
+ if ident in (pid, stem) and d.get("toolchain"):
2052
+ mine.append(d)
2053
+ failing = [d for d in mine if is_failure(d)]
2054
+ if not failing:
2055
+ raise NoFailure(f"{ident}: no failing row in {', '.join(Path(str(p)).name for p in paths)}"
2056
+ + (f" ({len(mine)} rows of it, none failing)" if mine else ""))
2057
+ if program is not None:
2058
+ text, lang = Path(program).read_text(), lang_of(program)
2059
+ else:
2060
+ f = next((d["file"] for d in failing if d.get("file")), None)
2061
+ if not f:
2062
+ raise NoFailure(f"{ident}: its rows name no file; pass --program")
2063
+ p = Path(f) if Path(f).is_absolute() else Path(root) / f
2064
+ text, lang = p.read_text(), lang_of(p)
2065
+ pairs = [(Target(d["toolchain"], int(d.get("xnack", 0)), str(d.get("opt") or "-O2")), signature(d))
2066
+ for d in failing]
2067
+ top = Counter(s for _, s in pairs).most_common(1)[0][0]
2068
+ targets = list(dict.fromkeys(t for t, s in pairs if s == top))
2069
+ notes = [f"{t.label} fails another way: {s}" for t, s in pairs if s != top]
2070
+ return Job(text, lang, targets, top, f"row {ident} in {', '.join(Path(str(p)).name for p in paths)}",
2071
+ notes)
2072
+
2073
+
2074
+ def _mode_of_run(paths) -> int:
2075
+ for p in paths:
2076
+ meta = Path(str(p)).parent / "meta.json"
2077
+ if meta.is_file():
2078
+ try:
2079
+ d = json.loads(meta.read_text())
2080
+ sess = d.get("sessions") or [d]
2081
+ return int(sess[-1].get("xnack", 0))
2082
+ except (ValueError, KeyError, TypeError, IndexError):
2083
+ pass
2084
+ return 0
2085
+
2086
+
2087
+ def from_seed(seed: int, gen_version: str = "fuzz-1", base=()) -> Job:
2088
+ """The job of a fuzz seed: the program regenerated; targets and the
2089
+ signature are the caller's (the first run on the target decides)."""
2090
+ from offload.engine.fuzz import generate
2091
+ prog = generate(seed, gen_version, tuple(base))
2092
+ return Job(prog.source, "f90", [], None, f"fuzz seed {seed} ({gen_version})",
2093
+ [f"model checksum {prog.expected}"])
2094
+
2095
+
2096
+ def from_program(path: Path) -> Job:
2097
+ path = Path(path)
2098
+ return Job(path.read_text(), lang_of(path), [], None, str(path), [])