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,2583 @@
|
|
|
1
|
+
"""Fortran templates and per-atom recipes for ``offload.engine.gen``.
|
|
2
|
+
|
|
3
|
+
Every program has the same frame (``Src.render``): the module ``gen_mod`` when
|
|
4
|
+
a recipe needs one, then ``program gen_prog`` with the standard data
|
|
5
|
+
(``a(i) = i``, ``b(i) = 0``, host reference ``ref(i)``, ``host_flag = 1``),
|
|
6
|
+
a region of interest where the device computes ``b``, and
|
|
7
|
+
``ok = all(b == ref)`` plus the recipe's extra checks. The program prints
|
|
8
|
+
``RESULT ran_on_host=<0|1> check=<pass|fail>`` and always exits 0.
|
|
9
|
+
``host_flag`` is set inside the target region of interest (``region``), or in
|
|
10
|
+
a separate probe target region before it where that region may not contain an
|
|
11
|
+
OpenMP API call. Programs are deterministic: integers only, n = 64, one
|
|
12
|
+
writer per element, order-independent reductions, explicit ``map`` clauses.
|
|
13
|
+
Template families (``Region.kind``): ``loop``, ``block``, ``data``,
|
|
14
|
+
``declare`` and ``none`` (``requires`` with the standard kernel).
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
|
|
21
|
+
from offload.engine.catalog import (default_grammar, default_skeleton,
|
|
22
|
+
rule_valid, spelling_valid)
|
|
23
|
+
from offload.engine.rules import association, leaves
|
|
24
|
+
|
|
25
|
+
FLAG = "host_flag = merge(1, 0, omp_is_initial_device())"
|
|
26
|
+
BODY = ("b(i) = 2*a(i) + 1",)
|
|
27
|
+
STD_MAPS = "map(to: a) map(from: b)"
|
|
28
|
+
FLAG_MAP = "map(tofrom: host_flag)"
|
|
29
|
+
MAX_LINE = 96
|
|
30
|
+
# The host value of an item a region maps back (from) and the
|
|
31
|
+
# check reads, set before the region: no thread, team or device number,
|
|
32
|
+
# count or ticket the checks accept is negative, so a lost copy-back
|
|
33
|
+
# (a mutant, a compiler) fails deterministically instead of being judged
|
|
34
|
+
# on uninitialized memory
|
|
35
|
+
SENTINEL = -1
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TemplateError(Exception):
|
|
39
|
+
"""A recipe could not be built (a generator bug, never data)."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# --- the frame ----------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class Src:
|
|
46
|
+
"""One program before rendering. Lists hold source lines without the
|
|
47
|
+
frame's indentation."""
|
|
48
|
+
module: list = field(default_factory=list) # gen_mod body (+contains)
|
|
49
|
+
uses: list = field(default_factory=list) # extra USE statements
|
|
50
|
+
spec: list = field(default_factory=list) # directives in spec part
|
|
51
|
+
decls: list = field(default_factory=list)
|
|
52
|
+
init: list = field(default_factory=list) # host setup statements
|
|
53
|
+
region: list = field(default_factory=list)
|
|
54
|
+
post: list = field(default_factory=list)
|
|
55
|
+
checks: list = field(default_factory=list) # extra logical checks
|
|
56
|
+
ref: str = "2*i + 1"
|
|
57
|
+
observable: bool = True
|
|
58
|
+
note: str = ""
|
|
59
|
+
probe: bool = False
|
|
60
|
+
focus: str = "" # the clause text that exercises the atom, if any
|
|
61
|
+
std: str = "f2008"
|
|
62
|
+
needs: tuple = () # run-time needs (offload.engine.gen.NEEDS)
|
|
63
|
+
evidence: str = "construct" # offload.engine.gen.EVIDENCE
|
|
64
|
+
expect: str = "result" # offload.engine.gen.EXPECT
|
|
65
|
+
token: str = "" # compile-output probes: offload.engine.gen
|
|
66
|
+
header_notes: list = field(default_factory=list) # extra header lines
|
|
67
|
+
basis: str = "" # no-oracle programs: catalog/omp/no_oracle.yaml
|
|
68
|
+
legality_rule: str = "" # legality programs: the rule stem
|
|
69
|
+
|
|
70
|
+
def _strip_marks(self):
|
|
71
|
+
self.header_notes = [h for h in self.header_notes if not h.startswith(
|
|
72
|
+
("unverified (", "no oracle ("))]
|
|
73
|
+
self.basis = ""
|
|
74
|
+
|
|
75
|
+
def unverified(self, kind: str, reason: str,
|
|
76
|
+
checked: bool = False) -> "Src":
|
|
77
|
+
"""Mark the program evidence="unverified" (a deterministic
|
|
78
|
+
oracle for behaviour the spec fixes is possible but missing) with
|
|
79
|
+
its header line ``! unverified (<kind>): <reason>``
|
|
80
|
+
(offload.engine.gen.UNVERIFIED). Unless `checked` (the check does test
|
|
81
|
+
what the atom requires), the program is observable=False: it checks
|
|
82
|
+
the device kernel, not the atom."""
|
|
83
|
+
self.evidence = "unverified"
|
|
84
|
+
if not checked:
|
|
85
|
+
self.observable = False
|
|
86
|
+
self.note = "unverified: the check covers the device kernel, " \
|
|
87
|
+
"not the atom"
|
|
88
|
+
self._strip_marks()
|
|
89
|
+
self.header_notes.append(f"unverified ({kind}): {reason}")
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def no_oracle(self, kind: str, reason: str, basis: str,
|
|
93
|
+
checked: bool = False) -> "Src":
|
|
94
|
+
"""Mark a program whose atom no single run can separate from its
|
|
95
|
+
absence, for the reason the spec gives: evidence `kind`
|
|
96
|
+
(offload.engine.gen.NO_ORACLE: hint_only, implementation_defined,
|
|
97
|
+
equivalent_default), header lines ``! no oracle (<kind>):
|
|
98
|
+
<reason>`` and ``! basis: <cite>: "<quote>"`` (the basis id's
|
|
99
|
+
sentence in catalog/omp/no_oracle.yaml). Unless `checked` (the check
|
|
100
|
+
tests what the atom requires, so a wrong answer is a real failure),
|
|
101
|
+
the program is observable=False."""
|
|
102
|
+
self.evidence = kind
|
|
103
|
+
if not checked:
|
|
104
|
+
self.observable = False
|
|
105
|
+
self.note = f"{kind}: the check covers the device kernel, " \
|
|
106
|
+
"not the atom"
|
|
107
|
+
self._strip_marks()
|
|
108
|
+
self.basis = basis
|
|
109
|
+
self.header_notes.append(f"no oracle ({kind}): {reason}")
|
|
110
|
+
return self
|
|
111
|
+
|
|
112
|
+
def presence(self, names, present: bool) -> list[str]:
|
|
113
|
+
"""Host statements asserting (into `pres`, checked at the end) that
|
|
114
|
+
each array is (not) present in the default device's data
|
|
115
|
+
environment, in copy mode only.
|
|
116
|
+
|
|
117
|
+
Under unified memory (HSA_XNACK=1) AFAR maps nothing and
|
|
118
|
+
omp_target_is_present returns 0 even after target enter data, so
|
|
119
|
+
the assertions are skipped when the frame detects shared storage:
|
|
120
|
+
a target region writes a map(to:) array, and the host sees the
|
|
121
|
+
write only if host and device share the storage. Such programs
|
|
122
|
+
carry evidence copy_mode_only."""
|
|
123
|
+
self.shared_setup()
|
|
124
|
+
op = "/=" if present else "=="
|
|
125
|
+
return [f"if (.not. shared_mem) pres = pres .and. "
|
|
126
|
+
f"omp_target_is_present(c_loc({x}), "
|
|
127
|
+
f"omp_get_default_device()) {op} 0" for x in names]
|
|
128
|
+
|
|
129
|
+
def shared_setup(self):
|
|
130
|
+
"""`pres` (checked at the end) and `shared_mem` (host and device
|
|
131
|
+
share storage), once per program."""
|
|
132
|
+
if "logical :: pres, shared_mem" not in self.decls:
|
|
133
|
+
self.decls += ["logical :: pres, shared_mem", "integer :: zc(1)"]
|
|
134
|
+
self.init += ["pres = .true.", "zc = 0",
|
|
135
|
+
omp("target", "map(to: zc)"), "zc(1) = 1",
|
|
136
|
+
omp("end target"), "shared_mem = zc(1) == 1"]
|
|
137
|
+
self.checks.append("pres")
|
|
138
|
+
self.uses.append("use, intrinsic :: iso_c_binding, only: c_loc")
|
|
139
|
+
self.evidence = "copy_mode_only"
|
|
140
|
+
|
|
141
|
+
def copy_check(self, cond: str) -> list[str]:
|
|
142
|
+
"""A host statement asserting `cond` (into `pres`) in copy mode only:
|
|
143
|
+
a host value that a data transfer would change (a device write that
|
|
144
|
+
must not come back, a stale device copy). Under unified memory host
|
|
145
|
+
and device share the storage, so the program carries evidence
|
|
146
|
+
copy_mode_only."""
|
|
147
|
+
self.shared_setup()
|
|
148
|
+
return [f"if (.not. shared_mem) pres = pres .and. ({cond})"]
|
|
149
|
+
|
|
150
|
+
def address_moved(self, names) -> list[str]:
|
|
151
|
+
"""Inside a use_device_addr region: assert (copy mode only) that
|
|
152
|
+
c_loc of each array is no longer its host address, saved as
|
|
153
|
+
h<name> before the region by `save_addresses`."""
|
|
154
|
+
self.shared_setup()
|
|
155
|
+
return [f"if (.not. shared_mem) pres = pres .and. .not. "
|
|
156
|
+
f"c_associated(c_loc({x}), h{x})" for x in names]
|
|
157
|
+
|
|
158
|
+
def save_addresses(self, names) -> list[str]:
|
|
159
|
+
self.decls.append("type(c_ptr) :: " + ", ".join(f"h{x}" for x in
|
|
160
|
+
names))
|
|
161
|
+
self.uses.append("use, intrinsic :: iso_c_binding, only: c_ptr, "
|
|
162
|
+
"c_associated")
|
|
163
|
+
return [f"h{x} = c_loc({x})" for x in names]
|
|
164
|
+
|
|
165
|
+
def omp_names(self) -> list[str]:
|
|
166
|
+
"""The omp_lib names the program's statements and directives use
|
|
167
|
+
(the ONLY list: a USE without ONLY would hide the program's
|
|
168
|
+
declarations from the restriction rules)."""
|
|
169
|
+
names = {"omp_is_initial_device"}
|
|
170
|
+
for line in self.spec + self.decls + self.init + self.region \
|
|
171
|
+
+ self.post:
|
|
172
|
+
low = line.lower()
|
|
173
|
+
if low.lstrip().startswith("!$omp") or \
|
|
174
|
+
not low.lstrip().startswith("!"):
|
|
175
|
+
names.update(re.findall(r"\b(omp_\w+)", low))
|
|
176
|
+
return sorted(names)
|
|
177
|
+
|
|
178
|
+
def render(self, header: list[str]) -> str:
|
|
179
|
+
out = [f"! {h}" for h in header]
|
|
180
|
+
if self.module:
|
|
181
|
+
out += ["module gen_mod", " implicit none"]
|
|
182
|
+
out += _indent(self.module, 2)
|
|
183
|
+
out += ["end module gen_mod", ""]
|
|
184
|
+
out.append("program gen_prog")
|
|
185
|
+
out.append(" use omp_lib, only: " + ", ".join(self.omp_names()))
|
|
186
|
+
if self.module:
|
|
187
|
+
out.append(" use gen_mod")
|
|
188
|
+
out += _indent(self.uses, 2)
|
|
189
|
+
out.append(" implicit none")
|
|
190
|
+
body = list(self.spec) + [
|
|
191
|
+
"integer, parameter :: n = 64",
|
|
192
|
+
"integer, allocatable, target :: a(:), b(:)",
|
|
193
|
+
"integer :: ref(n)",
|
|
194
|
+
"integer :: i, host_flag",
|
|
195
|
+
"logical :: ok"] + list(self.decls) + [
|
|
196
|
+
"allocate(a(n), b(n))",
|
|
197
|
+
"do i = 1, n",
|
|
198
|
+
" a(i) = i",
|
|
199
|
+
" b(i) = 0",
|
|
200
|
+
f" ref(i) = {self.ref}",
|
|
201
|
+
"end do",
|
|
202
|
+
"host_flag = 1"] + list(self.init) + list(self.region) + list(
|
|
203
|
+
self.post)
|
|
204
|
+
ok = " .and. ".join(["all(b == ref)"] + list(self.checks))
|
|
205
|
+
body += [f"ok = {ok}",
|
|
206
|
+
"print '(a,i0,a,a)', 'RESULT ran_on_host=', host_flag, "
|
|
207
|
+
"' check=', merge('pass', 'fail', ok)",
|
|
208
|
+
"deallocate(a, b)"]
|
|
209
|
+
out += _indent(body, 2)
|
|
210
|
+
out.append("end program gen_prog")
|
|
211
|
+
return "\n".join(_wrap(line) for line in out) + "\n"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _indent(lines, n):
|
|
215
|
+
"""Indent `lines` by n blanks plus two per open construct (DO, IF ...
|
|
216
|
+
THEN, block and loop directives up to their end directives)."""
|
|
217
|
+
out, depth = [], 0
|
|
218
|
+
for line in lines:
|
|
219
|
+
low = line.strip().lower()
|
|
220
|
+
opens, closes = _opens(low), _closes(low)
|
|
221
|
+
depth -= closes
|
|
222
|
+
out.append(" " * (n + 2 * max(depth, 0)) + line.strip()
|
|
223
|
+
if line else "")
|
|
224
|
+
depth += opens
|
|
225
|
+
return out
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _opens(low: str) -> int:
|
|
229
|
+
if re.match(r"do(\s|$)", low) or re.match(r"if\s*\(.*\)\s*then$", low) \
|
|
230
|
+
or low.startswith("contains") or re.match(
|
|
231
|
+
r"(integer|logical)\s+function\b|subroutine\b", low) \
|
|
232
|
+
or re.match(r"type\s*::", low):
|
|
233
|
+
return 1
|
|
234
|
+
if low.startswith("!$omp") and not low.startswith("!$omp&"):
|
|
235
|
+
name = _omp_key(low)
|
|
236
|
+
return int(name is not None and association(name) in
|
|
237
|
+
("block", "loop"))
|
|
238
|
+
return 0
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _closes(low: str) -> int:
|
|
242
|
+
if re.match(r"end\s*(do|if|function|subroutine|type)\b", low):
|
|
243
|
+
return 1
|
|
244
|
+
if low.startswith("!$omp end"):
|
|
245
|
+
return 1
|
|
246
|
+
return 0
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _omp_key(low: str):
|
|
250
|
+
"""Skeleton key of a directive line (not an end directive), or None."""
|
|
251
|
+
from offload.engine.grammar import GrammarError, parse_directive
|
|
252
|
+
if low.startswith("!$omp end"):
|
|
253
|
+
return None
|
|
254
|
+
text = low.rstrip("&").rstrip()
|
|
255
|
+
try:
|
|
256
|
+
return parse_directive(text, "fortran", 60).name
|
|
257
|
+
except GrammarError:
|
|
258
|
+
try:
|
|
259
|
+
return parse_directive(text, "fortran", 52).name
|
|
260
|
+
except GrammarError:
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _wrap(line: str) -> str:
|
|
265
|
+
"""Split a long directive line at clause boundaries (free-form
|
|
266
|
+
continuation: ``&`` at the end, ``!$omp&`` on the next line)."""
|
|
267
|
+
if len(line) <= MAX_LINE or not line.lstrip().lower().startswith("!$omp"):
|
|
268
|
+
return line
|
|
269
|
+
ind = line[:len(line) - len(line.lstrip())]
|
|
270
|
+
parts, depth, cur = [], 0, ""
|
|
271
|
+
for ch in line.strip():
|
|
272
|
+
if ch == "(":
|
|
273
|
+
depth += 1
|
|
274
|
+
elif ch == ")":
|
|
275
|
+
depth -= 1
|
|
276
|
+
if ch == " " and depth == 0:
|
|
277
|
+
parts.append(cur)
|
|
278
|
+
cur = ""
|
|
279
|
+
continue
|
|
280
|
+
cur += ch
|
|
281
|
+
parts.append(cur)
|
|
282
|
+
lines, cur = [], ""
|
|
283
|
+
for p in parts:
|
|
284
|
+
if cur and len(ind) + len(cur) + len(p) + 3 > MAX_LINE:
|
|
285
|
+
lines.append(cur)
|
|
286
|
+
cur = "!$omp& " + p
|
|
287
|
+
else:
|
|
288
|
+
cur = f"{cur} {p}" if cur else p
|
|
289
|
+
lines.append(cur)
|
|
290
|
+
return " &\n".join(ind + x for x in lines)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# --- directive facts ------------------------------------------------------------
|
|
294
|
+
|
|
295
|
+
def spell(key: str, version: int) -> str:
|
|
296
|
+
"""A valid spelling of skeleton directive `key` at `version` (blanks
|
|
297
|
+
preferred)."""
|
|
298
|
+
sk, g = default_skeleton(), default_grammar()
|
|
299
|
+
cands = [key, key.replace(" ", "_")] + [
|
|
300
|
+
s.text for s in sk.directive(key).spellings] + [
|
|
301
|
+
e.spelling for e in g.spellings if e.directive == key]
|
|
302
|
+
for c in cands:
|
|
303
|
+
if spelling_valid(sk, g, key, c, version):
|
|
304
|
+
return c
|
|
305
|
+
raise TemplateError(f"no spelling of {key!r} at {version}")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def permits(key: str, clause: str, version: int) -> bool:
|
|
309
|
+
rule = default_grammar().allowed(default_skeleton(), key).get(clause)
|
|
310
|
+
return rule is not None and rule_valid(rule, version)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def tablegen_permits(key: str, clause: str, version: int) -> bool:
|
|
314
|
+
"""Whether the TableGen skeleton itself (without spec overrides) allows
|
|
315
|
+
`clause` on `key` at `version`."""
|
|
316
|
+
rule = default_skeleton().directive(key).allowed.get(clause)
|
|
317
|
+
return rule is not None and rule_valid(rule, version)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def hygiene(key: str, version: int, shared) -> str:
|
|
321
|
+
"""``default(none) shared(...)`` where the directive allows both. The
|
|
322
|
+
hygiene clause is added only where TableGen allows `default` too: where
|
|
323
|
+
only a provisional spec override allows it (target teams distribute simd
|
|
324
|
+
at 5.2) a compiler may reject it, and the rejection would be read as one
|
|
325
|
+
of the atom under test."""
|
|
326
|
+
if shared and permits(key, "default", version) \
|
|
327
|
+
and tablegen_permits(key, "default", version) \
|
|
328
|
+
and permits(key, "shared", version):
|
|
329
|
+
return f"default(none) shared({', '.join(shared)})"
|
|
330
|
+
return ""
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def concurrent(key: str, clauses: str) -> bool:
|
|
334
|
+
"""Whether the region may not contain an OpenMP API call."""
|
|
335
|
+
return bool({"loop", "simd", "workdistribute"} & set(leaves(key))) \
|
|
336
|
+
or bool(re.search(r"\border\s*\(", clauses))
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def omp(*parts: str) -> str:
|
|
340
|
+
return "!$omp " + " ".join(p for p in parts if p)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def seq_loop(body=BODY, flag=False, var="i"):
|
|
344
|
+
out = [f"do {var} = 1, n"] + [" " + b for b in body]
|
|
345
|
+
if flag:
|
|
346
|
+
out.append(f" if ({var} == 1) {FLAG}")
|
|
347
|
+
return out + ["end do"]
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def guarded(cond: str, lines) -> list:
|
|
351
|
+
return [f"if ({cond}) then"] + [" " + x for x in lines] + ["end if"]
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# A teams region may not call omp_is_initial_device directly (5.2
|
|
355
|
+
# §10.2, p.232: omp_get_num_teams and omp_get_team_num regions are the only
|
|
356
|
+
# routine regions that may be strictly nested in it; 6.0 §12.2, p.396: only
|
|
357
|
+
# teams-nestable routines, and omp_is_initial_device is not one, §24.9), so
|
|
358
|
+
# a teams region reports the device from a one-thread parallel region
|
|
359
|
+
# (parallel is teams-nestable).
|
|
360
|
+
TEAMS_FLAG = [omp("parallel", "num_threads(1)"), FLAG, omp("end parallel")]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# The probe region: reports the device in host_flag and sets the device
|
|
364
|
+
# copy of the declare-target variable on_dev (host copy 0) to 1. The
|
|
365
|
+
# construct of interest then computes b(i) = 2*a(i) + on_dev, so a host
|
|
366
|
+
# fallback of the construct alone reads the host copy: b = 2*a, the check
|
|
367
|
+
# fails and the frame sets host_flag = 1 (PROBE_POST).
|
|
368
|
+
PROBE = [omp("target", FLAG_MAP), FLAG, "on_dev = 1", omp("end target")]
|
|
369
|
+
# The extended-list form (5.2 §7.8.1, p.206; 6.0 §9.9.1, p.346: as if an
|
|
370
|
+
# enter clause were given; 5.0 §2.12.7, p.180: as if a to clause were
|
|
371
|
+
# given) is legal at every version and accepted by AFAR and CCE 19 (which
|
|
372
|
+
# lacks the enter clause).
|
|
373
|
+
PROBE_MODULE = {v: ["integer :: on_dev = 0", "!$omp declare target(on_dev)"]
|
|
374
|
+
for v in (50, 52, 60)}
|
|
375
|
+
PROBE_POST = ["if (all(b == 2*a)) host_flag = 1"]
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# --- the region of interest ---------------------------------------------------------
|
|
379
|
+
|
|
380
|
+
@dataclass
|
|
381
|
+
class Region:
|
|
382
|
+
"""The construct of interest and how it is embedded.
|
|
383
|
+
|
|
384
|
+
`clauses` go on the construct of interest; `maps` (and the host_flag
|
|
385
|
+
map) on the outermost target construct; `wrap` are extra clauses for a
|
|
386
|
+
wrapper target construct; `shared` the variables the construct shares
|
|
387
|
+
(for the default(none) hygiene); `loop` replaces the DO loop (a list of
|
|
388
|
+
lines, or a function of `flag` returning lines); `body` the loop body.
|
|
389
|
+
"""
|
|
390
|
+
key: str
|
|
391
|
+
clauses: str = ""
|
|
392
|
+
maps: str = STD_MAPS
|
|
393
|
+
wrap: str = ""
|
|
394
|
+
shared: tuple = ("a", "b")
|
|
395
|
+
body: tuple = BODY
|
|
396
|
+
loop: object = None
|
|
397
|
+
hyg: bool = True
|
|
398
|
+
wrapper: str | None = None # force the wrapper construct
|
|
399
|
+
pre: tuple = () # wrapper statements before the construct (nested form)
|
|
400
|
+
# the region may call the OpenMP API although a leaf is simd
|
|
401
|
+
# (simd restricts constructs, not routines, 5.2 §10.4, 6.0 §12.4)
|
|
402
|
+
api: bool = False
|
|
403
|
+
# the loop body of a probe region (default: b(i) = 2*a(i) + on_dev),
|
|
404
|
+
# which must compute b = 2*a + on_dev
|
|
405
|
+
probe_body: tuple = ()
|
|
406
|
+
|
|
407
|
+
def lines(self, version: int) -> tuple[list, bool]:
|
|
408
|
+
"""(source lines, whether a separate probe region is used)."""
|
|
409
|
+
key, v = self.key, version
|
|
410
|
+
ls = leaves(key)
|
|
411
|
+
sp = spell(key, v)
|
|
412
|
+
conc = concurrent(key, self.clauses) and not self.api
|
|
413
|
+
assoc = association(key)
|
|
414
|
+
|
|
415
|
+
def hyg(extra=()):
|
|
416
|
+
if not self.hyg or re.search(r"\bdefault\s*\(", self.clauses):
|
|
417
|
+
return ""
|
|
418
|
+
h = hygiene(key, v, tuple(self.shared) + tuple(extra))
|
|
419
|
+
# 5.0: a sequential loop's variable is predetermined
|
|
420
|
+
# private only in a parallel or task generating construct (5.0
|
|
421
|
+
# §2.19.1.1 p.271; 5.2 adds teams), so under default(none) a
|
|
422
|
+
# teams block lists the DO variable (AFAR -fopenmp-version=50
|
|
423
|
+
# rejects it otherwise)
|
|
424
|
+
if h and v == 50 and assoc != "loop" and "teams" in ls \
|
|
425
|
+
and "parallel" not in ls:
|
|
426
|
+
h += " private(i)"
|
|
427
|
+
return h
|
|
428
|
+
|
|
429
|
+
def loop(flag):
|
|
430
|
+
if self.loop is None:
|
|
431
|
+
return seq_loop(self.body, flag)
|
|
432
|
+
return self.loop(flag) if callable(self.loop) else list(self.loop)
|
|
433
|
+
|
|
434
|
+
def probe_loop():
|
|
435
|
+
if self.probe_body:
|
|
436
|
+
return seq_loop(self.probe_body)
|
|
437
|
+
if self.loop is not None or tuple(self.body) != BODY:
|
|
438
|
+
raise TemplateError(f"probe region for {key!r} needs the "
|
|
439
|
+
"standard body")
|
|
440
|
+
return seq_loop(("b(i) = 2*a(i) + on_dev",))
|
|
441
|
+
|
|
442
|
+
array_probe = "b = 2*a + on_dev"
|
|
443
|
+
|
|
444
|
+
def end():
|
|
445
|
+
return [omp("end", sp)]
|
|
446
|
+
|
|
447
|
+
if ls[0] == "target":
|
|
448
|
+
inner = ls[1:]
|
|
449
|
+
if assoc == "loop":
|
|
450
|
+
if conc:
|
|
451
|
+
return PROBE + [omp(sp, self.maps, hyg(["on_dev"]),
|
|
452
|
+
self.clauses)] \
|
|
453
|
+
+ probe_loop() + end(), True
|
|
454
|
+
return [omp(sp, self.maps, FLAG_MAP, hyg(["host_flag"]),
|
|
455
|
+
self.clauses)] + loop(True) + end(), False
|
|
456
|
+
if inner and inner[-1] == "workdistribute":
|
|
457
|
+
return PROBE + [omp(sp, self.maps, hyg(["on_dev"]),
|
|
458
|
+
self.clauses), array_probe] + end(), True
|
|
459
|
+
head = omp(sp, self.maps, FLAG_MAP, hyg(["host_flag"]),
|
|
460
|
+
self.clauses)
|
|
461
|
+
if not inner:
|
|
462
|
+
return [head, FLAG] + loop(False) + end(), False
|
|
463
|
+
cond = {"parallel": "omp_get_thread_num() == 0",
|
|
464
|
+
"teams": "omp_get_team_num() == 0"}[inner[-1]]
|
|
465
|
+
flag = TEAMS_FLAG if inner[-1] == "teams" else [FLAG]
|
|
466
|
+
return [head] + guarded(cond, flag + loop(False)) + end(), \
|
|
467
|
+
False
|
|
468
|
+
if ls[0] == "teams":
|
|
469
|
+
wrapper = omp("target", self.maps, self.wrap)
|
|
470
|
+
if assoc == "loop":
|
|
471
|
+
if conc:
|
|
472
|
+
return PROBE + [wrapper, omp(sp, hyg(["on_dev"]),
|
|
473
|
+
self.clauses)] + \
|
|
474
|
+
probe_loop() + end() + [omp("end target")], True
|
|
475
|
+
return [omp("target", self.maps, FLAG_MAP, self.wrap),
|
|
476
|
+
omp(sp, hyg(["host_flag"]), self.clauses)] + \
|
|
477
|
+
loop(True) + end() + [omp("end target")], False
|
|
478
|
+
if ls[-1] == "workdistribute":
|
|
479
|
+
return PROBE + [wrapper, omp(sp, hyg(["on_dev"]),
|
|
480
|
+
self.clauses), array_probe] + \
|
|
481
|
+
end() + [omp("end target")], True
|
|
482
|
+
return [omp("target", self.maps, FLAG_MAP, self.wrap),
|
|
483
|
+
omp(sp, hyg(["host_flag"]), self.clauses)] + \
|
|
484
|
+
guarded("omp_get_team_num() == 0", TEAMS_FLAG + loop(False)) + \
|
|
485
|
+
end() + [omp("end target")], False
|
|
486
|
+
# a construct nested in a wrapper target region with a statement
|
|
487
|
+
kind = self.wrapper
|
|
488
|
+
if kind is None:
|
|
489
|
+
if ls[0] == "distribute":
|
|
490
|
+
kind = "target teams"
|
|
491
|
+
elif ls[0] == "loop":
|
|
492
|
+
b = re.search(r"\bbind\s*\(\s*(\w+)", self.clauses)
|
|
493
|
+
kind = {"parallel": "target parallel", "thread": "target"}.get(
|
|
494
|
+
b.group(1) if b else "", "target teams")
|
|
495
|
+
else:
|
|
496
|
+
kind = "target"
|
|
497
|
+
flag = {"target": [FLAG],
|
|
498
|
+
"target teams": guarded("omp_get_team_num() == 0",
|
|
499
|
+
TEAMS_FLAG),
|
|
500
|
+
"target parallel": [f"if (omp_get_thread_num() == 0) {FLAG}"]
|
|
501
|
+
}[kind]
|
|
502
|
+
return [omp(kind, self.maps, FLAG_MAP, self.wrap)] + flag + list(
|
|
503
|
+
self.pre) + [omp(sp, hyg(), self.clauses)] + loop(False) + \
|
|
504
|
+
end() + [omp("end", kind)], False
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def kernel(src: Src, region: Region, version: int) -> Src:
|
|
508
|
+
lines, probe = region.lines(version)
|
|
509
|
+
src.region += lines
|
|
510
|
+
if probe:
|
|
511
|
+
src.probe = True
|
|
512
|
+
src.module = PROBE_MODULE[version] + src.module
|
|
513
|
+
src.post = PROBE_POST + src.post
|
|
514
|
+
if "workdistribute" in leaves(region.key):
|
|
515
|
+
src.header_notes.append("unverified: no toolchain compiles this "
|
|
516
|
+
"(6.0 workdistribute), so the on_dev "
|
|
517
|
+
"evidence is untested here")
|
|
518
|
+
if not src.focus:
|
|
519
|
+
src.focus = region.clauses
|
|
520
|
+
return src
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
DEFAULT_KEY = "target teams distribute parallel do"
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# --- recipes -------------------------------------------------------------------------
|
|
527
|
+
#
|
|
528
|
+
# recipe(atom, version) -> Src. Directive atoms use the directive's own
|
|
529
|
+
# family; clause atoms put the clause on a construct that allows it at the
|
|
530
|
+
# version, with the context it needs. Each builder states its choice.
|
|
531
|
+
|
|
532
|
+
def _k(version, region: Region, **src) -> Src:
|
|
533
|
+
return kernel(Src(**src), region, version)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
# Compute directives: what the check reads to tell the construct from the
|
|
537
|
+
# loop or block run without it.
|
|
538
|
+
#
|
|
539
|
+
# * distribute / loop forms in a target teams region of num_teams(4) teams
|
|
540
|
+
# (exactly 4: 5.2 §10.2.1, 6.0 §12.2.1): every iteration counts itself in
|
|
541
|
+
# its team's column of cnt (the team number tm, taken in the teams region
|
|
542
|
+
# before the construct, so no API call is needed in a simd or loop
|
|
543
|
+
# region); with the construct each iteration runs once in the league,
|
|
544
|
+
# without it every team runs every iteration (sum 4).
|
|
545
|
+
# * teams forms nested in target, with num_teams(4): the region reads
|
|
546
|
+
# omp_get_num_teams(); without the construct it is 1.
|
|
547
|
+
# * parallel do: the team size (omp_get_num_threads in the loop) must be
|
|
548
|
+
# more than one whenever a plain parallel region before it got more than
|
|
549
|
+
# one thread (the reference region; a one-thread team is conforming and
|
|
550
|
+
# then nothing is required); without the construct the loop runs on the
|
|
551
|
+
# target's one thread.
|
|
552
|
+
LEAGUE = {"distribute", "distribute parallel do",
|
|
553
|
+
"distribute parallel do simd", "distribute simd", "loop"}
|
|
554
|
+
TEAMS_N = {"teams", "teams distribute", "teams distribute parallel do",
|
|
555
|
+
"teams distribute simd", "teams distribute parallel do simd"}
|
|
556
|
+
# A simd region may call the OpenMP API (simd
|
|
557
|
+
# restricts only the constructs in its region to SIMDizable ones, 5.2
|
|
558
|
+
# §10.4 p.234, 6.0 §12.4 p.398), so the simd compound forms read the team
|
|
559
|
+
# or league like their non-simd counterparts.
|
|
560
|
+
SIMD_API = {"parallel do simd", "teams distribute simd",
|
|
561
|
+
"teams distribute parallel do simd"}
|
|
562
|
+
# parallel loop and teams loop: the region may not call the
|
|
563
|
+
# OpenMP API (a loop construct behaves as if order(concurrent) were
|
|
564
|
+
# specified, 5.2 §11.7 / 6.0 §13.8, and such a region may not contain API
|
|
565
|
+
# calls, 5.2 §10.3, or only order-concurrent-nestable routines, 6.0 §12.3,
|
|
566
|
+
# none of which queries the team or league), and the combined construct
|
|
567
|
+
# leaves no statement between its constituents, so who ran the iterations
|
|
568
|
+
# is not observable in one run. What is observable is the data environment
|
|
569
|
+
# the construct creates: t (mapped tofrom, 7) is private on the construct
|
|
570
|
+
# and every iteration writes it; with the construct the target's t stays 7,
|
|
571
|
+
# without it (or with the construct run as a plain loop that does not
|
|
572
|
+
# privatize) the iterations write the target's t. The check therefore
|
|
573
|
+
# shows that the construct and its data environment were created on the
|
|
574
|
+
# device, not that more than one thread or team ran the loop.
|
|
575
|
+
PRIVATE_LOOP = {"parallel loop", "teams loop"}
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def compute_prog(key: str, version: int) -> Src:
|
|
579
|
+
if key in LEAGUE:
|
|
580
|
+
shared = ("a", "b", "cnt", "tm")
|
|
581
|
+
return _k(version, Region(
|
|
582
|
+
key, maps=f"{STD_MAPS} map(tofrom: cnt)",
|
|
583
|
+
wrap="num_teams(4) private(tm)", shared=shared,
|
|
584
|
+
pre=("tm = omp_get_team_num()",),
|
|
585
|
+
body=BODY + ("cnt(i, tm + 1) = cnt(i, tm + 1) + 1",)),
|
|
586
|
+
decls=["integer :: cnt(n, 4), tm"], init=["cnt = 0"],
|
|
587
|
+
checks=["all(sum(cnt, dim=2) == 1)"])
|
|
588
|
+
if key == "teams":
|
|
589
|
+
def loop(flag):
|
|
590
|
+
return ["nt = omp_get_num_teams()"] + seq_loop()
|
|
591
|
+
return _k(version, Region(key, "num_teams(4)",
|
|
592
|
+
maps=f"{STD_MAPS} map(from: nt)",
|
|
593
|
+
shared=("a", "b", "nt"), loop=loop),
|
|
594
|
+
decls=["integer :: nt"], init=[f"nt = {SENTINEL}"],
|
|
595
|
+
checks=["(nt == 4)"],
|
|
596
|
+
focus="")
|
|
597
|
+
if key in TEAMS_N:
|
|
598
|
+
return _k(version, Region(
|
|
599
|
+
key, "num_teams(4)", maps=f"{STD_MAPS} map(from: nt)",
|
|
600
|
+
shared=("a", "b", "nt"), api=key in SIMD_API,
|
|
601
|
+
body=BODY + ("if (i == 1) nt = omp_get_num_teams()",)),
|
|
602
|
+
decls=["integer :: nt"], init=[f"nt = {SENTINEL}"],
|
|
603
|
+
checks=["(nt == 4)"], focus="")
|
|
604
|
+
if key in ("parallel do", "parallel do simd"):
|
|
605
|
+
return _k(version, Region(
|
|
606
|
+
key, maps=f"{STD_MAPS} map(from: nref, nthr)",
|
|
607
|
+
shared=("a", "b", "nthr"),
|
|
608
|
+
pre=(omp("parallel"),
|
|
609
|
+
"if (omp_get_thread_num() == 0) nref = omp_get_num_threads()",
|
|
610
|
+
omp("end parallel")),
|
|
611
|
+
body=BODY + ("if (i == 1) nthr = omp_get_num_threads()",)),
|
|
612
|
+
decls=["integer :: nref, nthr"],
|
|
613
|
+
init=[f"nref = {SENTINEL}", f"nthr = {SENTINEL}"],
|
|
614
|
+
checks=["(nref == 1 .or. nthr > 1)"])
|
|
615
|
+
if key in PRIVATE_LOOP:
|
|
616
|
+
return _k(version, Region(
|
|
617
|
+
key, "private(t)", maps=f"{STD_MAPS} map(tofrom: t)",
|
|
618
|
+
body=("t = 2*a(i)", "b(i) = t + 1"),
|
|
619
|
+
probe_body=("t = 2*a(i)", "b(i) = 2*a(i) + on_dev")),
|
|
620
|
+
decls=["integer :: t"], init=["t = 7"], checks=["(t == 7)"],
|
|
621
|
+
focus="")
|
|
622
|
+
return _k(version, Region(key))
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def directive_recipe(key: str, version: int) -> Src:
|
|
626
|
+
if key == "target data":
|
|
627
|
+
return data_prog(version)
|
|
628
|
+
if key == "target enter data" or key == "target exit data":
|
|
629
|
+
return enter_exit_prog(version)
|
|
630
|
+
if key == "target update":
|
|
631
|
+
return update_prog(version)
|
|
632
|
+
if key == "declare target":
|
|
633
|
+
# the extended-list form on a variable: a declare target procedure
|
|
634
|
+
# cannot be told from an implicitly declare target one (see
|
|
635
|
+
# IMPLICIT_PROCEDURE_NOTE), a variable's device copy can
|
|
636
|
+
return declare_var_prog(version, "!$omp declare target(dv)")
|
|
637
|
+
if key == "requires":
|
|
638
|
+
return requires_prog(version, "atomic_default_mem_order(seq_cst)")
|
|
639
|
+
return compute_prog(key, version)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
# target data / enter / exit / update ------------------------------------------------
|
|
643
|
+
|
|
644
|
+
INNER = [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop() + \
|
|
645
|
+
[omp("end target")]
|
|
646
|
+
# The kernel inside a data construct: map(alloc: ...) moves no data, so the
|
|
647
|
+
# result depends on the construct's own maps (copy mode); the presence
|
|
648
|
+
# assertions of the frame depend on it in every mode where the runtime
|
|
649
|
+
# keeps a mapping table.
|
|
650
|
+
INNER_ALLOC = [omp("target", "map(alloc: a, b)", FLAG_MAP), FLAG] + \
|
|
651
|
+
seq_loop() + [omp("end target")]
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def data_prog(version, clauses="", maps=STD_MAPS, **src):
|
|
655
|
+
"""target data around an alloc-mapped kernel; a and b must be present
|
|
656
|
+
inside the region and absent after it."""
|
|
657
|
+
sp = spell("target data", version)
|
|
658
|
+
s = Src(**src)
|
|
659
|
+
s.region += [omp(sp, maps, clauses)] + s.presence(("a", "b"), True) + \
|
|
660
|
+
INNER_ALLOC + [omp("end", sp)] + s.presence(("a", "b"), False)
|
|
661
|
+
s.focus = s.focus or clauses
|
|
662
|
+
return s
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def enter_exit_prog(version, enter="map(to: a) map(alloc: b)",
|
|
666
|
+
exit_="map(from: b) map(release: a)", twice=False,
|
|
667
|
+
**src):
|
|
668
|
+
"""enter data, an alloc-mapped kernel, exit data, with presence
|
|
669
|
+
assertions after each. With `twice`, a is entered a second time
|
|
670
|
+
(reference count 2) before exit data, which must then leave a present
|
|
671
|
+
when it releases a and absent when it deletes a."""
|
|
672
|
+
s = Src(**src)
|
|
673
|
+
ent, ext = spell("target enter data", version), spell("target exit "
|
|
674
|
+
"data", version)
|
|
675
|
+
s.region += [omp(ent, enter)] + s.presence(("a", "b"), True)
|
|
676
|
+
if twice:
|
|
677
|
+
s.region.append(omp(ent, "map(to: a)"))
|
|
678
|
+
s.region += INNER_ALLOC + [omp(ext, exit_)]
|
|
679
|
+
if twice and "release: a" in exit_:
|
|
680
|
+
s.region += s.presence(("a",), True) + [omp(ext, "map(release: a)")]
|
|
681
|
+
s.region += s.presence(("a", "b"), False)
|
|
682
|
+
return s
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def update_prog(version, to="to(a)", frm="from(b)", **src):
|
|
686
|
+
"""Copy-mode evidence only: under unified memory a missing update
|
|
687
|
+
still gives the right answer (see offload.engine.gen)."""
|
|
688
|
+
s = Src(ref="2*(i + 10) + 1", evidence="copy_mode_only", **src)
|
|
689
|
+
s.region += [omp(spell("target enter data", version),
|
|
690
|
+
"map(to: a) map(alloc: b)")] + [
|
|
691
|
+
"do i = 1, n", " a(i) = a(i) + 10", "end do",
|
|
692
|
+
omp(spell("target update", version), to)] + INNER + [
|
|
693
|
+
omp(spell("target update", version), frm),
|
|
694
|
+
omp(spell("target exit data", version), "map(release: a, b)")]
|
|
695
|
+
return s
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
# declare target ---------------------------------------------------------------------
|
|
699
|
+
|
|
700
|
+
def proc_list(version: int, name: str = "f") -> str:
|
|
701
|
+
"""The list clause of a procedure's declare target directive: ``to`` at
|
|
702
|
+
5.2 (deprecated, still valid) and ``enter`` at 6.0 (``to`` is no longer
|
|
703
|
+
allowed there). CCE 19 rejects ``enter`` (ftn-1725), which at 5.2 would
|
|
704
|
+
otherwise be read as a rejection of the clause under test."""
|
|
705
|
+
return f"to({name})" if version < 60 else f"enter({name})"
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def declare_prog(version, var="", func="", body=BODY, maps=STD_MAPS,
|
|
709
|
+
pre=(), **src):
|
|
710
|
+
"""Module gen_mod with variables dv, dw (= 1), the function f(x) =
|
|
711
|
+
2*x + 1 and the host function g; the declare target directive `var` in
|
|
712
|
+
the module's specification part, `func` in f's."""
|
|
713
|
+
s = Src(**src)
|
|
714
|
+
s.module = ["integer :: dv = 1", "integer :: dw = 1"] + (
|
|
715
|
+
[var] if var else []) + [
|
|
716
|
+
"contains", " integer function f(x)"] + (
|
|
717
|
+
[" " + func] if func else []) + [
|
|
718
|
+
" integer, intent(in) :: x", " f = 2*x + 1",
|
|
719
|
+
" end function f"] + s.module
|
|
720
|
+
s.region += [omp("target", maps, FLAG_MAP), FLAG] + list(pre) + \
|
|
721
|
+
seq_loop(body) + [omp("end target")]
|
|
722
|
+
return s
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def declare_var_prog(version, directive, focus="", data=False):
|
|
726
|
+
"""Device-copy oracle for a declare target variable: a first target
|
|
727
|
+
region sets dv = 5 with no map clause, the checked region computes
|
|
728
|
+
b = 2*a + dv - 4 (also with no map of dv). With the directive both
|
|
729
|
+
regions use the one device copy of dv (enter/extended list: allocated
|
|
730
|
+
for the whole program; link: inside a target data region that maps dv
|
|
731
|
+
with `data`; local: a device-local copy). Without it dv is a scalar
|
|
732
|
+
referenced in a target construct, implicitly firstprivate (5.2 §5.8.1,
|
|
733
|
+
6.0 §7.9.3), so the first region's write is lost, dv is 1 in the
|
|
734
|
+
checked region and the check fails, in both memory modes
|
|
735
|
+
(mutation-checked under HSA_XNACK 0 and 1 on AFAR, and on CCE 19 for
|
|
736
|
+
link and the extended list; CCE 19 rejects enter(dv) and local)."""
|
|
737
|
+
s = declare_prog(version, var=directive, body=("b(i) = 2*a(i) + dv - 4",),
|
|
738
|
+
focus=focus)
|
|
739
|
+
first = [omp("target"), "dv = 5", omp("end target")]
|
|
740
|
+
if data:
|
|
741
|
+
sp = spell("target data", version)
|
|
742
|
+
s.init += [omp(sp, "map(tofrom: dv)")] + first
|
|
743
|
+
s.post.insert(0, omp("end", sp))
|
|
744
|
+
else:
|
|
745
|
+
s.init += first
|
|
746
|
+
return s
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
# A procedure referenced in a target construct is treated as if it appeared
|
|
750
|
+
# in an enter clause (5.2 §13.8 p.285, 6.0 §15.8 p.461), so in a one-file
|
|
751
|
+
# program a declare target directive on f changes nothing observable.
|
|
752
|
+
IMPLICIT_PROCEDURE_NOTE = (
|
|
753
|
+
"f is implicitly declare target without the directive (a procedure "
|
|
754
|
+
"referenced in a target construct, 5.2 §13.8 p.285, 6.0 §15.8 p.461), "
|
|
755
|
+
"so the clause has no checked effect")
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
# requires clauses whose programs need memory the device can access
|
|
759
|
+
# (under HSA_XNACK=0 AFAR aborts and CCE reports an unsupported requirement)
|
|
760
|
+
ZERO_COPY_REQUIRES = ("unified_shared_memory", "self_maps")
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def requires_prog(version, clause, **src):
|
|
764
|
+
if clause.split("(")[0] in ZERO_COPY_REQUIRES:
|
|
765
|
+
src.setdefault("needs", ("zero_copy",))
|
|
766
|
+
s = Src(observable=False, note="requires only changes semantics: no "
|
|
767
|
+
"checked device effect", **src)
|
|
768
|
+
s.spec.append(omp("requires", clause))
|
|
769
|
+
s.focus = clause
|
|
770
|
+
return kernel(s, Region(DEFAULT_KEY), version)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
# reductions ---------------------------------------------------------------------------
|
|
774
|
+
|
|
775
|
+
# identifier -> (operator text, type, initial value, update statement)
|
|
776
|
+
RED = {
|
|
777
|
+
"plus": ("+", "integer", "0", "{x} = {x} + a(i)"),
|
|
778
|
+
"minus": ("-", "integer", "0", "{x} = {x} - a(i)"),
|
|
779
|
+
"times": ("*", "integer", "1", "{x} = {x} * merge(2, 1, a(i) <= 10)"),
|
|
780
|
+
"max": ("max", "integer", "0", "{x} = max({x}, a(i))"),
|
|
781
|
+
"min": ("min", "integer", "1000", "{x} = min({x}, a(i))"),
|
|
782
|
+
"iand": ("iand", "integer", "-1", "{x} = iand({x}, a(i) + 64)"),
|
|
783
|
+
"ior": ("ior", "integer", "0", "{x} = ior({x}, a(i))"),
|
|
784
|
+
"ieor": ("ieor", "integer", "0", "{x} = ieor({x}, a(i))"),
|
|
785
|
+
"and": (".and.", "logical", ".true.", "{x} = {x} .and. (a(i) > 0)"),
|
|
786
|
+
"or": (".or.", "logical", ".false.", "{x} = {x} .or. (a(i) == n)"),
|
|
787
|
+
"eqv": (".eqv.", "logical", ".true.", "{x} = {x} .eqv. (a(i) > 0)"),
|
|
788
|
+
"neqv": (".neqv.", "logical", ".false.",
|
|
789
|
+
"{x} = {x} .neqv. (mod(a(i), 3) == 0)"),
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
# Logical reductions whose combined value does not show a missing
|
|
794
|
+
# privatisation (every contribution is .true.): the original value is
|
|
795
|
+
# .false. and the body reads the reduction variable before its update,
|
|
796
|
+
# which is the private copy, initialised to the identity .true. (5.2
|
|
797
|
+
# §5.5.5, 6.0 §7.6.6); without the reduction the body reads the shared
|
|
798
|
+
# .false. and b is wrong.
|
|
799
|
+
RED["and"] = (".and.", "logical", ".false.", "{x} = {x} .and. (a(i) > 0)")
|
|
800
|
+
RED["eqv"] = (".eqv.", "logical", ".false.", "{x} = {x} .eqv. (a(i) > 0)")
|
|
801
|
+
RED_BODY = {"and": ("b(i) = 2*a(i) + merge(1, 5, ls)",),
|
|
802
|
+
"eqv": ("b(i) = 2*a(i) + merge(1, 5, ls)",)}
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
def red_body(ident):
|
|
806
|
+
"""The loop body before the reduction update."""
|
|
807
|
+
return RED_BODY.get(ident, BODY)
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
# in_reduction on target need not create a private copy (5.2 §5.5.11, 6.0
|
|
811
|
+
# §7.6.12: "a private copy may be created"), so its programs cannot read
|
|
812
|
+
# the identity: their logical reductions start from .true. and combine to
|
|
813
|
+
# .false. instead (a value the target's firstprivate copy, which is what
|
|
814
|
+
# the scalar becomes without the clause, never brings back): 17 is the
|
|
815
|
+
# one false contribution of and, the 9 multiples of 7 make eqv false.
|
|
816
|
+
RED_TASK = {"and": (".and.", "logical", ".true.",
|
|
817
|
+
"{x} = {x} .and. (a(i) /= 17)"),
|
|
818
|
+
"eqv": (".eqv.", "logical", ".true.",
|
|
819
|
+
"{x} = {x} .eqv. (mod(a(i), 7) /= 0)")}
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def red_vars(ident, table=None):
|
|
823
|
+
"""(op, var, declarations, host init + sequential reference, check)."""
|
|
824
|
+
op, typ, init, upd = (table or RED).get(ident, RED[ident])
|
|
825
|
+
x = "s" if typ == "integer" else "ls"
|
|
826
|
+
decls = [f"{typ} :: {x}, {x}ref"]
|
|
827
|
+
host = [f"{x} = {init}", f"{x}ref = {init}", "do i = 1, n",
|
|
828
|
+
" " + upd.format(x=f"{x}ref"), "end do"]
|
|
829
|
+
check = f"({x} == {x}ref)" if typ == "integer" else f"({x} .eqv. {x}ref)"
|
|
830
|
+
return op, x, decls, host, check, upd.format(x=x)
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def reduction_prog(version, ident="plus", modifier="", key=DEFAULT_KEY,
|
|
834
|
+
hyg=True):
|
|
835
|
+
op, x, decls, host, check, upd = red_vars(ident)
|
|
836
|
+
mod = f"{modifier}, " if modifier else ""
|
|
837
|
+
clause = f"reduction({mod}{op}: {x})"
|
|
838
|
+
return _k(version, Region(key, clause, maps=f"{STD_MAPS} map(tofrom: {x})",
|
|
839
|
+
body=red_body(ident) + (upd,), hyg=hyg),
|
|
840
|
+
decls=decls, init=host, checks=[check], focus=clause)
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def in_reduction_prog(version, ident="plus"):
|
|
844
|
+
op, x, decls, host, check, upd = red_vars(ident, RED_TASK)
|
|
845
|
+
clause = f"in_reduction({op}: {x})"
|
|
846
|
+
s = Src(decls=decls, init=host, checks=[check], focus=clause)
|
|
847
|
+
s.region += [omp("taskgroup", f"task_reduction({op}: {x})"),
|
|
848
|
+
omp("target", STD_MAPS, FLAG_MAP, clause), FLAG] + \
|
|
849
|
+
seq_loop(BODY + (upd,)) + [omp("end target"), omp("end taskgroup")]
|
|
850
|
+
return s
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def task_reduction_prog(version):
|
|
854
|
+
"""reduction(task, ...) on a worksharing-loop construct in a parallel
|
|
855
|
+
region in the target region (the do and its end directive are
|
|
856
|
+
separate, for the nowait transform of reduction-clause-005)."""
|
|
857
|
+
op, x, decls, host, check, upd = red_vars("plus")
|
|
858
|
+
clause = f"reduction(task, {op}: {x})"
|
|
859
|
+
s = Src(decls=decls, init=host, checks=[check], focus=clause)
|
|
860
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP, f"map(tofrom: {x})"),
|
|
861
|
+
FLAG, omp("parallel", f"default(none) shared(a, b, {x})"),
|
|
862
|
+
omp("do", clause)] + seq_loop(BODY + (upd,)) + [
|
|
863
|
+
omp("end do"), omp("end parallel"), omp("end target")]
|
|
864
|
+
return s
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def inscan_prog(version):
|
|
868
|
+
"""An inclusive scan: b(i) = a(1) + ... + a(i)."""
|
|
869
|
+
def loop(flag):
|
|
870
|
+
return ["do i = 1, n", " s = s + a(i)", " " + omp("scan",
|
|
871
|
+
"inclusive(s)"), " b(i) = s", "end do"]
|
|
872
|
+
clause = "reduction(inscan, +: s)"
|
|
873
|
+
return _k(version, Region("parallel do", clause,
|
|
874
|
+
maps=f"{STD_MAPS} map(tofrom: s)", loop=loop),
|
|
875
|
+
decls=["integer :: s"], init=["s = 0"], ref="i*(i + 1)/2",
|
|
876
|
+
focus=clause)
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
# shared helpers ----------------------------------------------------------------------
|
|
880
|
+
|
|
881
|
+
def pdo(version, clauses, **kw) -> Src:
|
|
882
|
+
"""The clause on parallel do in a target region."""
|
|
883
|
+
src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
|
|
884
|
+
return _k(version, Region("parallel do", clauses, **kw), **src)
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
def pdo_simd(version, clauses, **kw) -> Src:
|
|
888
|
+
src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
|
|
889
|
+
return _k(version, Region("parallel do simd", clauses, **kw), **src)
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def combined(version, clauses, key=DEFAULT_KEY, **kw) -> Src:
|
|
893
|
+
src = {k: kw.pop(k) for k in list(kw) if k in Src.__dataclass_fields__}
|
|
894
|
+
return _k(version, Region(key, clauses, **kw), **src)
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
def collapse_loop(flag):
|
|
898
|
+
"""A 2 x 32 nest over b; each iteration records the thread that ran it
|
|
899
|
+
(tid) and iteration (1, 1) the team size (nthr)."""
|
|
900
|
+
idx = "(j - 1)*32 + k"
|
|
901
|
+
return ["do j = 1, 2", " do k = 1, 32",
|
|
902
|
+
f" b({idx}) = 2*a({idx}) + 1",
|
|
903
|
+
f" tid({idx}) = omp_get_thread_num()",
|
|
904
|
+
" if (j == 1 .and. k == 1) nthr = omp_get_num_threads()",
|
|
905
|
+
" end do", "end do"]
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
def count_owners(arr: str, var: str, upto: str) -> list[str]:
|
|
909
|
+
"""Host statements: `var` = how many of the values 0 .. upto-1 occur
|
|
910
|
+
in `arr` (the threads or teams that ran at least one iteration)."""
|
|
911
|
+
return [f"{var} = 0", f"do i = 0, {upto} - 1",
|
|
912
|
+
f" if (any({arr} == i)) {var} = {var} + 1", "end do"]
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def blocked(arr: str, m: str, parts: str, var: str) -> list[str]:
|
|
916
|
+
"""Host statements: `var` = what the static schedule without a chunk
|
|
917
|
+
size guarantees for the m iterations recorded in `arr` (owner per
|
|
918
|
+
iteration; the owners are the `parts` threads or teams): every
|
|
919
|
+
iteration ran on a maintainer 0 .. parts - 1 (its slot holds a maintainer
|
|
920
|
+
number, not the sentinel), and each owner's iterations are one
|
|
921
|
+
contiguous run. The spec text: chunks are "contiguous non-empty subsets"
|
|
922
|
+
and "at most one chunk is distributed to each thread" (5.2 §11.5.3
|
|
923
|
+
p.252; dist_schedule: each initial team, §11.6.1 p.256; 5.0 §2.9.2
|
|
924
|
+
p.103 (chunks), p.104 (Table 2.5), §2.9.4.1 p.122; 6.0 §13.6.3 p.418,
|
|
925
|
+
§13.7.1 p.422, contiguity from the Glossary "chunk" p.31). Nothing about the chunk
|
|
926
|
+
sizes is checked: "approximately equal" is illustrated by the Note
|
|
927
|
+
(5.2 §11.5.3 p.253, 5.0 §2.9.2 p.105) with two compliant
|
|
928
|
+
implementations, chunks of ceil(n/p) (the last shorter) and q / q - 1
|
|
929
|
+
splits, and neither the number of chunks nor their order over the
|
|
930
|
+
owners is fixed."""
|
|
931
|
+
return [f"{var} = all({arr} >= 0 .and. {arr} < {parts})",
|
|
932
|
+
f"do i = 2, {m}",
|
|
933
|
+
f" if ({arr}(i) /= {arr}(i - 1)) {var} = {var} .and. "
|
|
934
|
+
f".not. any({arr}(1:i - 1) == {arr}(i))", "end do"]
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def round_robin(arr: str, m: str, chunk: int, parts: str,
|
|
938
|
+
var: str) -> list[str]:
|
|
939
|
+
"""Host statements: `var` = whether iteration i ran on owner
|
|
940
|
+
mod((i - 1)/chunk, parts): chunks of `chunk` iterations assigned to
|
|
941
|
+
the owners round-robin in the order of their number (schedule(static,
|
|
942
|
+
c), dist_schedule(static, c))."""
|
|
943
|
+
return [f"{var} = .true.", f"do i = 1, {m}",
|
|
944
|
+
f" {var} = {var} .and. {arr}(i) == mod((i - 1)/{chunk}, "
|
|
945
|
+
f"{parts})", "end do"]
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
# The schedule programs run a loop of m = 1000 iterations (b is written by
|
|
949
|
+
# the first n), more than the team of either toolchain has threads, so the
|
|
950
|
+
# assignment of iterations to threads shows the schedule.
|
|
951
|
+
SCHED_M = 1000
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def schedule_prog(version, clause, chunk=None):
|
|
955
|
+
"""schedule(static[, chunk]) on parallel do in a target region: each
|
|
956
|
+
iteration records its thread; the static schedule fixes the assignment
|
|
957
|
+
(5.2 §11.5.3, 6.0 §13.6.3: without a chunk size one chunk of
|
|
958
|
+
approximately m/T iterations per thread, with chunk size c chunks of c
|
|
959
|
+
assigned round-robin in thread order)."""
|
|
960
|
+
def loop(flag):
|
|
961
|
+
return ["do i = 1, m", " if (i <= n) b(i) = 2*a(i) + 1",
|
|
962
|
+
" if (i == 1) nthr = omp_get_num_threads()",
|
|
963
|
+
" tid(i) = omp_get_thread_num()", "end do"]
|
|
964
|
+
check = blocked("tid", "m", "nthr", "sok") if chunk is None else \
|
|
965
|
+
round_robin("tid", "m", chunk, "nthr", "sok")
|
|
966
|
+
s = pdo(version, clause, loop=loop,
|
|
967
|
+
maps=f"{STD_MAPS} map(from: tid, nthr)",
|
|
968
|
+
shared=("a", "b", "tid", "nthr"),
|
|
969
|
+
decls=[f"integer, parameter :: m = {SCHED_M}",
|
|
970
|
+
"integer :: tid(m), nthr", "logical :: sok"],
|
|
971
|
+
init=[f"tid = {SENTINEL}", f"nthr = {SENTINEL}"],
|
|
972
|
+
post=check, checks=["(nthr >= 1)", "sok"])
|
|
973
|
+
if chunk is None:
|
|
974
|
+
# See STATIC_NO_CHUNK_WHY
|
|
975
|
+
s.no_oracle("implementation_defined", STATIC_NO_CHUNK_WHY,
|
|
976
|
+
"schedule_default", checked=True)
|
|
977
|
+
return s
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
def team_param(version, clause, record, decls, check, key=DEFAULT_KEY,
|
|
981
|
+
extra_decls=(), extra_shared=(), **kw):
|
|
982
|
+
"""`clause` on `key` (a target construct); iteration 1 (or every
|
|
983
|
+
iteration) records what the clause controls with an OpenMP API call
|
|
984
|
+
(`record`, lines of the loop body), checked on the host (`check`).
|
|
985
|
+
`extra_shared`: variables of a clause expression evaluated inside an
|
|
986
|
+
outer leaf, listed under default(none)."""
|
|
987
|
+
names = [d.split("::")[1].strip() for d in decls]
|
|
988
|
+
names = [x.split("(")[0] for n in names for x in n.split(", ")]
|
|
989
|
+
# Every recorded item starts at the sentinel, which the
|
|
990
|
+
# check rejects, so a lost copy-back fails deterministically
|
|
991
|
+
kw["init"] = list(kw.get("init", [])) + [f"{x} = {SENTINEL}"
|
|
992
|
+
for x in names]
|
|
993
|
+
return combined(version, clause, key=key, body=BODY + tuple(record),
|
|
994
|
+
maps=f"{STD_MAPS} map(from: {', '.join(names)})",
|
|
995
|
+
shared=("a", "b") + tuple(names) + tuple(extra_shared),
|
|
996
|
+
decls=list(decls) + list(extra_decls),
|
|
997
|
+
checks=list(check), **kw)
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
def linear_prog(version, clause, var="x", step=1):
|
|
1001
|
+
"""x is linear with `step`: at the start of iteration i it is
|
|
1002
|
+
3 + step*(i - 1) (the body adds the step, so the value at the end of
|
|
1003
|
+
an iteration is the next one's), and n*step + 3 after the loop."""
|
|
1004
|
+
body = (f"b(i) = 2*a(i) + 1 + ({var} - ({step}*i + {3 - step}))",
|
|
1005
|
+
f"{var} = {var} + {step}")
|
|
1006
|
+
return pdo(version, clause, maps=f"{STD_MAPS} map(tofrom: {var})",
|
|
1007
|
+
body=body, decls=[f"integer :: {var}"], init=[f"{var} = 3"],
|
|
1008
|
+
checks=[f"({var} == {step}*n + 3)"])
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def linear_simd_decl_prog(version, mod):
|
|
1012
|
+
"""ref / uval are allowed only on declare simd (5.2 §5.4.6, 6.0
|
|
1013
|
+
§7.5.6): a declare simd function called in the target region. 5.0
|
|
1014
|
+
: the modifier(list) syntax, and every modifier (val too) is
|
|
1015
|
+
for declare simd only, since the worksharing-loop and simd constructs
|
|
1016
|
+
take none (5.0 §2.9.2 p.107, §2.9.3.1 p.113); a ref list item must not
|
|
1017
|
+
be an assumed-shape array (5.0 §2.19.4.6 p.293), so x is a scalar
|
|
1018
|
+
dummy argument."""
|
|
1019
|
+
ds = spell("declare simd", version)
|
|
1020
|
+
if version == 50:
|
|
1021
|
+
func = [" integer function lf(x)", " " + omp("declare target"),
|
|
1022
|
+
" " + omp(f"{ds}(lf)", f"linear({mod}(x))"),
|
|
1023
|
+
" integer, intent(in) :: x",
|
|
1024
|
+
" lf = 2*x + 1", " end function lf"]
|
|
1025
|
+
s = Src(module=["contains"] + func, focus=f"linear({mod}(x))",
|
|
1026
|
+
note=f"linear({mod}(x)) on a declare simd directive; the "
|
|
1027
|
+
"SIMD variant need not be called", observable=False)
|
|
1028
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop(
|
|
1029
|
+
("b(i) = lf(a(i))",)) + [omp("end target")]
|
|
1030
|
+
return s
|
|
1031
|
+
if mod == "ref":
|
|
1032
|
+
func = [" integer function lf(x)", " " + omp("declare target"),
|
|
1033
|
+
" " + omp(f"{ds}(lf)", "linear(x: ref)"),
|
|
1034
|
+
" integer, intent(in) :: x(:)",
|
|
1035
|
+
" lf = 2*x(1) + 1", " end function lf"]
|
|
1036
|
+
body = ("b(i) = lf(a(i:i))",)
|
|
1037
|
+
else:
|
|
1038
|
+
func = [" integer function lf(x)", " " + omp("declare target"),
|
|
1039
|
+
" " + omp(f"{ds}(lf)", "linear(x: uval)"),
|
|
1040
|
+
" integer, intent(in) :: x",
|
|
1041
|
+
" lf = 2*x + 1", " end function lf"]
|
|
1042
|
+
body = ("b(i) = lf(a(i))",)
|
|
1043
|
+
s = Src(module=["contains"] + func, focus=f"linear(x: {mod})",
|
|
1044
|
+
note=f"linear({mod}) on a declare simd directive; the SIMD "
|
|
1045
|
+
"variant need not be called", observable=False)
|
|
1046
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop(body) + \
|
|
1047
|
+
[omp("end target")]
|
|
1048
|
+
return s
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def defaultmap_prog(version, mtype, cat=""):
|
|
1052
|
+
"""defaultmap on target; the region writes the implicitly mapped
|
|
1053
|
+
variables of the category (scalar c, array w, allocatable wa, pointer
|
|
1054
|
+
wp) before reading them. What tells the clause from its absence (a
|
|
1055
|
+
scalar is firstprivate and an array tofrom without it):
|
|
1056
|
+
|
|
1057
|
+
* tofrom / from on a category with the scalar c: the device's c = 1
|
|
1058
|
+
comes back to the host (both memory modes);
|
|
1059
|
+
* to / alloc on the arrays: the device's writes stay on the device, so
|
|
1060
|
+
the host copy is unchanged (copy mode only);
|
|
1061
|
+
* present: c and w are present (enter data) with values the host then
|
|
1062
|
+
changes without an update: the region must use the device copies
|
|
1063
|
+
(copy mode only; without the clause c is firstprivate, the host's).
|
|
1064
|
+
"""
|
|
1065
|
+
clause = f"defaultmap({mtype}: {cat})" if cat else f"defaultmap({mtype})"
|
|
1066
|
+
decls = ["integer :: c", "integer :: w(n)"]
|
|
1067
|
+
init, post, pre = [], [], []
|
|
1068
|
+
arr = None
|
|
1069
|
+
if mtype == "none":
|
|
1070
|
+
pre, body = [], BODY
|
|
1071
|
+
elif mtype == "present":
|
|
1072
|
+
body = ("b(i) = w(i) + c",)
|
|
1073
|
+
elif cat == "scalar":
|
|
1074
|
+
pre, body = ["c = 1"], ("b(i) = 2*a(i) + c",)
|
|
1075
|
+
elif cat == "allocatable":
|
|
1076
|
+
decls.append("integer, allocatable :: wa(:)")
|
|
1077
|
+
init, post = ["allocate(wa(n))", "wa = 0"], ["deallocate(wa)"]
|
|
1078
|
+
body, arr = ("wa(i) = 2*a(i)", "b(i) = wa(i) + 1"), "wa"
|
|
1079
|
+
elif cat == "pointer":
|
|
1080
|
+
decls.append("integer, pointer :: wp(:)")
|
|
1081
|
+
init, post = ["allocate(wp(n))", "wp = 0"], ["deallocate(wp)"]
|
|
1082
|
+
body, arr = ("wp(i) = 2*a(i)", "b(i) = wp(i) + 1"), "wp"
|
|
1083
|
+
elif cat == "aggregate":
|
|
1084
|
+
body, arr = ("w(i) = 2*a(i)", "b(i) = w(i) + 1"), "w"
|
|
1085
|
+
else: # all variables / no category: a scalar and an aggregate
|
|
1086
|
+
pre, body = ["c = 1"], ("w(i) = 2*a(i)", "b(i) = w(i) + c")
|
|
1087
|
+
arr = "w"
|
|
1088
|
+
init = ["c = 0", "w = 0"] + init
|
|
1089
|
+
s = Src(decls=decls, init=init, post=post, focus=clause)
|
|
1090
|
+
if mtype == "present":
|
|
1091
|
+
ent = spell("target enter data", version)
|
|
1092
|
+
ext = spell("target exit data", version)
|
|
1093
|
+
s.shared_setup()
|
|
1094
|
+
s.init += ["c = 1", "w = 2*a", omp(ent, "map(to: c, w)"),
|
|
1095
|
+
"if (.not. shared_mem) then", " c = 7", " w = 0",
|
|
1096
|
+
"end if"]
|
|
1097
|
+
s.post.insert(0, omp(ext, "map(release: c, w)"))
|
|
1098
|
+
if mtype in ("tofrom", "from") and cat in ("", "scalar"):
|
|
1099
|
+
s.checks.append("(c == 1)")
|
|
1100
|
+
if mtype in ("to", "alloc") and arr is not None:
|
|
1101
|
+
s.post[0:0] = s.copy_check(f"all({arr} == 0)")
|
|
1102
|
+
if mtype == "self":
|
|
1103
|
+
s.note = ("self maps the original storage: needs memory the device "
|
|
1104
|
+
"can access (zero-copy)")
|
|
1105
|
+
s.needs = ("zero_copy",)
|
|
1106
|
+
# i is predetermined private (5.2 §5.1.1); listed anyway under
|
|
1107
|
+
# defaultmap(none), which AFAR otherwise rejects, and defaultmap(present),
|
|
1108
|
+
# under which AFAR's runtime otherwise requires i to be present
|
|
1109
|
+
# ("device mapping required by 'present' ... (4 bytes)") and aborts
|
|
1110
|
+
extra = "private(i)" if mtype in ("none", "present") else ""
|
|
1111
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP, clause, extra),
|
|
1112
|
+
FLAG] + pre + seq_loop(body) + [omp("end target")]
|
|
1113
|
+
return s
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def defaultmap_character_prog(version):
|
|
1117
|
+
"""omp.clause.defaultmap.character (a semantic atom): no
|
|
1118
|
+
defaultmap clause; the target region writes a character scalar ch, a
|
|
1119
|
+
character(len=4) s and an integer scalar k. For Fortran a variable of
|
|
1120
|
+
character type is an aggregate, not a scalar (5.2 §1.2.6 p.14, 6.0
|
|
1121
|
+
Glossary pp.20, 94), so ch and s are implicitly mapped tofrom and the
|
|
1122
|
+
device's writes come back (5.2 §5.8.1 p.148, 6.0 §7.9.3 p.277); k is a
|
|
1123
|
+
scalar without TARGET, ALLOCATABLE or POINTER, so it is firstprivate and
|
|
1124
|
+
the host's k stays 1. Both memory modes: under unified memory a tofrom
|
|
1125
|
+
map shares the storage (the write is seen) and firstprivate is still a
|
|
1126
|
+
private copy. A host fallback writes k too (check fails)."""
|
|
1127
|
+
s = Src(decls=["character :: ch", "character(len=4) :: s", "integer :: k"],
|
|
1128
|
+
init=["ch = 'a'", "s = 'abcd'", "k = 1"],
|
|
1129
|
+
checks=["(ch == 'b')", "(s == 'wxyz')", "(k == 1)"],
|
|
1130
|
+
focus="implicit data-mapping of character variables (no "
|
|
1131
|
+
"defaultmap clause)")
|
|
1132
|
+
s.header_notes.append(
|
|
1133
|
+
"semantic atom: no defaultmap clause; Fortran character variables "
|
|
1134
|
+
"are aggregates (implicitly mapped tofrom), the integer scalar k is "
|
|
1135
|
+
"firstprivate")
|
|
1136
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG,
|
|
1137
|
+
"ch = 'b'", "s = 'wxyz'", "k = 2"] + seq_loop() + [
|
|
1138
|
+
omp("end target")]
|
|
1139
|
+
return s
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
# Device spin of the producer target task: about one second of
|
|
1143
|
+
# integer work on the MI300A (AFAR and CCE), long enough for a consumer
|
|
1144
|
+
# target task that ignores the dependence to run first.
|
|
1145
|
+
DEPEND_SPIN = 20000000
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def _depend_clauses(s: Src, first: str, second: str) -> tuple[str, str]:
|
|
1149
|
+
d1 = f"depend({first}: w)"
|
|
1150
|
+
d2 = f"depend({second}: w)"
|
|
1151
|
+
if first == "iterator":
|
|
1152
|
+
s.decls.append("integer :: k")
|
|
1153
|
+
d1 = "depend(iterator(k = 1:n), out: w(k))"
|
|
1154
|
+
d2 = "depend(iterator(k = 1:n), in: w(k))"
|
|
1155
|
+
if first == "depobj":
|
|
1156
|
+
s.decls.append("integer(omp_depend_kind) :: obj")
|
|
1157
|
+
s.init.append(omp("depobj(obj)", "depend(out: w)"))
|
|
1158
|
+
d1 = "depend(depobj: obj)"
|
|
1159
|
+
return d1, d2
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
def depend_prog(version, first="out", second="in", writer=False):
|
|
1163
|
+
"""Device only: two target tasks ordered only by a dependence
|
|
1164
|
+
on w. The producer (`first` dependence, target nowait) spins on the
|
|
1165
|
+
device for about a second (DEPEND_SPIN iterations; the spin value goes
|
|
1166
|
+
to spin(1), so it cannot be optimised away) and then writes w = 2*a;
|
|
1167
|
+
the consumer (`second` dependence, target nowait) maps w to the device
|
|
1168
|
+
and computes b = w + 1; a taskwait completes both. Without the
|
|
1169
|
+
dependence a runtime that runs the two target tasks concurrently gives
|
|
1170
|
+
the consumer w before the producer's write (w = 0, or a partial w) and
|
|
1171
|
+
the check fails; a runtime that runs each target task to completion
|
|
1172
|
+
when it is encountered cannot fail it (the audit says which toolchain
|
|
1173
|
+
does what). w is mapped tofrom by the producer and to by the consumer,
|
|
1174
|
+
so the order also holds in copy mode. `writer` (the dependence types a
|
|
1175
|
+
producer carries: out, inout, inoutset, mutexinoutset): the producer
|
|
1176
|
+
also has a dependence of another type on a token tk, so that its mutant
|
|
1177
|
+
keeps a depend clause: CCE's C runtime starts the consumer's
|
|
1178
|
+
depend(in) task only after a producer without any depend clause has
|
|
1179
|
+
finished (the mutant survived), not after one with a dependence on
|
|
1180
|
+
something else."""
|
|
1181
|
+
s = Src(decls=["integer :: w(n)", "integer :: j, sp, spin(1)"],
|
|
1182
|
+
init=["w = 0", "spin = 0"])
|
|
1183
|
+
d1, d2 = _depend_clauses(s, first, second)
|
|
1184
|
+
if writer:
|
|
1185
|
+
s.decls.append("integer :: tk")
|
|
1186
|
+
s.init.append("tk = 0")
|
|
1187
|
+
d1 += f" depend({'out' if first == 'inout' else 'inout'}: tk)"
|
|
1188
|
+
s.region += [omp("target", "nowait", "map(to: a) map(tofrom: w)",
|
|
1189
|
+
"map(from: spin)", d1),
|
|
1190
|
+
"sp = 1", f"do j = 1, {DEPEND_SPIN}",
|
|
1191
|
+
" sp = mod(7*sp + j, 1000003)", "end do", "spin(1) = sp",
|
|
1192
|
+
"do j = 1, n", " w(j) = 2*a(j)", "end do",
|
|
1193
|
+
omp("end target"),
|
|
1194
|
+
omp("target", "nowait", "map(to: w) map(from: b)", FLAG_MAP,
|
|
1195
|
+
d2), FLAG] + \
|
|
1196
|
+
seq_loop(("b(i) = w(i) + 1",)) + [omp("end target"), omp("taskwait")]
|
|
1197
|
+
if first == "depobj":
|
|
1198
|
+
s.post.append(omp("depobj(obj)", "destroy"))
|
|
1199
|
+
s.header_notes.append(
|
|
1200
|
+
"device only: the dependence orders two target tasks (target nowait "
|
|
1201
|
+
"producer and consumer, completed by taskwait)")
|
|
1202
|
+
s.focus = d2 if first == "out" and second == "in" else d1
|
|
1203
|
+
return s
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
# The realistic use each host+device pattern stands for (owner
|
|
1207
|
+
# exception): its header line ``! scope: host+device: <use>``.
|
|
1208
|
+
DEPEND_HOST_USE = ("CPU producer / GPU consumer pipeline: a host task "
|
|
1209
|
+
"prepares data (depend(out)) that a dependent target task "
|
|
1210
|
+
"consumes on the device (depend(in)), as in task-based "
|
|
1211
|
+
"codes that overlap host preprocessing with offload")
|
|
1212
|
+
THREADS_USE = ("multi-threaded offload: host threads (one per chunk, as in "
|
|
1213
|
+
"MPI+OpenMP or task-parallel host codes) each launch their "
|
|
1214
|
+
"own target region on the same device")
|
|
1215
|
+
THREADS_NOWAIT_USE = ("multi-threaded asynchronous offload: host threads "
|
|
1216
|
+
"each launch a target nowait region on the same "
|
|
1217
|
+
"device; the parallel region's end completes the "
|
|
1218
|
+
"target tasks")
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def depend_host_prog(version, first="out", second="in"):
|
|
1222
|
+
"""host+device pattern (the earlier depend programs): a
|
|
1223
|
+
host task and a target task generated in a host parallel region's
|
|
1224
|
+
single construct, ordered only by a dependence on w: the task (`first`
|
|
1225
|
+
dependence) spins for 0.5 s of wall time before writing w = 2*a on the
|
|
1226
|
+
host, the target task (`second` dependence) maps w to the device and
|
|
1227
|
+
computes b = w + 1. Without the dependence the target task maps the
|
|
1228
|
+
host's w, still 0, while the task spins, and the check fails (a
|
|
1229
|
+
runtime that runs the task at once, undeferred, cannot fail it). The
|
|
1230
|
+
spin is on the host clock, so the ordering does not depend on how the
|
|
1231
|
+
device schedules its kernels. Judged on the device result (b, and
|
|
1232
|
+
ran_on_host from the target task)."""
|
|
1233
|
+
s = Src(decls=["integer :: w(n)", "double precision :: t0"],
|
|
1234
|
+
init=["w = 0"])
|
|
1235
|
+
d1, d2 = _depend_clauses(s, first, second)
|
|
1236
|
+
s.region += [omp("target", "map(tofrom: w)"), "w(1) = 0",
|
|
1237
|
+
omp("end target"),
|
|
1238
|
+
omp("parallel", "num_threads(2)"), omp("single"),
|
|
1239
|
+
omp("task", d1),
|
|
1240
|
+
"t0 = omp_get_wtime()",
|
|
1241
|
+
"do while (omp_get_wtime() - t0 < 0.5d0)", "end do",
|
|
1242
|
+
"w = 2*a", omp("end task"),
|
|
1243
|
+
omp("target", "map(to: w) map(from: b)", FLAG_MAP, d2),
|
|
1244
|
+
FLAG] + \
|
|
1245
|
+
seq_loop(("b(i) = w(i) + 1",)) + [omp("end target"), omp("taskwait"),
|
|
1246
|
+
omp("end single"),
|
|
1247
|
+
omp("end parallel")]
|
|
1248
|
+
if first == "depobj":
|
|
1249
|
+
s.post.append(omp("depobj(obj)", "destroy"))
|
|
1250
|
+
s.header_notes.append(f"scope: host+device: {DEPEND_HOST_USE}")
|
|
1251
|
+
s.focus = d2 if first == "out" and second == "in" else d1
|
|
1252
|
+
return s
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
def threads_offload_prog(version, nowait=False):
|
|
1256
|
+
"""host+device pattern: a host parallel do over four chunks of
|
|
1257
|
+
b; each iteration (one host thread each, num_threads(4)) launches its
|
|
1258
|
+
own target region for its chunk (`nowait`: a target nowait region,
|
|
1259
|
+
completed by the parallel region's end). Each target region reports
|
|
1260
|
+
the device into hf(c); the check is the device result b and
|
|
1261
|
+
ran_on_host = 1 when any hf(c) is 1.
|
|
1262
|
+
|
|
1263
|
+
A and b (and hf) are mapped whole once, by target enter data
|
|
1264
|
+
before the parallel region, and copied back by target exit data after
|
|
1265
|
+
it. The form mapped each thread's sections (map(to: a(lo:hi))
|
|
1266
|
+
map(from: b(lo:hi))) on the threads' target constructs; the target
|
|
1267
|
+
region references the whole arrays, so they are also implicitly
|
|
1268
|
+
mapped (5.2 §5.8.1), and once sections created by two earlier
|
|
1269
|
+
constructs are present when a third thread's construct maps the array
|
|
1270
|
+
"the behavior is unspecified" (5.2 §5.8.3 p.155; 6.0 §7.9.6 p.286:
|
|
1271
|
+
the storage "must correspond to a single mappable storage block").
|
|
1272
|
+
Without nowait each thread moves its own section with target update
|
|
1273
|
+
(to before, from after its region)."""
|
|
1274
|
+
c = "nowait" if nowait else ""
|
|
1275
|
+
s = Src(decls=["integer :: c, lo, hi, hf(4)"], init=["hf = 1"])
|
|
1276
|
+
if nowait:
|
|
1277
|
+
enter = omp("target enter data", "map(to: a, hf) map(alloc: b)")
|
|
1278
|
+
exit_ = omp("target exit data", "map(from: b, hf) map(release: a)")
|
|
1279
|
+
before, after = [], []
|
|
1280
|
+
else:
|
|
1281
|
+
enter = omp("target enter data", "map(alloc: a, b) map(to: hf)")
|
|
1282
|
+
exit_ = omp("target exit data", "map(from: hf) map(release: a, b)")
|
|
1283
|
+
before = [" " + omp("target update", "to(a(lo:hi))")]
|
|
1284
|
+
after = [" " + omp("target update", "from(b(lo:hi))")]
|
|
1285
|
+
s.region += [enter, omp("parallel do", "num_threads(4) private(lo, hi)"),
|
|
1286
|
+
"do c = 1, 4", " lo = (c - 1)*16 + 1", " hi = c*16"] \
|
|
1287
|
+
+ before + [" " + omp("target", c),
|
|
1288
|
+
" hf(c) = merge(1, 0, omp_is_initial_device())",
|
|
1289
|
+
" do i = lo, hi", " b(i) = 2*a(i) + 1",
|
|
1290
|
+
" end do", " " + omp("end target")] + after \
|
|
1291
|
+
+ ["end do", omp("end parallel do"), exit_, "host_flag = 0",
|
|
1292
|
+
"do c = 1, 4", " if (hf(c) == 1) host_flag = 1", "end do"]
|
|
1293
|
+
s.header_notes.append("scope: host+device: " + (
|
|
1294
|
+
THREADS_NOWAIT_USE if nowait else THREADS_USE))
|
|
1295
|
+
s.focus = c or "target"
|
|
1296
|
+
return s
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
# CCE 19 does not run target nowait tasks synchronously (it
|
|
1300
|
+
# kills every depend mutant: without the dependence the consumer target
|
|
1301
|
+
# task reads the producer's output before it is written; audit);
|
|
1302
|
+
# AFAR completes each one before the next outside a parallel region (it
|
|
1303
|
+
# survives them)
|
|
1304
|
+
NOWAIT_WHY = ("an implementation may run a target nowait task synchronously "
|
|
1305
|
+
"(AFAR does outside a parallel region; CCE 19 runs two of them "
|
|
1306
|
+
"concurrently, as the depend programs show), so no single run "
|
|
1307
|
+
"of a conforming program can tell nowait from its absence")
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
def device_ptr_prog(version, data_extra=""):
|
|
1311
|
+
"""use_device_ptr on target data, is_device_ptr on the target inside.
|
|
1312
|
+
5.0: an is_device_ptr list item must be a dummy argument
|
|
1313
|
+
without the ALLOCATABLE, POINTER or VALUE attribute (5.0 §2.12.5
|
|
1314
|
+
p.176), so the target region is in a module subroutine whose dummy
|
|
1315
|
+
arguments are the two pointers."""
|
|
1316
|
+
sp = spell("target data", version)
|
|
1317
|
+
if version == 50:
|
|
1318
|
+
s = Src(uses=["use, intrinsic :: iso_c_binding, only: c_ptr, c_loc"],
|
|
1319
|
+
decls=["type(c_ptr) :: pa, pb"],
|
|
1320
|
+
init=["pa = c_loc(a)", "pb = c_loc(b)"],
|
|
1321
|
+
needs=("device_c_f_pointer",), evidence="copy_mode_only")
|
|
1322
|
+
s.module = ["contains", " subroutine dev_kernel(pa, pb, m, hf)",
|
|
1323
|
+
" use omp_lib, only: omp_is_initial_device",
|
|
1324
|
+
" use, intrinsic :: iso_c_binding, only: c_ptr, "
|
|
1325
|
+
"c_f_pointer",
|
|
1326
|
+
" type(c_ptr) :: pa, pb",
|
|
1327
|
+
" integer :: m, hf, i",
|
|
1328
|
+
" integer, pointer :: pp(:), qq(:)",
|
|
1329
|
+
" " + omp("target", "is_device_ptr(pa, pb)",
|
|
1330
|
+
"private(pp, qq)", "map(tofrom: hf)"),
|
|
1331
|
+
" hf = merge(1, 0, omp_is_initial_device())",
|
|
1332
|
+
" call c_f_pointer(pa, pp, [m])",
|
|
1333
|
+
" call c_f_pointer(pb, qq, [m])",
|
|
1334
|
+
" do i = 1, m", " qq(i) = 2*pp(i) + 1",
|
|
1335
|
+
" end do", " " + omp("end target"),
|
|
1336
|
+
" end subroutine dev_kernel"]
|
|
1337
|
+
s.region += [omp(sp, STD_MAPS, "use_device_ptr(pa, pb)", data_extra),
|
|
1338
|
+
"call dev_kernel(pa, pb, n, host_flag)", omp("end", sp)]
|
|
1339
|
+
return s
|
|
1340
|
+
s = Src(uses=["use, intrinsic :: iso_c_binding, only: c_ptr, c_loc, "
|
|
1341
|
+
"c_f_pointer"],
|
|
1342
|
+
decls=["type(c_ptr) :: pa, pb", "integer, pointer :: pp(:), qq(:)"],
|
|
1343
|
+
init=["pa = c_loc(a)", "pb = c_loc(b)"],
|
|
1344
|
+
needs=("device_c_f_pointer",), evidence="copy_mode_only")
|
|
1345
|
+
s.region += [omp(sp, STD_MAPS, "use_device_ptr(pa, pb)", data_extra),
|
|
1346
|
+
omp("target", "is_device_ptr(pa, pb)", "private(pp, qq)",
|
|
1347
|
+
FLAG_MAP), FLAG,
|
|
1348
|
+
"call c_f_pointer(pa, pp, [n])",
|
|
1349
|
+
"call c_f_pointer(pb, qq, [n])"] + seq_loop(
|
|
1350
|
+
("qq(i) = 2*pp(i) + 1",)) + [omp("end target"), omp("end", sp)]
|
|
1351
|
+
return s
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
def device_addr_prog(version, data_extra=""):
|
|
1355
|
+
"""use_device_addr on target data, has_device_addr on the target
|
|
1356
|
+
inside. Inside the data region c_loc(a) must differ from a's host
|
|
1357
|
+
address (checked in copy mode only: under unified memory the device
|
|
1358
|
+
address is the host address). 5.0 has no has_device_addr: an
|
|
1359
|
+
outer target data maps a and b, an inner one with use_device_addr(a, b)
|
|
1360
|
+
holds the address check, and the kernel follows it in the outer region
|
|
1361
|
+
(a and b present)."""
|
|
1362
|
+
s = Src(evidence="copy_mode_only")
|
|
1363
|
+
sp = spell("target data", version)
|
|
1364
|
+
if version == 50:
|
|
1365
|
+
s.region += s.save_addresses(("a", "b")) + [
|
|
1366
|
+
omp(sp, STD_MAPS), omp(sp, "use_device_addr(a, b)", data_extra)] \
|
|
1367
|
+
+ s.address_moved(("a", "b")) + [omp("end", sp),
|
|
1368
|
+
omp("target", FLAG_MAP), FLAG] \
|
|
1369
|
+
+ seq_loop() + [omp("end target"), omp("end", sp)]
|
|
1370
|
+
return s
|
|
1371
|
+
s.region += s.save_addresses(("a", "b")) + [
|
|
1372
|
+
omp(sp, STD_MAPS, "use_device_addr(a, b)", data_extra)] + \
|
|
1373
|
+
s.address_moved(("a", "b")) + [
|
|
1374
|
+
omp("target", "has_device_addr(a, b)", FLAG_MAP), FLAG] + \
|
|
1375
|
+
seq_loop() + [omp("end target"), omp("end", sp)]
|
|
1376
|
+
return s
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
def mapper_prog(version):
|
|
1380
|
+
"""The mapper maps s%v with map type to; the clause invoking it says
|
|
1381
|
+
tofrom, and the decayed map type is to (5.2 Table 5.3, 6.0 map-type
|
|
1382
|
+
decay): the region's write of s%v stays on the device (copy mode).
|
|
1383
|
+
Without the mapper modifier the default mapper maps s tofrom and the
|
|
1384
|
+
write comes back."""
|
|
1385
|
+
dm = spell("declare mapper", version)
|
|
1386
|
+
c = "map(mapper(mm), tofrom: s)"
|
|
1387
|
+
s = Src(spec=["type :: dt", " integer :: v", "end type dt",
|
|
1388
|
+
omp(f"{dm}(mm: dt :: q)", "map(to: q%v)")],
|
|
1389
|
+
decls=["type(dt) :: s"], init=["s%v = 1"], focus=c)
|
|
1390
|
+
s.region += [omp("target", STD_MAPS, c, FLAG_MAP), FLAG] + seq_loop(
|
|
1391
|
+
("b(i) = 2*a(i) + s%v",)) + ["s%v = 7", omp("end target")]
|
|
1392
|
+
s.post += s.copy_check("s%v == 1")
|
|
1393
|
+
return s
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
def ancestor_prog(version, modifier="ancestor: 1"):
|
|
1397
|
+
"""A reverse-offload region (device(ancestor: 1)) inside a target
|
|
1398
|
+
region. Its body has no checked effect: no OpenMP construct or API call
|
|
1399
|
+
may appear in it (5.2 §13.8, 6.0 §15.8)."""
|
|
1400
|
+
s = Src(spec=[omp("requires", "reverse_offload")],
|
|
1401
|
+
decls=["integer :: t"], init=["t = 0"], observable=False,
|
|
1402
|
+
note="the reverse-offload region has no checked effect",
|
|
1403
|
+
focus=f"device({modifier})")
|
|
1404
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP), FLAG] + seq_loop() + [
|
|
1405
|
+
omp("target", f"device({modifier})", "firstprivate(t)"),
|
|
1406
|
+
"t = t + 1", omp("end target"), omp("end target")]
|
|
1407
|
+
return s
|
|
1408
|
+
|
|
1409
|
+
|
|
1410
|
+
def alloc_prog(version, alloc_clause, wrap="uses_allocators("
|
|
1411
|
+
"omp_default_mem_alloc)", decls=(), shared=("a", "b")):
|
|
1412
|
+
"""`shared` names the variables the parallel do shares under the
|
|
1413
|
+
default(none) hygiene; an allocator handle variable named in the
|
|
1414
|
+
allocate clause must be among them (CCE 19 ftn-1510)."""
|
|
1415
|
+
return pdo(version, f"private(t) {alloc_clause}", wrap=wrap,
|
|
1416
|
+
body=("t = 2*a(i)", "b(i) = t + 1"), shared=tuple(shared),
|
|
1417
|
+
decls=["integer :: t"] + list(decls), focus=alloc_clause)
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
TRAITS = ["integer(omp_allocator_handle_kind) :: my_alloc",
|
|
1421
|
+
"type(omp_alloctrait), parameter :: my_traits(1) = &",
|
|
1422
|
+
" [omp_alloctrait(omp_atk_alignment, 64)]"]
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
# --- clause atoms -----------------------------------------------------------------
|
|
1426
|
+
|
|
1427
|
+
# message/severity on a compute construct (the
|
|
1428
|
+
# re-audit with AFAR's -fopenmp-version=60 shows the mutant survives).
|
|
1429
|
+
def error_probe(version, sev):
|
|
1430
|
+
"""Message and severity (6.0) on an error
|
|
1431
|
+
directive with at(compilation) in a target region (before,
|
|
1432
|
+
the main program's specification part), message PROBE_TOKEN: with
|
|
1433
|
+
severity(warning) the compiler must show the message and go on
|
|
1434
|
+
(expect compile_message; the program then runs the standard kernel),
|
|
1435
|
+
with severity(fatal) it must stop the compilation (expect
|
|
1436
|
+
compile_error) (6.0 §10.1 error directive, §10.3 message, §10.4
|
|
1437
|
+
severity). Mutants: the message removed (the token disappears), the
|
|
1438
|
+
severity removed (fatal by default: the compile stops), fatal replaced
|
|
1439
|
+
by warning (the compile goes on)."""
|
|
1440
|
+
from offload.engine.gen import PROBE_TOKEN
|
|
1441
|
+
c = f'message("{PROBE_TOKEN}")'
|
|
1442
|
+
s = Src(evidence="probe", token=PROBE_TOKEN,
|
|
1443
|
+
expect="compile_error" if sev == "fatal" else "compile_message",
|
|
1444
|
+
focus=f"severity({sev})")
|
|
1445
|
+
s.header_notes.append(
|
|
1446
|
+
"compile-output probe: the error directive's message must appear in "
|
|
1447
|
+
"the compiler output " + ("and stop the compilation (fatal)"
|
|
1448
|
+
if sev == "fatal" else
|
|
1449
|
+
"as a warning, and the compilation go on"))
|
|
1450
|
+
# The error directive is in the target region (device code),
|
|
1451
|
+
# not in the host program's specification part
|
|
1452
|
+
s.region += [omp("target", STD_MAPS, FLAG_MAP),
|
|
1453
|
+
omp("error", "at(compilation)", f"severity({sev})", c),
|
|
1454
|
+
FLAG] + seq_loop() + [omp("end target")]
|
|
1455
|
+
return s
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
# message/severity on parallel do: the directive-name family members keep
|
|
1459
|
+
# this form (offload.engine.gen._DNM_RECIPE)
|
|
1460
|
+
ERROR_CLAUSE_WHY = ("message and severity on parallel act only when a "
|
|
1461
|
+
"num_threads(strict: n) request cannot be met (6.0 "
|
|
1462
|
+
"§12.1.2: error termination as if by an error directive "
|
|
1463
|
+
"with these clauses); neither AFAR nor CCE 19 accepts "
|
|
1464
|
+
"the strict modifier (omp.clause.num_threads.strict is "
|
|
1465
|
+
"rejected by both), so no program can trigger them here, "
|
|
1466
|
+
"and a passing program raises no error")
|
|
1467
|
+
# The present modifiers as negative probes: an absent item with
|
|
1468
|
+
# present requires runtime error termination (5.2 §5.8.3 p.152 and §5.9
|
|
1469
|
+
# p.165; 6.0 §7.9.6 p.283 and §7.10 p.296); without the modifier the map
|
|
1470
|
+
# maps it and the update skips it, and the program goes on.
|
|
1471
|
+
PRESENT_CITE = {"map": {52: "5.2 §5.8.3 p.152", 60: "6.0 §7.9.6 p.283"},
|
|
1472
|
+
"update": {52: "5.2 §5.9 p.165", 60: "6.0 §7.10 p.296"}}
|
|
1473
|
+
|
|
1474
|
+
|
|
1475
|
+
def present_probe(version, which):
|
|
1476
|
+
"""Negative probe for map(present, ...), to(present: ...) or
|
|
1477
|
+
from(present: ...): the standard kernel runs first (device evidence; it
|
|
1478
|
+
maps a and b only for its region), then the construct names an item
|
|
1479
|
+
that is not present with the present modifier. A correct run ends there
|
|
1480
|
+
with runtime error termination (offload.engine.harness.ERROR_TERMINATION);
|
|
1481
|
+
a run that goes on sets `continued` and prints check=fail."""
|
|
1482
|
+
up = spell("target update", version)
|
|
1483
|
+
if which == "map":
|
|
1484
|
+
c = "map(present, to: a)"
|
|
1485
|
+
probe = [omp("target", c, "map(from: b)")] + seq_loop() + [
|
|
1486
|
+
omp("end target")]
|
|
1487
|
+
else:
|
|
1488
|
+
c = "to(present: a)" if which == "to" else "from(present: b)"
|
|
1489
|
+
probe = [omp(up, c)]
|
|
1490
|
+
what = "map" if which == "map" else "update"
|
|
1491
|
+
s = Src(decls=["logical :: continued"], init=["continued = .false."],
|
|
1492
|
+
checks=["(.not. continued)"], focus=c, evidence="probe",
|
|
1493
|
+
expect="error_termination")
|
|
1494
|
+
s.header_notes.append(
|
|
1495
|
+
f"negative probe: {c} names an item that is not present, which "
|
|
1496
|
+
f"requires runtime error termination ({PRESENT_CITE[what][version]}); "
|
|
1497
|
+
"reaching the end of the program prints check=fail")
|
|
1498
|
+
s.region += INNER + probe + ["continued = .true."]
|
|
1499
|
+
return s
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
SELF_WHY = ("re-categorised (was implementation_defined): a self "
|
|
1503
|
+
"map is 'a mapping operation for which the corresponding storage "
|
|
1504
|
+
"is the same as its original storage' (6.0 Glossary p.95), and a "
|
|
1505
|
+
"map-entering self map of storage the device cannot access must "
|
|
1506
|
+
"end in runtime error termination (6.0 §7.9.6 p.285), so the "
|
|
1507
|
+
"spec fixes the behaviour: in copy mode (HSA_XNACK=0) a device "
|
|
1508
|
+
"write of a self-mapped a must be seen by the host, or the "
|
|
1509
|
+
"run must stop; without self the write stays on the device. That "
|
|
1510
|
+
"check is not written: the program needs zero-copy memory as "
|
|
1511
|
+
"generated, and AFAR at HSA_XNACK=0 copies even with self (probe "
|
|
1512
|
+
"2026-10-18: map(self, to: a) with a device write of a leaves the "
|
|
1513
|
+
"host's a unchanged), which the check would report as a wrong "
|
|
1514
|
+
"answer")
|
|
1515
|
+
CLOSE_WHY = ("close is a hint to allocate the device copy in memory close to "
|
|
1516
|
+
"the device; values and presence are the same without it")
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
def _feat_map(version, feat):
|
|
1520
|
+
"""map clause features."""
|
|
1521
|
+
ent, ext = (spell("target enter data", version),
|
|
1522
|
+
spell("target exit data", version))
|
|
1523
|
+
if feat in (None, "to", "from"):
|
|
1524
|
+
# the device overwrites a after reading it: with map(to: a) the
|
|
1525
|
+
# write stays on the device (copy mode), so the host's a is
|
|
1526
|
+
# unchanged; without the map type (or the clause) a is mapped
|
|
1527
|
+
# tofrom and the write comes back
|
|
1528
|
+
s = _k(version, Region("target", body=BODY + ("a(i) = 0",)),
|
|
1529
|
+
focus="map(to: a)")
|
|
1530
|
+
s.post += s.copy_check("all(2*a + 1 == ref)")
|
|
1531
|
+
return s
|
|
1532
|
+
if feat == "tofrom":
|
|
1533
|
+
return _k(version, Region("target", maps="map(to: a) map(tofrom: b)"),
|
|
1534
|
+
focus="map(tofrom: b)")
|
|
1535
|
+
if feat == "always":
|
|
1536
|
+
# a is present (enter data) when the host changes it: only the
|
|
1537
|
+
# always modifier copies the new values to the present device copy
|
|
1538
|
+
# (copy mode; without it the region reads the stale copy)
|
|
1539
|
+
c = "map(always, to: a)"
|
|
1540
|
+
s = _k(version, Region("target", maps=f"{c} map(from: b)"),
|
|
1541
|
+
focus=c, ref="2*(i + 10) + 1", evidence="copy_mode_only",
|
|
1542
|
+
init=[omp(ent, "map(to: a)"), "do i = 1, n",
|
|
1543
|
+
" a(i) = a(i) + 10", "end do"],
|
|
1544
|
+
post=[omp(ext, "map(release: a)")])
|
|
1545
|
+
return s
|
|
1546
|
+
if feat == "close":
|
|
1547
|
+
c = f"map({feat}, to: a)"
|
|
1548
|
+
return _k(version, Region("target", maps=f"{c} map(from: b)"),
|
|
1549
|
+
focus=c).no_oracle("hint_only", CLOSE_WHY, "close_hint")
|
|
1550
|
+
if feat == "present":
|
|
1551
|
+
return present_probe(version, "map")
|
|
1552
|
+
if feat == "alloc":
|
|
1553
|
+
return enter_exit_prog(version, focus="map(to: a) map(alloc: b)")
|
|
1554
|
+
if feat == "release":
|
|
1555
|
+
return enter_exit_prog(version, twice=True,
|
|
1556
|
+
focus="map(from: b) map(release: a)")
|
|
1557
|
+
if feat == "delete":
|
|
1558
|
+
return enter_exit_prog(version, exit_="map(from: b) map(delete: a)",
|
|
1559
|
+
twice=True, focus="map(delete: a)")
|
|
1560
|
+
if feat == "storage":
|
|
1561
|
+
return enter_exit_prog(version, enter="map(to: a) map(storage: b)",
|
|
1562
|
+
focus="map(storage: b)")
|
|
1563
|
+
if feat == "self":
|
|
1564
|
+
c = "map(self: a)"
|
|
1565
|
+
return _k(version, Region("target", maps=f"{c} map(from: b)"),
|
|
1566
|
+
focus=c, note="self maps the original storage: needs memory "
|
|
1567
|
+
"the device can access (zero-copy)", needs=("zero_copy",)
|
|
1568
|
+
).unverified("oracle_not_written", SELF_WHY)
|
|
1569
|
+
if feat == "ref_ptee":
|
|
1570
|
+
# ref_ptee maps the pointee only (6.0 §7.9.5): the region, which
|
|
1571
|
+
# dereferences p, maps the pointer itself (ref_ptr), attached to
|
|
1572
|
+
# the pointee that the enter data made present with its values
|
|
1573
|
+
c = "map(ref_ptee, to: p)"
|
|
1574
|
+
return _k(version, Region("target", maps="map(ref_ptr, to: p) "
|
|
1575
|
+
"map(from: b)", body=("b(i) = 2*p(i) + 1",)),
|
|
1576
|
+
decls=["integer, pointer :: p(:)"],
|
|
1577
|
+
init=["p => a", omp(ent, c)],
|
|
1578
|
+
post=[omp(ext, "map(ref_ptee, release: p)")], focus=c)
|
|
1579
|
+
if feat in ("ref_ptr", "ref_ptr_ptee"):
|
|
1580
|
+
c = f"map({feat}, to: p)"
|
|
1581
|
+
maps = f"map(to: a) {c} map(from: b)" if feat == "ref_ptr" \
|
|
1582
|
+
else f"{c} map(from: b)"
|
|
1583
|
+
s = _k(version, Region("target", maps=maps,
|
|
1584
|
+
body=("b(i) = 2*p(i) + 1",)),
|
|
1585
|
+
decls=["integer, pointer :: p(:)"], init=["p => a"], focus=c)
|
|
1586
|
+
if feat == "ref_ptr_ptee":
|
|
1587
|
+
s.no_oracle("equivalent_default", "without a ref-modifier the "
|
|
1588
|
+
"map applies to the referring pointer and its "
|
|
1589
|
+
"pointee (6.0 §7.9.6), i.e. ref_ptr_ptee",
|
|
1590
|
+
"ref_ptr_ptee_default")
|
|
1591
|
+
return s
|
|
1592
|
+
if feat == "iterator":
|
|
1593
|
+
# only the items the iterator generates are mapped: for k = 1 the
|
|
1594
|
+
# section w(2:n/2) is present after enter data, the elements around
|
|
1595
|
+
# it are not (copy mode: under unified memory nothing is reported
|
|
1596
|
+
# present); an empty range maps nothing. One iterator
|
|
1597
|
+
# value, since list items generated from different values must not
|
|
1598
|
+
# have the same containing array (5.2 §5.8.3 p.155, 6.0 §7.9.6
|
|
1599
|
+
# p.286); the form w(k), k = 1:n:2, broke that restriction
|
|
1600
|
+
c = "map(iterator(k = 1:1), to: w(k + 1:n/2))"
|
|
1601
|
+
s = _k(version, Region("target"), focus=c,
|
|
1602
|
+
decls=["integer :: k", "integer, target :: w(n)"],
|
|
1603
|
+
init=["w = 0", omp(ent, c)])
|
|
1604
|
+
s.post += s.presence(("w(2)", "w(n/2)"), True) + s.presence(
|
|
1605
|
+
("w(1)", "w(n/2 + 1)"), False) + [
|
|
1606
|
+
omp(ext, "map(iterator(k = 1:1), release: w(k + 1:n/2))")]
|
|
1607
|
+
return s
|
|
1608
|
+
if feat == "mapper":
|
|
1609
|
+
return mapper_prog(version)
|
|
1610
|
+
return None
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
def _motion(version, which, feat):
|
|
1614
|
+
"""to / from on target update."""
|
|
1615
|
+
arr = "a" if which == "to" else "b"
|
|
1616
|
+
if feat is None:
|
|
1617
|
+
c = f"{which}({arr})"
|
|
1618
|
+
elif feat == "present":
|
|
1619
|
+
return present_probe(version, which)
|
|
1620
|
+
elif feat == "mapper":
|
|
1621
|
+
return motion_mapper_prog(version, which)
|
|
1622
|
+
elif feat == "iterator":
|
|
1623
|
+
c = f"{which}(iterator(k = 1:1): {arr}(k:n))"
|
|
1624
|
+
else:
|
|
1625
|
+
return None
|
|
1626
|
+
kw = {"to": c} if which == "to" else {"frm": c}
|
|
1627
|
+
s = update_prog(version, focus=c, **kw)
|
|
1628
|
+
if feat == "iterator":
|
|
1629
|
+
s.decls.append("integer :: k")
|
|
1630
|
+
return s
|
|
1631
|
+
|
|
1632
|
+
|
|
1633
|
+
def motion_mapper_prog(version, which):
|
|
1634
|
+
"""The mapper mm of type dt maps only the
|
|
1635
|
+
component v; s (v = w = 1) is entered whole. to: the host sets v = w =
|
|
1636
|
+
5 and `target update to(mapper(mm): s)` copies only v, so the device
|
|
1637
|
+
sees v = 5, w = 1. from: the device sets v = w = 5 and `target update
|
|
1638
|
+
from(mapper(mm): s)` copies only v back, so the host sees v = 5, w = 1.
|
|
1639
|
+
Without the modifier the update copies all of s (w = 5 too). Copy mode
|
|
1640
|
+
only (shared storage hides the copies)."""
|
|
1641
|
+
dm, up = spell("declare mapper", version), spell("target update",
|
|
1642
|
+
version)
|
|
1643
|
+
ent, ext = (spell("target enter data", version),
|
|
1644
|
+
spell("target exit data", version))
|
|
1645
|
+
c = f"{which}(mapper(mm): s)"
|
|
1646
|
+
s = Src(spec=["type :: dt", " integer :: v(8)", " integer :: w(8)",
|
|
1647
|
+
"end type dt", omp(f"{dm}(mm: dt :: q)", "map(q%v)")],
|
|
1648
|
+
decls=["type(dt) :: s", "integer :: rv, rw"],
|
|
1649
|
+
init=["s%v = 1", "s%w = 1", "rv = 0", "rw = 0"], focus=c)
|
|
1650
|
+
s.region += INNER + [omp(ent, "map(to: s)")]
|
|
1651
|
+
if which == "to":
|
|
1652
|
+
s.region += ["s%v = 5", "s%w = 5", omp(up, c),
|
|
1653
|
+
omp("target", "map(from: rv, rw)"), "rv = s%v(1)",
|
|
1654
|
+
"rw = s%w(1)", omp("end target")]
|
|
1655
|
+
cond = "rv == 5 .and. rw == 1"
|
|
1656
|
+
else:
|
|
1657
|
+
s.region += [omp("target"), "s%v = 5", "s%w = 5", omp("end target"),
|
|
1658
|
+
omp(up, c), "rv = s%v(1)", "rw = s%w(1)"]
|
|
1659
|
+
cond = "rv == 5 .and. rw == 1"
|
|
1660
|
+
s.region += [omp(ext, "map(delete: s)")]
|
|
1661
|
+
s.post += s.copy_check(cond)
|
|
1662
|
+
return s
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
SCHEDULE = {None: "schedule(static)", "static": "schedule(static)",
|
|
1666
|
+
"dynamic": "schedule(dynamic)", "guided": "schedule(guided)",
|
|
1667
|
+
"auto": "schedule(auto)", "runtime": "schedule(runtime)",
|
|
1668
|
+
"chunk_size": "schedule(static, 4)",
|
|
1669
|
+
"monotonic": "schedule(monotonic: static)",
|
|
1670
|
+
"nonmonotonic": "schedule(nonmonotonic: dynamic)",
|
|
1671
|
+
"simd": "schedule(simd: static)"}
|
|
1672
|
+
|
|
1673
|
+
REQUIRES = {"unified_address", "unified_shared_memory", "reverse_offload",
|
|
1674
|
+
"dynamic_allocators", "self_maps", "device_safesync"}
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
# Triage (probe on Tuolumne, 2026-10-18, HSA_XNACK=0): with a chunk
|
|
1678
|
+
# size the dynamic and guided schedules would be checkable (each chunk is a
|
|
1679
|
+
# contiguous block of chunk_size iterations, at least chunk_size for
|
|
1680
|
+
# guided), but schedule(dynamic, 4) and schedule(guided, 8) on parallel do in
|
|
1681
|
+
# a target region give iteration i to thread mod(i - 1, T) on AFAR (T = 32)
|
|
1682
|
+
# and CCE 19 (T = 256), as schedule(static, 4) does (the unaudited
|
|
1683
|
+
# omp.clause.schedule.chunk_size), so no chunked mutant could be killed.
|
|
1684
|
+
SCHED_CHUNK_PROBE = (
|
|
1685
|
+
"; with a chunk size the chunks would be checkable (a chunk is a "
|
|
1686
|
+
"contiguous set of iterations executed by one thread: 5.2/6.0 glossary, "
|
|
1687
|
+
"§11.5.3 / §13.6.3), but both toolchains split chunks across threads on "
|
|
1688
|
+
"the device (probe 2026-10-18: schedule(dynamic, 4) and schedule(guided, "
|
|
1689
|
+
"8) give iteration i to thread mod(i - 1, T), as schedule(static, 4) "
|
|
1690
|
+
"does), which violates the spec rather than using a freedom it grants; "
|
|
1691
|
+
"that failure is measured by the unaudited schedule.chunk_size and "
|
|
1692
|
+
"schedule.static programs, and no chunked mutant could be killed here")
|
|
1693
|
+
# Re-categorisation (was implementation_defined): with a chunk size
|
|
1694
|
+
# the spec fixes the chunks of dynamic and guided ("Each chunk contains
|
|
1695
|
+
# chunk_size iterations, except for the chunk that contains the
|
|
1696
|
+
# sequentially last iteration", 5.2 §11.5.3 p.253, 6.0 §13.6.3 p.418; for
|
|
1697
|
+
# guided "the chunks do not contain fewer than k iterations"), so a
|
|
1698
|
+
# deterministic check exists (each chunk is one thread's), and run-sched-var
|
|
1699
|
+
# can be set on the device with omp_set_schedule.
|
|
1700
|
+
SCHED_UNWRITTEN_WHY = {
|
|
1701
|
+
"dynamic": "the program uses schedule(dynamic), whose chunk size 1 lets "
|
|
1702
|
+
"any assignment conform, but the kind fixes its chunks once a "
|
|
1703
|
+
"chunk size is given (5.2 §11.5.3 p.253, 6.0 §13.6.3 p.418: "
|
|
1704
|
+
"each chunk is chunk_size contiguous iterations run by one "
|
|
1705
|
+
"thread), so schedule(dynamic, 4) with a check that every "
|
|
1706
|
+
"aligned block of 4 iterations ran on one thread is a "
|
|
1707
|
+
"deterministic oracle; it is not written" + SCHED_CHUNK_PROBE,
|
|
1708
|
+
"guided": "the program uses schedule(guided), whose chunk size 1 lets "
|
|
1709
|
+
"any assignment conform, but with a chunk size k no chunk but "
|
|
1710
|
+
"the last may have fewer than k iterations (5.2 §11.5.3 p.253, "
|
|
1711
|
+
"6.0 §13.6.3 p.419), so schedule(guided, 8) with a check that "
|
|
1712
|
+
"every run of iterations on one thread but the last is at "
|
|
1713
|
+
"least 8 long is a deterministic oracle; it is not written"
|
|
1714
|
+
+ SCHED_CHUNK_PROBE,
|
|
1715
|
+
"runtime": "run-sched-var's initial value is implementation defined "
|
|
1716
|
+
"(5.2 §2.2 p.40, 6.0 §3.2 p.118; the harness clears "
|
|
1717
|
+
"OMP_SCHEDULE), but omp_set_schedule in the target region "
|
|
1718
|
+
"fixes it, after which schedule(runtime) is the schedule set "
|
|
1719
|
+
"(5.2 §11.5.3 p.253), a deterministic oracle with the static "
|
|
1720
|
+
"kind; it is not written: CCE 19 does not link "
|
|
1721
|
+
"omp_set_schedule in device code (probe 2026-10-18) and the "
|
|
1722
|
+
"chunked kinds are those the toolchains do not honour on the "
|
|
1723
|
+
"device"}
|
|
1724
|
+
# Schedule(static) and dist_schedule(static) without
|
|
1725
|
+
# a chunk size, one reading for both
|
|
1726
|
+
STATIC_NO_CHUNK_WHY = (
|
|
1727
|
+
"the static kind without a chunk size guarantees only contiguous "
|
|
1728
|
+
"chunks, at most one per thread (team), and the spec's Note calls "
|
|
1729
|
+
"chunks of ceil(n/p) and q / q - 1 splits alike compliant, so the check "
|
|
1730
|
+
"tests contiguity and that every iteration ran on an owner; without the "
|
|
1731
|
+
"clause the schedule is implementation defined by the spec (worksharing "
|
|
1732
|
+
"loop: def-sched-var / auto; distribute: stated in the construct's "
|
|
1733
|
+
"section), so it may be such a split and no single run separates the "
|
|
1734
|
+
"clause from its absence (the label rests on this spec text, not on an "
|
|
1735
|
+
"observed default); the chunk_size forms fix one assignment exactly and "
|
|
1736
|
+
"keep an audited oracle")
|
|
1737
|
+
SCHED_WHY = {
|
|
1738
|
+
"auto": "the auto schedule delegates the assignment of iterations to "
|
|
1739
|
+
"threads to the compiler and runtime, so every assignment is "
|
|
1740
|
+
"conforming (5.2 §11.5.3, 6.0 §13.6.3)",
|
|
1741
|
+
"simd": "the simd modifier only rounds the chunk size up to a multiple "
|
|
1742
|
+
"of the implementation-defined SIMD width (5.2 §11.5.3, 6.0 "
|
|
1743
|
+
"§13.6.3), and both toolchains ignore schedule chunk sizes on "
|
|
1744
|
+
"the device (probe 2026-10-18)"}
|
|
1745
|
+
SCHED_BASIS = {"auto": "schedule_auto", "simd": "schedule_simd_width"}
|
|
1746
|
+
NONMONOTONIC_WHY = ("nonmonotonic is the modifier the dynamic kind the "
|
|
1747
|
+
"program uses has without one (5.0 and later)")
|
|
1748
|
+
MONOTONIC_WHY = (
|
|
1749
|
+
"Schedule(monotonic: dynamic) with a check of the order each "
|
|
1750
|
+
"thread ran its iterations in (a per-thread counter, firstprivate from "
|
|
1751
|
+
"0): monotonic requires increasing logical iteration order per thread, "
|
|
1752
|
+
"and a wrong answer is a real failure; without the modifier the dynamic "
|
|
1753
|
+
"kind is nonmonotonic, under which chunks may run in any order, "
|
|
1754
|
+
"increasing order included, so a conforming run without it can "
|
|
1755
|
+
"coincide")
|
|
1756
|
+
PROC_BIND_WHY = ("if bind-var is false the proc_bind clause has no effect, "
|
|
1757
|
+
"and the initial value of bind-var is implementation "
|
|
1758
|
+
"defined (5.2 §2.5 Table 2.4, 6.0 §3.5), so a run without "
|
|
1759
|
+
"the clause may coincide with one with it; omp_get_proc_bind "
|
|
1760
|
+
"returns the policy for nested regions without a proc_bind "
|
|
1761
|
+
"clause (5.2 §18.3.1), never the clause's own value; on the "
|
|
1762
|
+
"device AFAR reports omp_get_num_places() = 0 and CCE 19 "
|
|
1763
|
+
"does not link the places routines in device code (probe "
|
|
1764
|
+
"2026-10-18), so no place check is available either")
|
|
1765
|
+
SIMD_WHY = {
|
|
1766
|
+
"safelen": "safelen bounds the distance between concurrent iterations; "
|
|
1767
|
+
"the number of iterations run concurrently is implementation "
|
|
1768
|
+
"defined, so an implementation that runs one at a time meets "
|
|
1769
|
+
"the bound vacuously (the program's loop has no dependence "
|
|
1770
|
+
"the bound would protect)",
|
|
1771
|
+
"simdlen": "simdlen is a hint for the preferred number of concurrent "
|
|
1772
|
+
"iterations",
|
|
1773
|
+
"nontemporal": "nontemporal states a property of the accesses (low "
|
|
1774
|
+
"temporal locality), not a behaviour",
|
|
1775
|
+
"aligned": "aligned asserts an alignment the program ensures; it "
|
|
1776
|
+
"requires nothing observable of the implementation"}
|
|
1777
|
+
SIMD_BASIS = {"safelen": "simd_concurrency", "simdlen": "simdlen_hint",
|
|
1778
|
+
"nontemporal": "nontemporal_locality",
|
|
1779
|
+
"aligned": "aligned_assertion"}
|
|
1780
|
+
ORDER_WHY = ("order(concurrent) permits but does not require concurrent "
|
|
1781
|
+
"execution; the region may not call the OpenMP API (5.2 §10.3) "
|
|
1782
|
+
"that could see the threads, so a single run cannot tell it "
|
|
1783
|
+
"from the loop without the clause")
|
|
1784
|
+
# The 5.0 programs cite the 5.0 restriction (the
|
|
1785
|
+
# worksharing-loop with order(concurrent) takes the loop construct's
|
|
1786
|
+
# restrictions, 5.0 §2.9.2 p.108)
|
|
1787
|
+
ORDER_WHY_50 = ("order(concurrent) permits but does not require concurrent "
|
|
1788
|
+
"execution; the region may not call the OpenMP Runtime API "
|
|
1789
|
+
"(5.0 §2.9.5 p.131: \"A loop region corresponding to a loop "
|
|
1790
|
+
"construct may not contain calls to the OpenMP Runtime "
|
|
1791
|
+
"API\", which applies with order(concurrent), 5.0 §2.9.2 "
|
|
1792
|
+
"p.108) that could see the threads, so a single run cannot "
|
|
1793
|
+
"tell it from the loop without the clause")
|
|
1794
|
+
ORDER_REPRODUCIBLE_WHY = (
|
|
1795
|
+
"re-categorised (was implementation_defined): two "
|
|
1796
|
+
"worksharing-loops with the same reproducible schedule have consistent "
|
|
1797
|
+
"schedules, and the implementation guarantees that the memory effects "
|
|
1798
|
+
"of an iteration of the first happen before the same iteration of the "
|
|
1799
|
+
"second (5.2 §4.4.5 p.95, 6.0 §6.4.4 p.205), so a first loop with "
|
|
1800
|
+
"nowait that writes w(i) and a second that reads it is a deterministic "
|
|
1801
|
+
"oracle (no barrier between them); it is not written (AFAR rejects "
|
|
1802
|
+
"order on the worksharing-loop construct)")
|
|
1803
|
+
ORDER_UNCONSTRAINED_WHY = (
|
|
1804
|
+
"corrected: unconstrained is not 'the default order-modifier' "
|
|
1805
|
+
"(with schedule(static) it removes the reproducible schedule the loop "
|
|
1806
|
+
"would otherwise have, 5.2 §11.5 p.248, 6.0 §13.6 p.414), but this "
|
|
1807
|
+
"program has no schedule clause, so its schedule is not reproducible "
|
|
1808
|
+
"with or without the modifier and the forms are the same")
|
|
1809
|
+
|
|
1810
|
+
|
|
1811
|
+
# The loop construct's iterations "may execute in any order,
|
|
1812
|
+
# including concurrently" (5.2 §11.7 p.257, 6.0 §13.8; order(concurrent)
|
|
1813
|
+
# implied), so a per-thread counter `mine = mine + 1` updated by every
|
|
1814
|
+
# iteration is a data race between the iterations one thread runs (AFAR
|
|
1815
|
+
# printed 1 per thread for bind(thread) in Fortran). Each iteration
|
|
1816
|
+
# therefore records itself in its own element, hit(i, owner + 1) (owner:
|
|
1817
|
+
# the thread or team), and the host sums each owner's column into tot.
|
|
1818
|
+
BIND_HIT_DECLS = ["integer :: hit(n, 1024), tot(1024), k"]
|
|
1819
|
+
BIND_HIT_POST = ["do k = 1, 1024", " tot(k) = 0", " do i = 1, n",
|
|
1820
|
+
" tot(k) = tot(k) + hit(i, k)", " end do", "end do"]
|
|
1821
|
+
|
|
1822
|
+
|
|
1823
|
+
def bind_orphan_prog(version, kind):
|
|
1824
|
+
"""An orphaned loop bind(parallel|teams)
|
|
1825
|
+
in a declare target subroutine, called from every thread of a parallel
|
|
1826
|
+
region (from every team of a teams region of num_teams(4)) in a target
|
|
1827
|
+
region; each iteration a thread (team) runs is recorded in its own
|
|
1828
|
+
element of the declare target module array hit(i, owner + 1)
|
|
1829
|
+
(64 rows: n, written as a literal for the C port's file-scope array) (BIND_HIT_DECLS: race-free), summed into tot(thread or team + 1) on the
|
|
1830
|
+
host after target update from(hit). With the binding the team (league)
|
|
1831
|
+
shares the iterations: sum(tot) == n. Without bind an orphaned loop
|
|
1832
|
+
binds to the encountering thread (5.2 §11.7.1 p.257, 6.0 §13.8.1), so
|
|
1833
|
+
each thread (team) runs all n and the sum is T*n."""
|
|
1834
|
+
sub = ["integer :: hit(64, 1024)", omp("declare target(hit)"),
|
|
1835
|
+
"contains", "subroutine work(t)", omp("declare target"),
|
|
1836
|
+
"integer, intent(in) :: t",
|
|
1837
|
+
"integer :: i", omp("loop", f"bind({kind})"), "do i = 1, 64",
|
|
1838
|
+
" hit(i, t) = hit(i, t) + 1", "end do", omp("end loop"),
|
|
1839
|
+
"end subroutine work"]
|
|
1840
|
+
s = Src(module=sub, decls=["integer :: tot(1024), k", "integer :: nthr"],
|
|
1841
|
+
init=["hit = 0", omp("target update", "to(hit)"),
|
|
1842
|
+
f"nthr = {SENTINEL}"],
|
|
1843
|
+
post=[omp("target update", "from(hit)")] + [
|
|
1844
|
+
x.replace("do i = 1, n", "do i = 1, 64")
|
|
1845
|
+
for x in BIND_HIT_POST],
|
|
1846
|
+
checks=["(nthr >= 1 .and. nthr <= 1024)", "(sum(tot) == n)"],
|
|
1847
|
+
focus=f"bind({kind})")
|
|
1848
|
+
head = omp("target", STD_MAPS, "map(tofrom: nthr)", FLAG_MAP)
|
|
1849
|
+
if kind == "parallel":
|
|
1850
|
+
s.region += [head, FLAG] + seq_loop() + [
|
|
1851
|
+
omp("parallel"),
|
|
1852
|
+
"if (omp_get_thread_num() < 1024) call work("
|
|
1853
|
+
"omp_get_thread_num() + 1)",
|
|
1854
|
+
"if (omp_get_thread_num() == 0) nthr = omp_get_num_threads()",
|
|
1855
|
+
omp("end parallel"), omp("end target")]
|
|
1856
|
+
else:
|
|
1857
|
+
s.region += [head, omp("teams", "num_teams(4)")] + \
|
|
1858
|
+
guarded("omp_get_team_num() == 0",
|
|
1859
|
+
TEAMS_FLAG + ["nthr = omp_get_num_teams()"]
|
|
1860
|
+
+ seq_loop()) + [
|
|
1861
|
+
"if (omp_get_team_num() < 1024) call work("
|
|
1862
|
+
"omp_get_team_num() + 1)",
|
|
1863
|
+
omp("end teams"), omp("end target")]
|
|
1864
|
+
return s
|
|
1865
|
+
|
|
1866
|
+
|
|
1867
|
+
def bind_thread_prog(version):
|
|
1868
|
+
"""loop bind(thread) in a parallel region in a target region: every
|
|
1869
|
+
thread of the team runs all n iterations (the binding region is the
|
|
1870
|
+
thread's own), each recorded in the thread's own column of hit
|
|
1871
|
+
(BIND_HIT_DECLS), summed into tot(thread + 1) on the host; thread 0
|
|
1872
|
+
alone writes b. Without bind the loop binds to the parallel region and
|
|
1873
|
+
the team shares the iterations, so with more than one thread some
|
|
1874
|
+
thread counts fewer than n (and b is only partly written). A
|
|
1875
|
+
one-thread team cannot tell them apart. Fix: the team size is implementation defined (NVHPC's nvc makes
|
|
1876
|
+
110,560 threads), so the check no longer assumes nthr <= 1024: the
|
|
1877
|
+
first min(nthr, 1024) threads record their iterations and are
|
|
1878
|
+
checked."""
|
|
1879
|
+
s = Src(decls=BIND_HIT_DECLS + ["integer :: tid, nthr"],
|
|
1880
|
+
init=["hit = 0", f"nthr = {SENTINEL}"], post=list(BIND_HIT_POST),
|
|
1881
|
+
checks=["(nthr >= 1)",
|
|
1882
|
+
"all(tot(1:min(nthr, 1024)) == n)"],
|
|
1883
|
+
focus="bind(thread)")
|
|
1884
|
+
s.region += [omp("target", STD_MAPS, "map(tofrom: hit, nthr)", FLAG_MAP),
|
|
1885
|
+
FLAG,
|
|
1886
|
+
omp("parallel", "default(none) private(tid) "
|
|
1887
|
+
"shared(a, b, hit, nthr)"),
|
|
1888
|
+
"tid = omp_get_thread_num()",
|
|
1889
|
+
"if (tid == 0) nthr = omp_get_num_threads()",
|
|
1890
|
+
omp("loop", "bind(thread)"),
|
|
1891
|
+
"do i = 1, n", " if (tid == 0) b(i) = 2*a(i) + 1",
|
|
1892
|
+
" if (tid < 1024) hit(i, tid + 1) = hit(i, tid + 1) + 1",
|
|
1893
|
+
"end do", omp("end loop"),
|
|
1894
|
+
omp("end parallel"), omp("end target")]
|
|
1895
|
+
return s
|
|
1896
|
+
|
|
1897
|
+
|
|
1898
|
+
def bind_teams_thread_prog(version):
|
|
1899
|
+
"""omp.clause.bind: loop bind(thread) closely nested in a
|
|
1900
|
+
teams region of num_teams(4) teams (exactly 4: 5.2 §10.2.1, 6.0
|
|
1901
|
+
§12.2.1) in a target region. With binding thread the binding region is
|
|
1902
|
+
not defined and the binding thread set is the encountering thread (5.2
|
|
1903
|
+
§11.7.1 p.258, 6.0 §13.8.1), so every team's initial thread runs all n
|
|
1904
|
+
iterations, each recorded in the team's own column of hit
|
|
1905
|
+
(BIND_HIT_DECLS), summed into tot(team + 1) on the host; team 0 alone
|
|
1906
|
+
writes b. Without the clause the loop, closely nested in teams, binds
|
|
1907
|
+
to the teams region and the league shares the iterations, so some team
|
|
1908
|
+
counts fewer than n. Unlike the orphaned forms (bind.parallel,
|
|
1909
|
+
bind.teams), deleting the clause leaves a conforming program (an
|
|
1910
|
+
orphaned loop must have bind, 5.2 §11.7 p.257); unlike bind.thread
|
|
1911
|
+
(closely nested in parallel), the binding it replaces is the
|
|
1912
|
+
league's."""
|
|
1913
|
+
s = Src(decls=BIND_HIT_DECLS + ["integer :: tm, nt"],
|
|
1914
|
+
init=["hit = 0", f"nt = {SENTINEL}"], post=list(BIND_HIT_POST),
|
|
1915
|
+
checks=["(nt >= 1 .and. nt <= 1024)",
|
|
1916
|
+
"all(tot(1:min(nt, 1024)) == n)"],
|
|
1917
|
+
focus="bind(thread)")
|
|
1918
|
+
s.region += [omp("target", STD_MAPS, "map(tofrom: hit, nt)", FLAG_MAP),
|
|
1919
|
+
omp("teams", "num_teams(4) default(none) private(tm) "
|
|
1920
|
+
"shared(a, b, hit, nt, host_flag)"),
|
|
1921
|
+
"tm = omp_get_team_num()",
|
|
1922
|
+
"if (tm == 0) nt = omp_get_num_teams()",
|
|
1923
|
+
*guarded("tm == 0", TEAMS_FLAG),
|
|
1924
|
+
omp("loop", "bind(thread)"),
|
|
1925
|
+
"do i = 1, n", " if (tm == 0) b(i) = 2*a(i) + 1",
|
|
1926
|
+
" if (tm < 1024) hit(i, tm + 1) = hit(i, tm + 1) + 1",
|
|
1927
|
+
"end do", omp("end loop"),
|
|
1928
|
+
omp("end teams"), omp("end target")]
|
|
1929
|
+
return s
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
def task_default_prog(version, clause):
|
|
1933
|
+
"""default(shared) on a task: in a parallel region in the
|
|
1934
|
+
target region, each thread's c is private (c = 7); a task with
|
|
1935
|
+
`clause` sets c = 42 and, after taskwait, thread 0 reports its c in r.
|
|
1936
|
+
With default(shared) the task shares the generating thread's c, so r
|
|
1937
|
+
is 42. Without a default clause, c, which is not shared by all implicit
|
|
1938
|
+
tasks of the team in the enclosing context, is firstprivate in the task
|
|
1939
|
+
(5.2 §5.1.1 p.100, 6.0 §7.1.1 p.214): the write is lost and r is 7. In a
|
|
1940
|
+
parallel construct default(shared) would be the default and change
|
|
1941
|
+
nothing; on a task it does not."""
|
|
1942
|
+
s = Src(decls=["integer :: c, r"], init=["r = 0"],
|
|
1943
|
+
checks=["(r == 42)"], focus=clause)
|
|
1944
|
+
s.region += [omp("target", STD_MAPS, "map(from: r)", FLAG_MAP), FLAG] + \
|
|
1945
|
+
seq_loop() + [omp("parallel", "private(c)"), "c = 7",
|
|
1946
|
+
omp("task", clause), "c = 42", omp("end task"),
|
|
1947
|
+
omp("taskwait"),
|
|
1948
|
+
"if (omp_get_thread_num() == 0) r = c",
|
|
1949
|
+
omp("end parallel"), omp("end target")]
|
|
1950
|
+
return s
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
def monotonic_prog(version):
|
|
1954
|
+
"""schedule(monotonic: dynamic) on parallel do in a target region (Task
|
|
1955
|
+
15): every iteration records its thread (tid) and its place in that
|
|
1956
|
+
thread's execution order (ord, from a per-thread counter firstprivate
|
|
1957
|
+
from 0). Monotonic requires each thread to run the chunks it is
|
|
1958
|
+
assigned in increasing logical iteration order (5.2 §11.5.3 p.254, 6.0
|
|
1959
|
+
§13.6.3 p.419): for two iterations of one thread, the later iteration
|
|
1960
|
+
has the larger ord. Without the modifier the dynamic kind is
|
|
1961
|
+
nonmonotonic, which permits that order too (see MONOTONIC_WHY)."""
|
|
1962
|
+
def loop(flag):
|
|
1963
|
+
return ["do i = 1, m", " if (i <= n) b(i) = 2*a(i) + 1",
|
|
1964
|
+
" mine = mine + 1", " ord(i) = mine",
|
|
1965
|
+
" tid(i) = omp_get_thread_num()", "end do"]
|
|
1966
|
+
post = ["mok = .true.", "do i = 2, m", " do k = 1, i - 1",
|
|
1967
|
+
" if (tid(k) == tid(i) .and. ord(k) >= ord(i)) mok = .false.",
|
|
1968
|
+
" end do", "end do"]
|
|
1969
|
+
s = pdo(version, "schedule(monotonic: dynamic) firstprivate(mine)",
|
|
1970
|
+
loop=loop, maps=f"{STD_MAPS} map(from: tid, ord)",
|
|
1971
|
+
shared=("a", "b", "tid", "ord"),
|
|
1972
|
+
decls=[f"integer, parameter :: m = {SCHED_M}",
|
|
1973
|
+
"integer :: tid(m), ord(m), mine, k", "logical :: mok"],
|
|
1974
|
+
init=["mine = 0", f"tid = {SENTINEL}", f"ord = {SENTINEL}"],
|
|
1975
|
+
post=post, checks=["mok"])
|
|
1976
|
+
s.focus = "schedule(monotonic: dynamic)"
|
|
1977
|
+
return s.no_oracle("implementation_defined", MONOTONIC_WHY,
|
|
1978
|
+
"schedule_nonmonotonic_order", checked=True)
|
|
1979
|
+
|
|
1980
|
+
|
|
1981
|
+
def if_prog(version):
|
|
1982
|
+
"""The if clause judged on the device. if(use_par) with use_par
|
|
1983
|
+
false on a parallel do nested in a target region: the parallel region
|
|
1984
|
+
is inactive, a team of one thread (iteration 1 records the team size),
|
|
1985
|
+
while the region runs on the device. Without the clause the team has
|
|
1986
|
+
more than one thread on both toolchains. (The if clause on a target
|
|
1987
|
+
construct with a false condition runs the region on the host, which is
|
|
1988
|
+
not device offload; the clause is the same atom.)"""
|
|
1989
|
+
s = pdo(version, "if(use_par)",
|
|
1990
|
+
maps=f"{STD_MAPS} map(from: nthr)",
|
|
1991
|
+
body=BODY + ("if (i == 1) nthr = omp_get_num_threads()",),
|
|
1992
|
+
shared=("a", "b", "nthr"),
|
|
1993
|
+
decls=["integer :: nthr", "logical :: use_par"],
|
|
1994
|
+
init=["use_par = .false.", "nthr = 0"], checks=["(nthr == 1)"])
|
|
1995
|
+
s.focus = "if(use_par)"
|
|
1996
|
+
return s
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
def if_dnm_prog(version):
|
|
2000
|
+
"""if(parallel: use_dev) with use_dev false on the combined construct:
|
|
2001
|
+
the parallel constituent is inactive, a team of one thread, while the
|
|
2002
|
+
region still runs on the device. Without the modifier the if clause
|
|
2003
|
+
applies to the target constituent too and the region runs on the host
|
|
2004
|
+
(the audit's kill is a host fallback)."""
|
|
2005
|
+
return team_param(version, "if(parallel: use_dev)",
|
|
2006
|
+
["if (i == 1) nthr = omp_get_num_threads()"],
|
|
2007
|
+
["integer :: nthr"], ["(nthr == 1)"],
|
|
2008
|
+
extra_decls=["logical :: use_dev"],
|
|
2009
|
+
# If(parallel: ...) is evaluated inside the
|
|
2010
|
+
# teams region (5.2 §17.2 p.340) and default(none)
|
|
2011
|
+
# applies to teams (5.2 §5.4.1 p.109): use_dev is listed
|
|
2012
|
+
extra_shared=("use_dev",),
|
|
2013
|
+
init=["use_dev = .false."])
|
|
2014
|
+
|
|
2015
|
+
|
|
2016
|
+
def _UNCHECKED_CLAUSE(clause):
|
|
2017
|
+
"""Src fields for a clause whose own effect the program cannot
|
|
2018
|
+
observe (the check covers the enclosing construct only)."""
|
|
2019
|
+
return {"observable": False,
|
|
2020
|
+
"note": f"the check covers the enclosing target data, not "
|
|
2021
|
+
f"{clause} itself",
|
|
2022
|
+
"header_notes": [f"clause not checked: {clause}"]}
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
def clause_recipe(clause: str, feat: str | None, version: int) -> Src | None:
|
|
2026
|
+
"""The program for omp.clause.<clause>[.<feat>] (directive_name is
|
|
2027
|
+
built by offload.engine.gen from the clause's base program)."""
|
|
2028
|
+
v = version
|
|
2029
|
+
if clause in REQUIRES:
|
|
2030
|
+
if feat in (None, "required"):
|
|
2031
|
+
return requires_prog(v, clause if feat is None
|
|
2032
|
+
else f"{clause}(.true.)")
|
|
2033
|
+
return None
|
|
2034
|
+
if clause == "atomic_default_mem_order":
|
|
2035
|
+
return requires_prog(v, f"{clause}({feat or 'seq_cst'})")
|
|
2036
|
+
if clause == "map":
|
|
2037
|
+
return _feat_map(v, feat)
|
|
2038
|
+
if clause in ("to", "from") and feat in (None, "present", "mapper",
|
|
2039
|
+
"iterator"):
|
|
2040
|
+
if clause == "to" and feat is None:
|
|
2041
|
+
# the declare target clause and the motion clause share the
|
|
2042
|
+
# atom; the motion clause is the one with a device effect
|
|
2043
|
+
return _motion(v, "to", None)
|
|
2044
|
+
return _motion(v, clause, feat)
|
|
2045
|
+
if clause == "reduction":
|
|
2046
|
+
if feat in RED:
|
|
2047
|
+
return reduction_prog(v, feat)
|
|
2048
|
+
if feat is None:
|
|
2049
|
+
return reduction_prog(v)
|
|
2050
|
+
if feat == "default":
|
|
2051
|
+
return reduction_prog(v, modifier="default").no_oracle(
|
|
2052
|
+
"equivalent_default", "default is the default "
|
|
2053
|
+
"reduction-modifier", "reduction_default_modifier")
|
|
2054
|
+
if feat == "original":
|
|
2055
|
+
return reduction_prog(v, modifier="original(shared)")
|
|
2056
|
+
if feat == "task":
|
|
2057
|
+
return task_reduction_prog(v)
|
|
2058
|
+
if feat == "inscan":
|
|
2059
|
+
return inscan_prog(v)
|
|
2060
|
+
return None
|
|
2061
|
+
if clause == "in_reduction":
|
|
2062
|
+
return in_reduction_prog(v, feat or "plus") \
|
|
2063
|
+
if feat is None or feat in RED else None
|
|
2064
|
+
if clause == "schedule":
|
|
2065
|
+
c = SCHEDULE.get(feat)
|
|
2066
|
+
if c is None:
|
|
2067
|
+
return None
|
|
2068
|
+
if feat in (None, "static"):
|
|
2069
|
+
return schedule_prog(v, c)
|
|
2070
|
+
if feat == "chunk_size":
|
|
2071
|
+
return schedule_prog(v, c, chunk=4)
|
|
2072
|
+
if feat == "monotonic":
|
|
2073
|
+
return monotonic_prog(v)
|
|
2074
|
+
s = (pdo_simd if feat == "simd" else pdo)(v, c)
|
|
2075
|
+
if feat == "nonmonotonic":
|
|
2076
|
+
return s.no_oracle("equivalent_default", NONMONOTONIC_WHY,
|
|
2077
|
+
"schedule_default_modifier")
|
|
2078
|
+
if feat in SCHED_UNWRITTEN_WHY:
|
|
2079
|
+
return s.unverified("oracle_not_written",
|
|
2080
|
+
SCHED_UNWRITTEN_WHY[feat])
|
|
2081
|
+
return s.no_oracle("implementation_defined", SCHED_WHY[feat],
|
|
2082
|
+
SCHED_BASIS[feat])
|
|
2083
|
+
if clause == "proc_bind":
|
|
2084
|
+
return pdo(v, f"proc_bind({feat or 'close'})").no_oracle(
|
|
2085
|
+
"implementation_defined", PROC_BIND_WHY, "proc_bind_bind_var")
|
|
2086
|
+
if clause == "num_threads":
|
|
2087
|
+
if feat not in (None, "strict"):
|
|
2088
|
+
return None
|
|
2089
|
+
c = "num_threads(strict: 4)" if feat == "strict" else "num_threads(4)"
|
|
2090
|
+
# at most 4 threads (exactly 4 with strict); both toolchains give
|
|
2091
|
+
# more than 4 without the clause
|
|
2092
|
+
want = "nthr == 4" if feat == "strict" else "nthr >= 1 .and. nthr <= 4"
|
|
2093
|
+
return pdo(v, c, body=BODY + ("if (i == 1) nthr = "
|
|
2094
|
+
"omp_get_num_threads()",),
|
|
2095
|
+
maps=f"{STD_MAPS} map(from: nthr)",
|
|
2096
|
+
shared=("a", "b", "nthr"), decls=["integer :: nthr"],
|
|
2097
|
+
init=[f"nthr = {SENTINEL}"], checks=[f"({want})"])
|
|
2098
|
+
if clause == "message" and feat is None:
|
|
2099
|
+
return error_probe(v, "warning")
|
|
2100
|
+
if clause == "severity" and feat in (None, "warning", "fatal"):
|
|
2101
|
+
return error_probe(v, feat or "warning")
|
|
2102
|
+
if clause in ("safelen", "simdlen") and feat is None:
|
|
2103
|
+
return pdo_simd(v, f"{clause}({8 if clause == 'safelen' else 4})"
|
|
2104
|
+
).no_oracle("hint_only", SIMD_WHY[clause],
|
|
2105
|
+
SIMD_BASIS[clause])
|
|
2106
|
+
if clause == "nontemporal" and feat is None:
|
|
2107
|
+
return pdo_simd(v, "nontemporal(b)").no_oracle(
|
|
2108
|
+
"hint_only", SIMD_WHY[clause], SIMD_BASIS[clause])
|
|
2109
|
+
if clause == "aligned" and feat in (None, "alignment"):
|
|
2110
|
+
return pdo_simd(v, "aligned(a: 8)").no_oracle(
|
|
2111
|
+
"hint_only", SIMD_WHY[clause], SIMD_BASIS[clause])
|
|
2112
|
+
if clause == "order":
|
|
2113
|
+
c = {None: "order(concurrent)", "concurrent": "order(concurrent)",
|
|
2114
|
+
"reproducible": "order(reproducible: concurrent)",
|
|
2115
|
+
"unconstrained": "order(unconstrained: concurrent)"}.get(feat)
|
|
2116
|
+
if c is None:
|
|
2117
|
+
return None
|
|
2118
|
+
if feat == "unconstrained":
|
|
2119
|
+
return pdo(v, c).no_oracle(
|
|
2120
|
+
"equivalent_default", ORDER_UNCONSTRAINED_WHY,
|
|
2121
|
+
"order_unconstrained_reproducible")
|
|
2122
|
+
if feat == "reproducible":
|
|
2123
|
+
return pdo(v, c).unverified("oracle_not_written",
|
|
2124
|
+
ORDER_REPRODUCIBLE_WHY)
|
|
2125
|
+
return pdo(v, c).no_oracle("implementation_defined",
|
|
2126
|
+
ORDER_WHY_50 if v == 50 else ORDER_WHY,
|
|
2127
|
+
"order_concurrent")
|
|
2128
|
+
if clause == "bind":
|
|
2129
|
+
if feat == "thread":
|
|
2130
|
+
return bind_thread_prog(v)
|
|
2131
|
+
if feat is None:
|
|
2132
|
+
return bind_teams_thread_prog(v)
|
|
2133
|
+
return bind_orphan_prog(v, feat)
|
|
2134
|
+
if clause == "collapse" and feat is None:
|
|
2135
|
+
# with collapse(2) the 64 iterations of the 2 x 32 nest are shared
|
|
2136
|
+
# by the team (schedule(static): one chunk per thread, min(T, 64)
|
|
2137
|
+
# threads busy); without it only the 2 outer iterations are, so at
|
|
2138
|
+
# most 2 threads run the loop
|
|
2139
|
+
s = pdo(v, "collapse(2) schedule(static)", loop=collapse_loop,
|
|
2140
|
+
maps=f"{STD_MAPS} map(from: tid, nthr)",
|
|
2141
|
+
shared=("a", "b", "tid", "nthr"),
|
|
2142
|
+
decls=["integer :: j, k", "integer :: tid(n), nthr, nd"],
|
|
2143
|
+
init=[f"tid = {SENTINEL}", f"nthr = {SENTINEL}"],
|
|
2144
|
+
post=count_owners("tid", "nd", "nthr"),
|
|
2145
|
+
checks=["(nd == min(nthr, n))"])
|
|
2146
|
+
s.focus = "collapse(2)"
|
|
2147
|
+
return s
|
|
2148
|
+
if clause == "ordered":
|
|
2149
|
+
if feat is None:
|
|
2150
|
+
# the ordered regions run in iteration order: each takes the
|
|
2151
|
+
# next ticket; without the ordered clause (and regions) the
|
|
2152
|
+
# team races for the counter
|
|
2153
|
+
def loop(flag):
|
|
2154
|
+
return ["do i = 1, n", " b(i) = 2*a(i) + 1",
|
|
2155
|
+
" " + omp("ordered"), " cnt = cnt + 1",
|
|
2156
|
+
" seq(i) = cnt", " " + omp("end ordered"),
|
|
2157
|
+
"end do"]
|
|
2158
|
+
s = pdo(v, "ordered", loop=loop,
|
|
2159
|
+
maps=f"{STD_MAPS} map(tofrom: cnt) map(from: seq)",
|
|
2160
|
+
shared=("a", "b", "cnt", "seq"),
|
|
2161
|
+
decls=["integer :: cnt, seq(n)"],
|
|
2162
|
+
init=["cnt = 0", f"seq = {SENTINEL}"],
|
|
2163
|
+
checks=["all(2*seq + 1 == ref)"])
|
|
2164
|
+
s.focus = "ordered"
|
|
2165
|
+
return s
|
|
2166
|
+
if feat == "n":
|
|
2167
|
+
return pdo(v, "ordered(1)").no_oracle(
|
|
2168
|
+
"equivalent_default", "re-categorised (was "
|
|
2169
|
+
"implementation_defined): without n the ordered clause "
|
|
2170
|
+
"behaves as if n were the collapse value, here 1, so "
|
|
2171
|
+
"ordered(1) on this one-loop nest is the ordered clause "
|
|
2172
|
+
"without its argument, with or without doacross constructs",
|
|
2173
|
+
"ordered_n_default")
|
|
2174
|
+
return None
|
|
2175
|
+
if clause == "private" and feat is None:
|
|
2176
|
+
# t is written by every iteration; the target's t (mapped tofrom,
|
|
2177
|
+
# 7) must not change: without private(t) the team shares it
|
|
2178
|
+
return pdo(v, "private(t)", body=("t = 2*a(i)", "b(i) = t + 1"),
|
|
2179
|
+
maps=f"{STD_MAPS} map(tofrom: t)", decls=["integer :: t"],
|
|
2180
|
+
init=["t = 7"], checks=["(t == 7)"])
|
|
2181
|
+
if clause == "firstprivate":
|
|
2182
|
+
c = {None: "firstprivate(c)", "saved": "firstprivate(saved: c)"}.get(
|
|
2183
|
+
feat)
|
|
2184
|
+
if c is None:
|
|
2185
|
+
return None
|
|
2186
|
+
# each thread's c starts at 1 (the body reads mod(c, 100) = 1 and
|
|
2187
|
+
# adds 100); the target's c (mapped tofrom) must stay 1: without
|
|
2188
|
+
# the clause the team shares c and the additions come back
|
|
2189
|
+
return pdo(v, c, body=("b(i) = 2*a(i) + mod(c, 100)", "c = c + 100"),
|
|
2190
|
+
maps=f"{STD_MAPS} map(tofrom: c)", decls=["integer :: c"],
|
|
2191
|
+
init=["c = 1"], checks=["(c == 1)"])
|
|
2192
|
+
if clause == "lastprivate":
|
|
2193
|
+
if feat is None:
|
|
2194
|
+
c, body, want = "lastprivate(t)", ("t = a(i)",), "n"
|
|
2195
|
+
elif feat == "conditional":
|
|
2196
|
+
c, body, want = ("lastprivate(conditional: t)",
|
|
2197
|
+
("if (a(i) <= 10) t = a(i)",), "10")
|
|
2198
|
+
else:
|
|
2199
|
+
return None
|
|
2200
|
+
return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: t)",
|
|
2201
|
+
body=BODY + body, decls=["integer :: t"], init=["t = 0"],
|
|
2202
|
+
checks=[f"(t == {want})"])
|
|
2203
|
+
if clause == "shared" and feat is None:
|
|
2204
|
+
# under default(private) only shared(a, b) lets the team write the
|
|
2205
|
+
# target's b
|
|
2206
|
+
s = pdo(v, "default(private) shared(a, b)", hyg=False)
|
|
2207
|
+
s.focus = "shared(a, b)"
|
|
2208
|
+
return s
|
|
2209
|
+
if clause == "default":
|
|
2210
|
+
if feat in (None, "none", "shared", "private", "firstprivate"):
|
|
2211
|
+
# the base atom uses the observable private form
|
|
2212
|
+
feat = feat or "private"
|
|
2213
|
+
c = f"default({feat}) shared(a, b)"
|
|
2214
|
+
elif feat in ("aggregate", "all", "allocatable", "pointer", "scalar"):
|
|
2215
|
+
c = f"default(shared: {feat}) shared(a, b)"
|
|
2216
|
+
else:
|
|
2217
|
+
return None
|
|
2218
|
+
if feat in ("private", "firstprivate"):
|
|
2219
|
+
# c is not listed: private (firstprivate) by default, written by
|
|
2220
|
+
# every iteration; the target's c (mapped tofrom, 7) must not
|
|
2221
|
+
# change, as it would if the team shared c
|
|
2222
|
+
return pdo(v, c, focus=c.split(" shared")[0],
|
|
2223
|
+
body=("c = 2*a(i)", "b(i) = c + 1"),
|
|
2224
|
+
maps=f"{STD_MAPS} map(tofrom: c)",
|
|
2225
|
+
decls=["integer :: c"], init=["c = 7"],
|
|
2226
|
+
checks=["(c == 7)"])
|
|
2227
|
+
if feat == "shared":
|
|
2228
|
+
return task_default_prog(v, "default(shared)")
|
|
2229
|
+
if feat == "all":
|
|
2230
|
+
return task_default_prog(v, "default(shared: all)").no_oracle(
|
|
2231
|
+
"equivalent_default", "default(shared: all) is "
|
|
2232
|
+
"default(shared) (all is the variable category without one); "
|
|
2233
|
+
"the program checks what both require: a task in a target "
|
|
2234
|
+
"region shares the generating thread's private c",
|
|
2235
|
+
"default_category_all", checked=True)
|
|
2236
|
+
s = pdo(v, c, focus=c.split(" shared")[0])
|
|
2237
|
+
if feat == "none":
|
|
2238
|
+
s.evidence = "legality"
|
|
2239
|
+
s.legality_rule = "default-dsa-001"
|
|
2240
|
+
s.observable = False
|
|
2241
|
+
s.note = ("default(none) has no run-time effect; its oracle is "
|
|
2242
|
+
"the illegal variant without a listed variable")
|
|
2243
|
+
return s
|
|
2244
|
+
return s
|
|
2245
|
+
if clause == "linear":
|
|
2246
|
+
# the step forms use step 2, which the mutation (step removed: 1)
|
|
2247
|
+
# changes
|
|
2248
|
+
c = {None: "linear(x: 1)", "step_simple_modifier": "linear(x: 2)",
|
|
2249
|
+
"step": "linear(x: step(2))", "val": "linear(x: val, step(1))"
|
|
2250
|
+
}.get(feat)
|
|
2251
|
+
if v == 50 and feat == "val":
|
|
2252
|
+
return linear_simd_decl_prog(v, feat)
|
|
2253
|
+
if c:
|
|
2254
|
+
s = linear_prog(v, c, step=2 if feat in (
|
|
2255
|
+
"step", "step_simple_modifier") else 1)
|
|
2256
|
+
if feat == "val":
|
|
2257
|
+
s.no_oracle("equivalent_default", "on a worksharing-loop the "
|
|
2258
|
+
"only linear-modifier allowed is val, and the "
|
|
2259
|
+
"clause's loop semantics do not depend on it, so "
|
|
2260
|
+
"linear(x: val, step(1)) is linear(x: step(1))",
|
|
2261
|
+
"linear_modifier_val")
|
|
2262
|
+
return s
|
|
2263
|
+
if feat in ("ref", "uval"):
|
|
2264
|
+
return linear_simd_decl_prog(v, feat)
|
|
2265
|
+
return None
|
|
2266
|
+
if clause == "induction":
|
|
2267
|
+
c = {None: "induction(step(1), +: x)", "plus":
|
|
2268
|
+
"induction(step(1), +: x)", "step": "induction(step(1), +: x)",
|
|
2269
|
+
"strict": "induction(step(1), strict, +: x)",
|
|
2270
|
+
"relaxed": "induction(step(1), relaxed, +: x)",
|
|
2271
|
+
"times": "induction(step(-1), *: y)"}.get(feat)
|
|
2272
|
+
if c is None:
|
|
2273
|
+
return None
|
|
2274
|
+
# the body applies the inductor, so the value at the end of each
|
|
2275
|
+
# iteration is the closed form for the next (relaxed, 6.0 §7.6.13)
|
|
2276
|
+
if feat == "times":
|
|
2277
|
+
# y = (-1)**(i-1) at the start of iteration i
|
|
2278
|
+
return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: y)",
|
|
2279
|
+
body=("b(i) = 2*a(i) + 1 + (y - merge(1, -1, "
|
|
2280
|
+
"mod(i, 2) == 1))", "y = y * (-1)"),
|
|
2281
|
+
decls=["integer :: y"], init=["y = 1"],
|
|
2282
|
+
checks=["(y == 1)"])
|
|
2283
|
+
return pdo(v, c, maps=f"{STD_MAPS} map(tofrom: x)",
|
|
2284
|
+
body=("b(i) = 2*a(i) + 1 + (x - (i + 2))", "x = x + 1"),
|
|
2285
|
+
decls=["integer :: x"], init=["x = 3"],
|
|
2286
|
+
checks=["(x == n + 3)"])
|
|
2287
|
+
if clause == "allocate":
|
|
2288
|
+
c = {None: "allocate(t)",
|
|
2289
|
+
"allocator": "allocate(allocator(omp_default_mem_alloc): t)",
|
|
2290
|
+
"allocator_simple_modifier": "allocate(omp_default_mem_alloc: t)",
|
|
2291
|
+
# the allocator-complex-modifier with the align modifier (5.2
|
|
2292
|
+
# §6.6 p.179): names an allocator, so no requires is needed
|
|
2293
|
+
"align": "allocate(allocator(omp_default_mem_alloc), "
|
|
2294
|
+
"align(8): t)"}.get(feat)
|
|
2295
|
+
if c and feat is None:
|
|
2296
|
+
# An allocate clause in a target region must name an
|
|
2297
|
+
# allocator unless requires dynamic_allocators is present (5.2
|
|
2298
|
+
# §6.6 p.179, 6.0 §8.6 p.313); the form without one (the bare list) gets the
|
|
2299
|
+
# requirement, which also makes uses_allocators unnecessary
|
|
2300
|
+
# (5.2 §13.8 p.286, 6.0 §15.8 p.463)
|
|
2301
|
+
s = alloc_prog(v, c, wrap="")
|
|
2302
|
+
s.spec = [omp("requires", "dynamic_allocators")] + s.spec
|
|
2303
|
+
return s
|
|
2304
|
+
return alloc_prog(v, c) if c else None
|
|
2305
|
+
if clause == "uses_allocators":
|
|
2306
|
+
if feat is None:
|
|
2307
|
+
s = alloc_prog(v, "allocate(omp_default_mem_alloc: t)")
|
|
2308
|
+
s.focus = "uses_allocators(omp_default_mem_alloc)"
|
|
2309
|
+
return s
|
|
2310
|
+
mod = {"memspace": "memspace(omp_default_mem_space)",
|
|
2311
|
+
"traits": "traits(my_traits)"}.get(feat)
|
|
2312
|
+
if mod is None:
|
|
2313
|
+
return None
|
|
2314
|
+
c = f"uses_allocators({mod}: my_alloc)"
|
|
2315
|
+
if v == 50: # 5.0 §2.12.5 p.170: allocator(allocator-traits-array)
|
|
2316
|
+
c = "uses_allocators(my_alloc(my_traits))"
|
|
2317
|
+
s = alloc_prog(v, "allocate(my_alloc: t)", wrap=c, decls=TRAITS,
|
|
2318
|
+
shared=("a", "b", "my_alloc"))
|
|
2319
|
+
s.focus = c
|
|
2320
|
+
if feat == "memspace":
|
|
2321
|
+
s.no_oracle("equivalent_default", "the program names "
|
|
2322
|
+
"omp_default_mem_space, the memory space used "
|
|
2323
|
+
"without the modifier", "memspace_default")
|
|
2324
|
+
else: # CCE passes, the mutant ICEs
|
|
2325
|
+
s.unverified("oracle_not_written", "re-categorised "
|
|
2326
|
+
"(was implementation_defined): memory from the "
|
|
2327
|
+
"allocator 'will be byte aligned to at least the "
|
|
2328
|
+
"value specified for the alignment trait' (5.2 §6.2 "
|
|
2329
|
+
"p.173, 6.0 §8.2 p.306" + (", 5.0 §2.11.2 p.153"
|
|
2330
|
+
if v == 50 else "")
|
|
2331
|
+
+ "), so a check that t's "
|
|
2332
|
+
"address is a multiple of 64 is a deterministic "
|
|
2333
|
+
"oracle of the original; it is not written. A "
|
|
2334
|
+
"mutant without the trait could still be 64-byte "
|
|
2335
|
+
"aligned (any alignment conforms then), AFAR "
|
|
2336
|
+
"rejects uses_allocators and CCE 19's mutant without "
|
|
2337
|
+
"the trait is an internal compiler error "
|
|
2338
|
+
"(final-review re-audit)")
|
|
2339
|
+
return s
|
|
2340
|
+
if clause == "defaultmap":
|
|
2341
|
+
if feat == "character":
|
|
2342
|
+
return defaultmap_character_prog(v)
|
|
2343
|
+
if feat in (None, "scalar"):
|
|
2344
|
+
return defaultmap_prog(v, "tofrom", "scalar")
|
|
2345
|
+
if feat in ("aggregate", "allocatable", "pointer"):
|
|
2346
|
+
# to: an array of the category is not copied back; the
|
|
2347
|
+
# mutation's other category leaves it tofrom
|
|
2348
|
+
return defaultmap_prog(v, "to", feat)
|
|
2349
|
+
if feat == "all":
|
|
2350
|
+
# to: all: the array w written on
|
|
2351
|
+
# the device is not copied back (copy mode); the mutation all ->
|
|
2352
|
+
# scalar leaves arrays tofrom
|
|
2353
|
+
return defaultmap_prog(v, "to", feat)
|
|
2354
|
+
if feat == "default":
|
|
2355
|
+
return defaultmap_prog(v, feat).no_oracle(
|
|
2356
|
+
"equivalent_default", "defaultmap(default) is the behaviour "
|
|
2357
|
+
"without the clause", "defaultmap_default")
|
|
2358
|
+
return defaultmap_prog(v, feat)
|
|
2359
|
+
if clause == "depend":
|
|
2360
|
+
if feat is None:
|
|
2361
|
+
return depend_prog(v, "out", "in")
|
|
2362
|
+
if feat == "in":
|
|
2363
|
+
s = depend_prog(v, "out", "in")
|
|
2364
|
+
s.focus = "depend(in: w)"
|
|
2365
|
+
return s
|
|
2366
|
+
if feat in ("out", "inout", "inoutset", "mutexinoutset"):
|
|
2367
|
+
s = depend_prog(v, feat, "in", writer=True)
|
|
2368
|
+
s.focus = f"depend({feat}: w)"
|
|
2369
|
+
return s
|
|
2370
|
+
if feat in ("iterator", "depobj"):
|
|
2371
|
+
return depend_prog(v, feat, "in")
|
|
2372
|
+
return None
|
|
2373
|
+
if clause == "device":
|
|
2374
|
+
if feat == "ancestor":
|
|
2375
|
+
return ancestor_prog(v)
|
|
2376
|
+
c = {None: "device(dev)", "device_num": "device(device_num: dev)"
|
|
2377
|
+
}.get(feat)
|
|
2378
|
+
if c is None:
|
|
2379
|
+
return None
|
|
2380
|
+
# the last device, not the default one (0): the region must report
|
|
2381
|
+
# it (omp_get_device_num); with one device both are device 0
|
|
2382
|
+
s = team_param(v, c, ["if (i == 1) dn = omp_get_device_num()"],
|
|
2383
|
+
["integer :: dn"], ["(dn == dev)"],
|
|
2384
|
+
extra_decls=["integer :: dev"],
|
|
2385
|
+
init=["dev = max(0, omp_get_num_devices() - 1)"])
|
|
2386
|
+
if feat == "device_num":
|
|
2387
|
+
s.no_oracle("equivalent_default", "device_num is the default "
|
|
2388
|
+
"device-modifier: device(n) is device(device_num: "
|
|
2389
|
+
"n)", "device_num_default")
|
|
2390
|
+
return s
|
|
2391
|
+
if clause == "if" and feat is None:
|
|
2392
|
+
return if_prog(v)
|
|
2393
|
+
if clause == "nowait":
|
|
2394
|
+
c = {None: "nowait", "do_not_synchronize": "nowait(.true.)"}.get(feat)
|
|
2395
|
+
if c is None:
|
|
2396
|
+
return None
|
|
2397
|
+
s = combined(v, c, post=[omp("taskwait")])
|
|
2398
|
+
return s.no_oracle("implementation_defined", NOWAIT_WHY,
|
|
2399
|
+
"target_nowait") \
|
|
2400
|
+
if feat is None else s.no_oracle(
|
|
2401
|
+
"equivalent_default", "nowait(.true.) is nowait (the "
|
|
2402
|
+
"argument defaults to true), and nowait itself has no "
|
|
2403
|
+
"separating oracle (an implementation may run the target "
|
|
2404
|
+
"task synchronously)", "nowait_true_default")
|
|
2405
|
+
if clause == "thread_limit" and feat is None:
|
|
2406
|
+
# at most 16 threads per team (both toolchains use more without)
|
|
2407
|
+
return team_param(v, "thread_limit(16)",
|
|
2408
|
+
["if (i == 1) nthr = omp_get_num_threads()"],
|
|
2409
|
+
["integer :: nthr"],
|
|
2410
|
+
["(nthr >= 1 .and. nthr <= 16)"])
|
|
2411
|
+
if clause == "num_teams":
|
|
2412
|
+
c = {None: "num_teams(4)", "lower_bound": "num_teams(2: 4)"}.get(feat)
|
|
2413
|
+
if c is None:
|
|
2414
|
+
return None
|
|
2415
|
+
# without a lower bound it equals the upper bound: exactly 4 teams
|
|
2416
|
+
# (5.2 §10.2.1, 6.0 §12.2.1)
|
|
2417
|
+
want = "(nt == 4)" if feat is None else "(nt >= 2 .and. nt <= 4)"
|
|
2418
|
+
if v == 50:
|
|
2419
|
+
# 5.0: the clause is an upper bound ('less than or
|
|
2420
|
+
# equal to the value specified in the num_teams clause', 5.0
|
|
2421
|
+
# §2.7 p.83), so the program asks for one team, which leaves
|
|
2422
|
+
# exactly one: AFAR creates two teams for this loop without the
|
|
2423
|
+
# clause (CCE 19 one), so the check can fail (a bound of 4 was
|
|
2424
|
+
# met without the clause on both, audit)
|
|
2425
|
+
c, want = "num_teams(1)", "(nt == 1)"
|
|
2426
|
+
s = team_param(v, c, ["if (i == 1) nt = omp_get_num_teams()"],
|
|
2427
|
+
["integer :: nt"], [want])
|
|
2428
|
+
if feat == "lower_bound":
|
|
2429
|
+
s.no_oracle("implementation_defined", "the number of teams is "
|
|
2430
|
+
"an implementation-defined value between the lower "
|
|
2431
|
+
"and the upper bound (5.2 §10.2.1, 6.0 §12.2.1); "
|
|
2432
|
+
"without the lower bound exactly the upper bound "
|
|
2433
|
+
"(4) teams are created, which is also a conforming "
|
|
2434
|
+
"execution of the form with the lower bound 2",
|
|
2435
|
+
"num_teams_bounds", checked=True)
|
|
2436
|
+
return s
|
|
2437
|
+
if clause == "dist_schedule":
|
|
2438
|
+
c = {None: "dist_schedule(static)", "static": "dist_schedule(static)",
|
|
2439
|
+
"chunk_size": "dist_schedule(static, 4)"}.get(feat)
|
|
2440
|
+
if c is None:
|
|
2441
|
+
return None
|
|
2442
|
+
# 4 teams (num_teams(4)); each iteration records its team: one
|
|
2443
|
+
# chunk of about n/4 iterations per team, or chunks of 4
|
|
2444
|
+
# round-robin
|
|
2445
|
+
check = blocked("team", "n", "nt", "dok") if feat != "chunk_size" \
|
|
2446
|
+
else round_robin("team", "n", 4, "nt", "dok")
|
|
2447
|
+
s = team_param(v, f"num_teams(4) {c}",
|
|
2448
|
+
["team(i) = omp_get_team_num()",
|
|
2449
|
+
"if (i == 1) nt = omp_get_num_teams()"],
|
|
2450
|
+
["integer :: team(n), nt"], ["(nt >= 1)", "dok"],
|
|
2451
|
+
extra_decls=["logical :: dok"], post=check)
|
|
2452
|
+
s.focus = c
|
|
2453
|
+
if feat != "chunk_size":
|
|
2454
|
+
# With the corrected oracle (idle teams allowed) the
|
|
2455
|
+
# implementation-defined default distribution of AFAR and CCE
|
|
2456
|
+
# also passes, so the clause cannot be told apart from its
|
|
2457
|
+
# absence in one run; the check still tests what the clause
|
|
2458
|
+
# requires (checked: a failure is a real one)
|
|
2459
|
+
s.no_oracle("implementation_defined", STATIC_NO_CHUNK_WHY,
|
|
2460
|
+
"dist_schedule_default", checked=True)
|
|
2461
|
+
return s
|
|
2462
|
+
if clause == "replayable":
|
|
2463
|
+
c = {None: "replayable", "replayable_expression": "replayable(.true.)"
|
|
2464
|
+
}.get(feat)
|
|
2465
|
+
if c is None:
|
|
2466
|
+
return None
|
|
2467
|
+
s = combined(v, c, key="target")
|
|
2468
|
+
return s.no_oracle("equivalent_default", "replayable(.true.) is "
|
|
2469
|
+
"replayable (the argument defaults to true)",
|
|
2470
|
+
"replayable_true_default") if feat else s
|
|
2471
|
+
if clause == "transparent":
|
|
2472
|
+
c = {None: "transparent", "impex_type": "transparent(omp_impex)"
|
|
2473
|
+
}.get(feat)
|
|
2474
|
+
return data_prog(v, c, **_UNCHECKED_CLAUSE("transparent")) \
|
|
2475
|
+
if c else None
|
|
2476
|
+
if clause == "nogroup":
|
|
2477
|
+
c = {None: "nogroup", "do_not_synchronize": "nogroup(.true.)"}.get(
|
|
2478
|
+
feat)
|
|
2479
|
+
return data_prog(v, c, **_UNCHECKED_CLAUSE("nogroup")) if c else None
|
|
2480
|
+
if clause in ("use_device_ptr", "is_device_ptr") and feat is None:
|
|
2481
|
+
s = device_ptr_prog(v)
|
|
2482
|
+
s.focus = "use_device_ptr(pa, pb)" if clause == "use_device_ptr" \
|
|
2483
|
+
else "is_device_ptr(pa, pb)"
|
|
2484
|
+
return s
|
|
2485
|
+
if clause in ("use_device_addr", "has_device_addr") and feat is None:
|
|
2486
|
+
s = device_addr_prog(v)
|
|
2487
|
+
s.focus = f"{clause}(a, b)"
|
|
2488
|
+
if clause == "has_device_addr":
|
|
2489
|
+
# Removing has_device_addr leaves the result unchanged on AFAR
|
|
2490
|
+
# and CCE 19 in both memory modes (they pass the device address
|
|
2491
|
+
# through an implicit map), and a device-side c_loc comparison
|
|
2492
|
+
# did not separate the two either: no check found.
|
|
2493
|
+
s.observable = False
|
|
2494
|
+
s.note = "has_device_addr itself is not checked"
|
|
2495
|
+
s.header_notes.append(
|
|
2496
|
+
"clause not checked: has_device_addr (removing it leaves the "
|
|
2497
|
+
"result unchanged on AFAR and CCE 19)")
|
|
2498
|
+
s.unverified("oracle_not_written", "re-categorised "
|
|
2499
|
+
"(was implementation_defined): the spec fixes what "
|
|
2500
|
+
"has_device_addr does (its list items 'may be "
|
|
2501
|
+
"directly accessed from a target device', 5.2 "
|
|
2502
|
+
"§5.4.9 p.122; their storage location is the same "
|
|
2503
|
+
"inside the construct as for the original list "
|
|
2504
|
+
"item, 6.0 §7.5.9 p.238), so a device-side check "
|
|
2505
|
+
"that c_loc(a) in the region equals a's address in "
|
|
2506
|
+
"the use_device_addr region (copy mode) is a "
|
|
2507
|
+
"deterministic oracle of the original; it is not "
|
|
2508
|
+
"written. Without the clause the program maps "
|
|
2509
|
+
"storage that 'may not be accessible from the host "
|
|
2510
|
+
"device' (5.2 §5.4.10 p.123), so no conforming "
|
|
2511
|
+
"mutant exists: AFAR and CCE 19 translate it back "
|
|
2512
|
+
"to the same device storage (9c probes)")
|
|
2513
|
+
return s
|
|
2514
|
+
return declare_clause(clause, feat, v)
|
|
2515
|
+
|
|
2516
|
+
|
|
2517
|
+
def declare_clause(clause, feat, v) -> Src | None:
|
|
2518
|
+
"""Clauses of declare target."""
|
|
2519
|
+
if clause == "enter":
|
|
2520
|
+
if feat is None:
|
|
2521
|
+
return declare_var_prog(v, omp("declare target", "enter(dv)"),
|
|
2522
|
+
focus="enter(dv)")
|
|
2523
|
+
if feat == "automap":
|
|
2524
|
+
# pv is an array: without the directive it is implicitly mapped
|
|
2525
|
+
# tofrom, so the device write of the first region comes back
|
|
2526
|
+
# to the host; with it pv is present and the host copy stays
|
|
2527
|
+
# unchanged. Copy mode only (shared storage hides the copy);
|
|
2528
|
+
# mutation-checked on AFAR (CCE 19 rejects enter(automap:)).
|
|
2529
|
+
s = declare_prog(v, focus="enter(automap: pv)",
|
|
2530
|
+
body=("b(i) = 2*(pv(i) - 4) + 1",))
|
|
2531
|
+
s.module[2:2] = ["integer, allocatable :: pv(:)",
|
|
2532
|
+
omp("declare target", "enter(automap: pv)")]
|
|
2533
|
+
s.shared_setup()
|
|
2534
|
+
s.init += ["allocate(pv(n))", "pv = a",
|
|
2535
|
+
omp(spell("target update", v), "to(pv)"),
|
|
2536
|
+
omp("target")] + seq_loop(("pv(i) = pv(i) + 4",)) + [
|
|
2537
|
+
omp("end target")]
|
|
2538
|
+
s.post += ["if (.not. shared_mem) pres = pres .and. all(pv == a)",
|
|
2539
|
+
"deallocate(pv)"]
|
|
2540
|
+
return s
|
|
2541
|
+
return None
|
|
2542
|
+
if clause == "to" and feat is None:
|
|
2543
|
+
return declare_prog(v, var=omp("declare target", "to(dv)"),
|
|
2544
|
+
body=("b(i) = 2*a(i) + dv",), focus="to(dv)")
|
|
2545
|
+
if clause == "link" and feat is None:
|
|
2546
|
+
return declare_var_prog(v, omp("declare target", "link(dv)"),
|
|
2547
|
+
focus="link(dv)", data=True)
|
|
2548
|
+
if clause == "local" and feat is None:
|
|
2549
|
+
return declare_var_prog(v, omp("declare target", "local(dv)"),
|
|
2550
|
+
focus="local(dv)")
|
|
2551
|
+
if clause == "device_type":
|
|
2552
|
+
kind = feat or "any"
|
|
2553
|
+
if kind == "host":
|
|
2554
|
+
s = declare_prog(v, focus="device_type(host)", observable=False,
|
|
2555
|
+
note="device_type(host): the function is only "
|
|
2556
|
+
"called on the host")
|
|
2557
|
+
s.module += ["contains"] if "contains" not in s.module else []
|
|
2558
|
+
s.module += [" integer function g(x)",
|
|
2559
|
+
" " + omp("declare target", proc_list(v, "g"),
|
|
2560
|
+
"device_type(host)"),
|
|
2561
|
+
" integer, intent(in) :: x", " g = 2*x + 1",
|
|
2562
|
+
" end function g"]
|
|
2563
|
+
s.init += ["do i = 1, n", " ref(i) = g(i)", "end do"]
|
|
2564
|
+
return s
|
|
2565
|
+
if kind not in ("any", "nohost"):
|
|
2566
|
+
return None
|
|
2567
|
+
return declare_prog(v, func=omp("declare target", proc_list(v),
|
|
2568
|
+
f"device_type({kind})"),
|
|
2569
|
+
body=("b(i) = f(a(i))",),
|
|
2570
|
+
focus=f"device_type({kind})", observable=False,
|
|
2571
|
+
note=IMPLICIT_PROCEDURE_NOTE)
|
|
2572
|
+
if clause == "indirect":
|
|
2573
|
+
c = {None: "indirect", "invoked_by_fptr": "indirect(.true.)"}.get(feat)
|
|
2574
|
+
if c is None:
|
|
2575
|
+
return None
|
|
2576
|
+
# enter at both versions: AFAR rejects indirect with to ("The
|
|
2577
|
+
# INDIRECT clause cannot be used without the ENTER clause"), CCE 19
|
|
2578
|
+
# rejects enter (a listed confound in offload.engine.report)
|
|
2579
|
+
return declare_prog(v, func=omp("declare target", "enter(f)", c),
|
|
2580
|
+
body=("b(i) = f(a(i))",), focus=c,
|
|
2581
|
+
observable=False, note="f is called directly; "
|
|
2582
|
+
"no indirect call is checked")
|
|
2583
|
+
return None
|