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