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