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.
- offload/__init__.py +10 -0
- offload/__main__.py +5 -0
- offload/_bundle.py +74 -0
- offload/_root/catalog/acc/claims.yaml +197 -0
- offload/_root/catalog/acc/clauses.yaml +391 -0
- offload/_root/catalog/acc/no_oracle.yaml +89 -0
- offload/_root/catalog/acc/out_of_scope.yaml +17 -0
- offload/_root/catalog/acc/rules.yaml +433 -0
- offload/_root/catalog/acc/semantic_atoms.yaml +27 -0
- offload/_root/catalog/acc/skeleton.yaml +1567 -0
- offload/_root/catalog/acc/spec_overrides.yaml +72 -0
- offload/_root/catalog/later_forms.yaml +76 -0
- offload/_root/catalog/machines.yaml +218 -0
- offload/_root/catalog/omp/claims.yaml +2047 -0
- offload/_root/catalog/omp/clauses.yaml +914 -0
- offload/_root/catalog/omp/derivations.yaml +177 -0
- offload/_root/catalog/omp/memory_modes.yaml +40 -0
- offload/_root/catalog/omp/no_oracle.yaml +373 -0
- offload/_root/catalog/omp/omp50_delta.yaml +377 -0
- offload/_root/catalog/omp/out_of_scope.yaml +64 -0
- offload/_root/catalog/omp/port_equivalence.yaml +352 -0
- offload/_root/catalog/omp/rules.yaml +3131 -0
- offload/_root/catalog/omp/rules_coverage.yaml +9939 -0
- offload/_root/catalog/omp/semantic_atoms.yaml +34 -0
- offload/_root/catalog/omp/skeleton.yaml +9181 -0
- offload/_root/catalog/omp/spec_overrides.yaml +932 -0
- offload/_root/catalog/toolchains.yaml +472 -0
- offload/_root/catalog/vendor_claims_generic.yaml +40925 -0
- offload/_root/data/canuse/b.json +1 -0
- offload/_root/data/canuse/c.json +1 -0
- offload/_root/data/canuse/columns.json +1 -0
- offload/_root/data/canuse/d.json +1 -0
- offload/_root/data/canuse/f.json +1 -0
- offload/_root/data/canuse/index.json +1 -0
- offload/_root/data/canuse/p.json +1 -0
- offload/_root/data/compilers/afar.json +1 -0
- offload/_root/data/compilers/cce19.json +1 -0
- offload/_root/data/compilers/cce20.json +1 -0
- offload/_root/data/compilers/cce21.json +1 -0
- offload/_root/data/compilers/cce21_4.json +1 -0
- offload/_root/data/compilers/gcc.json +1 -0
- offload/_root/data/compilers/gcc_sm80.json +1 -0
- offload/_root/data/compilers/nvhpc.json +1 -0
- offload/_root/data/compilers/nvhpc269.json +1 -0
- offload/_root/data/findings/AUTHORING.md +107 -0
- offload/_root/data/findings/README.md +33 -0
- offload/_root/data/findings/afar-c-error-directive-in-target-link.c +37 -0
- offload/_root/data/findings/afar-c-lastprivate-distribute-parallel-for-small-type.c +56 -0
- offload/_root/data/findings/afar-c-teams-distribute-reduction-first-team.c +59 -0
- offload/_root/data/findings/afar-c-update-iterator-copy-mode.c +45 -0
- offload/_root/data/findings/afar-c-xnack1-target-is-present.c +31 -0
- offload/_root/data/findings/afar-cce19-character-defaultmap.f90 +27 -0
- offload/_root/data/findings/afar-cce19-task-in-target-ice.f90 +23 -0
- offload/_root/data/findings/afar-clang-requires-device-safesync-hang.c +24 -0
- offload/_root/data/findings/afar-flang-firstprivate-map-two-targets.f90 +34 -0
- offload/_root/data/findings/afar-flang-loop-bind-teams-reduction.f90 +27 -0
- offload/_root/data/findings/afar-flang-task-reduction-max.f90 +31 -0
- offload/_root/data/findings/afar-flang-thread-limit-target-parallel-do.f90 +22 -0
- offload/_root/data/findings/afar-linear-ref-omp50.f90 +29 -0
- offload/_root/data/findings/afar-loop-bind-thread-in-teams.f90 +44 -0
- offload/_root/data/findings/afar-nontemporal-assumed-shape-ice.f90 +38 -0
- offload/_root/data/findings/afar-udr-derived-type-link.f90 +41 -0
- offload/_root/data/findings/afar-xnack1-atomic-min-nested-parallel.f90 +53 -0
- offload/_root/data/findings/afar-xnack1-present-not-enforced.f90 +36 -0
- offload/_root/data/findings/afar-xnack1-target-is-present.f90 +47 -0
- offload/_root/data/findings/apps/afar-cce-teams-private-array-nested-parallel-wrong.f90 +100 -0
- offload/_root/data/findings/apps/cce20-device-link-infer-address-spaces-crash.f90 +159 -0
- offload/_root/data/findings/apps/cce21-device-link-agpr-copy-mfma-assert.f90 +343 -0
- offload/_root/data/findings/apps/cce21-private-array-loaded-subscript-wrong.f90 +113 -0
- offload/_root/data/findings/apps/controls/bubble-simple-rddot.f90 +284 -0
- offload/_root/data/findings/apps/controls/bubble-two-substeps.f90 +287 -0
- offload/_root/data/findings/apps/controls/cbc-inline.f90 +97 -0
- offload/_root/data/findings/apps/controls/cbc-integers.f90 +91 -0
- offload/_root/data/findings/apps/controls/cbc-only-density.f90 +97 -0
- offload/_root/data/findings/apps/controls/cbc-only-velocity.f90 +97 -0
- offload/_root/data/findings/apps/controls/gcc-acc-c-tile-control.c +26 -0
- offload/_root/data/findings/apps/controls/gcc-acc-tile-control.f90 +29 -0
- offload/_root/data/findings/apps/controls/private-array-subscript-probes.f90 +45 -0
- offload/_root/data/findings/apps/controls/teams-private-array-read-par.f90 +31 -0
- offload/_root/data/findings/apps/controls/teams-private-array-seq.f90 +30 -0
- offload/_root/data/findings/apps/controls/teams-private-array-shared.f90 +34 -0
- offload/_root/data/findings/apps/controls/teams-private-array-split.f90 +36 -0
- offload/_root/data/findings/apps/controls/teams-private-array-write-par.f90 +31 -0
- offload/_root/data/findings/apps/gcc-acc-c-tile-partial-tiles-wrong.c +60 -0
- offload/_root/data/findings/apps/gcc-acc-tile-partial-tiles-wrong.f90 +64 -0
- offload/_root/data/findings/catalog/afar-c-complex-reduction-link.c +66 -0
- offload/_root/data/findings/catalog/afar-c-critical-hint-link.c +68 -0
- offload/_root/data/findings/catalog/afar-c-ordered-doacross-link.c +62 -0
- offload/_root/data/findings/catalog/afar-c-reduction-task-link.c +81 -0
- offload/_root/data/findings/catalog/afar-flang-absent-optional-target-crash.f90 +76 -0
- offload/_root/data/findings/catalog/afar-flang-critical-target-parallel-wrong.f90 +74 -0
- offload/_root/data/findings/catalog/afar-flang-dist-schedule-chunk.f90 +80 -0
- offload/_root/data/findings/catalog/afar-flang-linear-parallel-ice.f90 +68 -0
- offload/_root/data/findings/catalog/afar-flang-named-critical-link.f90 +67 -0
- offload/_root/data/findings/catalog/afar-flang-ordered-wrong.f90 +78 -0
- offload/_root/data/findings/catalog/afar-flang-reduction-minus-rejected.f90 +77 -0
- offload/_root/data/findings/catalog/afar-flang-reduction-task-ice.f90 +68 -0
- offload/_root/data/findings/catalog/afar-flang-sections-in-target-ice.f90 +67 -0
- offload/_root/data/findings/catalog/afar-flang-workdistribute-ice.f90 +71 -0
- offload/_root/data/findings/catalog/c-allocate-const-mem-firstprivate-wrong.c +79 -0
- offload/_root/data/findings/catalog/cce-acc-allocate-in-compute-crash.f90 +65 -0
- offload/_root/data/findings/catalog/cce-acc-character-dummy-routine-ice.f90 +81 -0
- offload/_root/data/findings/catalog/cce-acc-loop-seq-in-routine-wrong.f90 +81 -0
- offload/_root/data/findings/catalog/cce-acc-stop-in-compute-crash.f90 +62 -0
- offload/_root/data/findings/catalog/cce-allocators-stack-ice.f90 +81 -0
- offload/_root/data/findings/catalog/cce-atomic-default-mem-order-acquire-release-rejected.f90 +61 -0
- offload/_root/data/findings/catalog/cce-c-allocate-align-link.c +83 -0
- offload/_root/data/findings/catalog/cce-c-allocate-allocator-modifier-rejected.c +77 -0
- offload/_root/data/findings/catalog/cce-c-device-type-directive-name-ice.c +70 -0
- offload/_root/data/findings/catalog/cce-c-dist-schedule-chunk.c +81 -0
- offload/_root/data/findings/catalog/cce-c-f-pointer-device-link.f90 +70 -0
- offload/_root/data/findings/catalog/cce-c-lastprivate-parallel-in-seq-loop-wrong.c +64 -0
- offload/_root/data/findings/catalog/cce-c-long-double-ice.c +61 -0
- offload/_root/data/findings/catalog/cce-c-map-iterator-ice.c +92 -0
- offload/_root/data/findings/catalog/cce-c-motion-iterator-rejected.c +68 -0
- offload/_root/data/findings/catalog/cce-c-ordered-doacross-link.c +64 -0
- offload/_root/data/findings/catalog/cce-c-ordered-link.c +77 -0
- offload/_root/data/findings/catalog/cce-c-recursive-function-ice.c +62 -0
- offload/_root/data/findings/catalog/cce-c-reduction-inscan-ice.c +67 -0
- offload/_root/data/findings/catalog/cce-c-reduction-task-link.c +73 -0
- offload/_root/data/findings/catalog/cce-c-sections-in-target-wrong.c +70 -0
- offload/_root/data/findings/catalog/cce-c-single-masked-task-link.c +66 -0
- offload/_root/data/findings/catalog/cce-c-task-in-target-link.c +75 -0
- offload/_root/data/findings/catalog/cce-c-uses-allocators-traits-rejected.c +71 -0
- offload/_root/data/findings/catalog/cce-flang-allocate-const-mem-ice.f90 +67 -0
- offload/_root/data/findings/catalog/cce-flang-allocate-in-target-crash.f90 +68 -0
- offload/_root/data/findings/catalog/cce-flang-lastprivate-parallel-in-seq-loop-wrong.f90 +83 -0
- offload/_root/data/findings/catalog/cce-flang-stop-in-target-crash.f90 +64 -0
- offload/_root/data/findings/catalog/cce-iterator-link.f90 +67 -0
- offload/_root/data/findings/catalog/cce-linear-lastprivate-ice.f90 +69 -0
- offload/_root/data/findings/catalog/cce-nohost-link.f90 +90 -0
- offload/_root/data/findings/catalog/cce-reduction-inscan-ice.f90 +66 -0
- offload/_root/data/findings/catalog/cce-reduction-task-link.f90 +76 -0
- offload/_root/data/findings/catalog/cce19-reduction-task-addrspacecast-ice.f90 +77 -0
- offload/_root/data/findings/catalog/cce20-c-defaultmap-private-storage-rejected.c +85 -0
- offload/_root/data/findings/catalog/cce20-map-self-rejected.f90 +64 -0
- offload/_root/data/findings/catalog/cce21-c-declare-target-local-rejected.c +73 -0
- offload/_root/data/findings/catalog/cce21-c-target-loop-ice.c +77 -0
- offload/_root/data/findings/catalog/defaultmap-present-private-array-abort.f90 +73 -0
- offload/_root/data/findings/catalog/nvhpc-acc-associate-illegal-address.f90 +66 -0
- offload/_root/data/findings/catalog/nvhpc-c-function-pointer-kernel-missing.c +68 -0
- offload/_root/data/findings/catalog/nvhpc-c-long-double-kernel-missing.c +64 -0
- offload/_root/data/findings/catalog/nvhpc-c-static-local-kernel-missing.c +73 -0
- offload/_root/data/findings/catalog/nvhpc-c-vla-kernel-missing.c +63 -0
- offload/_root/data/findings/catalog/nvhpc-flang-type-bound-kernel-missing.f90 +86 -0
- offload/_root/data/findings/cce-acc-init-set-runtime-assert.f90 +35 -0
- offload/_root/data/findings/cce-acc-reduction-200-int8-scalars-wrong.f90 +1278 -0
- offload/_root/data/findings/cce-acc-routine-bind-module-ice.f90 +35 -0
- offload/_root/data/findings/cce-acc-self-condition-ignored.f90 +17 -0
- offload/_root/data/findings/cce-c-dist-schedule-segv.c +34 -0
- offload/_root/data/findings/cce-c-map-delete-refcount.c +47 -0
- offload/_root/data/findings/cce-c-map-present-assert.c +45 -0
- offload/_root/data/findings/cce-c-openmp45-device-link.c +34 -0
- offload/_root/data/findings/cce-c-parallel-for-in-target.c +73 -0
- offload/_root/data/findings/cce-defaultmap-tofrom-explicit-map.f90 +92 -0
- offload/_root/data/findings/cce-mapper-next-map-copy-back.f90 +48 -0
- offload/_root/data/findings/cce-trim-merge-output-one-character.f90 +40 -0
- offload/_root/data/findings/cce-uses-allocators-traits-omp50.f90 +28 -0
- offload/_root/data/findings/cce19-acc-if-false-link.f90 +17 -0
- offload/_root/data/findings/cce19-c-reduction-63-scalars-one-wrong.c +235 -0
- offload/_root/data/findings/cce19-target-parallel-guarded-loop.f90 +35 -0
- offload/_root/data/findings/cce19-target-parallel-single-ice.f90 +38 -0
- offload/_root/data/findings/cce21-alloc-2e32-sum-wrong.f90 +68 -0
- offload/_root/data/findings/cce21-defaultmap-aggregate-flag.f90 +52 -0
- offload/_root/data/findings/cce21-in-reduction-assumed-shape-write-lost.f90 +61 -0
- offload/_root/data/findings/cce21-in-reduction-taskwait-link.f90 +59 -0
- offload/_root/data/findings/cce21-linear-modifier-default-none.f90 +59 -0
- offload/_root/data/findings/clang-c-declare-target-to-omp52.c +50 -0
- offload/_root/data/findings/clang-c-linear-directive-name-hang.c +30 -0
- offload/_root/data/findings/clang-c-map-iterator-omp51.c +51 -0
- offload/_root/data/findings/clang-c-shared-declare-target-combined.c +58 -0
- offload/_root/data/findings/dataenv/cce-value-dummy-target-write-visible.f90 +74 -0
- offload/_root/data/findings/defaultmap-none-loop-variable.f90 +69 -0
- offload/_root/data/findings/fortran-loop-bind-thread-in-parallel.f90 +53 -0
- offload/_root/data/findings/fortran-schedule-static-in-target.f90 +55 -0
- offload/_root/data/findings/fresh/cce-common-block-name-in-map-rejected.f90 +47 -0
- offload/_root/data/findings/fresh/cce-if-stop-in-target-ice-one-condition.f90 +38 -0
- offload/_root/data/findings/fresh/cce-if-stop-in-target-ice.f90 +43 -0
- offload/_root/data/findings/fresh/cce-list-directed-print-in-target-copy-crash.f90 +40 -0
- offload/_root/data/findings/fresh/cce19-acc-automatic-array-copyout-copy-crash.f90 +56 -0
- offload/_root/data/findings/fresh/cce21-combined-if-runtime-expr-loop-skipped.f90 +57 -0
- offload/_root/data/findings/fresh/nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.f90 +67 -0
- offload/_root/data/findings/fresh/nvhpc-teams-loop-module-allocatable-assign-fort2-crash.f90 +73 -0
- offload/_root/data/findings/fuzz/1131-afar.f90 +48 -0
- offload/_root/data/findings/fuzz/1391-cce20.f90 +40 -0
- offload/_root/data/findings/fuzz/14-cce20.f90 +36 -0
- offload/_root/data/findings/fuzz/1505-cce20.f90 +88 -0
- offload/_root/data/findings/fuzz/1540-afar.f90 +44 -0
- offload/_root/data/findings/fuzz/158-afar.f90 +63 -0
- offload/_root/data/findings/fuzz/16-cce20.f90 +39 -0
- offload/_root/data/findings/fuzz/1644-cce21.f90 +31 -0
- offload/_root/data/findings/fuzz/165-cce19.f90 +52 -0
- offload/_root/data/findings/fuzz/1749-cce20.f90 +34 -0
- offload/_root/data/findings/fuzz/1936-afar.f90 +61 -0
- offload/_root/data/findings/fuzz/1978-cce19.f90 +41 -0
- offload/_root/data/findings/fuzz/2014-cce19.f90 +46 -0
- offload/_root/data/findings/fuzz/2224-cce19.f90 +30 -0
- offload/_root/data/findings/fuzz/2334-cce19.f90 +57 -0
- offload/_root/data/findings/fuzz/536-cce19.f90 +38 -0
- offload/_root/data/findings/fuzz/591-cce19.f90 +54 -0
- offload/_root/data/findings/fuzz/650-afar.f90 +58 -0
- offload/_root/data/findings/fuzz/698-afar.f90 +68 -0
- offload/_root/data/findings/fuzz/700-afar.f90 +46 -0
- offload/_root/data/findings/fuzz/775-cce21.f90 +33 -0
- offload/_root/data/findings/fuzz/87-afar.f90 +47 -0
- offload/_root/data/findings/fuzz/916-afar.f90 +67 -0
- offload/_root/data/findings/fuzz/958-cce20.f90 +44 -0
- offload/_root/data/findings/fuzz/98-cce19.f90 +40 -0
- offload/_root/data/findings/fuzz/README.md +236 -0
- offload/_root/data/findings/fuzz/b1-121-afar.f90 +47 -0
- offload/_root/data/findings/fuzz/b1-183-afar.f90 +55 -0
- offload/_root/data/findings/fuzz/b1-22-cce20.f90 +38 -0
- offload/_root/data/findings/fuzz/b1-231-afar.f90 +56 -0
- offload/_root/data/findings/fuzz/b1-268-afar.f90 +46 -0
- offload/_root/data/findings/fuzz/b2-183-afar.f90 +57 -0
- offload/_root/data/findings/fuzz/b2-209-cce19.f90 +51 -0
- offload/_root/data/findings/fuzz/b2-213-afar.f90 +63 -0
- offload/_root/data/findings/fuzz/b2-52-afar.f90 +56 -0
- offload/_root/data/findings/fuzz/b2-68-afar.f90 +54 -0
- offload/_root/data/findings/fuzz/b2-85-cce20.f90 +55 -0
- offload/_root/data/findings/fuzz/b3-188-cce19.f90 +90 -0
- offload/_root/data/findings/fuzz/b3-190-cce21.f90 +88 -0
- offload/_root/data/findings/fuzz/b3-197-cce19.f90 +77 -0
- offload/_root/data/findings/fuzz/b3-250-cce20.f90 +89 -0
- offload/_root/data/findings/fuzz/b3-278-cce19.f90 +70 -0
- offload/_root/data/findings/fuzz/b3-45-cce21.f90 +83 -0
- offload/_root/data/findings/fuzz/b3-54-afar.f90 +67 -0
- offload/_root/data/findings/fuzz/b3-61-cce20.f90 +100 -0
- offload/_root/data/findings/fuzz/b3-9-afar.f90 +100 -0
- offload/_root/data/findings/fuzz/b3-9-cce21.f90 +103 -0
- offload/_root/data/findings/fuzz/b3-94-afar.f90 +80 -0
- offload/_root/data/findings/fuzz/b4-39-afar.f90 +141 -0
- offload/_root/data/findings/fuzz/b4-86-cce19.f90 +71 -0
- offload/_root/data/findings/fuzz/controls/gcc-complex-pointer-store-control.f90 +28 -0
- offload/_root/data/findings/fuzz/controls/gcc-declare-reduction-module-control.f90 +29 -0
- offload/_root/data/findings/fuzz/controls/gcc-target-simd-map-nontemporal-control.f90 +21 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-mapped-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-unreferenced-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-default-none-removed-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-default-shared-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-firstprivate-removed-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-reduction-mapped-control.f90 +17 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-removed-control.f90 +17 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-tofrom-lastprivate-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-depend-explicit-map-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-depend-removed-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-end-distribute-control.f90 +16 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-num-threads-removed-control.f90 +26 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-removed-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-simd-no-end-directive-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-explicit-length-character-dummy-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-array-value-control.f90 +23 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-distribute-control.f90 +20 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-map-always-first-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-map-always-removed-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-num-threads-removed-control.f90 +12 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-firstprivate-dropped-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-single-nowait-control.f90 +13 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-split-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-collapse-split-control.f90 +21 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-end-directive-control.f90 +14 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-no-collapse-control.f90 +19 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-split-no-end-directive-control.f90 +15 -0
- offload/_root/data/findings/fuzz/gcc-declare-reduction-module-not-found.f90 +66 -0
- offload/_root/data/findings/fuzz/gcc-strided-target-update-rejected.f90 +58 -0
- offload/_root/data/findings/fuzz/gcc-target-simd-map-nontemporal-rejected.f90 +54 -0
- offload/_root/data/findings/fuzz/nvhpc-allocatable-component-implicit-map-illegal-address.f90 +84 -0
- offload/_root/data/findings/fuzz/nvhpc-assumed-length-character-dummy-map-ice.f90 +69 -0
- offload/_root/data/findings/fuzz/nvhpc-default-none-reduction-lastprivate-fort1-crash.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-defaultmap-firstprivate-lastprivate-ice.f90 +71 -0
- offload/_root/data/findings/fuzz/nvhpc-defaultmap-to-scalar-reduction-lost.f90 +82 -0
- offload/_root/data/findings/fuzz/nvhpc-depend-implicit-map-illegal-address.f90 +81 -0
- offload/_root/data/findings/fuzz/nvhpc-distribute-parallel-num-threads-writes-lost.f90 +79 -0
- offload/_root/data/findings/fuzz/nvhpc-end-distribute-simd-rejected.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-loop-character-achar-unsupported.f90 +75 -0
- offload/_root/data/findings/fuzz/nvhpc-map-always-after-map-to-not-copied-back.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-num-threads-reduction-identity-lost.f90 +54 -0
- offload/_root/data/findings/fuzz/nvhpc-target-parallel-firstprivate-translate-failure.f90 +66 -0
- offload/_root/data/findings/fuzz/nvhpc-target-parallel-single-hang.f90 +68 -0
- offload/_root/data/findings/fuzz/nvhpc-target-simd-collapse-translate-failure.f90 +73 -0
- offload/_root/data/findings/fuzz/nvhpc-target-simd-no-end-directive-rejected.f90 +68 -0
- offload/_root/data/findings/fuzz/par-s1-2082-gcc_g90.f90 +71 -0
- offload/_root/data/findings/fuzz/s1-2370-cce21.f90 +67 -0
- offload/_root/data/findings/fuzz/s1-2592-cce21.f90 +56 -0
- offload/_root/data/findings/fuzz/s1-3463-cce21.f90 +92 -0
- offload/_root/data/findings/fuzz/s1-4339-afar.f90 +79 -0
- offload/_root/data/findings/fuzz/s1-6085-cce21.f90 +96 -0
- offload/_root/data/findings/fuzz/s1-6760-cce20.f90 +65 -0
- offload/_root/data/findings/fuzz/s1-6888-cce21.f90 +76 -0
- offload/_root/data/findings/fuzz/s1-7882-cce20.f90 +63 -0
- offload/_root/data/findings/fuzz/s1-8634-cce21.f90 +45 -0
- offload/_root/data/findings/fuzz/s1-91-cce21.f90 +92 -0
- offload/_root/data/findings/fuzz/s1-9443-afar.f90 +59 -0
- offload/_root/data/findings/fuzz/s1-9914-cce19.f90 +78 -0
- offload/_root/data/findings/gcc/controls/gcc-acc-c-detach-control.c +34 -0
- offload/_root/data/findings/gcc/controls/gcc-acc-detach-control.f90 +26 -0
- offload/_root/data/findings/gcc/controls/gcc-defaultmap-to-pointer-control.f90 +31 -0
- offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.c +26 -0
- offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.f90 +21 -0
- offload/_root/data/findings/gcc/gcc-acc-bind-device-type-rejected.f90 +71 -0
- offload/_root/data/findings/gcc/gcc-acc-c-bind-device-type-rejected.c +76 -0
- offload/_root/data/findings/gcc/gcc-acc-c-detach-delete-attach-underflow.c +74 -0
- offload/_root/data/findings/gcc/gcc-acc-c-gang-dim-rejected.c +79 -0
- offload/_root/data/findings/gcc/gcc-acc-c-link-in-routine-rejected.c +82 -0
- offload/_root/data/findings/gcc/gcc-acc-detach-delete-attach-underflow.f90 +66 -0
- offload/_root/data/findings/gcc/gcc-acc-gang-dim-collapse-force-rejected.f90 +69 -0
- offload/_root/data/findings/gcc/gcc-acc-init-set-shutdown-rejected.f90 +70 -0
- offload/_root/data/findings/gcc/gcc-acc-link-in-routine-rejected.f90 +82 -0
- offload/_root/data/findings/gcc/gcc-acc-use-device-allocatable-rejected.f90 +88 -0
- offload/_root/data/findings/gcc/gcc-c-motion-mapper-rejected.c +110 -0
- offload/_root/data/findings/gcc/gcc-c-omp60-undocumented-rejected.c +76 -0
- offload/_root/data/findings/gcc/gcc-c-omp60-unimplemented-rejected.c +119 -0
- offload/_root/data/findings/gcc/gcc-c-target-in-task-mandatory-device-not-found.c +87 -0
- offload/_root/data/findings/gcc/gcc-c-uses-allocators-traits-unimplemented.c +85 -0
- offload/_root/data/findings/gcc/gcc-declare-mapper-rejected.f90 +85 -0
- offload/_root/data/findings/gcc/gcc-defaultmap-to-pointer-copy-back.f90 +66 -0
- offload/_root/data/findings/gcc/gcc-loop-bind-default-none-rejected.f90 +93 -0
- offload/_root/data/findings/gcc/gcc-omp60-undocumented-rejected.f90 +72 -0
- offload/_root/data/findings/gcc/gcc-omp60-unimplemented-rejected.f90 +112 -0
- offload/_root/data/findings/gcc/gcc-target-in-task-mandatory-device-not-found.f90 +83 -0
- offload/_root/data/findings/gcc/gcc-uses-allocators-traits-unimplemented.f90 +78 -0
- offload/_root/data/findings/harvest/afar-flang-strided-target-update.f90 +96 -0
- offload/_root/data/findings/harvest/cce-acc-declare-create-module-allocatable-xnack1-fault.f90 +58 -0
- offload/_root/data/findings/harvest/cce-c-implicit-struct-map-partly-present.c +100 -0
- offload/_root/data/findings/harvest/cce-c-strided-target-update.c +74 -0
- offload/_root/data/findings/harvest/cce-ftn-target-parallel-thread-query-ice.f90 +67 -0
- offload/_root/data/findings/harvest/cce-named-constant-array-declare-target-routine-rejected.f90 +69 -0
- offload/_root/data/findings/harvest/cce-use-device-ptr-c-ptr-not-converted.f90 +64 -0
- offload/_root/data/findings/harvest/cce21-declare-target-link-module-variable-write-lost.f90 +63 -0
- offload/_root/data/findings/harvest/cce21-internal-procedure-calls-internal-in-target-fault.f90 +71 -0
- offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-target-only-control.c +22 -0
- offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-whole-control.c +23 -0
- offload/_root/data/findings/harvest/controls/gcc-acc-declare-create-allocatable-control.f90 +34 -0
- offload/_root/data/findings/harvest/controls/gcc-c-same-item-two-maps-control.c +17 -0
- offload/_root/data/findings/harvest/controls/gcc-c-target-data-struct-pointer-section-control.c +27 -0
- offload/_root/data/findings/harvest/controls/gcc-c-target-data-two-maps-control.c +20 -0
- offload/_root/data/findings/harvest/controls/gcc-same-item-two-maps-control.f90 +16 -0
- offload/_root/data/findings/harvest/controls/gcc-teams-simd-matmul-allocatable-control.f90 +27 -0
- offload/_root/data/findings/harvest/gcc-acc-declare-create-allocatable-not-mapped.f90 +66 -0
- offload/_root/data/findings/harvest/gcc-c-same-item-two-maps-rejected.c +45 -0
- offload/_root/data/findings/harvest/gcc-c-strided-section-rejected.c +70 -0
- offload/_root/data/findings/harvest/gcc-c-target-data-struct-pointer-section-ice.c +78 -0
- offload/_root/data/findings/harvest/gcc-c-target-data-two-maps-ice.c +49 -0
- offload/_root/data/findings/harvest/gcc-same-item-two-maps-rejected.f90 +46 -0
- offload/_root/data/findings/harvest/gcc-teams-simd-matmul-allocatable-ice.f90 +53 -0
- offload/_root/data/findings/harvest/nvhpc-c-target-data-if-false-use-device-ptr-garbage.c +49 -0
- offload/_root/data/findings/harvest/nvhpc-named-constant-array-declare-target-routine-rejected.f90 +65 -0
- offload/_root/data/findings/harvest/nvhpc-use-device-ptr-c-ptr-not-found.f90 +60 -0
- offload/_root/data/findings/harvest/nvhpc2511-target-nowait-in-parallel-for-ice.c +59 -0
- offload/_root/data/findings/hecbench/controls/f16-ctl-float-array.c +28 -0
- offload/_root/data/findings/hecbench/controls/pc-ctl-builtin-popcount.c +37 -0
- offload/_root/data/findings/hecbench/controls/pc-ctl-shift-sum.c +43 -0
- offload/_root/data/findings/hecbench/gcc-c-float16-array-in-target-lto-ice.c +65 -0
- offload/_root/data/findings/hecbench/gcc-c-swar-popcount-assembler-error.c +80 -0
- offload/_root/data/findings/index.yaml +2453 -0
- offload/_root/data/findings/malloc/afar-device-malloc-in-kernel-corrupts-records.c +371 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-direct.c +52 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-fence.c +57 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-flush.c +57 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-stack.c +52 -0
- offload/_root/data/findings/multithread-allocatable-section-maps.f90 +91 -0
- offload/_root/data/findings/nested/afar-cce-c-call-result-member-in-target-rejected.c +69 -0
- offload/_root/data/findings/nested/afar-cce-c-update-struct-and-member-rejected.c +92 -0
- offload/_root/data/findings/nested/afar-flang-pointer-root-allocatable-component-enter-data-fault.f90 +121 -0
- offload/_root/data/findings/nested/cce-c-struct-and-nested-pointee-sections-ice.c +79 -0
- offload/_root/data/findings/nested/cce-fortran-nested-allocatable-component-copy-fault.f90 +64 -0
- offload/_root/data/findings/nested/cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.f90 +93 -0
- offload/_root/data/findings/nested/cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.f90 +80 -0
- offload/_root/data/findings/nested/controls/c3-ctl-nested-data.c +27 -0
- offload/_root/data/findings/nested/controls/c5-acc-serial-struct-and-array.c +26 -0
- offload/_root/data/findings/nested/controls/e2-ctl-two-directives.c +42 -0
- offload/_root/data/findings/nested/controls/e3-ctl-attach-clause.c +42 -0
- offload/_root/data/findings/nested/controls/f3-pointer-array-only.c +33 -0
- offload/_root/data/findings/nested/controls/f5-ctl-copyout-exit.c +33 -0
- offload/_root/data/findings/nested/controls/f6-ctl-pointer-to-struct-array.c +28 -0
- offload/_root/data/findings/nested/controls/g2-ctl-allocatable.f90 +34 -0
- offload/_root/data/findings/nested/controls/g2-ctl-entered-to.f90 +34 -0
- offload/_root/data/findings/nested/controls/g3-ctl-from-struct-no-element.c +27 -0
- offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-to.c +27 -0
- offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-tofrom.c +27 -0
- offload/_root/data/findings/nested/controls/g5-ctl-element-pointers-only.f90 +36 -0
- offload/_root/data/findings/nested/controls/g5-ctl-root-pointer-only.f90 +36 -0
- offload/_root/data/findings/nested/controls/gp2-ctl-no-mapper-explicit-maps.c +24 -0
- offload/_root/data/findings/nested/controls/gt2-ctl-no-always.c +26 -0
- offload/_root/data/findings/nested/controls/gt3-ctl-always-on-structure-only.c +26 -0
- offload/_root/data/findings/nested/controls/j1-embedded-struct-pointer-to-struct.c +24 -0
- offload/_root/data/findings/nested/controls/j2-ctl-pointer-instead-of-embedded.c +24 -0
- offload/_root/data/findings/nested/controls/j6-ctl-struct-and-one-pointee-level.c +23 -0
- offload/_root/data/findings/nested/controls/j7-ctl-struct-and-embedded-pointee.c +24 -0
- offload/_root/data/findings/nested/controls/m2-ctl-variable-root.f90 +32 -0
- offload/_root/data/findings/nested/controls/m3-ctl-fixed-array-component.f90 +32 -0
- offload/_root/data/findings/nested/controls/m5-ctl-no-enter-data.f90 +33 -0
- offload/_root/data/findings/nested/controls/n8-ctl-fixed-array-in-component.f90 +33 -0
- offload/_root/data/findings/nested/controls/n9-ctl-allocatable-in-the-variable.f90 +33 -0
- offload/_root/data/findings/nested/controls/ns4-ctl-two-directives-struct-then-pointee.c +27 -0
- offload/_root/data/findings/nested/controls/ns8-ctl-enter-together-exit-split.c +26 -0
- offload/_root/data/findings/nested/controls/nu3-ctl-update-pointee-only.c +28 -0
- offload/_root/data/findings/nested/controls/p2-ctl-pointer-variable.c +27 -0
- offload/_root/data/findings/nested/controls/p3-ctl-direct-member.c +26 -0
- offload/_root/data/findings/nested/controls/q2-ctl-fixed-array.f90 +37 -0
- offload/_root/data/findings/nested/controls/q3-ctl-allocatable-component-of-the-variable.f90 +38 -0
- offload/_root/data/findings/nested/controls/ta2-ctl-no-allocatable-component.f90 +42 -0
- offload/_root/data/findings/nested/controls/ta3-ctl-allocatable-scalar-in-the-variable.f90 +38 -0
- offload/_root/data/findings/nested/controls/ta4-ctl-pointer-component-only.f90 +42 -0
- offload/_root/data/findings/nested/controls/u3-ctl-update-member-only.c +24 -0
- offload/_root/data/findings/nested/controls/u4-ctl-two-updates.c +25 -0
- offload/_root/data/findings/nested/controls/u5-ctl-same-list-in-map.c +25 -0
- offload/_root/data/findings/nested/controls/ua2-ctl-variable-root.f90 +37 -0
- offload/_root/data/findings/nested/controls/ua4-ctl-to-in-place-of-alloc-and-update.f90 +37 -0
- offload/_root/data/findings/nested/controls/v2-ctl-parent-first.c +25 -0
- offload/_root/data/findings/nested/controls/v4-ctl-embedded-member.c +24 -0
- offload/_root/data/findings/nested/gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.c +83 -0
- offload/_root/data/findings/nested/gcc-acc-c-parent-and-shared-pointee-one-directive.c +88 -0
- offload/_root/data/findings/nested/gcc-acc-c-struct-and-member-array-one-directive.c +84 -0
- offload/_root/data/findings/nested/gcc-c-always-on-pointer-member-section-ice.c +75 -0
- offload/_root/data/findings/nested/gcc-c-declare-mapper-pointer-member-section-gpu-fault.c +93 -0
- offload/_root/data/findings/nested/gcc-c-from-struct-and-pointee-section-rejected.c +83 -0
- offload/_root/data/findings/nested/gcc-fortran-pointer-component-alloc-bounds-lost.f90 +62 -0
- offload/_root/data/findings/nested/gcc-fortran-root-and-element-pointer-components-ice.f90 +77 -0
- offload/_root/data/findings/nested/nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.c +55 -0
- offload/_root/data/findings/nested/nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.c +86 -0
- offload/_root/data/findings/nvhpc-acc-atomic-update-sum-fort2-crash.f90 +53 -0
- offload/_root/data/findings/nvhpc-c-acc-atomic-update-sum-ice.c +43 -0
- offload/_root/data/findings/nvhpc-c-dist-schedule-chunk-compile-hang.c +41 -0
- offload/_root/data/findings/nvhpc-c-map-present-ignored.c +42 -0
- offload/_root/data/findings/nvhpc-c-ordered-in-target-nvcpfe-crash.c +92 -0
- offload/_root/data/findings/nvhpc-c-parallel-for-firstprivate-in-target.c +62 -0
- offload/_root/data/findings/nvhpc-c-parallel-if-in-target-nvvm-ice.c +56 -0
- offload/_root/data/findings/nvhpc-defaultmap-firstprivate-explicit-map.f90 +45 -0
- offload/_root/data/findings/nvhpc-defaultmap-from-allocatable-indexing.f90 +60 -0
- offload/_root/data/findings/nvhpc-depend-inoutset-ignored.f90 +64 -0
- offload/_root/data/findings/nvhpc-dist-schedule-chunk-illegal-address.f90 +52 -0
- offload/_root/data/findings/nvhpc-managed-acc-shutdown-host-access.f90 +42 -0
- offload/_root/data/findings/nvhpc-parallel-if-in-target-nvvm-ice.f90 +60 -0
- offload/_root/data/findings/oop/afar-final-dealloc-device-link.f90 +85 -0
- offload/_root/data/findings/oop/afar-pdt-length-parameter-not-implemented.f90 +65 -0
- offload/_root/data/findings/oop/cce-class-is-device-link.f90 +86 -0
- offload/_root/data/findings/oop/cce-copy-alloc-component-section-fault.f90 +73 -0
- offload/_root/data/findings/oop/cce-copy-parent-to-component-tofrom-lost.f90 +65 -0
- offload/_root/data/findings/oop/cce-copy-second-member-section-lost.f90 +65 -0
- offload/_root/data/findings/oop/cce-element-component-map-lost.f90 +64 -0
- offload/_root/data/findings/oop/cce-final-dealloc-device-abort.f90 +85 -0
- offload/_root/data/findings/oop/cce-pdt-default-mapper-rejected.f90 +71 -0
- offload/_root/data/findings/oop/cce-polymorphic-dispatch-device-link.f90 +100 -0
- offload/_root/data/findings/oop/cce-section-of-element-component-overlap.f90 +76 -0
- offload/_root/data/findings/oop/cce-select-type-guard-not-taken.f90 +91 -0
- offload/_root/data/findings/oop/cce-udr-derived-type-unsupported.f90 +69 -0
- offload/_root/data/findings/oop/cce21-element-component-map-lost.f90 +62 -0
- offload/_root/data/findings/oop/controls/alloccomp-comp-only.f90 +44 -0
- offload/_root/data/findings/oop/controls/alloccomp-whole-explicit-component.f90 +50 -0
- offload/_root/data/findings/oop/controls/alloccomp-whole.f90 +49 -0
- offload/_root/data/findings/oop/controls/elemsec-element.f90 +47 -0
- offload/_root/data/findings/oop/controls/final-local.f90 +64 -0
- offload/_root/data/findings/oop/controls/host-class-star-select-type.f90 +58 -0
- offload/_root/data/findings/oop/controls/host-firstprivate-defined-assignment.f90 +54 -0
- offload/_root/data/findings/oop/controls/host-lastprivate-defined-assignment.f90 +58 -0
- offload/_root/data/findings/oop/controls/host-private-finalization.f90 +51 -0
- offload/_root/data/findings/oop/controls/host-select-type.f90 +65 -0
- offload/_root/data/findings/oop/controls/parent-and-comp-component-only.f90 +45 -0
- offload/_root/data/findings/oop/controls/pdt-kind.f90 +46 -0
- offload/_root/data/findings/oop/controls/pdt-len-host.f90 +43 -0
- offload/_root/data/findings/oop/controls/pdt-len-unparameterized.f90 +46 -0
- offload/_root/data/findings/oop/controls/poly-dispatch-static.f90 +75 -0
- offload/_root/data/findings/oop/controls/section-alloc-whole-component.f90 +49 -0
- offload/_root/data/findings/oop/controls/section-element-whole-array.f90 +50 -0
- offload/_root/data/findings/oop/controls/section-element-whole-component.f90 +50 -0
- offload/_root/data/findings/oop/controls/section-fixed.f90 +43 -0
- offload/_root/data/findings/oop/controls/tbp-override.f90 +64 -0
- offload/_root/data/findings/oop/gcc-acc-parent-and-member-data-rejected.f90 +63 -0
- offload/_root/data/findings/oop/gcc-pdt-length-parameter-abort.f90 +68 -0
- offload/_root/data/findings/oop/gcc-polymorphic-dispatch-abort.f90 +97 -0
- offload/_root/data/findings/oop/gcc-vtab-not-marked-for-offload.f90 +94 -0
- offload/_root/data/findings/oop/nvhpc-copy-alloc-component-illegal-address.f90 +76 -0
- offload/_root/data/findings/oop/nvhpc-copy-pdt-length-parameter-illegal-address.f90 +69 -0
- offload/_root/data/findings/oop/nvhpc-named-mapper-rejected.f90 +65 -0
- offload/_root/data/findings/oop/nvhpc-polymorphic-dispatch-translate-failure.f90 +90 -0
- offload/_root/data/findings/oop/nvhpc-polymorphism-runtime-unsupported.f90 +94 -0
- offload/_root/data/findings/oop/nvhpc-type-bound-call-rejected.f90 +89 -0
- offload/_root/data/findings/oop/nvhpc-udr-derived-type-rejected.f90 +67 -0
- offload/_root/data/findings/reviews/afar-c-error-directive-in-target-link.json +217 -0
- offload/_root/data/findings/reviews/afar-c-lastprivate-distribute-parallel-for-small-type.json +113 -0
- offload/_root/data/findings/reviews/afar-c-teams-distribute-reduction-first-team.json +130 -0
- offload/_root/data/findings/reviews/afar-c-update-iterator-copy-mode.json +113 -0
- offload/_root/data/findings/reviews/afar-cce19-character-defaultmap.json +292 -0
- offload/_root/data/findings/reviews/afar-clang-requires-device-safesync-hang.json +191 -0
- offload/_root/data/findings/reviews/afar-flang-firstprivate-map-two-targets.json +130 -0
- offload/_root/data/findings/reviews/afar-flang-loop-bind-teams-reduction.json +126 -0
- offload/_root/data/findings/reviews/afar-flang-task-reduction-max.json +113 -0
- offload/_root/data/findings/reviews/afar-flang-thread-limit-target-parallel-do.json +113 -0
- offload/_root/data/findings/reviews/afar-loop-bind-thread-in-teams.json +191 -0
- offload/_root/data/findings/reviews/afar-nontemporal-assumed-shape-ice.json +113 -0
- offload/_root/data/findings/reviews/afar-udr-derived-type-link.json +113 -0
- offload/_root/data/findings/reviews/afar-xnack1-atomic-min-nested-parallel.json +113 -0
- offload/_root/data/findings/reviews/afar-xnack1-target-is-present.json +113 -0
- offload/_root/data/findings/reviews/apps--afar-cce-teams-private-array-nested-parallel-wrong.json +113 -0
- offload/_root/data/findings/reviews/apps--cce20-device-link-infer-address-spaces-crash.json +113 -0
- offload/_root/data/findings/reviews/apps--cce21-device-link-agpr-copy-mfma-assert.json +113 -0
- offload/_root/data/findings/reviews/apps--cce21-private-array-loaded-subscript-wrong.json +113 -0
- offload/_root/data/findings/reviews/apps--gcc-acc-c-tile-partial-tiles-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-c-ordered-doacross-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-c-reduction-task-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-absent-optional-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-critical-target-parallel-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-dist-schedule-chunk.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-linear-parallel-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-named-critical-link.json +126 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-ordered-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-reduction-minus-rejected.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-sections-in-target-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-workdistribute-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--c-allocate-const-mem-firstprivate-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-acc-character-dummy-routine-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-acc-loop-seq-in-routine-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-allocators-stack-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-allocate-align-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-allocate-allocator-modifier-rejected.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-dist-schedule-chunk.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-f-pointer-device-link.json +130 -0
- offload/_root/data/findings/reviews/catalog--cce-c-long-double-ice.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce-c-map-iterator-ice.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce-c-recursive-function-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-sections-in-target-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-allocate-in-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-lastprivate-parallel-in-seq-loop-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-stop-in-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-iterator-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-linear-lastprivate-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-reduction-inscan-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-reduction-task-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce19-reduction-task-addrspacecast-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce20-c-defaultmap-private-storage-rejected.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce20-map-self-rejected.json +204 -0
- offload/_root/data/findings/reviews/catalog--cce21-c-target-loop-ice.json +126 -0
- offload/_root/data/findings/reviews/catalog--defaultmap-present-private-array-abort.json +236 -0
- offload/_root/data/findings/reviews/catalog--nvhpc-acc-associate-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/catalog--nvhpc-c-function-pointer-kernel-missing.json +268 -0
- offload/_root/data/findings/reviews/cce-acc-init-set-runtime-assert.json +113 -0
- offload/_root/data/findings/reviews/cce-acc-reduction-200-int8-scalars-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce-acc-routine-bind-module-ice.json +113 -0
- offload/_root/data/findings/reviews/cce-c-dist-schedule-segv.json +113 -0
- offload/_root/data/findings/reviews/cce-c-map-delete-refcount.json +149 -0
- offload/_root/data/findings/reviews/cce-c-map-present-assert.json +126 -0
- offload/_root/data/findings/reviews/cce-c-openmp45-device-link.json +113 -0
- offload/_root/data/findings/reviews/cce-c-parallel-for-in-target.json +141 -0
- offload/_root/data/findings/reviews/cce-mapper-next-map-copy-back.json +208 -0
- offload/_root/data/findings/reviews/cce-uses-allocators-traits-omp50.json +191 -0
- offload/_root/data/findings/reviews/cce19-c-reduction-63-scalars-one-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce19-target-parallel-guarded-loop.json +113 -0
- offload/_root/data/findings/reviews/cce19-target-parallel-single-ice.json +113 -0
- offload/_root/data/findings/reviews/cce21-alloc-2e32-sum-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce21-defaultmap-aggregate-flag.json +210 -0
- offload/_root/data/findings/reviews/cce21-in-reduction-assumed-shape-write-lost.json +113 -0
- offload/_root/data/findings/reviews/cce21-in-reduction-taskwait-link.json +113 -0
- offload/_root/data/findings/reviews/clang-c-declare-target-to-omp52.json +113 -0
- offload/_root/data/findings/reviews/clang-c-linear-directive-name-hang.json +113 -0
- offload/_root/data/findings/reviews/clang-c-shared-declare-target-combined.json +132 -0
- offload/_root/data/findings/reviews/dataenv--cce-value-dummy-target-write-visible.json +113 -0
- offload/_root/data/findings/reviews/defaultmap-none-loop-variable.json +113 -0
- offload/_root/data/findings/reviews/fortran-schedule-static-in-target.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce-common-block-name-in-map-rejected.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce-list-directed-print-in-target-copy-crash.json +217 -0
- offload/_root/data/findings/reviews/fresh--cce19-acc-automatic-array-copyout-copy-crash.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce21-combined-if-runtime-expr-loop-skipped.json +113 -0
- offload/_root/data/findings/reviews/fresh--nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.json +113 -0
- offload/_root/data/findings/reviews/fresh--nvhpc-teams-loop-module-allocatable-assign-fort2-crash.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1505-cce20.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1540-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1978-cce19.json +130 -0
- offload/_root/data/findings/reviews/fuzz--2334-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--591-cce19.json +147 -0
- offload/_root/data/findings/reviews/fuzz--650-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--916-afar.json +130 -0
- offload/_root/data/findings/reviews/fuzz--98-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b1-121-afar.json +160 -0
- offload/_root/data/findings/reviews/fuzz--b2-209-cce19.json +141 -0
- offload/_root/data/findings/reviews/fuzz--b2-68-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b2-85-cce20.json +217 -0
- offload/_root/data/findings/reviews/fuzz--b3-188-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b3-278-cce19.json +212 -0
- offload/_root/data/findings/reviews/fuzz--b3-54-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b3-61-cce20.json +191 -0
- offload/_root/data/findings/reviews/fuzz--b4-86-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--gcc-declare-reduction-module-not-found.json +113 -0
- offload/_root/data/findings/reviews/fuzz--gcc-target-simd-map-nontemporal-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-allocatable-component-implicit-map-illegal-address.json +141 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-assumed-length-character-dummy-map-ice.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-default-none-reduction-lastprivate-fort1-crash.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-firstprivate-lastprivate-ice.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-to-scalar-reduction-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-depend-implicit-map-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-distribute-parallel-num-threads-writes-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-end-distribute-simd-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-loop-character-achar-unsupported.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-map-always-after-map-to-not-copied-back.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-num-threads-reduction-identity-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-firstprivate-translate-failure.json +191 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-single-hang.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-collapse-translate-failure.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-no-end-directive-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--par-s1-2082-gcc_g90.json +126 -0
- offload/_root/data/findings/reviews/fuzz--s1-2370-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-2592-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-3463-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-6760-cce20.json +130 -0
- offload/_root/data/findings/reviews/fuzz--s1-6888-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-7882-cce20.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-8634-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-91-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-9443-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-9914-cce19.json +141 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-bind-device-type-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-detach-delete-attach-underflow.json +314 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-gang-dim-collapse-force-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-init-set-shutdown-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-link-in-routine-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-use-device-allocatable-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-motion-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-omp60-undocumented-rejected.json +217 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-omp60-unimplemented-rejected.json +236 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-target-in-task-mandatory-device-not-found.json +236 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-uses-allocators-traits-unimplemented.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-declare-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-defaultmap-to-pointer-copy-back.json +217 -0
- offload/_root/data/findings/reviews/gcc--gcc-loop-bind-default-none-rejected.json +113 -0
- offload/_root/data/findings/reviews/harvest--afar-flang-strided-target-update.json +141 -0
- offload/_root/data/findings/reviews/harvest--cce-acc-declare-create-module-allocatable-xnack1-fault.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce-c-implicit-struct-map-partly-present.json +217 -0
- offload/_root/data/findings/reviews/harvest--cce-c-strided-target-update.json +141 -0
- offload/_root/data/findings/reviews/harvest--cce-ftn-target-parallel-thread-query-ice.json +130 -0
- offload/_root/data/findings/reviews/harvest--cce-named-constant-array-declare-target-routine-rejected.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce-use-device-ptr-c-ptr-not-converted.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce21-declare-target-link-module-variable-write-lost.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce21-internal-procedure-calls-internal-in-target-fault.json +158 -0
- offload/_root/data/findings/reviews/harvest--gcc-acc-declare-create-allocatable-not-mapped.json +204 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-same-item-two-maps-rejected.json +143 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-strided-section-rejected.json +164 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-target-data-struct-pointer-section-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-target-data-two-maps-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--gcc-teams-simd-matmul-allocatable-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-c-target-data-if-false-use-device-ptr-garbage.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-named-constant-array-declare-target-routine-rejected.json +126 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-use-device-ptr-c-ptr-not-found.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc2511-target-nowait-in-parallel-for-ice.json +130 -0
- offload/_root/data/findings/reviews/hecbench--gcc-c-float16-array-in-target-lto-ice.json +217 -0
- offload/_root/data/findings/reviews/hecbench--gcc-c-swar-popcount-assembler-error.json +126 -0
- offload/_root/data/findings/reviews/malloc--afar-device-malloc-in-kernel-corrupts-records.json +270 -0
- offload/_root/data/findings/reviews/multithread-allocatable-section-maps.json +191 -0
- offload/_root/data/findings/reviews/nested--afar-cce-c-call-result-member-in-target-rejected.json +113 -0
- offload/_root/data/findings/reviews/nested--afar-cce-c-update-struct-and-member-rejected.json +126 -0
- offload/_root/data/findings/reviews/nested--afar-flang-pointer-root-allocatable-component-enter-data-fault.json +191 -0
- offload/_root/data/findings/reviews/nested--cce-c-struct-and-nested-pointee-sections-ice.json +113 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-nested-allocatable-component-copy-fault.json +214 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.json +191 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.json +297 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.json +204 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-parent-and-shared-pointee-one-directive.json +130 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-struct-and-member-array-one-directive.json +141 -0
- offload/_root/data/findings/reviews/nested--gcc-c-always-on-pointer-member-section-ice.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-c-declare-mapper-pointer-member-section-gpu-fault.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-c-from-struct-and-pointee-section-rejected.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-fortran-pointer-component-alloc-bounds-lost.json +217 -0
- offload/_root/data/findings/reviews/nested--gcc-fortran-root-and-element-pointer-components-ice.json +217 -0
- offload/_root/data/findings/reviews/nested--nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.json +204 -0
- offload/_root/data/findings/reviews/nested--nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.json +191 -0
- offload/_root/data/findings/reviews/nvhpc-c-acc-atomic-update-sum-ice.json +277 -0
- offload/_root/data/findings/reviews/nvhpc-c-dist-schedule-chunk-compile-hang.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-map-present-ignored.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-ordered-in-target-nvcpfe-crash.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-parallel-for-firstprivate-in-target.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-defaultmap-firstprivate-explicit-map.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-defaultmap-from-allocatable-indexing.json +132 -0
- offload/_root/data/findings/reviews/nvhpc-depend-inoutset-ignored.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-dist-schedule-chunk-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-managed-acc-shutdown-host-access.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-parallel-if-in-target-nvvm-ice.json +113 -0
- offload/_root/data/findings/reviews/oop--afar-final-dealloc-device-link.json +132 -0
- offload/_root/data/findings/reviews/oop--afar-pdt-length-parameter-not-implemented.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-class-is-device-link.json +130 -0
- offload/_root/data/findings/reviews/oop--cce-copy-alloc-component-section-fault.json +217 -0
- offload/_root/data/findings/reviews/oop--cce-copy-parent-to-component-tofrom-lost.json +217 -0
- offload/_root/data/findings/reviews/oop--cce-copy-second-member-section-lost.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-element-component-map-lost.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-final-dealloc-device-abort.json +208 -0
- offload/_root/data/findings/reviews/oop--cce-pdt-default-mapper-rejected.json +191 -0
- offload/_root/data/findings/reviews/oop--cce-polymorphic-dispatch-device-link.json +247 -0
- offload/_root/data/findings/reviews/oop--cce-section-of-element-component-overlap.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-select-type-guard-not-taken.json +191 -0
- offload/_root/data/findings/reviews/oop--cce-udr-derived-type-unsupported.json +132 -0
- offload/_root/data/findings/reviews/oop--gcc-acc-parent-and-member-data-rejected.json +141 -0
- offload/_root/data/findings/reviews/oop--gcc-pdt-length-parameter-abort.json +141 -0
- offload/_root/data/findings/reviews/oop--gcc-polymorphic-dispatch-abort.json +217 -0
- offload/_root/data/findings/reviews/oop--gcc-vtab-not-marked-for-offload.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-copy-alloc-component-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-copy-pdt-length-parameter-illegal-address.json +236 -0
- offload/_root/data/findings/reviews/oop--nvhpc-named-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-polymorphic-dispatch-translate-failure.json +223 -0
- offload/_root/data/findings/reviews/oop--nvhpc-polymorphism-runtime-unsupported.json +126 -0
- offload/_root/data/findings/reviews/oop--nvhpc-type-bound-call-rejected.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-udr-derived-type-rejected.json +113 -0
- offload/_root/data/findings/reviews/stress--afar-c-lastprivate-combined-target-not-mapped.json +113 -0
- offload/_root/data/findings/reviews/stress--afar-target-scalar-implicit-firstprivate.json +175 -0
- offload/_root/data/findings/reviews/stress--cce-acc-declare-create-scalar-serial-firstprivate.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-c-distribute-lastprivate-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-c-flto-offload-dropped.json +204 -0
- offload/_root/data/findings/reviews/stress--cce-ftn-cif-record-split-abort.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-lastprivate-num-threads-collapse-simd.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-teams-reduction-many-teams-wrap.json +113 -0
- offload/_root/data/findings/reviews/stress--cce21-declare-target-scalar-map-write-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--gcc-declare-target-allocatable-component-ice.json +191 -0
- offload/_root/data/findings/reviews/stress--nvhpc-acc-derived-type-component-not-copied.json +126 -0
- offload/_root/data/findings/reviews/stress--nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-declare-target-scalar-write-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-lastprivate-combined-target-not-mapped.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-target-scalar-implicit-firstprivate.json +130 -0
- offload/_root/data/findings/reviews/target-update-sections-in-parallel.json +113 -0
- offload/_root/data/findings/stress/afar-c-lastprivate-combined-target-not-mapped.c +72 -0
- offload/_root/data/findings/stress/afar-target-scalar-implicit-firstprivate.f90 +47 -0
- offload/_root/data/findings/stress/cce-acc-declare-create-scalar-serial-firstprivate.f90 +43 -0
- offload/_root/data/findings/stress/cce-c-distribute-lastprivate-lost.c +52 -0
- offload/_root/data/findings/stress/cce-c-flto-offload-dropped.c +73 -0
- offload/_root/data/findings/stress/cce-ftn-cif-record-split-abort.f90 +109 -0
- offload/_root/data/findings/stress/cce-lastprivate-num-threads-collapse-simd.f90 +66 -0
- offload/_root/data/findings/stress/cce-teams-reduction-many-teams-wrap.f90 +55 -0
- offload/_root/data/findings/stress/cce21-declare-target-scalar-map-write-lost.f90 +58 -0
- offload/_root/data/findings/stress/controls/gcc-declare-target-allocatable-component-control.f90 +29 -0
- offload/_root/data/findings/stress/gcc-declare-target-allocatable-component-ice.f90 +61 -0
- offload/_root/data/findings/stress/nvhpc-acc-derived-type-component-not-copied.f90 +33 -0
- offload/_root/data/findings/stress/nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.c +62 -0
- offload/_root/data/findings/stress/nvhpc-declare-target-scalar-write-lost.f90 +43 -0
- offload/_root/data/findings/stress/nvhpc-lastprivate-combined-target-not-mapped.f90 +60 -0
- offload/_root/data/findings/stress/nvhpc-target-scalar-implicit-firstprivate.f90 +41 -0
- offload/_root/data/findings/target-update-sections-in-parallel.f90 +53 -0
- offload/_root/data/issues.json +1 -0
- offload/_root/data/search.json +1 -0
- offload/_root/data/vendor/checks/build.py +288 -0
- offload/_version.py +24 -0
- offload/canuse.py +88 -0
- offload/cli.py +52 -0
- offload/data.py +85 -0
- offload/engine/__init__.py +0 -0
- offload/engine/acc_c_gen.py +415 -0
- offload/engine/acc_gen.py +1104 -0
- offload/engine/acc_rules.py +656 -0
- offload/engine/apps.py +804 -0
- offload/engine/baseline.py +324 -0
- offload/engine/c_gen.py +883 -0
- offload/engine/c_program.py +913 -0
- offload/engine/c_rules.py +503 -0
- offload/engine/campaign.py +1180 -0
- offload/engine/campaigns/__init__.py +2 -0
- offload/engine/campaigns/hecbench.py +153 -0
- offload/engine/campaigns/nested.py +76 -0
- offload/engine/campaigns/oop.py +61 -0
- offload/engine/canary.py +275 -0
- offload/engine/catalog.py +860 -0
- offload/engine/citations.py +532 -0
- offload/engine/clang_oracle.py +208 -0
- offload/engine/combos.py +234 -0
- offload/engine/confirm/__init__.py +2 -0
- offload/engine/confirm/backlog.py +159 -0
- offload/engine/confirm/claims.py +423 -0
- offload/engine/confirm/collectors.py +665 -0
- offload/engine/confirm/decisions.py +202 -0
- offload/engine/confirm/draft.py +195 -0
- offload/engine/confirm/engine.py +157 -0
- offload/engine/confirm/execute.py +382 -0
- offload/engine/confirm/facts.py +467 -0
- offload/engine/confirm/findings.py +617 -0
- offload/engine/confirm/forms.py +145 -0
- offload/engine/confirm/ingest.py +372 -0
- offload/engine/confirm/jobs.py +215 -0
- offload/engine/confirm/legality.py +323 -0
- offload/engine/confirm/machines.py +254 -0
- offload/engine/confirm/minimize.py +268 -0
- offload/engine/confirm/model.py +249 -0
- offload/engine/confirm/ports.py +107 -0
- offload/engine/confirm/programs.py +222 -0
- offload/engine/confirm/registry.py +151 -0
- offload/engine/confirm/regress.py +278 -0
- offload/engine/confirm/rules.py +565 -0
- offload/engine/confirm/store.py +63 -0
- offload/engine/confirm/triage.py +838 -0
- offload/engine/confirm/validate.py +607 -0
- offload/engine/confirm/witness.py +355 -0
- offload/engine/corpus_check.py +388 -0
- offload/engine/coverage.py +318 -0
- offload/engine/dataenv_gen.py +1318 -0
- offload/engine/dataenv_rules.py +278 -0
- offload/engine/devtemps.py +146 -0
- offload/engine/expected.py +2559 -0
- offload/engine/f2c.py +1783 -0
- offload/engine/finding_claims.py +330 -0
- offload/engine/finding_header.py +229 -0
- offload/engine/finding_rows.py +382 -0
- offload/engine/flang_oracle.py +299 -0
- offload/engine/fsource.py +620 -0
- offload/engine/fuzz.py +2455 -0
- offload/engine/fuzz_base.py +2016 -0
- offload/engine/fuzz_campaign.py +592 -0
- offload/engine/fuzz_exec.py +798 -0
- offload/engine/fuzz_min.py +1049 -0
- offload/engine/fuzz_profile.py +340 -0
- offload/engine/fuzz_report.py +1270 -0
- offload/engine/fuzz_steer.py +181 -0
- offload/engine/gate.py +1013 -0
- offload/engine/gate_report.py +711 -0
- offload/engine/gate_run.py +118 -0
- offload/engine/gate_stored.py +466 -0
- offload/engine/gen.py +1114 -0
- offload/engine/gen_templates.py +2583 -0
- offload/engine/grammar.py +695 -0
- offload/engine/harness.py +1472 -0
- offload/engine/harvest/__init__.py +3 -0
- offload/engine/harvest/c_runtime.py +465 -0
- offload/engine/harvest/checks.py +534 -0
- offload/engine/harvest/convert.py +784 -0
- offload/engine/harvest/f_runtime.py +668 -0
- offload/engine/harvest/filters.py +338 -0
- offload/engine/harvest/history.py +122 -0
- offload/engine/harvest/inventory.py +399 -0
- offload/engine/harvest2/__init__.py +5 -0
- offload/engine/harvest2/aomp.py +119 -0
- offload/engine/harvest2/examples.py +116 -0
- offload/engine/harvest2/gomp.py +679 -0
- offload/engine/harvest2/hand.py +150 -0
- offload/engine/harvest2/split.py +78 -0
- offload/engine/harvest_known/__init__.py +2 -0
- offload/engine/harvest_known/apps.py +65 -0
- offload/engine/harvest_known/cases.py +128 -0
- offload/engine/harvest_known/centres.py +184 -0
- offload/engine/harvest_known/forum.py +180 -0
- offload/engine/harvest_known/relnotes.py +42 -0
- offload/engine/harvest_known/textscan.py +107 -0
- offload/engine/hdrwrap.py +42 -0
- offload/engine/hecbench.py +568 -0
- offload/engine/hecbench_report.py +265 -0
- offload/engine/hecbench_run.py +483 -0
- offload/engine/idioms.py +1427 -0
- offload/engine/kbody_cites.py +81 -0
- offload/engine/kbody_emit.py +949 -0
- offload/engine/kbody_gen.py +795 -0
- offload/engine/legality_review.py +236 -0
- offload/engine/localcfg.py +58 -0
- offload/engine/malloc_dd.py +267 -0
- offload/engine/mutate.py +1933 -0
- offload/engine/nested_cites.py +196 -0
- offload/engine/nested_emit.py +789 -0
- offload/engine/nested_gen.py +1721 -0
- offload/engine/offload_scope.py +197 -0
- offload/engine/ompvv.py +624 -0
- offload/engine/oop_gen.py +2249 -0
- offload/engine/prerun.py +1435 -0
- offload/engine/program_source.py +739 -0
- offload/engine/provenance.py +103 -0
- offload/engine/reduce.py +2098 -0
- offload/engine/report.py +3374 -0
- offload/engine/root.py +33 -0
- offload/engine/rules.py +4256 -0
- offload/engine/run_matrix.py +325 -0
- offload/engine/site/__init__.py +1 -0
- offload/engine/site/columns.py +53 -0
- offload/engine/site/data.py +648 -0
- offload/engine/site/header.py +228 -0
- offload/engine/site/inputs.py +115 -0
- offload/engine/site/matrix.py +434 -0
- offload/engine/site/scrub.py +294 -0
- offload/engine/skeleton.py +121 -0
- offload/engine/spec_text.py +242 -0
- offload/engine/stress.py +4122 -0
- offload/engine/stress_c.py +1000 -0
- offload/engine/stress_exec.py +805 -0
- offload/engine/stress_min.py +248 -0
- offload/engine/stress_report.py +552 -0
- offload/engine/textclean.py +97 -0
- offload/engine/toolchains.py +1070 -0
- offload/engine/transforms.py +511 -0
- offload/engine/usage.py +1252 -0
- offload/engine/widen.py +673 -0
- offload/engine/widen_progs.py +2396 -0
- offload/issues.py +146 -0
- offtarget-0.1.0.dist-info/METADATA +86 -0
- offtarget-0.1.0.dist-info/RECORD +886 -0
- offtarget-0.1.0.dist-info/WHEEL +4 -0
- offtarget-0.1.0.dist-info/entry_points.txt +2 -0
- offtarget-0.1.0.dist-info/licenses/LICENSE +21 -0
- offtarget-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +20 -0
offload/engine/reduce.py
ADDED
|
@@ -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), [])
|