offtarget 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- offload/__init__.py +10 -0
- offload/__main__.py +5 -0
- offload/_bundle.py +74 -0
- offload/_root/catalog/acc/claims.yaml +197 -0
- offload/_root/catalog/acc/clauses.yaml +391 -0
- offload/_root/catalog/acc/no_oracle.yaml +89 -0
- offload/_root/catalog/acc/out_of_scope.yaml +17 -0
- offload/_root/catalog/acc/rules.yaml +433 -0
- offload/_root/catalog/acc/semantic_atoms.yaml +27 -0
- offload/_root/catalog/acc/skeleton.yaml +1567 -0
- offload/_root/catalog/acc/spec_overrides.yaml +72 -0
- offload/_root/catalog/later_forms.yaml +76 -0
- offload/_root/catalog/machines.yaml +218 -0
- offload/_root/catalog/omp/claims.yaml +2047 -0
- offload/_root/catalog/omp/clauses.yaml +914 -0
- offload/_root/catalog/omp/derivations.yaml +177 -0
- offload/_root/catalog/omp/memory_modes.yaml +40 -0
- offload/_root/catalog/omp/no_oracle.yaml +373 -0
- offload/_root/catalog/omp/omp50_delta.yaml +377 -0
- offload/_root/catalog/omp/out_of_scope.yaml +64 -0
- offload/_root/catalog/omp/port_equivalence.yaml +352 -0
- offload/_root/catalog/omp/rules.yaml +3131 -0
- offload/_root/catalog/omp/rules_coverage.yaml +9939 -0
- offload/_root/catalog/omp/semantic_atoms.yaml +34 -0
- offload/_root/catalog/omp/skeleton.yaml +9181 -0
- offload/_root/catalog/omp/spec_overrides.yaml +932 -0
- offload/_root/catalog/toolchains.yaml +472 -0
- offload/_root/catalog/vendor_claims_generic.yaml +40925 -0
- offload/_root/data/canuse/b.json +1 -0
- offload/_root/data/canuse/c.json +1 -0
- offload/_root/data/canuse/columns.json +1 -0
- offload/_root/data/canuse/d.json +1 -0
- offload/_root/data/canuse/f.json +1 -0
- offload/_root/data/canuse/index.json +1 -0
- offload/_root/data/canuse/p.json +1 -0
- offload/_root/data/compilers/afar.json +1 -0
- offload/_root/data/compilers/cce19.json +1 -0
- offload/_root/data/compilers/cce20.json +1 -0
- offload/_root/data/compilers/cce21.json +1 -0
- offload/_root/data/compilers/cce21_4.json +1 -0
- offload/_root/data/compilers/gcc.json +1 -0
- offload/_root/data/compilers/gcc_sm80.json +1 -0
- offload/_root/data/compilers/nvhpc.json +1 -0
- offload/_root/data/compilers/nvhpc269.json +1 -0
- offload/_root/data/findings/AUTHORING.md +107 -0
- offload/_root/data/findings/README.md +33 -0
- offload/_root/data/findings/afar-c-error-directive-in-target-link.c +37 -0
- offload/_root/data/findings/afar-c-lastprivate-distribute-parallel-for-small-type.c +56 -0
- offload/_root/data/findings/afar-c-teams-distribute-reduction-first-team.c +59 -0
- offload/_root/data/findings/afar-c-update-iterator-copy-mode.c +45 -0
- offload/_root/data/findings/afar-c-xnack1-target-is-present.c +31 -0
- offload/_root/data/findings/afar-cce19-character-defaultmap.f90 +27 -0
- offload/_root/data/findings/afar-cce19-task-in-target-ice.f90 +23 -0
- offload/_root/data/findings/afar-clang-requires-device-safesync-hang.c +24 -0
- offload/_root/data/findings/afar-flang-firstprivate-map-two-targets.f90 +34 -0
- offload/_root/data/findings/afar-flang-loop-bind-teams-reduction.f90 +27 -0
- offload/_root/data/findings/afar-flang-task-reduction-max.f90 +31 -0
- offload/_root/data/findings/afar-flang-thread-limit-target-parallel-do.f90 +22 -0
- offload/_root/data/findings/afar-linear-ref-omp50.f90 +29 -0
- offload/_root/data/findings/afar-loop-bind-thread-in-teams.f90 +44 -0
- offload/_root/data/findings/afar-nontemporal-assumed-shape-ice.f90 +38 -0
- offload/_root/data/findings/afar-udr-derived-type-link.f90 +41 -0
- offload/_root/data/findings/afar-xnack1-atomic-min-nested-parallel.f90 +53 -0
- offload/_root/data/findings/afar-xnack1-present-not-enforced.f90 +36 -0
- offload/_root/data/findings/afar-xnack1-target-is-present.f90 +47 -0
- offload/_root/data/findings/apps/afar-cce-teams-private-array-nested-parallel-wrong.f90 +100 -0
- offload/_root/data/findings/apps/cce20-device-link-infer-address-spaces-crash.f90 +159 -0
- offload/_root/data/findings/apps/cce21-device-link-agpr-copy-mfma-assert.f90 +343 -0
- offload/_root/data/findings/apps/cce21-private-array-loaded-subscript-wrong.f90 +113 -0
- offload/_root/data/findings/apps/controls/bubble-simple-rddot.f90 +284 -0
- offload/_root/data/findings/apps/controls/bubble-two-substeps.f90 +287 -0
- offload/_root/data/findings/apps/controls/cbc-inline.f90 +97 -0
- offload/_root/data/findings/apps/controls/cbc-integers.f90 +91 -0
- offload/_root/data/findings/apps/controls/cbc-only-density.f90 +97 -0
- offload/_root/data/findings/apps/controls/cbc-only-velocity.f90 +97 -0
- offload/_root/data/findings/apps/controls/gcc-acc-c-tile-control.c +26 -0
- offload/_root/data/findings/apps/controls/gcc-acc-tile-control.f90 +29 -0
- offload/_root/data/findings/apps/controls/private-array-subscript-probes.f90 +45 -0
- offload/_root/data/findings/apps/controls/teams-private-array-read-par.f90 +31 -0
- offload/_root/data/findings/apps/controls/teams-private-array-seq.f90 +30 -0
- offload/_root/data/findings/apps/controls/teams-private-array-shared.f90 +34 -0
- offload/_root/data/findings/apps/controls/teams-private-array-split.f90 +36 -0
- offload/_root/data/findings/apps/controls/teams-private-array-write-par.f90 +31 -0
- offload/_root/data/findings/apps/gcc-acc-c-tile-partial-tiles-wrong.c +60 -0
- offload/_root/data/findings/apps/gcc-acc-tile-partial-tiles-wrong.f90 +64 -0
- offload/_root/data/findings/catalog/afar-c-complex-reduction-link.c +66 -0
- offload/_root/data/findings/catalog/afar-c-critical-hint-link.c +68 -0
- offload/_root/data/findings/catalog/afar-c-ordered-doacross-link.c +62 -0
- offload/_root/data/findings/catalog/afar-c-reduction-task-link.c +81 -0
- offload/_root/data/findings/catalog/afar-flang-absent-optional-target-crash.f90 +76 -0
- offload/_root/data/findings/catalog/afar-flang-critical-target-parallel-wrong.f90 +74 -0
- offload/_root/data/findings/catalog/afar-flang-dist-schedule-chunk.f90 +80 -0
- offload/_root/data/findings/catalog/afar-flang-linear-parallel-ice.f90 +68 -0
- offload/_root/data/findings/catalog/afar-flang-named-critical-link.f90 +67 -0
- offload/_root/data/findings/catalog/afar-flang-ordered-wrong.f90 +78 -0
- offload/_root/data/findings/catalog/afar-flang-reduction-minus-rejected.f90 +77 -0
- offload/_root/data/findings/catalog/afar-flang-reduction-task-ice.f90 +68 -0
- offload/_root/data/findings/catalog/afar-flang-sections-in-target-ice.f90 +67 -0
- offload/_root/data/findings/catalog/afar-flang-workdistribute-ice.f90 +71 -0
- offload/_root/data/findings/catalog/c-allocate-const-mem-firstprivate-wrong.c +79 -0
- offload/_root/data/findings/catalog/cce-acc-allocate-in-compute-crash.f90 +65 -0
- offload/_root/data/findings/catalog/cce-acc-character-dummy-routine-ice.f90 +81 -0
- offload/_root/data/findings/catalog/cce-acc-loop-seq-in-routine-wrong.f90 +81 -0
- offload/_root/data/findings/catalog/cce-acc-stop-in-compute-crash.f90 +62 -0
- offload/_root/data/findings/catalog/cce-allocators-stack-ice.f90 +81 -0
- offload/_root/data/findings/catalog/cce-atomic-default-mem-order-acquire-release-rejected.f90 +61 -0
- offload/_root/data/findings/catalog/cce-c-allocate-align-link.c +83 -0
- offload/_root/data/findings/catalog/cce-c-allocate-allocator-modifier-rejected.c +77 -0
- offload/_root/data/findings/catalog/cce-c-device-type-directive-name-ice.c +70 -0
- offload/_root/data/findings/catalog/cce-c-dist-schedule-chunk.c +81 -0
- offload/_root/data/findings/catalog/cce-c-f-pointer-device-link.f90 +70 -0
- offload/_root/data/findings/catalog/cce-c-lastprivate-parallel-in-seq-loop-wrong.c +64 -0
- offload/_root/data/findings/catalog/cce-c-long-double-ice.c +61 -0
- offload/_root/data/findings/catalog/cce-c-map-iterator-ice.c +92 -0
- offload/_root/data/findings/catalog/cce-c-motion-iterator-rejected.c +68 -0
- offload/_root/data/findings/catalog/cce-c-ordered-doacross-link.c +64 -0
- offload/_root/data/findings/catalog/cce-c-ordered-link.c +77 -0
- offload/_root/data/findings/catalog/cce-c-recursive-function-ice.c +62 -0
- offload/_root/data/findings/catalog/cce-c-reduction-inscan-ice.c +67 -0
- offload/_root/data/findings/catalog/cce-c-reduction-task-link.c +73 -0
- offload/_root/data/findings/catalog/cce-c-sections-in-target-wrong.c +70 -0
- offload/_root/data/findings/catalog/cce-c-single-masked-task-link.c +66 -0
- offload/_root/data/findings/catalog/cce-c-task-in-target-link.c +75 -0
- offload/_root/data/findings/catalog/cce-c-uses-allocators-traits-rejected.c +71 -0
- offload/_root/data/findings/catalog/cce-flang-allocate-const-mem-ice.f90 +67 -0
- offload/_root/data/findings/catalog/cce-flang-allocate-in-target-crash.f90 +68 -0
- offload/_root/data/findings/catalog/cce-flang-lastprivate-parallel-in-seq-loop-wrong.f90 +83 -0
- offload/_root/data/findings/catalog/cce-flang-stop-in-target-crash.f90 +64 -0
- offload/_root/data/findings/catalog/cce-iterator-link.f90 +67 -0
- offload/_root/data/findings/catalog/cce-linear-lastprivate-ice.f90 +69 -0
- offload/_root/data/findings/catalog/cce-nohost-link.f90 +90 -0
- offload/_root/data/findings/catalog/cce-reduction-inscan-ice.f90 +66 -0
- offload/_root/data/findings/catalog/cce-reduction-task-link.f90 +76 -0
- offload/_root/data/findings/catalog/cce19-reduction-task-addrspacecast-ice.f90 +77 -0
- offload/_root/data/findings/catalog/cce20-c-defaultmap-private-storage-rejected.c +85 -0
- offload/_root/data/findings/catalog/cce20-map-self-rejected.f90 +64 -0
- offload/_root/data/findings/catalog/cce21-c-declare-target-local-rejected.c +73 -0
- offload/_root/data/findings/catalog/cce21-c-target-loop-ice.c +77 -0
- offload/_root/data/findings/catalog/defaultmap-present-private-array-abort.f90 +73 -0
- offload/_root/data/findings/catalog/nvhpc-acc-associate-illegal-address.f90 +66 -0
- offload/_root/data/findings/catalog/nvhpc-c-function-pointer-kernel-missing.c +68 -0
- offload/_root/data/findings/catalog/nvhpc-c-long-double-kernel-missing.c +64 -0
- offload/_root/data/findings/catalog/nvhpc-c-static-local-kernel-missing.c +73 -0
- offload/_root/data/findings/catalog/nvhpc-c-vla-kernel-missing.c +63 -0
- offload/_root/data/findings/catalog/nvhpc-flang-type-bound-kernel-missing.f90 +86 -0
- offload/_root/data/findings/cce-acc-init-set-runtime-assert.f90 +35 -0
- offload/_root/data/findings/cce-acc-reduction-200-int8-scalars-wrong.f90 +1278 -0
- offload/_root/data/findings/cce-acc-routine-bind-module-ice.f90 +35 -0
- offload/_root/data/findings/cce-acc-self-condition-ignored.f90 +17 -0
- offload/_root/data/findings/cce-c-dist-schedule-segv.c +34 -0
- offload/_root/data/findings/cce-c-map-delete-refcount.c +47 -0
- offload/_root/data/findings/cce-c-map-present-assert.c +45 -0
- offload/_root/data/findings/cce-c-openmp45-device-link.c +34 -0
- offload/_root/data/findings/cce-c-parallel-for-in-target.c +73 -0
- offload/_root/data/findings/cce-defaultmap-tofrom-explicit-map.f90 +92 -0
- offload/_root/data/findings/cce-mapper-next-map-copy-back.f90 +48 -0
- offload/_root/data/findings/cce-trim-merge-output-one-character.f90 +40 -0
- offload/_root/data/findings/cce-uses-allocators-traits-omp50.f90 +28 -0
- offload/_root/data/findings/cce19-acc-if-false-link.f90 +17 -0
- offload/_root/data/findings/cce19-c-reduction-63-scalars-one-wrong.c +235 -0
- offload/_root/data/findings/cce19-target-parallel-guarded-loop.f90 +35 -0
- offload/_root/data/findings/cce19-target-parallel-single-ice.f90 +38 -0
- offload/_root/data/findings/cce21-alloc-2e32-sum-wrong.f90 +68 -0
- offload/_root/data/findings/cce21-defaultmap-aggregate-flag.f90 +52 -0
- offload/_root/data/findings/cce21-in-reduction-assumed-shape-write-lost.f90 +61 -0
- offload/_root/data/findings/cce21-in-reduction-taskwait-link.f90 +59 -0
- offload/_root/data/findings/cce21-linear-modifier-default-none.f90 +59 -0
- offload/_root/data/findings/clang-c-declare-target-to-omp52.c +50 -0
- offload/_root/data/findings/clang-c-linear-directive-name-hang.c +30 -0
- offload/_root/data/findings/clang-c-map-iterator-omp51.c +51 -0
- offload/_root/data/findings/clang-c-shared-declare-target-combined.c +58 -0
- offload/_root/data/findings/dataenv/cce-value-dummy-target-write-visible.f90 +74 -0
- offload/_root/data/findings/defaultmap-none-loop-variable.f90 +69 -0
- offload/_root/data/findings/fortran-loop-bind-thread-in-parallel.f90 +53 -0
- offload/_root/data/findings/fortran-schedule-static-in-target.f90 +55 -0
- offload/_root/data/findings/fresh/cce-common-block-name-in-map-rejected.f90 +47 -0
- offload/_root/data/findings/fresh/cce-if-stop-in-target-ice-one-condition.f90 +38 -0
- offload/_root/data/findings/fresh/cce-if-stop-in-target-ice.f90 +43 -0
- offload/_root/data/findings/fresh/cce-list-directed-print-in-target-copy-crash.f90 +40 -0
- offload/_root/data/findings/fresh/cce19-acc-automatic-array-copyout-copy-crash.f90 +56 -0
- offload/_root/data/findings/fresh/cce21-combined-if-runtime-expr-loop-skipped.f90 +57 -0
- offload/_root/data/findings/fresh/nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.f90 +67 -0
- offload/_root/data/findings/fresh/nvhpc-teams-loop-module-allocatable-assign-fort2-crash.f90 +73 -0
- offload/_root/data/findings/fuzz/1131-afar.f90 +48 -0
- offload/_root/data/findings/fuzz/1391-cce20.f90 +40 -0
- offload/_root/data/findings/fuzz/14-cce20.f90 +36 -0
- offload/_root/data/findings/fuzz/1505-cce20.f90 +88 -0
- offload/_root/data/findings/fuzz/1540-afar.f90 +44 -0
- offload/_root/data/findings/fuzz/158-afar.f90 +63 -0
- offload/_root/data/findings/fuzz/16-cce20.f90 +39 -0
- offload/_root/data/findings/fuzz/1644-cce21.f90 +31 -0
- offload/_root/data/findings/fuzz/165-cce19.f90 +52 -0
- offload/_root/data/findings/fuzz/1749-cce20.f90 +34 -0
- offload/_root/data/findings/fuzz/1936-afar.f90 +61 -0
- offload/_root/data/findings/fuzz/1978-cce19.f90 +41 -0
- offload/_root/data/findings/fuzz/2014-cce19.f90 +46 -0
- offload/_root/data/findings/fuzz/2224-cce19.f90 +30 -0
- offload/_root/data/findings/fuzz/2334-cce19.f90 +57 -0
- offload/_root/data/findings/fuzz/536-cce19.f90 +38 -0
- offload/_root/data/findings/fuzz/591-cce19.f90 +54 -0
- offload/_root/data/findings/fuzz/650-afar.f90 +58 -0
- offload/_root/data/findings/fuzz/698-afar.f90 +68 -0
- offload/_root/data/findings/fuzz/700-afar.f90 +46 -0
- offload/_root/data/findings/fuzz/775-cce21.f90 +33 -0
- offload/_root/data/findings/fuzz/87-afar.f90 +47 -0
- offload/_root/data/findings/fuzz/916-afar.f90 +67 -0
- offload/_root/data/findings/fuzz/958-cce20.f90 +44 -0
- offload/_root/data/findings/fuzz/98-cce19.f90 +40 -0
- offload/_root/data/findings/fuzz/README.md +236 -0
- offload/_root/data/findings/fuzz/b1-121-afar.f90 +47 -0
- offload/_root/data/findings/fuzz/b1-183-afar.f90 +55 -0
- offload/_root/data/findings/fuzz/b1-22-cce20.f90 +38 -0
- offload/_root/data/findings/fuzz/b1-231-afar.f90 +56 -0
- offload/_root/data/findings/fuzz/b1-268-afar.f90 +46 -0
- offload/_root/data/findings/fuzz/b2-183-afar.f90 +57 -0
- offload/_root/data/findings/fuzz/b2-209-cce19.f90 +51 -0
- offload/_root/data/findings/fuzz/b2-213-afar.f90 +63 -0
- offload/_root/data/findings/fuzz/b2-52-afar.f90 +56 -0
- offload/_root/data/findings/fuzz/b2-68-afar.f90 +54 -0
- offload/_root/data/findings/fuzz/b2-85-cce20.f90 +55 -0
- offload/_root/data/findings/fuzz/b3-188-cce19.f90 +90 -0
- offload/_root/data/findings/fuzz/b3-190-cce21.f90 +88 -0
- offload/_root/data/findings/fuzz/b3-197-cce19.f90 +77 -0
- offload/_root/data/findings/fuzz/b3-250-cce20.f90 +89 -0
- offload/_root/data/findings/fuzz/b3-278-cce19.f90 +70 -0
- offload/_root/data/findings/fuzz/b3-45-cce21.f90 +83 -0
- offload/_root/data/findings/fuzz/b3-54-afar.f90 +67 -0
- offload/_root/data/findings/fuzz/b3-61-cce20.f90 +100 -0
- offload/_root/data/findings/fuzz/b3-9-afar.f90 +100 -0
- offload/_root/data/findings/fuzz/b3-9-cce21.f90 +103 -0
- offload/_root/data/findings/fuzz/b3-94-afar.f90 +80 -0
- offload/_root/data/findings/fuzz/b4-39-afar.f90 +141 -0
- offload/_root/data/findings/fuzz/b4-86-cce19.f90 +71 -0
- offload/_root/data/findings/fuzz/controls/gcc-complex-pointer-store-control.f90 +28 -0
- offload/_root/data/findings/fuzz/controls/gcc-declare-reduction-module-control.f90 +29 -0
- offload/_root/data/findings/fuzz/controls/gcc-target-simd-map-nontemporal-control.f90 +21 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-mapped-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-unreferenced-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-default-none-removed-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-default-shared-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-firstprivate-removed-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-reduction-mapped-control.f90 +17 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-removed-control.f90 +17 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-tofrom-lastprivate-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-depend-explicit-map-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-depend-removed-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-end-distribute-control.f90 +16 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-num-threads-removed-control.f90 +26 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-removed-control.f90 +22 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-distribute-simd-no-end-directive-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-explicit-length-character-dummy-control.f90 +18 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-array-value-control.f90 +23 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-distribute-control.f90 +20 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-map-always-first-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-map-always-removed-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-num-threads-removed-control.f90 +12 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-firstprivate-dropped-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-single-nowait-control.f90 +13 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-split-control.f90 +15 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-collapse-split-control.f90 +21 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-end-directive-control.f90 +14 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-no-collapse-control.f90 +19 -0
- offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-split-no-end-directive-control.f90 +15 -0
- offload/_root/data/findings/fuzz/gcc-declare-reduction-module-not-found.f90 +66 -0
- offload/_root/data/findings/fuzz/gcc-strided-target-update-rejected.f90 +58 -0
- offload/_root/data/findings/fuzz/gcc-target-simd-map-nontemporal-rejected.f90 +54 -0
- offload/_root/data/findings/fuzz/nvhpc-allocatable-component-implicit-map-illegal-address.f90 +84 -0
- offload/_root/data/findings/fuzz/nvhpc-assumed-length-character-dummy-map-ice.f90 +69 -0
- offload/_root/data/findings/fuzz/nvhpc-default-none-reduction-lastprivate-fort1-crash.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-defaultmap-firstprivate-lastprivate-ice.f90 +71 -0
- offload/_root/data/findings/fuzz/nvhpc-defaultmap-to-scalar-reduction-lost.f90 +82 -0
- offload/_root/data/findings/fuzz/nvhpc-depend-implicit-map-illegal-address.f90 +81 -0
- offload/_root/data/findings/fuzz/nvhpc-distribute-parallel-num-threads-writes-lost.f90 +79 -0
- offload/_root/data/findings/fuzz/nvhpc-end-distribute-simd-rejected.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-loop-character-achar-unsupported.f90 +75 -0
- offload/_root/data/findings/fuzz/nvhpc-map-always-after-map-to-not-copied-back.f90 +70 -0
- offload/_root/data/findings/fuzz/nvhpc-num-threads-reduction-identity-lost.f90 +54 -0
- offload/_root/data/findings/fuzz/nvhpc-target-parallel-firstprivate-translate-failure.f90 +66 -0
- offload/_root/data/findings/fuzz/nvhpc-target-parallel-single-hang.f90 +68 -0
- offload/_root/data/findings/fuzz/nvhpc-target-simd-collapse-translate-failure.f90 +73 -0
- offload/_root/data/findings/fuzz/nvhpc-target-simd-no-end-directive-rejected.f90 +68 -0
- offload/_root/data/findings/fuzz/par-s1-2082-gcc_g90.f90 +71 -0
- offload/_root/data/findings/fuzz/s1-2370-cce21.f90 +67 -0
- offload/_root/data/findings/fuzz/s1-2592-cce21.f90 +56 -0
- offload/_root/data/findings/fuzz/s1-3463-cce21.f90 +92 -0
- offload/_root/data/findings/fuzz/s1-4339-afar.f90 +79 -0
- offload/_root/data/findings/fuzz/s1-6085-cce21.f90 +96 -0
- offload/_root/data/findings/fuzz/s1-6760-cce20.f90 +65 -0
- offload/_root/data/findings/fuzz/s1-6888-cce21.f90 +76 -0
- offload/_root/data/findings/fuzz/s1-7882-cce20.f90 +63 -0
- offload/_root/data/findings/fuzz/s1-8634-cce21.f90 +45 -0
- offload/_root/data/findings/fuzz/s1-91-cce21.f90 +92 -0
- offload/_root/data/findings/fuzz/s1-9443-afar.f90 +59 -0
- offload/_root/data/findings/fuzz/s1-9914-cce19.f90 +78 -0
- offload/_root/data/findings/gcc/controls/gcc-acc-c-detach-control.c +34 -0
- offload/_root/data/findings/gcc/controls/gcc-acc-detach-control.f90 +26 -0
- offload/_root/data/findings/gcc/controls/gcc-defaultmap-to-pointer-control.f90 +31 -0
- offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.c +26 -0
- offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.f90 +21 -0
- offload/_root/data/findings/gcc/gcc-acc-bind-device-type-rejected.f90 +71 -0
- offload/_root/data/findings/gcc/gcc-acc-c-bind-device-type-rejected.c +76 -0
- offload/_root/data/findings/gcc/gcc-acc-c-detach-delete-attach-underflow.c +74 -0
- offload/_root/data/findings/gcc/gcc-acc-c-gang-dim-rejected.c +79 -0
- offload/_root/data/findings/gcc/gcc-acc-c-link-in-routine-rejected.c +82 -0
- offload/_root/data/findings/gcc/gcc-acc-detach-delete-attach-underflow.f90 +66 -0
- offload/_root/data/findings/gcc/gcc-acc-gang-dim-collapse-force-rejected.f90 +69 -0
- offload/_root/data/findings/gcc/gcc-acc-init-set-shutdown-rejected.f90 +70 -0
- offload/_root/data/findings/gcc/gcc-acc-link-in-routine-rejected.f90 +82 -0
- offload/_root/data/findings/gcc/gcc-acc-use-device-allocatable-rejected.f90 +88 -0
- offload/_root/data/findings/gcc/gcc-c-motion-mapper-rejected.c +110 -0
- offload/_root/data/findings/gcc/gcc-c-omp60-undocumented-rejected.c +76 -0
- offload/_root/data/findings/gcc/gcc-c-omp60-unimplemented-rejected.c +119 -0
- offload/_root/data/findings/gcc/gcc-c-target-in-task-mandatory-device-not-found.c +87 -0
- offload/_root/data/findings/gcc/gcc-c-uses-allocators-traits-unimplemented.c +85 -0
- offload/_root/data/findings/gcc/gcc-declare-mapper-rejected.f90 +85 -0
- offload/_root/data/findings/gcc/gcc-defaultmap-to-pointer-copy-back.f90 +66 -0
- offload/_root/data/findings/gcc/gcc-loop-bind-default-none-rejected.f90 +93 -0
- offload/_root/data/findings/gcc/gcc-omp60-undocumented-rejected.f90 +72 -0
- offload/_root/data/findings/gcc/gcc-omp60-unimplemented-rejected.f90 +112 -0
- offload/_root/data/findings/gcc/gcc-target-in-task-mandatory-device-not-found.f90 +83 -0
- offload/_root/data/findings/gcc/gcc-uses-allocators-traits-unimplemented.f90 +78 -0
- offload/_root/data/findings/harvest/afar-flang-strided-target-update.f90 +96 -0
- offload/_root/data/findings/harvest/cce-acc-declare-create-module-allocatable-xnack1-fault.f90 +58 -0
- offload/_root/data/findings/harvest/cce-c-implicit-struct-map-partly-present.c +100 -0
- offload/_root/data/findings/harvest/cce-c-strided-target-update.c +74 -0
- offload/_root/data/findings/harvest/cce-ftn-target-parallel-thread-query-ice.f90 +67 -0
- offload/_root/data/findings/harvest/cce-named-constant-array-declare-target-routine-rejected.f90 +69 -0
- offload/_root/data/findings/harvest/cce-use-device-ptr-c-ptr-not-converted.f90 +64 -0
- offload/_root/data/findings/harvest/cce21-declare-target-link-module-variable-write-lost.f90 +63 -0
- offload/_root/data/findings/harvest/cce21-internal-procedure-calls-internal-in-target-fault.f90 +71 -0
- offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-target-only-control.c +22 -0
- offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-whole-control.c +23 -0
- offload/_root/data/findings/harvest/controls/gcc-acc-declare-create-allocatable-control.f90 +34 -0
- offload/_root/data/findings/harvest/controls/gcc-c-same-item-two-maps-control.c +17 -0
- offload/_root/data/findings/harvest/controls/gcc-c-target-data-struct-pointer-section-control.c +27 -0
- offload/_root/data/findings/harvest/controls/gcc-c-target-data-two-maps-control.c +20 -0
- offload/_root/data/findings/harvest/controls/gcc-same-item-two-maps-control.f90 +16 -0
- offload/_root/data/findings/harvest/controls/gcc-teams-simd-matmul-allocatable-control.f90 +27 -0
- offload/_root/data/findings/harvest/gcc-acc-declare-create-allocatable-not-mapped.f90 +66 -0
- offload/_root/data/findings/harvest/gcc-c-same-item-two-maps-rejected.c +45 -0
- offload/_root/data/findings/harvest/gcc-c-strided-section-rejected.c +70 -0
- offload/_root/data/findings/harvest/gcc-c-target-data-struct-pointer-section-ice.c +78 -0
- offload/_root/data/findings/harvest/gcc-c-target-data-two-maps-ice.c +49 -0
- offload/_root/data/findings/harvest/gcc-same-item-two-maps-rejected.f90 +46 -0
- offload/_root/data/findings/harvest/gcc-teams-simd-matmul-allocatable-ice.f90 +53 -0
- offload/_root/data/findings/harvest/nvhpc-c-target-data-if-false-use-device-ptr-garbage.c +49 -0
- offload/_root/data/findings/harvest/nvhpc-named-constant-array-declare-target-routine-rejected.f90 +65 -0
- offload/_root/data/findings/harvest/nvhpc-use-device-ptr-c-ptr-not-found.f90 +60 -0
- offload/_root/data/findings/harvest/nvhpc2511-target-nowait-in-parallel-for-ice.c +59 -0
- offload/_root/data/findings/hecbench/controls/f16-ctl-float-array.c +28 -0
- offload/_root/data/findings/hecbench/controls/pc-ctl-builtin-popcount.c +37 -0
- offload/_root/data/findings/hecbench/controls/pc-ctl-shift-sum.c +43 -0
- offload/_root/data/findings/hecbench/gcc-c-float16-array-in-target-lto-ice.c +65 -0
- offload/_root/data/findings/hecbench/gcc-c-swar-popcount-assembler-error.c +80 -0
- offload/_root/data/findings/index.yaml +2453 -0
- offload/_root/data/findings/malloc/afar-device-malloc-in-kernel-corrupts-records.c +371 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-direct.c +52 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-fence.c +57 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-flush.c +57 -0
- offload/_root/data/findings/malloc/controls/afar-first-use-malloc-stack.c +52 -0
- offload/_root/data/findings/multithread-allocatable-section-maps.f90 +91 -0
- offload/_root/data/findings/nested/afar-cce-c-call-result-member-in-target-rejected.c +69 -0
- offload/_root/data/findings/nested/afar-cce-c-update-struct-and-member-rejected.c +92 -0
- offload/_root/data/findings/nested/afar-flang-pointer-root-allocatable-component-enter-data-fault.f90 +121 -0
- offload/_root/data/findings/nested/cce-c-struct-and-nested-pointee-sections-ice.c +79 -0
- offload/_root/data/findings/nested/cce-fortran-nested-allocatable-component-copy-fault.f90 +64 -0
- offload/_root/data/findings/nested/cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.f90 +93 -0
- offload/_root/data/findings/nested/cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.f90 +80 -0
- offload/_root/data/findings/nested/controls/c3-ctl-nested-data.c +27 -0
- offload/_root/data/findings/nested/controls/c5-acc-serial-struct-and-array.c +26 -0
- offload/_root/data/findings/nested/controls/e2-ctl-two-directives.c +42 -0
- offload/_root/data/findings/nested/controls/e3-ctl-attach-clause.c +42 -0
- offload/_root/data/findings/nested/controls/f3-pointer-array-only.c +33 -0
- offload/_root/data/findings/nested/controls/f5-ctl-copyout-exit.c +33 -0
- offload/_root/data/findings/nested/controls/f6-ctl-pointer-to-struct-array.c +28 -0
- offload/_root/data/findings/nested/controls/g2-ctl-allocatable.f90 +34 -0
- offload/_root/data/findings/nested/controls/g2-ctl-entered-to.f90 +34 -0
- offload/_root/data/findings/nested/controls/g3-ctl-from-struct-no-element.c +27 -0
- offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-to.c +27 -0
- offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-tofrom.c +27 -0
- offload/_root/data/findings/nested/controls/g5-ctl-element-pointers-only.f90 +36 -0
- offload/_root/data/findings/nested/controls/g5-ctl-root-pointer-only.f90 +36 -0
- offload/_root/data/findings/nested/controls/gp2-ctl-no-mapper-explicit-maps.c +24 -0
- offload/_root/data/findings/nested/controls/gt2-ctl-no-always.c +26 -0
- offload/_root/data/findings/nested/controls/gt3-ctl-always-on-structure-only.c +26 -0
- offload/_root/data/findings/nested/controls/j1-embedded-struct-pointer-to-struct.c +24 -0
- offload/_root/data/findings/nested/controls/j2-ctl-pointer-instead-of-embedded.c +24 -0
- offload/_root/data/findings/nested/controls/j6-ctl-struct-and-one-pointee-level.c +23 -0
- offload/_root/data/findings/nested/controls/j7-ctl-struct-and-embedded-pointee.c +24 -0
- offload/_root/data/findings/nested/controls/m2-ctl-variable-root.f90 +32 -0
- offload/_root/data/findings/nested/controls/m3-ctl-fixed-array-component.f90 +32 -0
- offload/_root/data/findings/nested/controls/m5-ctl-no-enter-data.f90 +33 -0
- offload/_root/data/findings/nested/controls/n8-ctl-fixed-array-in-component.f90 +33 -0
- offload/_root/data/findings/nested/controls/n9-ctl-allocatable-in-the-variable.f90 +33 -0
- offload/_root/data/findings/nested/controls/ns4-ctl-two-directives-struct-then-pointee.c +27 -0
- offload/_root/data/findings/nested/controls/ns8-ctl-enter-together-exit-split.c +26 -0
- offload/_root/data/findings/nested/controls/nu3-ctl-update-pointee-only.c +28 -0
- offload/_root/data/findings/nested/controls/p2-ctl-pointer-variable.c +27 -0
- offload/_root/data/findings/nested/controls/p3-ctl-direct-member.c +26 -0
- offload/_root/data/findings/nested/controls/q2-ctl-fixed-array.f90 +37 -0
- offload/_root/data/findings/nested/controls/q3-ctl-allocatable-component-of-the-variable.f90 +38 -0
- offload/_root/data/findings/nested/controls/ta2-ctl-no-allocatable-component.f90 +42 -0
- offload/_root/data/findings/nested/controls/ta3-ctl-allocatable-scalar-in-the-variable.f90 +38 -0
- offload/_root/data/findings/nested/controls/ta4-ctl-pointer-component-only.f90 +42 -0
- offload/_root/data/findings/nested/controls/u3-ctl-update-member-only.c +24 -0
- offload/_root/data/findings/nested/controls/u4-ctl-two-updates.c +25 -0
- offload/_root/data/findings/nested/controls/u5-ctl-same-list-in-map.c +25 -0
- offload/_root/data/findings/nested/controls/ua2-ctl-variable-root.f90 +37 -0
- offload/_root/data/findings/nested/controls/ua4-ctl-to-in-place-of-alloc-and-update.f90 +37 -0
- offload/_root/data/findings/nested/controls/v2-ctl-parent-first.c +25 -0
- offload/_root/data/findings/nested/controls/v4-ctl-embedded-member.c +24 -0
- offload/_root/data/findings/nested/gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.c +83 -0
- offload/_root/data/findings/nested/gcc-acc-c-parent-and-shared-pointee-one-directive.c +88 -0
- offload/_root/data/findings/nested/gcc-acc-c-struct-and-member-array-one-directive.c +84 -0
- offload/_root/data/findings/nested/gcc-c-always-on-pointer-member-section-ice.c +75 -0
- offload/_root/data/findings/nested/gcc-c-declare-mapper-pointer-member-section-gpu-fault.c +93 -0
- offload/_root/data/findings/nested/gcc-c-from-struct-and-pointee-section-rejected.c +83 -0
- offload/_root/data/findings/nested/gcc-fortran-pointer-component-alloc-bounds-lost.f90 +62 -0
- offload/_root/data/findings/nested/gcc-fortran-root-and-element-pointer-components-ice.f90 +77 -0
- offload/_root/data/findings/nested/nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.c +55 -0
- offload/_root/data/findings/nested/nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.c +86 -0
- offload/_root/data/findings/nvhpc-acc-atomic-update-sum-fort2-crash.f90 +53 -0
- offload/_root/data/findings/nvhpc-c-acc-atomic-update-sum-ice.c +43 -0
- offload/_root/data/findings/nvhpc-c-dist-schedule-chunk-compile-hang.c +41 -0
- offload/_root/data/findings/nvhpc-c-map-present-ignored.c +42 -0
- offload/_root/data/findings/nvhpc-c-ordered-in-target-nvcpfe-crash.c +92 -0
- offload/_root/data/findings/nvhpc-c-parallel-for-firstprivate-in-target.c +62 -0
- offload/_root/data/findings/nvhpc-c-parallel-if-in-target-nvvm-ice.c +56 -0
- offload/_root/data/findings/nvhpc-defaultmap-firstprivate-explicit-map.f90 +45 -0
- offload/_root/data/findings/nvhpc-defaultmap-from-allocatable-indexing.f90 +60 -0
- offload/_root/data/findings/nvhpc-depend-inoutset-ignored.f90 +64 -0
- offload/_root/data/findings/nvhpc-dist-schedule-chunk-illegal-address.f90 +52 -0
- offload/_root/data/findings/nvhpc-managed-acc-shutdown-host-access.f90 +42 -0
- offload/_root/data/findings/nvhpc-parallel-if-in-target-nvvm-ice.f90 +60 -0
- offload/_root/data/findings/oop/afar-final-dealloc-device-link.f90 +85 -0
- offload/_root/data/findings/oop/afar-pdt-length-parameter-not-implemented.f90 +65 -0
- offload/_root/data/findings/oop/cce-class-is-device-link.f90 +86 -0
- offload/_root/data/findings/oop/cce-copy-alloc-component-section-fault.f90 +73 -0
- offload/_root/data/findings/oop/cce-copy-parent-to-component-tofrom-lost.f90 +65 -0
- offload/_root/data/findings/oop/cce-copy-second-member-section-lost.f90 +65 -0
- offload/_root/data/findings/oop/cce-element-component-map-lost.f90 +64 -0
- offload/_root/data/findings/oop/cce-final-dealloc-device-abort.f90 +85 -0
- offload/_root/data/findings/oop/cce-pdt-default-mapper-rejected.f90 +71 -0
- offload/_root/data/findings/oop/cce-polymorphic-dispatch-device-link.f90 +100 -0
- offload/_root/data/findings/oop/cce-section-of-element-component-overlap.f90 +76 -0
- offload/_root/data/findings/oop/cce-select-type-guard-not-taken.f90 +91 -0
- offload/_root/data/findings/oop/cce-udr-derived-type-unsupported.f90 +69 -0
- offload/_root/data/findings/oop/cce21-element-component-map-lost.f90 +62 -0
- offload/_root/data/findings/oop/controls/alloccomp-comp-only.f90 +44 -0
- offload/_root/data/findings/oop/controls/alloccomp-whole-explicit-component.f90 +50 -0
- offload/_root/data/findings/oop/controls/alloccomp-whole.f90 +49 -0
- offload/_root/data/findings/oop/controls/elemsec-element.f90 +47 -0
- offload/_root/data/findings/oop/controls/final-local.f90 +64 -0
- offload/_root/data/findings/oop/controls/host-class-star-select-type.f90 +58 -0
- offload/_root/data/findings/oop/controls/host-firstprivate-defined-assignment.f90 +54 -0
- offload/_root/data/findings/oop/controls/host-lastprivate-defined-assignment.f90 +58 -0
- offload/_root/data/findings/oop/controls/host-private-finalization.f90 +51 -0
- offload/_root/data/findings/oop/controls/host-select-type.f90 +65 -0
- offload/_root/data/findings/oop/controls/parent-and-comp-component-only.f90 +45 -0
- offload/_root/data/findings/oop/controls/pdt-kind.f90 +46 -0
- offload/_root/data/findings/oop/controls/pdt-len-host.f90 +43 -0
- offload/_root/data/findings/oop/controls/pdt-len-unparameterized.f90 +46 -0
- offload/_root/data/findings/oop/controls/poly-dispatch-static.f90 +75 -0
- offload/_root/data/findings/oop/controls/section-alloc-whole-component.f90 +49 -0
- offload/_root/data/findings/oop/controls/section-element-whole-array.f90 +50 -0
- offload/_root/data/findings/oop/controls/section-element-whole-component.f90 +50 -0
- offload/_root/data/findings/oop/controls/section-fixed.f90 +43 -0
- offload/_root/data/findings/oop/controls/tbp-override.f90 +64 -0
- offload/_root/data/findings/oop/gcc-acc-parent-and-member-data-rejected.f90 +63 -0
- offload/_root/data/findings/oop/gcc-pdt-length-parameter-abort.f90 +68 -0
- offload/_root/data/findings/oop/gcc-polymorphic-dispatch-abort.f90 +97 -0
- offload/_root/data/findings/oop/gcc-vtab-not-marked-for-offload.f90 +94 -0
- offload/_root/data/findings/oop/nvhpc-copy-alloc-component-illegal-address.f90 +76 -0
- offload/_root/data/findings/oop/nvhpc-copy-pdt-length-parameter-illegal-address.f90 +69 -0
- offload/_root/data/findings/oop/nvhpc-named-mapper-rejected.f90 +65 -0
- offload/_root/data/findings/oop/nvhpc-polymorphic-dispatch-translate-failure.f90 +90 -0
- offload/_root/data/findings/oop/nvhpc-polymorphism-runtime-unsupported.f90 +94 -0
- offload/_root/data/findings/oop/nvhpc-type-bound-call-rejected.f90 +89 -0
- offload/_root/data/findings/oop/nvhpc-udr-derived-type-rejected.f90 +67 -0
- offload/_root/data/findings/reviews/afar-c-error-directive-in-target-link.json +217 -0
- offload/_root/data/findings/reviews/afar-c-lastprivate-distribute-parallel-for-small-type.json +113 -0
- offload/_root/data/findings/reviews/afar-c-teams-distribute-reduction-first-team.json +130 -0
- offload/_root/data/findings/reviews/afar-c-update-iterator-copy-mode.json +113 -0
- offload/_root/data/findings/reviews/afar-cce19-character-defaultmap.json +292 -0
- offload/_root/data/findings/reviews/afar-clang-requires-device-safesync-hang.json +191 -0
- offload/_root/data/findings/reviews/afar-flang-firstprivate-map-two-targets.json +130 -0
- offload/_root/data/findings/reviews/afar-flang-loop-bind-teams-reduction.json +126 -0
- offload/_root/data/findings/reviews/afar-flang-task-reduction-max.json +113 -0
- offload/_root/data/findings/reviews/afar-flang-thread-limit-target-parallel-do.json +113 -0
- offload/_root/data/findings/reviews/afar-loop-bind-thread-in-teams.json +191 -0
- offload/_root/data/findings/reviews/afar-nontemporal-assumed-shape-ice.json +113 -0
- offload/_root/data/findings/reviews/afar-udr-derived-type-link.json +113 -0
- offload/_root/data/findings/reviews/afar-xnack1-atomic-min-nested-parallel.json +113 -0
- offload/_root/data/findings/reviews/afar-xnack1-target-is-present.json +113 -0
- offload/_root/data/findings/reviews/apps--afar-cce-teams-private-array-nested-parallel-wrong.json +113 -0
- offload/_root/data/findings/reviews/apps--cce20-device-link-infer-address-spaces-crash.json +113 -0
- offload/_root/data/findings/reviews/apps--cce21-device-link-agpr-copy-mfma-assert.json +113 -0
- offload/_root/data/findings/reviews/apps--cce21-private-array-loaded-subscript-wrong.json +113 -0
- offload/_root/data/findings/reviews/apps--gcc-acc-c-tile-partial-tiles-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-c-ordered-doacross-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-c-reduction-task-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-absent-optional-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-critical-target-parallel-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-dist-schedule-chunk.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-linear-parallel-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-named-critical-link.json +126 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-ordered-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-reduction-minus-rejected.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-sections-in-target-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--afar-flang-workdistribute-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--c-allocate-const-mem-firstprivate-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-acc-character-dummy-routine-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-acc-loop-seq-in-routine-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-allocators-stack-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-allocate-align-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-allocate-allocator-modifier-rejected.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-dist-schedule-chunk.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-f-pointer-device-link.json +130 -0
- offload/_root/data/findings/reviews/catalog--cce-c-long-double-ice.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce-c-map-iterator-ice.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce-c-recursive-function-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-c-sections-in-target-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-allocate-in-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-lastprivate-parallel-in-seq-loop-wrong.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-flang-stop-in-target-crash.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-iterator-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-linear-lastprivate-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-reduction-inscan-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce-reduction-task-link.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce19-reduction-task-addrspacecast-ice.json +113 -0
- offload/_root/data/findings/reviews/catalog--cce20-c-defaultmap-private-storage-rejected.json +217 -0
- offload/_root/data/findings/reviews/catalog--cce20-map-self-rejected.json +204 -0
- offload/_root/data/findings/reviews/catalog--cce21-c-target-loop-ice.json +126 -0
- offload/_root/data/findings/reviews/catalog--defaultmap-present-private-array-abort.json +236 -0
- offload/_root/data/findings/reviews/catalog--nvhpc-acc-associate-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/catalog--nvhpc-c-function-pointer-kernel-missing.json +268 -0
- offload/_root/data/findings/reviews/cce-acc-init-set-runtime-assert.json +113 -0
- offload/_root/data/findings/reviews/cce-acc-reduction-200-int8-scalars-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce-acc-routine-bind-module-ice.json +113 -0
- offload/_root/data/findings/reviews/cce-c-dist-schedule-segv.json +113 -0
- offload/_root/data/findings/reviews/cce-c-map-delete-refcount.json +149 -0
- offload/_root/data/findings/reviews/cce-c-map-present-assert.json +126 -0
- offload/_root/data/findings/reviews/cce-c-openmp45-device-link.json +113 -0
- offload/_root/data/findings/reviews/cce-c-parallel-for-in-target.json +141 -0
- offload/_root/data/findings/reviews/cce-mapper-next-map-copy-back.json +208 -0
- offload/_root/data/findings/reviews/cce-uses-allocators-traits-omp50.json +191 -0
- offload/_root/data/findings/reviews/cce19-c-reduction-63-scalars-one-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce19-target-parallel-guarded-loop.json +113 -0
- offload/_root/data/findings/reviews/cce19-target-parallel-single-ice.json +113 -0
- offload/_root/data/findings/reviews/cce21-alloc-2e32-sum-wrong.json +113 -0
- offload/_root/data/findings/reviews/cce21-defaultmap-aggregate-flag.json +210 -0
- offload/_root/data/findings/reviews/cce21-in-reduction-assumed-shape-write-lost.json +113 -0
- offload/_root/data/findings/reviews/cce21-in-reduction-taskwait-link.json +113 -0
- offload/_root/data/findings/reviews/clang-c-declare-target-to-omp52.json +113 -0
- offload/_root/data/findings/reviews/clang-c-linear-directive-name-hang.json +113 -0
- offload/_root/data/findings/reviews/clang-c-shared-declare-target-combined.json +132 -0
- offload/_root/data/findings/reviews/dataenv--cce-value-dummy-target-write-visible.json +113 -0
- offload/_root/data/findings/reviews/defaultmap-none-loop-variable.json +113 -0
- offload/_root/data/findings/reviews/fortran-schedule-static-in-target.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce-common-block-name-in-map-rejected.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce-list-directed-print-in-target-copy-crash.json +217 -0
- offload/_root/data/findings/reviews/fresh--cce19-acc-automatic-array-copyout-copy-crash.json +113 -0
- offload/_root/data/findings/reviews/fresh--cce21-combined-if-runtime-expr-loop-skipped.json +113 -0
- offload/_root/data/findings/reviews/fresh--nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.json +113 -0
- offload/_root/data/findings/reviews/fresh--nvhpc-teams-loop-module-allocatable-assign-fort2-crash.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1505-cce20.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1540-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--1978-cce19.json +130 -0
- offload/_root/data/findings/reviews/fuzz--2334-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--591-cce19.json +147 -0
- offload/_root/data/findings/reviews/fuzz--650-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--916-afar.json +130 -0
- offload/_root/data/findings/reviews/fuzz--98-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b1-121-afar.json +160 -0
- offload/_root/data/findings/reviews/fuzz--b2-209-cce19.json +141 -0
- offload/_root/data/findings/reviews/fuzz--b2-68-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b2-85-cce20.json +217 -0
- offload/_root/data/findings/reviews/fuzz--b3-188-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b3-278-cce19.json +212 -0
- offload/_root/data/findings/reviews/fuzz--b3-54-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--b3-61-cce20.json +191 -0
- offload/_root/data/findings/reviews/fuzz--b4-86-cce19.json +113 -0
- offload/_root/data/findings/reviews/fuzz--gcc-declare-reduction-module-not-found.json +113 -0
- offload/_root/data/findings/reviews/fuzz--gcc-target-simd-map-nontemporal-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-allocatable-component-implicit-map-illegal-address.json +141 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-assumed-length-character-dummy-map-ice.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-default-none-reduction-lastprivate-fort1-crash.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-firstprivate-lastprivate-ice.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-to-scalar-reduction-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-depend-implicit-map-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-distribute-parallel-num-threads-writes-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-end-distribute-simd-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-loop-character-achar-unsupported.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-map-always-after-map-to-not-copied-back.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-num-threads-reduction-identity-lost.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-firstprivate-translate-failure.json +191 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-single-hang.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-collapse-translate-failure.json +113 -0
- offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-no-end-directive-rejected.json +113 -0
- offload/_root/data/findings/reviews/fuzz--par-s1-2082-gcc_g90.json +126 -0
- offload/_root/data/findings/reviews/fuzz--s1-2370-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-2592-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-3463-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-6760-cce20.json +130 -0
- offload/_root/data/findings/reviews/fuzz--s1-6888-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-7882-cce20.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-8634-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-91-cce21.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-9443-afar.json +113 -0
- offload/_root/data/findings/reviews/fuzz--s1-9914-cce19.json +141 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-bind-device-type-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-detach-delete-attach-underflow.json +314 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-gang-dim-collapse-force-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-init-set-shutdown-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-link-in-routine-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-acc-use-device-allocatable-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-motion-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-omp60-undocumented-rejected.json +217 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-omp60-unimplemented-rejected.json +236 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-target-in-task-mandatory-device-not-found.json +236 -0
- offload/_root/data/findings/reviews/gcc--gcc-c-uses-allocators-traits-unimplemented.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-declare-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/gcc--gcc-defaultmap-to-pointer-copy-back.json +217 -0
- offload/_root/data/findings/reviews/gcc--gcc-loop-bind-default-none-rejected.json +113 -0
- offload/_root/data/findings/reviews/harvest--afar-flang-strided-target-update.json +141 -0
- offload/_root/data/findings/reviews/harvest--cce-acc-declare-create-module-allocatable-xnack1-fault.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce-c-implicit-struct-map-partly-present.json +217 -0
- offload/_root/data/findings/reviews/harvest--cce-c-strided-target-update.json +141 -0
- offload/_root/data/findings/reviews/harvest--cce-ftn-target-parallel-thread-query-ice.json +130 -0
- offload/_root/data/findings/reviews/harvest--cce-named-constant-array-declare-target-routine-rejected.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce-use-device-ptr-c-ptr-not-converted.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce21-declare-target-link-module-variable-write-lost.json +113 -0
- offload/_root/data/findings/reviews/harvest--cce21-internal-procedure-calls-internal-in-target-fault.json +158 -0
- offload/_root/data/findings/reviews/harvest--gcc-acc-declare-create-allocatable-not-mapped.json +204 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-same-item-two-maps-rejected.json +143 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-strided-section-rejected.json +164 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-target-data-struct-pointer-section-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--gcc-c-target-data-two-maps-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--gcc-teams-simd-matmul-allocatable-ice.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-c-target-data-if-false-use-device-ptr-garbage.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-named-constant-array-declare-target-routine-rejected.json +126 -0
- offload/_root/data/findings/reviews/harvest--nvhpc-use-device-ptr-c-ptr-not-found.json +113 -0
- offload/_root/data/findings/reviews/harvest--nvhpc2511-target-nowait-in-parallel-for-ice.json +130 -0
- offload/_root/data/findings/reviews/hecbench--gcc-c-float16-array-in-target-lto-ice.json +217 -0
- offload/_root/data/findings/reviews/hecbench--gcc-c-swar-popcount-assembler-error.json +126 -0
- offload/_root/data/findings/reviews/malloc--afar-device-malloc-in-kernel-corrupts-records.json +270 -0
- offload/_root/data/findings/reviews/multithread-allocatable-section-maps.json +191 -0
- offload/_root/data/findings/reviews/nested--afar-cce-c-call-result-member-in-target-rejected.json +113 -0
- offload/_root/data/findings/reviews/nested--afar-cce-c-update-struct-and-member-rejected.json +126 -0
- offload/_root/data/findings/reviews/nested--afar-flang-pointer-root-allocatable-component-enter-data-fault.json +191 -0
- offload/_root/data/findings/reviews/nested--cce-c-struct-and-nested-pointee-sections-ice.json +113 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-nested-allocatable-component-copy-fault.json +214 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.json +191 -0
- offload/_root/data/findings/reviews/nested--cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.json +297 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.json +204 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-parent-and-shared-pointee-one-directive.json +130 -0
- offload/_root/data/findings/reviews/nested--gcc-acc-c-struct-and-member-array-one-directive.json +141 -0
- offload/_root/data/findings/reviews/nested--gcc-c-always-on-pointer-member-section-ice.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-c-declare-mapper-pointer-member-section-gpu-fault.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-c-from-struct-and-pointee-section-rejected.json +113 -0
- offload/_root/data/findings/reviews/nested--gcc-fortran-pointer-component-alloc-bounds-lost.json +217 -0
- offload/_root/data/findings/reviews/nested--gcc-fortran-root-and-element-pointer-components-ice.json +217 -0
- offload/_root/data/findings/reviews/nested--nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.json +204 -0
- offload/_root/data/findings/reviews/nested--nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.json +191 -0
- offload/_root/data/findings/reviews/nvhpc-c-acc-atomic-update-sum-ice.json +277 -0
- offload/_root/data/findings/reviews/nvhpc-c-dist-schedule-chunk-compile-hang.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-map-present-ignored.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-ordered-in-target-nvcpfe-crash.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-c-parallel-for-firstprivate-in-target.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-defaultmap-firstprivate-explicit-map.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-defaultmap-from-allocatable-indexing.json +132 -0
- offload/_root/data/findings/reviews/nvhpc-depend-inoutset-ignored.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-dist-schedule-chunk-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-managed-acc-shutdown-host-access.json +113 -0
- offload/_root/data/findings/reviews/nvhpc-parallel-if-in-target-nvvm-ice.json +113 -0
- offload/_root/data/findings/reviews/oop--afar-final-dealloc-device-link.json +132 -0
- offload/_root/data/findings/reviews/oop--afar-pdt-length-parameter-not-implemented.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-class-is-device-link.json +130 -0
- offload/_root/data/findings/reviews/oop--cce-copy-alloc-component-section-fault.json +217 -0
- offload/_root/data/findings/reviews/oop--cce-copy-parent-to-component-tofrom-lost.json +217 -0
- offload/_root/data/findings/reviews/oop--cce-copy-second-member-section-lost.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-element-component-map-lost.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-final-dealloc-device-abort.json +208 -0
- offload/_root/data/findings/reviews/oop--cce-pdt-default-mapper-rejected.json +191 -0
- offload/_root/data/findings/reviews/oop--cce-polymorphic-dispatch-device-link.json +247 -0
- offload/_root/data/findings/reviews/oop--cce-section-of-element-component-overlap.json +113 -0
- offload/_root/data/findings/reviews/oop--cce-select-type-guard-not-taken.json +191 -0
- offload/_root/data/findings/reviews/oop--cce-udr-derived-type-unsupported.json +132 -0
- offload/_root/data/findings/reviews/oop--gcc-acc-parent-and-member-data-rejected.json +141 -0
- offload/_root/data/findings/reviews/oop--gcc-pdt-length-parameter-abort.json +141 -0
- offload/_root/data/findings/reviews/oop--gcc-polymorphic-dispatch-abort.json +217 -0
- offload/_root/data/findings/reviews/oop--gcc-vtab-not-marked-for-offload.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-copy-alloc-component-illegal-address.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-copy-pdt-length-parameter-illegal-address.json +236 -0
- offload/_root/data/findings/reviews/oop--nvhpc-named-mapper-rejected.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-polymorphic-dispatch-translate-failure.json +223 -0
- offload/_root/data/findings/reviews/oop--nvhpc-polymorphism-runtime-unsupported.json +126 -0
- offload/_root/data/findings/reviews/oop--nvhpc-type-bound-call-rejected.json +113 -0
- offload/_root/data/findings/reviews/oop--nvhpc-udr-derived-type-rejected.json +113 -0
- offload/_root/data/findings/reviews/stress--afar-c-lastprivate-combined-target-not-mapped.json +113 -0
- offload/_root/data/findings/reviews/stress--afar-target-scalar-implicit-firstprivate.json +175 -0
- offload/_root/data/findings/reviews/stress--cce-acc-declare-create-scalar-serial-firstprivate.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-c-distribute-lastprivate-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-c-flto-offload-dropped.json +204 -0
- offload/_root/data/findings/reviews/stress--cce-ftn-cif-record-split-abort.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-lastprivate-num-threads-collapse-simd.json +113 -0
- offload/_root/data/findings/reviews/stress--cce-teams-reduction-many-teams-wrap.json +113 -0
- offload/_root/data/findings/reviews/stress--cce21-declare-target-scalar-map-write-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--gcc-declare-target-allocatable-component-ice.json +191 -0
- offload/_root/data/findings/reviews/stress--nvhpc-acc-derived-type-component-not-copied.json +126 -0
- offload/_root/data/findings/reviews/stress--nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-declare-target-scalar-write-lost.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-lastprivate-combined-target-not-mapped.json +113 -0
- offload/_root/data/findings/reviews/stress--nvhpc-target-scalar-implicit-firstprivate.json +130 -0
- offload/_root/data/findings/reviews/target-update-sections-in-parallel.json +113 -0
- offload/_root/data/findings/stress/afar-c-lastprivate-combined-target-not-mapped.c +72 -0
- offload/_root/data/findings/stress/afar-target-scalar-implicit-firstprivate.f90 +47 -0
- offload/_root/data/findings/stress/cce-acc-declare-create-scalar-serial-firstprivate.f90 +43 -0
- offload/_root/data/findings/stress/cce-c-distribute-lastprivate-lost.c +52 -0
- offload/_root/data/findings/stress/cce-c-flto-offload-dropped.c +73 -0
- offload/_root/data/findings/stress/cce-ftn-cif-record-split-abort.f90 +109 -0
- offload/_root/data/findings/stress/cce-lastprivate-num-threads-collapse-simd.f90 +66 -0
- offload/_root/data/findings/stress/cce-teams-reduction-many-teams-wrap.f90 +55 -0
- offload/_root/data/findings/stress/cce21-declare-target-scalar-map-write-lost.f90 +58 -0
- offload/_root/data/findings/stress/controls/gcc-declare-target-allocatable-component-control.f90 +29 -0
- offload/_root/data/findings/stress/gcc-declare-target-allocatable-component-ice.f90 +61 -0
- offload/_root/data/findings/stress/nvhpc-acc-derived-type-component-not-copied.f90 +33 -0
- offload/_root/data/findings/stress/nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.c +62 -0
- offload/_root/data/findings/stress/nvhpc-declare-target-scalar-write-lost.f90 +43 -0
- offload/_root/data/findings/stress/nvhpc-lastprivate-combined-target-not-mapped.f90 +60 -0
- offload/_root/data/findings/stress/nvhpc-target-scalar-implicit-firstprivate.f90 +41 -0
- offload/_root/data/findings/target-update-sections-in-parallel.f90 +53 -0
- offload/_root/data/issues.json +1 -0
- offload/_root/data/search.json +1 -0
- offload/_root/data/vendor/checks/build.py +288 -0
- offload/_version.py +24 -0
- offload/canuse.py +88 -0
- offload/cli.py +52 -0
- offload/data.py +85 -0
- offload/engine/__init__.py +0 -0
- offload/engine/acc_c_gen.py +415 -0
- offload/engine/acc_gen.py +1104 -0
- offload/engine/acc_rules.py +656 -0
- offload/engine/apps.py +804 -0
- offload/engine/baseline.py +324 -0
- offload/engine/c_gen.py +883 -0
- offload/engine/c_program.py +913 -0
- offload/engine/c_rules.py +503 -0
- offload/engine/campaign.py +1180 -0
- offload/engine/campaigns/__init__.py +2 -0
- offload/engine/campaigns/hecbench.py +153 -0
- offload/engine/campaigns/nested.py +76 -0
- offload/engine/campaigns/oop.py +61 -0
- offload/engine/canary.py +275 -0
- offload/engine/catalog.py +860 -0
- offload/engine/citations.py +532 -0
- offload/engine/clang_oracle.py +208 -0
- offload/engine/combos.py +234 -0
- offload/engine/confirm/__init__.py +2 -0
- offload/engine/confirm/backlog.py +159 -0
- offload/engine/confirm/claims.py +423 -0
- offload/engine/confirm/collectors.py +665 -0
- offload/engine/confirm/decisions.py +202 -0
- offload/engine/confirm/draft.py +195 -0
- offload/engine/confirm/engine.py +157 -0
- offload/engine/confirm/execute.py +382 -0
- offload/engine/confirm/facts.py +467 -0
- offload/engine/confirm/findings.py +617 -0
- offload/engine/confirm/forms.py +145 -0
- offload/engine/confirm/ingest.py +372 -0
- offload/engine/confirm/jobs.py +215 -0
- offload/engine/confirm/legality.py +323 -0
- offload/engine/confirm/machines.py +254 -0
- offload/engine/confirm/minimize.py +268 -0
- offload/engine/confirm/model.py +249 -0
- offload/engine/confirm/ports.py +107 -0
- offload/engine/confirm/programs.py +222 -0
- offload/engine/confirm/registry.py +151 -0
- offload/engine/confirm/regress.py +278 -0
- offload/engine/confirm/rules.py +565 -0
- offload/engine/confirm/store.py +63 -0
- offload/engine/confirm/triage.py +838 -0
- offload/engine/confirm/validate.py +607 -0
- offload/engine/confirm/witness.py +355 -0
- offload/engine/corpus_check.py +388 -0
- offload/engine/coverage.py +318 -0
- offload/engine/dataenv_gen.py +1318 -0
- offload/engine/dataenv_rules.py +278 -0
- offload/engine/devtemps.py +146 -0
- offload/engine/expected.py +2559 -0
- offload/engine/f2c.py +1783 -0
- offload/engine/finding_claims.py +330 -0
- offload/engine/finding_header.py +229 -0
- offload/engine/finding_rows.py +382 -0
- offload/engine/flang_oracle.py +299 -0
- offload/engine/fsource.py +620 -0
- offload/engine/fuzz.py +2455 -0
- offload/engine/fuzz_base.py +2016 -0
- offload/engine/fuzz_campaign.py +592 -0
- offload/engine/fuzz_exec.py +798 -0
- offload/engine/fuzz_min.py +1049 -0
- offload/engine/fuzz_profile.py +340 -0
- offload/engine/fuzz_report.py +1270 -0
- offload/engine/fuzz_steer.py +181 -0
- offload/engine/gate.py +1013 -0
- offload/engine/gate_report.py +711 -0
- offload/engine/gate_run.py +118 -0
- offload/engine/gate_stored.py +466 -0
- offload/engine/gen.py +1114 -0
- offload/engine/gen_templates.py +2583 -0
- offload/engine/grammar.py +695 -0
- offload/engine/harness.py +1472 -0
- offload/engine/harvest/__init__.py +3 -0
- offload/engine/harvest/c_runtime.py +465 -0
- offload/engine/harvest/checks.py +534 -0
- offload/engine/harvest/convert.py +784 -0
- offload/engine/harvest/f_runtime.py +668 -0
- offload/engine/harvest/filters.py +338 -0
- offload/engine/harvest/history.py +122 -0
- offload/engine/harvest/inventory.py +399 -0
- offload/engine/harvest2/__init__.py +5 -0
- offload/engine/harvest2/aomp.py +119 -0
- offload/engine/harvest2/examples.py +116 -0
- offload/engine/harvest2/gomp.py +679 -0
- offload/engine/harvest2/hand.py +150 -0
- offload/engine/harvest2/split.py +78 -0
- offload/engine/harvest_known/__init__.py +2 -0
- offload/engine/harvest_known/apps.py +65 -0
- offload/engine/harvest_known/cases.py +128 -0
- offload/engine/harvest_known/centres.py +184 -0
- offload/engine/harvest_known/forum.py +180 -0
- offload/engine/harvest_known/relnotes.py +42 -0
- offload/engine/harvest_known/textscan.py +107 -0
- offload/engine/hdrwrap.py +42 -0
- offload/engine/hecbench.py +568 -0
- offload/engine/hecbench_report.py +265 -0
- offload/engine/hecbench_run.py +483 -0
- offload/engine/idioms.py +1427 -0
- offload/engine/kbody_cites.py +81 -0
- offload/engine/kbody_emit.py +949 -0
- offload/engine/kbody_gen.py +795 -0
- offload/engine/legality_review.py +236 -0
- offload/engine/localcfg.py +58 -0
- offload/engine/malloc_dd.py +267 -0
- offload/engine/mutate.py +1933 -0
- offload/engine/nested_cites.py +196 -0
- offload/engine/nested_emit.py +789 -0
- offload/engine/nested_gen.py +1721 -0
- offload/engine/offload_scope.py +197 -0
- offload/engine/ompvv.py +624 -0
- offload/engine/oop_gen.py +2249 -0
- offload/engine/prerun.py +1435 -0
- offload/engine/program_source.py +739 -0
- offload/engine/provenance.py +103 -0
- offload/engine/reduce.py +2098 -0
- offload/engine/report.py +3374 -0
- offload/engine/root.py +33 -0
- offload/engine/rules.py +4256 -0
- offload/engine/run_matrix.py +325 -0
- offload/engine/site/__init__.py +1 -0
- offload/engine/site/columns.py +53 -0
- offload/engine/site/data.py +648 -0
- offload/engine/site/header.py +228 -0
- offload/engine/site/inputs.py +115 -0
- offload/engine/site/matrix.py +434 -0
- offload/engine/site/scrub.py +294 -0
- offload/engine/skeleton.py +121 -0
- offload/engine/spec_text.py +242 -0
- offload/engine/stress.py +4122 -0
- offload/engine/stress_c.py +1000 -0
- offload/engine/stress_exec.py +805 -0
- offload/engine/stress_min.py +248 -0
- offload/engine/stress_report.py +552 -0
- offload/engine/textclean.py +97 -0
- offload/engine/toolchains.py +1070 -0
- offload/engine/transforms.py +511 -0
- offload/engine/usage.py +1252 -0
- offload/engine/widen.py +673 -0
- offload/engine/widen_progs.py +2396 -0
- offload/issues.py +146 -0
- offtarget-0.1.0.dist-info/METADATA +86 -0
- offtarget-0.1.0.dist-info/RECORD +886 -0
- offtarget-0.1.0.dist-info/WHEEL +4 -0
- offtarget-0.1.0.dist-info/entry_points.txt +2 -0
- offtarget-0.1.0.dist-info/licenses/LICENSE +21 -0
- offtarget-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +20 -0
offload/engine/rules.py
ADDED
|
@@ -0,0 +1,4256 @@
|
|
|
1
|
+
"""Restriction rules (layer 3 of the catalog) and the program tree they read.
|
|
2
|
+
|
|
3
|
+
``parse_program(source, version)`` scans free-form Fortran for its OpenMP
|
|
4
|
+
directives (``!$acc`` for version 34) and returns a ``ProgramTree``:
|
|
5
|
+
``directives``, ``nesting`` (parent, child pairs), ``assoc`` (the code each
|
|
6
|
+
directive applies to), ``ends`` (paired end directives) and the declaration
|
|
7
|
+
tables ``decls``, ``vartypes``, ``types`` and ``uses``. It raises
|
|
8
|
+
``ProgramError`` on unmatched or unclosed constructs.
|
|
9
|
+
``catalog/omp/rules.yaml`` and ``catalog/acc/rules.yaml`` hold the rules.
|
|
10
|
+
Each ``spec`` entry becomes one ``Rule`` with id ``OMP<ver>-<id>`` or
|
|
11
|
+
``ACC34-<stem>``. A static rule names a predicate registered with
|
|
12
|
+
``@predicate``; a runtime rule has none and gives a ``why``.
|
|
13
|
+
A predicate takes ``(tree, version)`` and returns the indices of the
|
|
14
|
+
offending directives. ``violations(tree, version)`` returns the sorted ids
|
|
15
|
+
of violated rules; ``findings`` returns ``(id, directive index)`` pairs."""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from functools import lru_cache
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Callable
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
from offload.engine.catalog import (DIRECTIVE_NAME_CLASS, catalog_dir,
|
|
27
|
+
default_grammar, default_skeleton, model_of,
|
|
28
|
+
rule_valid)
|
|
29
|
+
from offload.engine.grammar import (_FIXED_FORMS, _SENTINELS, Directive,
|
|
30
|
+
_is_comment, _strip_bang_comment,
|
|
31
|
+
parse_directive)
|
|
32
|
+
|
|
33
|
+
from offload.engine.root import ROOT # noqa: E402
|
|
34
|
+
|
|
35
|
+
RULES_PATH = ROOT / "catalog" / "omp" / "rules.yaml"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ProgramError(ValueError):
|
|
39
|
+
"""Directive structure that parse_program cannot pair or does not
|
|
40
|
+
support."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Assoc:
|
|
45
|
+
kind: str = "standalone" # block, loop, statement, standalone
|
|
46
|
+
line: int = 0
|
|
47
|
+
end_line: int | None = None
|
|
48
|
+
loop_depth: int = 0
|
|
49
|
+
loop_vars: tuple = ()
|
|
50
|
+
stmts: int = 0
|
|
51
|
+
unit: int = 0
|
|
52
|
+
depth_known: bool = True
|
|
53
|
+
scope: tuple = () # enclosing scoping units, outermost first
|
|
54
|
+
# Per loop of the DO chain (loop_vars order): the bounds
|
|
55
|
+
# (lb, ub, incr) as lower-cased text (incr "" when absent; DO
|
|
56
|
+
# CONCURRENT: its first index-spec), and the number of base-language
|
|
57
|
+
# statements directly in that loop's body outside the next loop of the
|
|
58
|
+
# chain (intervening code; 0 for every loop but the innermost means
|
|
59
|
+
# perfectly nested)
|
|
60
|
+
loop_bounds: tuple = ()
|
|
61
|
+
intervening: tuple = ()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class ProgramTree:
|
|
66
|
+
directives: list[Directive]
|
|
67
|
+
nesting: list[tuple[int, int]]
|
|
68
|
+
assoc: list[Assoc] = field(default_factory=list)
|
|
69
|
+
ends: dict[int, Directive] = field(default_factory=dict)
|
|
70
|
+
decls: dict = field(default_factory=dict) # (scope, name) -> attribute
|
|
71
|
+
vartypes: dict = field(default_factory=dict) # (scope, name) -> type
|
|
72
|
+
types: dict = field(default_factory=dict) # (scope, type) -> components
|
|
73
|
+
uses: dict = field(default_factory=dict) # scope -> [None | {names}]
|
|
74
|
+
# base-language statements (line, lower-cased text, scoping units) and
|
|
75
|
+
# named constants (scope, name): for the default(none) rule
|
|
76
|
+
statements: list = field(default_factory=list)
|
|
77
|
+
params: set = field(default_factory=set)
|
|
78
|
+
# (scope, name) -> intrinsic type keyword of its declaration
|
|
79
|
+
# ("integer", "real", "doubleprecision", "complex", "logical",
|
|
80
|
+
# "character", "type", "class"); names declared with an array spec;
|
|
81
|
+
# names with the SAVE attribute (attribute, SAVE statement, or an
|
|
82
|
+
# initializer); scoping unit -> (kind, name) for program units and
|
|
83
|
+
# subprograms (kind: program, module, submodule, blockdata,
|
|
84
|
+
# subroutine, function); scoping unit -> module names of its USE
|
|
85
|
+
# statements
|
|
86
|
+
vtypes: dict = field(default_factory=dict)
|
|
87
|
+
arrays: set = field(default_factory=set)
|
|
88
|
+
saved: set = field(default_factory=set)
|
|
89
|
+
units: dict = field(default_factory=dict)
|
|
90
|
+
use_modules: dict = field(default_factory=dict)
|
|
91
|
+
# The source language ("f90" or "c", offload.engine.c_program) and
|
|
92
|
+
# the C scanner's extra facts
|
|
93
|
+
lang: str = "f90"
|
|
94
|
+
extra: dict = field(default_factory=dict)
|
|
95
|
+
|
|
96
|
+
def _find(self, scope: tuple, name: str, table: dict):
|
|
97
|
+
"""(scope chain up to the declaring unit, entry) of `name` in
|
|
98
|
+
`table` from the innermost scoping unit of `scope` outward (host
|
|
99
|
+
association), or None when no declaration is visible: not
|
|
100
|
+
declared, or a USE statement in a scoping unit on the way (the
|
|
101
|
+
declaring one included) may supply the name."""
|
|
102
|
+
for k in range(len(scope) - 1, -1, -1):
|
|
103
|
+
sid = scope[k]
|
|
104
|
+
if any(u is None or name in u for u in self.uses.get(sid, ())):
|
|
105
|
+
return None
|
|
106
|
+
if (sid, name) in table:
|
|
107
|
+
return scope[:k + 1], table[(sid, name)]
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
def _lookup(self, scope: tuple, name: str, table: dict):
|
|
111
|
+
hit = self._find(scope, name, table)
|
|
112
|
+
return None if hit is None else hit[1]
|
|
113
|
+
|
|
114
|
+
def attribute(self, i: int, name: str) -> str | None:
|
|
115
|
+
"""Declared attribute of `name` seen from directive i: "pointer",
|
|
116
|
+
"allocatable" or "plain"; None when no declaration is visible."""
|
|
117
|
+
return self._lookup(self.assoc[i].scope, name, self.decls)
|
|
118
|
+
|
|
119
|
+
def designator_attribute(self, i: int, item: str) -> str | None:
|
|
120
|
+
"""Attribute of a list item `v` or `v%c%d` (C: `v.c`, `v->c`;
|
|
121
|
+
subscripts ignored) seen from directive i: for a component, every
|
|
122
|
+
derived type on the path must be defined in the visible source (a
|
|
123
|
+
use-associated type gives None)."""
|
|
124
|
+
item = re.sub(r"\[[^\[\]]*\]", "", item)
|
|
125
|
+
parts = [re.sub(r"\(.*", "", p).strip() for p in
|
|
126
|
+
re.split(r"%|->|\.", re.sub(r"\([^()]*\)", "", item))]
|
|
127
|
+
scope = self.assoc[i].scope
|
|
128
|
+
hit = self._find(scope, parts[0], self.decls)
|
|
129
|
+
if hit is None:
|
|
130
|
+
return None
|
|
131
|
+
# A type name is resolved where the entity naming it was declared:
|
|
132
|
+
# the variable's declaring unit, then the defining unit of each
|
|
133
|
+
# type on the path (a host's s keeps the host's type t).
|
|
134
|
+
scope, attr = hit
|
|
135
|
+
tname = self.vartypes.get((scope[-1], parts[0]))
|
|
136
|
+
for comp in parts[1:]:
|
|
137
|
+
found = self._find(scope, tname, self.types) if tname else None
|
|
138
|
+
if found is None or comp not in found[1]:
|
|
139
|
+
return None
|
|
140
|
+
scope, comps = found
|
|
141
|
+
attr, tname = comps[comp]
|
|
142
|
+
return attr
|
|
143
|
+
|
|
144
|
+
def __post_init__(self):
|
|
145
|
+
self.assoc += [Assoc() for _ in self.directives[len(self.assoc):]]
|
|
146
|
+
|
|
147
|
+
def parent(self, i: int) -> int | None:
|
|
148
|
+
for p, c in self.nesting:
|
|
149
|
+
if c == i:
|
|
150
|
+
return p
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
def children(self, i: int) -> list[int]:
|
|
154
|
+
return [c for p, c in self.nesting if p == i]
|
|
155
|
+
|
|
156
|
+
def descendants(self, i: int) -> list[int]:
|
|
157
|
+
out = []
|
|
158
|
+
for c in self.children(i):
|
|
159
|
+
out += [c] + self.descendants(c)
|
|
160
|
+
return out
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# --- directive facts from the skeleton ---------------------------------------
|
|
164
|
+
|
|
165
|
+
# Directives associated with one statement whose end directive is optional
|
|
166
|
+
# (5.2 §3.1 / 6.0 §5.1 and §6.3); when the next directive is their end
|
|
167
|
+
# directive they span the statements up to it.
|
|
168
|
+
_ONE_STATEMENT = {"atomic", "allocators", "dispatch"}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def leaves(key: str, model: str = "omp") -> list[str]:
|
|
172
|
+
"""Leaf constructs of a directive, outermost first (itself if a leaf)."""
|
|
173
|
+
return list(default_skeleton(model).directive(key).leaves) or [key]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def association(key: str, model: str = "omp") -> str:
|
|
177
|
+
"""block, loop, statement, loopseq or standalone."""
|
|
178
|
+
if key in _ONE_STATEMENT:
|
|
179
|
+
return "statement"
|
|
180
|
+
sk = default_skeleton(model)
|
|
181
|
+
assoc = sk.directive(key).association
|
|
182
|
+
if assoc == "FromLeaves":
|
|
183
|
+
assoc = sk.directive(leaves(key, model)[-1]).association
|
|
184
|
+
return {"Block": "block", "LoopNest": "loop",
|
|
185
|
+
"LoopSeq": "loopseq"}.get(assoc, "standalone")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
_association = association
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# --- the scanner -------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
_DO = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?do(?![\w=])\s*(.*)$")
|
|
194
|
+
_END_DO = re.compile(r"^end\s*do(?:\s+[a-z_]\w*)?$")
|
|
195
|
+
_UNIT_END = re.compile(r"^(?:end(?:\s*(?:program|subroutine|function|module|"
|
|
196
|
+
r"submodule|procedure|block\s*data)\b.*)?|contains)$")
|
|
197
|
+
_BLOCK = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?block$")
|
|
198
|
+
_END_BLOCK = re.compile(r"^end\s*block(?:\s+[a-z_]\w*)?$")
|
|
199
|
+
_LABEL = re.compile(r"^\d+\s+")
|
|
200
|
+
# Program unit and subprogram headers and ends, for the compilation unit of a
|
|
201
|
+
# directive (Fortran: a program unit; 5.2 §1.2.2, p.3). A separate module
|
|
202
|
+
# procedure (module procedure ... end procedure) is ignored as a pair.
|
|
203
|
+
_UNIT_HEAD = re.compile(
|
|
204
|
+
r"^(?:program\s+\w+|module\s+(?!procedure\b)\w+|submodule\s*\(.*|"
|
|
205
|
+
r"block\s*data\b.*|(?:[\w(),=*:]+\s+)*?(?:subroutine|function)\s+"
|
|
206
|
+
r"[a-z_]\w*\s*(?:\(.*|result\b.*|bind\b.*)?)$")
|
|
207
|
+
_UNIT_TAIL = re.compile(r"^end(?:\s*(?:program|subroutine|function|module|"
|
|
208
|
+
r"submodule|block\s*data)\b.*)?$")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@dataclass
|
|
212
|
+
class _Do:
|
|
213
|
+
var: str | None
|
|
214
|
+
line: int
|
|
215
|
+
owner: int | None = None # loop directive this DO is associated with
|
|
216
|
+
loops: list = field(default_factory=list)
|
|
217
|
+
directive: bool = False
|
|
218
|
+
transform: bool = False # a loop-associated directive in the body
|
|
219
|
+
bounds: tuple = ("", "", "") # lb, ub, incr (lower-cased text)
|
|
220
|
+
stmts: int = 0 # base-language statements directly in the body
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass
|
|
224
|
+
class _Blk:
|
|
225
|
+
line: int
|
|
226
|
+
owner: int | None = None # block directive whose structured block it is
|
|
227
|
+
loops: list = field(default_factory=list) # DO and BLOCK constructs
|
|
228
|
+
directive: bool = False
|
|
229
|
+
transform: bool = False
|
|
230
|
+
stmts: int = 0
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass
|
|
234
|
+
class _Dir:
|
|
235
|
+
index: int
|
|
236
|
+
kind: str # block, loop, statement
|
|
237
|
+
pending: bool = False # waiting for its DO loop / first statement
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _split_statements(text: str) -> list[str]:
|
|
241
|
+
"""Split a statement line at ';' outside character literals."""
|
|
242
|
+
parts, cur, quote = [], [], None
|
|
243
|
+
for ch in text:
|
|
244
|
+
if quote:
|
|
245
|
+
quote = None if ch == quote else quote
|
|
246
|
+
elif ch in "'\"":
|
|
247
|
+
quote = ch
|
|
248
|
+
elif ch == ";":
|
|
249
|
+
parts.append("".join(cur))
|
|
250
|
+
cur = []
|
|
251
|
+
continue
|
|
252
|
+
cur.append(ch)
|
|
253
|
+
parts.append("".join(cur))
|
|
254
|
+
return [p for p in parts if p.strip()]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _logical_lines(source: str, model: str = "omp"):
|
|
258
|
+
"""(first line number, kind, text): kind is "omp" (raw directive text
|
|
259
|
+
of the model's sentinel, continuation lines included) or "stmt"
|
|
260
|
+
(lower-cased statement)."""
|
|
261
|
+
_SENTINEL, _FIXED_FORM = _SENTINELS[model], _FIXED_FORMS[model]
|
|
262
|
+
lines = source.splitlines()
|
|
263
|
+
n = 0
|
|
264
|
+
while n < len(lines):
|
|
265
|
+
raw = lines[n]
|
|
266
|
+
start = n + 1
|
|
267
|
+
if _SENTINEL.match(raw) or _FIXED_FORM.match(raw):
|
|
268
|
+
group = [raw] # parse_directive rejects a fixed-form sentinel
|
|
269
|
+
while _strip_bang_comment(
|
|
270
|
+
_SENTINEL.sub("", group[-1], count=1)).rstrip() \
|
|
271
|
+
.endswith("&"):
|
|
272
|
+
n += 1
|
|
273
|
+
while n < len(lines) and _is_comment(lines[n]):
|
|
274
|
+
n += 1
|
|
275
|
+
if n >= len(lines):
|
|
276
|
+
raise ProgramError(f"line {start}: directive continued "
|
|
277
|
+
"past the end of the source")
|
|
278
|
+
group.append(lines[n])
|
|
279
|
+
n += 1
|
|
280
|
+
yield start, "omp", "\n".join(group)
|
|
281
|
+
continue
|
|
282
|
+
stripped = raw.lstrip()
|
|
283
|
+
if _is_comment(raw) or stripped.startswith("#") or (
|
|
284
|
+
stripped.startswith("!$")
|
|
285
|
+
and not re.match(r"!\$[\s&]", stripped)):
|
|
286
|
+
n += 1
|
|
287
|
+
continue
|
|
288
|
+
if stripped.startswith("!$"):
|
|
289
|
+
stripped = stripped[2:]
|
|
290
|
+
text = _strip_bang_comment(stripped).rstrip()
|
|
291
|
+
while text.endswith("&"):
|
|
292
|
+
n += 1
|
|
293
|
+
while n < len(lines) and _is_comment(lines[n]):
|
|
294
|
+
n += 1
|
|
295
|
+
if n >= len(lines):
|
|
296
|
+
break
|
|
297
|
+
nxt = _strip_bang_comment(lines[n].lstrip()).rstrip()
|
|
298
|
+
text = text[:-1] + (nxt[1:] if nxt.startswith("&") else nxt)
|
|
299
|
+
n += 1
|
|
300
|
+
for part in _split_statements(text):
|
|
301
|
+
yield start, "stmt", " ".join(part.lower().split())
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _is_do(stmt: str) -> bool:
|
|
305
|
+
"""A DO statement (not an assignment to a variable named do)."""
|
|
306
|
+
m = _DO.match(stmt)
|
|
307
|
+
return bool(m) and not m.group(1).startswith("=")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _do_var(rest: str, line: int) -> str | None:
|
|
311
|
+
"""Iteration variable of a DO statement, None if not canonical."""
|
|
312
|
+
if re.match(r"^\d", rest):
|
|
313
|
+
raise ProgramError(f"line {line}: label DO loops are not supported")
|
|
314
|
+
m = re.match(r"^concurrent\s*\(\s*(?:[^:()]*::)?\s*([a-z_]\w*)\s*=", rest)
|
|
315
|
+
if m:
|
|
316
|
+
return m.group(1)
|
|
317
|
+
m = re.match(r"^,?\s*([a-z_]\w*)\s*=", rest)
|
|
318
|
+
if m and not rest.startswith("while"):
|
|
319
|
+
return m.group(1)
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _depth(do: _Do) -> tuple:
|
|
324
|
+
"""(loop variables, known) of the DO chain starting at `do`: a loop
|
|
325
|
+
body holding exactly one DO loop, possibly inside BLOCK constructs
|
|
326
|
+
(5.2 §4.4.1, p.86), and no directive continues the chain. `known` is
|
|
327
|
+
False when a loop-associated (loop-transforming) directive in a body
|
|
328
|
+
stops it: its generated loops may continue the nest."""
|
|
329
|
+
dos, known = _chain(do)
|
|
330
|
+
return tuple(d.var for d in dos), known
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _chain(do: _Do) -> tuple:
|
|
334
|
+
"""(the _Do frames of the chain starting at `do`, known)."""
|
|
335
|
+
if do.var is None:
|
|
336
|
+
return [], True
|
|
337
|
+
inner, known = _body_chain(do)
|
|
338
|
+
return [do] + inner, known
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _body_chain(frame) -> tuple:
|
|
342
|
+
if frame.transform:
|
|
343
|
+
return [], False
|
|
344
|
+
# BLOCK constructs without a loop are intervening code
|
|
345
|
+
loops = [c for c in frame.loops if isinstance(c, _Do) or _has_loop(c)]
|
|
346
|
+
if len(loops) != 1 or frame.directive:
|
|
347
|
+
return [], True
|
|
348
|
+
inner = loops[0]
|
|
349
|
+
return _body_chain(inner) if isinstance(inner, _Blk) else _chain(inner)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _has_loop(blk: _Blk) -> bool:
|
|
353
|
+
return any(isinstance(c, _Do) or _has_loop(c) for c in blk.loops)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _intervening(dos: list) -> tuple:
|
|
357
|
+
"""Statements directly in each chain loop's body outside the next
|
|
358
|
+
chain loop: its own statements and those of the BLOCK constructs
|
|
359
|
+
around the next loop or beside it (BLOCK constructs are transparent,
|
|
360
|
+
5.2 §4.4.1)."""
|
|
361
|
+
out = []
|
|
362
|
+
for k, do in enumerate(dos):
|
|
363
|
+
nxt = dos[k + 1] if k + 1 < len(dos) else None
|
|
364
|
+
out.append(do.stmts + _between(do, nxt))
|
|
365
|
+
return tuple(out)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _contains(frame, target) -> bool:
|
|
369
|
+
return any(c is target or (isinstance(c, _Blk) and _contains(c, target))
|
|
370
|
+
for c in frame.loops)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _between(frame, target) -> int:
|
|
374
|
+
"""Statements in the BLOCK constructs of `frame` (recursively) that are
|
|
375
|
+
not inside `target` (None: all of them)."""
|
|
376
|
+
n = 0
|
|
377
|
+
for c in frame.loops:
|
|
378
|
+
if c is target or not isinstance(c, _Blk):
|
|
379
|
+
continue
|
|
380
|
+
n += c.stmts + _between(c, target)
|
|
381
|
+
return n
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _split_top(text: str, sep: str) -> list[str]:
|
|
385
|
+
"""Split at `sep` outside parentheses."""
|
|
386
|
+
parts, cur, depth = [], [], 0
|
|
387
|
+
for ch in text:
|
|
388
|
+
if ch in "([":
|
|
389
|
+
depth += 1
|
|
390
|
+
elif ch in ")]":
|
|
391
|
+
depth -= 1
|
|
392
|
+
elif ch == sep and depth == 0:
|
|
393
|
+
parts.append("".join(cur).strip())
|
|
394
|
+
cur = []
|
|
395
|
+
continue
|
|
396
|
+
cur.append(ch)
|
|
397
|
+
parts.append("".join(cur).strip())
|
|
398
|
+
return parts
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _do_bounds(rest: str) -> tuple:
|
|
402
|
+
"""(lb, ub, incr) of a DO statement from the text after DO (DO
|
|
403
|
+
CONCURRENT: its first index-spec); empty strings when unknown."""
|
|
404
|
+
m = re.match(r"^concurrent\s*\((.*)$", rest)
|
|
405
|
+
if m:
|
|
406
|
+
header, depth = [], 1
|
|
407
|
+
for ch in m.group(1):
|
|
408
|
+
depth += (ch == "(") - (ch == ")")
|
|
409
|
+
if depth == 0:
|
|
410
|
+
break
|
|
411
|
+
header.append(ch)
|
|
412
|
+
spec = "".join(header)
|
|
413
|
+
spec = spec.split("::", 1)[1] if "::" in spec else spec
|
|
414
|
+
first = _split_top(spec, ",")[0]
|
|
415
|
+
mm = re.match(r"^\s*[a-z_]\w*\s*=\s*(.*)$", first)
|
|
416
|
+
parts = _split_top(mm.group(1), ":") if mm else []
|
|
417
|
+
else:
|
|
418
|
+
m = re.match(r"^,?\s*[a-z_]\w*\s*=\s*(.*)$", rest)
|
|
419
|
+
parts = _split_top(m.group(1), ",") if m else []
|
|
420
|
+
return tuple((parts + ["", "", ""])[:3])
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
_TYPE_SPEC = re.compile(
|
|
424
|
+
r"^(?:integer|real|double\s*precision|double\s*complex|complex|logical|"
|
|
425
|
+
r"character|type\s*\(|class\s*\()")
|
|
426
|
+
_ATTR_STMT = re.compile(r"^(pointer|allocatable|dimension|target)\b\s*"
|
|
427
|
+
r"(?:::)?\s*(.*)$")
|
|
428
|
+
_TYPE_DEF = re.compile(r"^type\b(?!\s*\()(?!\s*is\b)\s*(?:,[^:]*)?(?:::)?"
|
|
429
|
+
r"\s*[a-z_]\w*(?:\s*\(.*\))?$")
|
|
430
|
+
_END_TYPE = re.compile(r"^end\s*type\b")
|
|
431
|
+
_RANK = {"pointer": 3, "allocatable": 2, "plain": 1}
|
|
432
|
+
_USE = re.compile(r"^use\b(?:\s*,\s*(?:non_)?intrinsic)?(?:\s*::)?\s*"
|
|
433
|
+
r"[a-z_]\w*\s*(?:,\s*(.*))?$")
|
|
434
|
+
# type(t) / class(t); class(*) and type(*) name no type, so a component of
|
|
435
|
+
# such an entity never resolves (never fires).
|
|
436
|
+
_DECL_TYPE = re.compile(r"^(?:type|class)\s*\(\s*([a-z_]\w*)\s*\)")
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _use_names(stmt: str):
|
|
440
|
+
"""For a USE statement: None if it may supply any name (no ONLY), else
|
|
441
|
+
the set of local names of its ONLY list (renames: the local name). Not
|
|
442
|
+
a USE statement: False."""
|
|
443
|
+
m = _USE.match(stmt)
|
|
444
|
+
if not m:
|
|
445
|
+
return False
|
|
446
|
+
rest = (m.group(1) or "").strip()
|
|
447
|
+
only = re.match(r"^only\s*:(.*)$", rest)
|
|
448
|
+
if not only:
|
|
449
|
+
return None
|
|
450
|
+
names = set()
|
|
451
|
+
for part in _split_statements_commas(only.group(1)):
|
|
452
|
+
local = part.split("=>")[0].strip()
|
|
453
|
+
if re.fullmatch(r"[a-z_]\w*", local):
|
|
454
|
+
names.add(local)
|
|
455
|
+
return names
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _entities(text: str) -> list[str]:
|
|
459
|
+
"""Names of an entity list (`a(:), b = 1, c`)."""
|
|
460
|
+
names = []
|
|
461
|
+
for part in _split_statements_commas(text):
|
|
462
|
+
m = re.match(r"\s*([a-z_]\w*)", part)
|
|
463
|
+
if m:
|
|
464
|
+
names.append(m.group(1))
|
|
465
|
+
return names
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _split_statements_commas(text: str) -> list[str]:
|
|
469
|
+
parts, cur, depth = [], [], 0
|
|
470
|
+
for ch in text:
|
|
471
|
+
if ch in "([":
|
|
472
|
+
depth += 1
|
|
473
|
+
elif ch in ")]":
|
|
474
|
+
depth -= 1
|
|
475
|
+
elif ch == "," and depth == 0:
|
|
476
|
+
parts.append("".join(cur))
|
|
477
|
+
cur = []
|
|
478
|
+
continue
|
|
479
|
+
cur.append(ch)
|
|
480
|
+
parts.append("".join(cur))
|
|
481
|
+
return parts
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _declaration(stmt: str) -> tuple[str, list[str]] | None:
|
|
485
|
+
"""(attribute, names) of a type declaration or attribute statement
|
|
486
|
+
(pointer, allocatable, dimension, target), else None. The attribute is
|
|
487
|
+
pointer, allocatable or plain."""
|
|
488
|
+
m = _ATTR_STMT.match(stmt)
|
|
489
|
+
if m and not re.match(r"^\w+\s*=", stmt):
|
|
490
|
+
attr = m.group(1) if m.group(1) in ("pointer", "allocatable") \
|
|
491
|
+
else "plain"
|
|
492
|
+
return attr, _entities(m.group(2))
|
|
493
|
+
if not _TYPE_SPEC.match(stmt) or re.search(r"\bfunction\b", stmt):
|
|
494
|
+
return None
|
|
495
|
+
if "::" in stmt:
|
|
496
|
+
spec, ents = stmt.split("::", 1)
|
|
497
|
+
else: # old style: type-spec [(kind)] [*len] entities
|
|
498
|
+
m = re.match(r"^((?:double\s*precision|double\s*complex|[a-z]+)"
|
|
499
|
+
r"\s*(?:\([^()]*(?:\([^()]*\)[^()]*)*\))?"
|
|
500
|
+
r"\s*(?:\*\s*\d+)?)\s+(.*)$", stmt)
|
|
501
|
+
if not m:
|
|
502
|
+
return None
|
|
503
|
+
spec, ents = m.group(1), m.group(2)
|
|
504
|
+
attrs = [a.strip().split("(")[0].strip()
|
|
505
|
+
for a in _split_statements_commas(spec)[1:]]
|
|
506
|
+
attr = "pointer" if "pointer" in attrs else (
|
|
507
|
+
"allocatable" if "allocatable" in attrs else "plain")
|
|
508
|
+
return attr, _entities(ents)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
_SAVE_STMT = re.compile(r"^save\b\s*(?:::)?\s*(.*)$")
|
|
512
|
+
_USE_MOD = re.compile(r"^use\b(?:\s*,\s*(?:non_)?intrinsic)?(?:\s*::)?\s*"
|
|
513
|
+
r"([a-z_]\w*)")
|
|
514
|
+
# "module integer function f(x)" (a separate module procedure's interface
|
|
515
|
+
# body or definition) is a function, not a module
|
|
516
|
+
_UNIT_KIND = re.compile(r"^(?:(program)|(module)(?=\s+\w+$)|(submodule)|"
|
|
517
|
+
r"(block\s*data)"
|
|
518
|
+
r"|.*?\b(subroutine|function))\s*(?:\([^)]*\)\s*)?"
|
|
519
|
+
r"([a-z_]\w*)?")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _decl_facts(stmt: str) -> tuple:
|
|
523
|
+
"""Facts of a specification statement: (intrinsic type keyword
|
|
524
|
+
or None, names with an array spec, names with SAVE, save_all) where
|
|
525
|
+
save_all marks a bare SAVE statement."""
|
|
526
|
+
m = _SAVE_STMT.match(stmt)
|
|
527
|
+
if m and not re.match(r"^save\s*=", stmt):
|
|
528
|
+
rest = m.group(1).strip()
|
|
529
|
+
return None, set(), set(_entities(re.sub(r"/\w+/", "", rest))), \
|
|
530
|
+
not rest
|
|
531
|
+
m = _ATTR_STMT.match(stmt)
|
|
532
|
+
if m and not re.match(r"^\w+\s*=", stmt):
|
|
533
|
+
ents = m.group(2)
|
|
534
|
+
arrays = {n for n in _entities(ents)
|
|
535
|
+
if re.search(rf"\b{n}\s*\(", ents)}
|
|
536
|
+
return None, arrays, set(), False
|
|
537
|
+
if not _TYPE_SPEC.match(stmt) or re.search(r"\bfunction\b", stmt):
|
|
538
|
+
return None, set(), set(), False
|
|
539
|
+
if "::" in stmt:
|
|
540
|
+
spec, ents = stmt.split("::", 1)
|
|
541
|
+
else:
|
|
542
|
+
m = re.match(r"^((?:double\s*precision|double\s*complex|[a-z]+)"
|
|
543
|
+
r"\s*(?:\([^()]*(?:\([^()]*\)[^()]*)*\))?"
|
|
544
|
+
r"\s*(?:\*\s*\d+)?)\s+(.*)$", stmt)
|
|
545
|
+
if not m:
|
|
546
|
+
return None, set(), set(), False
|
|
547
|
+
spec, ents = m.group(1), m.group(2)
|
|
548
|
+
vtype = re.sub(r"\s+", "", re.match(r"^(double\s*precision|double\s*"
|
|
549
|
+
r"complex|[a-z]+)", spec).group(1))
|
|
550
|
+
attrs = [a.strip().split("(")[0].strip()
|
|
551
|
+
for a in _split_statements_commas(spec)[1:]]
|
|
552
|
+
names, arrays, saved = _entities(ents), set(), set()
|
|
553
|
+
for part in _split_statements_commas(ents):
|
|
554
|
+
mm = re.match(r"\s*([a-z_]\w*)\s*(\()?", part)
|
|
555
|
+
if not mm:
|
|
556
|
+
continue
|
|
557
|
+
if mm.group(2) or "dimension" in attrs:
|
|
558
|
+
arrays.add(mm.group(1))
|
|
559
|
+
if ("save" in attrs or re.search(r"(?<![=<>/])=(?!=)", part)) \
|
|
560
|
+
and "parameter" not in attrs:
|
|
561
|
+
saved.add(mm.group(1))
|
|
562
|
+
return vtype, arrays, saved, False
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def parse_program(source: str, version: int,
|
|
566
|
+
lang: str | None = None) -> ProgramTree:
|
|
567
|
+
"""Directive tree of a free-form Fortran source (see the module doc),
|
|
568
|
+
or of a C source (``offload.engine.c_program``; `lang` "c", or
|
|
569
|
+
None and the source has a ``#pragma omp`` line)."""
|
|
570
|
+
from offload.engine.c_program import is_c_source, parse_c_program
|
|
571
|
+
if lang == "c" or (lang is None and is_c_source(source)):
|
|
572
|
+
return parse_c_program(source, version)
|
|
573
|
+
tree = _parse_fortran(source, version)
|
|
574
|
+
# Integer object-like macros of a preprocessed source (the
|
|
575
|
+
# omp_vv Fortran tests), for the iterator range rule
|
|
576
|
+
tree.extra["macros"] = {
|
|
577
|
+
k: int(v) for k, v in re.findall(
|
|
578
|
+
r"^\s*#\s*define\s+([A-Za-z_]\w*)\s+\(?\s*(-?\d+)\s*\)?\s*$",
|
|
579
|
+
source, re.M)}
|
|
580
|
+
return tree
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _parse_fortran(source: str, version: int) -> ProgramTree:
|
|
584
|
+
tree = ProgramTree([], [], [], {})
|
|
585
|
+
stack: list = []
|
|
586
|
+
just_closed: int | None = None
|
|
587
|
+
model = model_of(version)
|
|
588
|
+
|
|
589
|
+
def association(key):
|
|
590
|
+
return _association(key, model)
|
|
591
|
+
|
|
592
|
+
def innermost_dir():
|
|
593
|
+
for f in reversed(stack):
|
|
594
|
+
if isinstance(f, _Dir):
|
|
595
|
+
return f
|
|
596
|
+
return None
|
|
597
|
+
|
|
598
|
+
def close_loop_without_do(f: _Dir):
|
|
599
|
+
stack.pop()
|
|
600
|
+
tree.assoc[f.index].end_line = tree.assoc[f.index].line
|
|
601
|
+
|
|
602
|
+
def describe(f):
|
|
603
|
+
if isinstance(f, _Dir):
|
|
604
|
+
a = tree.assoc[f.index]
|
|
605
|
+
return (f"!${model} {tree.directives[f.index].name} "
|
|
606
|
+
f"(line {a.line})")
|
|
607
|
+
return f"{'DO' if isinstance(f, _Do) else 'BLOCK'} (line {f.line})"
|
|
608
|
+
|
|
609
|
+
def end_follows(k: int, name: str) -> bool:
|
|
610
|
+
"""Whether the next directive after item k is `end <name>`."""
|
|
611
|
+
for _, kind, text in items[k + 1:]:
|
|
612
|
+
if kind == "omp":
|
|
613
|
+
d = parse_directive(text, "fortran", version)
|
|
614
|
+
return d.end and d.name == name
|
|
615
|
+
return False
|
|
616
|
+
|
|
617
|
+
unit, depth = 0, 0 # program units started; header nesting depth
|
|
618
|
+
scopes: list[int] = [0] # scoping units; 0 is the file outside units
|
|
619
|
+
nscopes = 0
|
|
620
|
+
in_type = None # name of the derived type being defined
|
|
621
|
+
items = list(_logical_lines(source, model))
|
|
622
|
+
for k, (line, kind, text) in enumerate(items):
|
|
623
|
+
top = stack[-1] if stack else None
|
|
624
|
+
if isinstance(top, _Dir) and top.pending:
|
|
625
|
+
if top.kind == "loop":
|
|
626
|
+
if kind == "stmt" and _is_do(_LABEL.sub("", text)):
|
|
627
|
+
pass # the DO is handled below
|
|
628
|
+
elif kind == "omp":
|
|
629
|
+
d = parse_directive(text, "fortran", version)
|
|
630
|
+
if not d.end and association(d.name) == "loop":
|
|
631
|
+
raise ProgramError(
|
|
632
|
+
f"line {line}: loop-transforming or loop "
|
|
633
|
+
f"directive {d.name!r} between a loop directive "
|
|
634
|
+
"and its DO loop is not supported")
|
|
635
|
+
close_loop_without_do(top)
|
|
636
|
+
else:
|
|
637
|
+
close_loop_without_do(top)
|
|
638
|
+
else: # block directive: a BLOCK construct is handled below
|
|
639
|
+
top.pending = kind == "stmt" and bool(_BLOCK.match(text))
|
|
640
|
+
if kind == "omp":
|
|
641
|
+
d = parse_directive(text, "fortran", version)
|
|
642
|
+
if d.end:
|
|
643
|
+
if just_closed is not None \
|
|
644
|
+
and tree.directives[just_closed].name == d.name:
|
|
645
|
+
tree.ends[just_closed] = d
|
|
646
|
+
tree.assoc[just_closed].end_line = line
|
|
647
|
+
just_closed = None
|
|
648
|
+
continue
|
|
649
|
+
top = stack[-1] if stack else None
|
|
650
|
+
if not (isinstance(top, _Dir) and top.kind == "block"
|
|
651
|
+
and tree.directives[top.index].name == d.name):
|
|
652
|
+
opened = describe(top) if top else "no open construct"
|
|
653
|
+
raise ProgramError(f"line {line}: !${model} end "
|
|
654
|
+
f"{d.name} does not match {opened}")
|
|
655
|
+
stack.pop()
|
|
656
|
+
tree.ends[top.index] = d
|
|
657
|
+
tree.assoc[top.index].end_line = line
|
|
658
|
+
just_closed = None
|
|
659
|
+
continue
|
|
660
|
+
just_closed = None
|
|
661
|
+
idx = len(tree.directives)
|
|
662
|
+
parent = innermost_dir()
|
|
663
|
+
tree.directives.append(d)
|
|
664
|
+
if parent is not None:
|
|
665
|
+
tree.nesting.append((parent.index, idx))
|
|
666
|
+
how = association(d.name)
|
|
667
|
+
if stack and isinstance(stack[-1], (_Do, _Blk)):
|
|
668
|
+
stack[-1].directive = True
|
|
669
|
+
stack[-1].transform |= how == "loop"
|
|
670
|
+
if how == "loopseq":
|
|
671
|
+
raise ProgramError(f"line {line}: loop-sequence directive "
|
|
672
|
+
f"{d.name!r} is not supported")
|
|
673
|
+
tree.assoc.append(Assoc(kind=how, line=line, unit=unit,
|
|
674
|
+
scope=tuple(scopes)))
|
|
675
|
+
if how == "statement" and end_follows(k, d.name):
|
|
676
|
+
stack.append(_Dir(idx, "block")) # several statements
|
|
677
|
+
elif how in ("block", "loop", "statement"):
|
|
678
|
+
stack.append(_Dir(idx, how, pending=how != "statement"))
|
|
679
|
+
continue
|
|
680
|
+
# a base-language statement
|
|
681
|
+
stmt = _LABEL.sub("", text)
|
|
682
|
+
tree.statements.append((line, stmt, tuple(scopes)))
|
|
683
|
+
vtype, arrays, saved, save_all = _decl_facts(stmt)
|
|
684
|
+
if not in_type:
|
|
685
|
+
if vtype:
|
|
686
|
+
for name in (_declaration(stmt) or (None, []))[1]:
|
|
687
|
+
tree.vtypes[(scopes[-1], name)] = vtype
|
|
688
|
+
tree.arrays |= {(scopes[-1], x) for x in arrays}
|
|
689
|
+
tree.saved |= {(scopes[-1], x) for x in saved}
|
|
690
|
+
if save_all:
|
|
691
|
+
tree.saved.add((scopes[-1], "*"))
|
|
692
|
+
if just_closed is not None:
|
|
693
|
+
just_closed = None
|
|
694
|
+
owner = innermost_dir()
|
|
695
|
+
if owner is not None:
|
|
696
|
+
tree.assoc[owner.index].stmts += 1
|
|
697
|
+
if _UNIT_HEAD.match(stmt) and not stmt.startswith("end"):
|
|
698
|
+
depth += 1
|
|
699
|
+
if depth == 1:
|
|
700
|
+
unit += 1
|
|
701
|
+
nscopes += 1
|
|
702
|
+
scopes.append(nscopes)
|
|
703
|
+
km = _UNIT_KIND.match(stmt)
|
|
704
|
+
kind = next((g for g in km.groups()[:5] if g), "") if km else ""
|
|
705
|
+
tree.units[nscopes] = (re.sub(r"\s+", "", kind),
|
|
706
|
+
km.group(6) if km else None)
|
|
707
|
+
elif _UNIT_TAIL.match(stmt) and depth:
|
|
708
|
+
depth -= 1
|
|
709
|
+
scopes.pop()
|
|
710
|
+
elif _TYPE_DEF.match(stmt):
|
|
711
|
+
in_type = re.match(r".*?([a-z_]\w*)(?:\s*\(.*\))?$",
|
|
712
|
+
stmt).group(1)
|
|
713
|
+
tree.types.setdefault((scopes[-1], in_type), {})
|
|
714
|
+
elif _END_TYPE.match(stmt):
|
|
715
|
+
in_type = None
|
|
716
|
+
elif _use_names(stmt) is not False:
|
|
717
|
+
tree.uses.setdefault(scopes[-1], []).append(_use_names(stmt))
|
|
718
|
+
tree.use_modules.setdefault(scopes[-1], []).append(
|
|
719
|
+
_USE_MOD.match(stmt).group(1))
|
|
720
|
+
else:
|
|
721
|
+
decl = _declaration(stmt)
|
|
722
|
+
if decl and _TYPE_SPEC.match(stmt) and "::" in stmt and re.search(
|
|
723
|
+
r",\s*parameter\b", stmt.split("::", 1)[0]):
|
|
724
|
+
tree.params |= {(scopes[-1], x) for x in decl[1]}
|
|
725
|
+
if decl:
|
|
726
|
+
attr, names = decl
|
|
727
|
+
m = _DECL_TYPE.match(stmt)
|
|
728
|
+
tname = m.group(1) if m else None
|
|
729
|
+
for name in names:
|
|
730
|
+
if in_type:
|
|
731
|
+
tree.types[(scopes[-1], in_type)][name] = (attr,
|
|
732
|
+
tname)
|
|
733
|
+
continue
|
|
734
|
+
key = (scopes[-1], name)
|
|
735
|
+
old = tree.decls.get(key)
|
|
736
|
+
if old is None or _RANK[attr] > _RANK[old]:
|
|
737
|
+
tree.decls[key] = attr
|
|
738
|
+
if tname:
|
|
739
|
+
tree.vartypes[key] = tname
|
|
740
|
+
if _UNIT_END.match(stmt):
|
|
741
|
+
if stack:
|
|
742
|
+
raise ProgramError(f"line {line}: {stmt!r} while "
|
|
743
|
+
f"{describe(stack[-1])} is still open")
|
|
744
|
+
continue
|
|
745
|
+
if _BLOCK.match(stmt):
|
|
746
|
+
blk = _Blk(line)
|
|
747
|
+
top = stack[-1] if stack else None
|
|
748
|
+
if isinstance(top, (_Do, _Blk)):
|
|
749
|
+
top.loops.append(blk)
|
|
750
|
+
elif isinstance(top, _Dir) and top.kind == "block" and top.pending:
|
|
751
|
+
top.pending = False
|
|
752
|
+
blk.owner = top.index
|
|
753
|
+
tree.assoc[top.index].stmts -= 1 # the block itself
|
|
754
|
+
stack.append(blk)
|
|
755
|
+
continue
|
|
756
|
+
if _END_BLOCK.match(stmt):
|
|
757
|
+
top = stack[-1] if stack else None
|
|
758
|
+
if not isinstance(top, _Blk):
|
|
759
|
+
raise ProgramError(f"line {line}: END BLOCK does not match "
|
|
760
|
+
f"{describe(top) if top else 'any BLOCK'}")
|
|
761
|
+
stack.pop()
|
|
762
|
+
if top.owner is not None:
|
|
763
|
+
stack.pop() # the block directive whose block this is
|
|
764
|
+
tree.assoc[top.owner].stmts -= 1
|
|
765
|
+
tree.assoc[top.owner].end_line = line
|
|
766
|
+
just_closed = top.owner
|
|
767
|
+
continue
|
|
768
|
+
if _is_do(stmt):
|
|
769
|
+
rest = _DO.match(stmt).group(1)
|
|
770
|
+
do = _Do(_do_var(rest, line), line, bounds=_do_bounds(rest))
|
|
771
|
+
top = stack[-1] if stack else None
|
|
772
|
+
if isinstance(top, (_Do, _Blk)):
|
|
773
|
+
top.loops.append(do)
|
|
774
|
+
elif isinstance(top, _Dir) and top.kind == "loop" and top.pending:
|
|
775
|
+
top.pending = False
|
|
776
|
+
do.owner = top.index
|
|
777
|
+
stack.append(do)
|
|
778
|
+
continue
|
|
779
|
+
if _END_DO.match(stmt):
|
|
780
|
+
top = stack[-1] if stack else None
|
|
781
|
+
if not isinstance(top, _Do):
|
|
782
|
+
raise ProgramError(f"line {line}: END DO does not match "
|
|
783
|
+
f"{describe(top) if top else 'any DO'}")
|
|
784
|
+
stack.pop()
|
|
785
|
+
if top.owner is not None:
|
|
786
|
+
stack.pop() # the loop directive owning this DO
|
|
787
|
+
dos, known = _chain(top)
|
|
788
|
+
vars_ = tuple(d.var for d in dos)
|
|
789
|
+
a = tree.assoc[top.owner]
|
|
790
|
+
a.loop_depth, a.loop_vars = len(vars_), vars_
|
|
791
|
+
a.depth_known = known
|
|
792
|
+
a.loop_bounds = tuple(d.bounds for d in dos)
|
|
793
|
+
a.intervening = _intervening(dos)
|
|
794
|
+
a.end_line = line
|
|
795
|
+
just_closed = top.owner
|
|
796
|
+
continue
|
|
797
|
+
top = stack[-1] if stack else None
|
|
798
|
+
if isinstance(top, (_Do, _Blk)) and not _declaration(stmt) \
|
|
799
|
+
and _use_names(stmt) is False \
|
|
800
|
+
and not re.match(r"^implicit\b", stmt):
|
|
801
|
+
top.stmts += 1 # executable statement in a DO or BLOCK body
|
|
802
|
+
if isinstance(top, _Dir) and top.kind == "statement":
|
|
803
|
+
stack.pop()
|
|
804
|
+
tree.assoc[top.index].end_line = line
|
|
805
|
+
just_closed = top.index
|
|
806
|
+
if stack:
|
|
807
|
+
raise ProgramError(f"{describe(stack[-1])} is never closed")
|
|
808
|
+
return tree
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
# --- rules -------------------------------------------------------------------
|
|
812
|
+
|
|
813
|
+
class RuleError(ValueError):
|
|
814
|
+
"""A malformed rules file."""
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
@dataclass(frozen=True)
|
|
818
|
+
class Rule:
|
|
819
|
+
id: str # OMP<ver>-<construct-or-topic>-<kind>-<nnn>
|
|
820
|
+
text: str # the spec sentence, verbatim
|
|
821
|
+
citation: str # "OpenMP <ver> §<sec>, p.<n>"
|
|
822
|
+
check: str # static | runtime
|
|
823
|
+
status: str # provisional | reviewed
|
|
824
|
+
predicate: str | None
|
|
825
|
+
version: int = 0
|
|
826
|
+
also: tuple = ()
|
|
827
|
+
why: str = ""
|
|
828
|
+
example: str = ""
|
|
829
|
+
notes: str = ""
|
|
830
|
+
lang: str = "both" # "both", "f90" or "c"
|
|
831
|
+
|
|
832
|
+
def applies(self, lang: str) -> bool:
|
|
833
|
+
"""Whether the rule holds for a program in `lang`."""
|
|
834
|
+
return self.lang in ("both", lang)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
PREDICATES: dict[str, Callable] = {}
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def predicate(fn):
|
|
841
|
+
"""Register a rule predicate under its function name (a name may be
|
|
842
|
+
registered once: the OpenMP and OpenACC predicates share the
|
|
843
|
+
registry)."""
|
|
844
|
+
if fn.__name__ in PREDICATES and PREDICATES[fn.__name__] is not fn:
|
|
845
|
+
raise RuleError(f"predicate {fn.__name__!r} registered twice")
|
|
846
|
+
PREDICATES[fn.__name__] = fn
|
|
847
|
+
return fn
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
_ID = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*-[a-z]+-\d{3}$")
|
|
851
|
+
_CITATION = re.compile(r"^OpenMP (\d)\.(\d) §[\d.]+, p\.\d+$")
|
|
852
|
+
_CITATIONS = {"omp": _CITATION,
|
|
853
|
+
"acc": re.compile(r"^OpenACC (\d)\.(\d) §[\d.]+, p\.\d+$")}
|
|
854
|
+
RULE_PREFIX = {"omp": "OMP", "acc": "ACC"}
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def rules_from_list(entries: list, model: str = "omp") -> list[Rule]:
|
|
858
|
+
"""Expand rules.yaml entries into one Rule per version (see module doc;
|
|
859
|
+
OpenACC rules: ``ACC<ver>-<stem>``)."""
|
|
860
|
+
_CITATION = _CITATIONS[model]
|
|
861
|
+
out, seen = [], set()
|
|
862
|
+
for e in entries:
|
|
863
|
+
stem = e.get("id", "")
|
|
864
|
+
if not _ID.match(stem):
|
|
865
|
+
raise RuleError(f"bad rule id {stem!r}")
|
|
866
|
+
check, pred = e.get("check"), e.get("predicate")
|
|
867
|
+
if check not in ("static", "runtime"):
|
|
868
|
+
raise RuleError(f"{stem}: check must be static or runtime")
|
|
869
|
+
if e.get("status") not in ("provisional", "reviewed"):
|
|
870
|
+
raise RuleError(f"{stem}: status must be provisional or reviewed")
|
|
871
|
+
if check == "static" and pred not in PREDICATES:
|
|
872
|
+
raise RuleError(f"{stem}: static rule needs a registered "
|
|
873
|
+
f"predicate, got {pred!r}")
|
|
874
|
+
if check == "runtime" and (pred is not None or not e.get("why")):
|
|
875
|
+
raise RuleError(f"{stem}: a runtime rule has predicate: null "
|
|
876
|
+
"and says why in `why`")
|
|
877
|
+
spec = e.get("spec") or {}
|
|
878
|
+
if not spec:
|
|
879
|
+
raise RuleError(f"{stem}: no spec entry")
|
|
880
|
+
lang = e.get("lang", "both")
|
|
881
|
+
if lang not in ("both", "f90", "c"):
|
|
882
|
+
raise RuleError(f"{stem}: lang must be both, f90 or c")
|
|
883
|
+
for ver, s in spec.items():
|
|
884
|
+
rid = f"{RULE_PREFIX[model]}{ver}-{stem}"
|
|
885
|
+
if model_of(int(ver)) != model:
|
|
886
|
+
raise RuleError(f"{rid}: version {ver} is not {model}")
|
|
887
|
+
m = _CITATION.match(s.get("citation", ""))
|
|
888
|
+
if not m or int(m.group(1) + m.group(2)) != ver:
|
|
889
|
+
raise RuleError(f"{rid}: bad citation {s.get('citation')!r}")
|
|
890
|
+
if rid in seen:
|
|
891
|
+
raise RuleError(f"duplicate rule id {rid}")
|
|
892
|
+
seen.add(rid)
|
|
893
|
+
out.append(Rule(rid, " ".join(s["text"].split()), s["citation"],
|
|
894
|
+
check, e["status"], pred, int(ver),
|
|
895
|
+
tuple(s.get("also", ())), e.get("why", ""),
|
|
896
|
+
e.get("example", ""), e.get("notes", ""), lang))
|
|
897
|
+
return out
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
def load_rules(path: Path = RULES_PATH, model: str = "omp") -> list[Rule]:
|
|
901
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
902
|
+
return rules_from_list(data["rules"], model)
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
@lru_cache(maxsize=1)
|
|
906
|
+
def default_rules() -> tuple:
|
|
907
|
+
"""The OpenMP rules (catalog/omp/rules.yaml)."""
|
|
908
|
+
return tuple(load_rules())
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
@lru_cache(maxsize=1)
|
|
912
|
+
def acc_rules() -> tuple:
|
|
913
|
+
"""The OpenACC rules (catalog/acc/rules.yaml); their predicates are
|
|
914
|
+
registered by offload.engine.acc_rules."""
|
|
915
|
+
import offload.engine.acc_rules # noqa: F401 - registers the predicates
|
|
916
|
+
return tuple(load_rules(catalog_dir("acc") / "rules.yaml", "acc"))
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
def rules_for(version: int) -> tuple:
|
|
920
|
+
"""The rules of the model of `version`."""
|
|
921
|
+
return acc_rules() if model_of(version) == "acc" else default_rules()
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def findings(tree: ProgramTree, version: int,
|
|
925
|
+
rules=None) -> list[tuple[str, int]]:
|
|
926
|
+
"""(rule id, directive index) for every violation of a static rule
|
|
927
|
+
that holds in the tree's language (Rule.lang)."""
|
|
928
|
+
out = set()
|
|
929
|
+
lang = getattr(tree, "lang", "f90")
|
|
930
|
+
for r in rules if rules is not None else rules_for(version):
|
|
931
|
+
if r.version == version and r.check == "static" and r.applies(lang):
|
|
932
|
+
out.update((r.id, i) for i in PREDICATES[r.predicate](tree,
|
|
933
|
+
version))
|
|
934
|
+
return sorted(out)
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def violations(tree: ProgramTree, version: int, rules=None) -> list[str]:
|
|
938
|
+
"""Sorted ids of the static rules the program violates, each once."""
|
|
939
|
+
return sorted({rid for rid, _ in findings(tree, version, rules)})
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
# --- helpers for predicates --------------------------------------------------
|
|
943
|
+
|
|
944
|
+
def _norm(text: str) -> str:
|
|
945
|
+
return re.sub(r"\s+", "", text.lower())
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
def _compact(name: str) -> str:
|
|
949
|
+
return name.replace(" ", "").replace("_", "")
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
@lru_cache(maxsize=1)
|
|
953
|
+
def _names() -> dict:
|
|
954
|
+
"""Directive spelling without blanks or underscores -> skeleton key."""
|
|
955
|
+
out = {}
|
|
956
|
+
for key, spec in default_skeleton().directives.items():
|
|
957
|
+
for s in spec.spellings:
|
|
958
|
+
out.setdefault(_compact(s.text), key)
|
|
959
|
+
for e in default_grammar().spellings:
|
|
960
|
+
out.setdefault(_compact(e.spelling), e.directive)
|
|
961
|
+
return out
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
def _clauses(tree: ProgramTree, i: int, *names: str, end: bool = False):
|
|
965
|
+
"""Clauses of directive i (and of its end directive if `end`)."""
|
|
966
|
+
ds = [tree.directives[i]]
|
|
967
|
+
if end and i in tree.ends:
|
|
968
|
+
ds.append(tree.ends[i])
|
|
969
|
+
return [c for d in ds for c in d.clauses if not names or c.name in names]
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def _items(clauses) -> list[str]:
|
|
973
|
+
return [_norm(a) for c in clauses for a in c.args]
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def _base(item: str) -> str:
|
|
977
|
+
m = re.match(r"(/\w+/|[a-z_]\w*)", item)
|
|
978
|
+
return m.group(1) if m else item
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
# List items in C: a[...] for an element or section, s.c / p->c
|
|
982
|
+
# for a structure element
|
|
983
|
+
_C_PART = re.compile(r"\[|->|\.(?=[a-z_])")
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
def _is_part(item: str) -> bool:
|
|
987
|
+
"""A list item that is part of another variable: an array element or
|
|
988
|
+
section, or a structure element (Fortran x(...), s%c; C x[...], s.c,
|
|
989
|
+
p->c)."""
|
|
990
|
+
return "(" in item or "%" in item or bool(_C_PART.search(item))
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def _is_component(item: str) -> bool:
|
|
994
|
+
"""A structure element (Fortran s%c, C s.c or p->c)."""
|
|
995
|
+
return "%" in item or "->" in item or bool(
|
|
996
|
+
re.search(r"\.(?=[a-z_])", item))
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
def _section_base(item: str) -> str | None:
|
|
1000
|
+
"""The base expression text of an array section item (Fortran
|
|
1001
|
+
b(l:u), C b[l:n]), None if the item is not a section."""
|
|
1002
|
+
m = re.match(r"^(.*?)\((?=[^()]*:)", item) or re.match(
|
|
1003
|
+
r"^(.*?)\[(?=[^\[\]]*:)", item)
|
|
1004
|
+
return m.group(1) if m else None
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
def _int(expr: str) -> int | None:
|
|
1008
|
+
"""The value of an integer literal (optionally signed, kind, parens)."""
|
|
1009
|
+
m = re.fullmatch(r"\(*([+-]?\d+)(?:_\w+)?\)*", _norm(expr))
|
|
1010
|
+
return int(m.group(1)) if m else None
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
def _leaves(tree: ProgramTree, i: int) -> list[str]:
|
|
1014
|
+
return leaves(tree.directives[i].name)
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
def _ancestors(tree: ProgramTree, i: int) -> list[int]:
|
|
1018
|
+
out = []
|
|
1019
|
+
p = tree.parent(i)
|
|
1020
|
+
while p is not None:
|
|
1021
|
+
out.append(p)
|
|
1022
|
+
p = tree.parent(p)
|
|
1023
|
+
return out
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
def _enclosing_leaf(tree: ProgramTree, i: int, leaf: str) -> str | None:
|
|
1027
|
+
"""The construct that immediately encloses the `leaf` constituent of
|
|
1028
|
+
directive i: the previous leaf of a compound directive, else the
|
|
1029
|
+
innermost leaf of the lexically enclosing construct, else None."""
|
|
1030
|
+
ls = _leaves(tree, i)
|
|
1031
|
+
k = ls.index(leaf)
|
|
1032
|
+
if k:
|
|
1033
|
+
return ls[k - 1]
|
|
1034
|
+
p = tree.parent(i)
|
|
1035
|
+
return None if p is None else _leaves(tree, p)[-1]
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
def _dnm(clause, key: str, version: int) -> list[str]:
|
|
1039
|
+
"""The directive-name modifiers of a clause, as written."""
|
|
1040
|
+
entry = default_grammar().clauses.get(clause.name)
|
|
1041
|
+
form = entry.form_for(key, version) if entry else None
|
|
1042
|
+
if form is None:
|
|
1043
|
+
return []
|
|
1044
|
+
|
|
1045
|
+
def has_dnm(nodes):
|
|
1046
|
+
return any((n.kind == "mods" and DIRECTIVE_NAME_CLASS in n.values)
|
|
1047
|
+
or (n.kind == "opt" and has_dnm(n.body)) for n in nodes)
|
|
1048
|
+
|
|
1049
|
+
if not any(has_dnm(alt) for alt in form.syntax):
|
|
1050
|
+
return []
|
|
1051
|
+
keywords = {k for mc in form.modifiers.values() for k in mc.keywords}
|
|
1052
|
+
return [m for m in clause.modifiers
|
|
1053
|
+
if _compact(m) in _names() and m not in keywords]
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
# 6.0 target_data is a composite of three constituent directives (6.0 §15.7,
|
|
1057
|
+
# p.458) that the skeleton does not list as leaves.
|
|
1058
|
+
_TARGET_DATA_60 = ["target enter data", "task", "target exit data"]
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
def _rule_leaves(key: str, version: int) -> list[str]:
|
|
1062
|
+
"""Leaves of a directive for the rules (6.0 target data: its three
|
|
1063
|
+
constituent directives)."""
|
|
1064
|
+
if key == "target data" and version >= 60:
|
|
1065
|
+
return list(_TARGET_DATA_60)
|
|
1066
|
+
return leaves(key)
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
def _constituents(key: str, version: int) -> set[str]:
|
|
1070
|
+
"""The directive and its constituent directives: each leaf and each
|
|
1071
|
+
directive named by consecutive leaves (6.0 glossary, p.35)."""
|
|
1072
|
+
ls = _rule_leaves(key, version)
|
|
1073
|
+
out = {key}
|
|
1074
|
+
for a in range(len(ls)):
|
|
1075
|
+
for b in range(a + 1, len(ls) + 1):
|
|
1076
|
+
hit = _names().get(_compact(" ".join(ls[a:b])))
|
|
1077
|
+
if hit:
|
|
1078
|
+
out.add(hit)
|
|
1079
|
+
return out
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def _has_ancestor_device(tree: ProgramTree, i: int) -> bool:
|
|
1083
|
+
return any("ancestor" in c.modifiers for c in _clauses(tree, i, "device"))
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
_DEVICE_CONSTRUCTS = {"target", "target data", "target enter data",
|
|
1087
|
+
"target exit data", "target update"}
|
|
1088
|
+
_LOOP_TRANSFORMS = {"fuse", "interchange", "reverse", "split", "stripe",
|
|
1089
|
+
"tile", "unroll"}
|
|
1090
|
+
_WORKSHARING = {"do", "for", "sections", "single", "workshare", "scope"}
|
|
1091
|
+
_DSA = ("private", "firstprivate", "lastprivate", "shared", "linear",
|
|
1092
|
+
"reduction", "in_reduction", "is_device_ptr", "use_device_ptr",
|
|
1093
|
+
"has_device_addr", "use_device_addr")
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def _each(tree: ProgramTree, test) -> list[int]:
|
|
1097
|
+
return [i for i in range(len(tree.directives)) if test(i)]
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
def _map_construct(tree: ProgramTree, i: int) -> bool:
|
|
1101
|
+
"""Map clauses "on a construct": not those of declare mapper."""
|
|
1102
|
+
return tree.directives[i].name != "declare mapper"
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
# --- Helpers -----------------------------------------------------------
|
|
1106
|
+
|
|
1107
|
+
# Directives that are not constructs (declarative, informational/utility,
|
|
1108
|
+
# subsidiary and separating directives): the nesting rules about
|
|
1109
|
+
# constructs and regions do not count them (assume is an informational
|
|
1110
|
+
# directive: lenient, it is outside the slice).
|
|
1111
|
+
_NOT_CONSTRUCT = {
|
|
1112
|
+
"declare target", "declare simd", "declare variant", "declare mapper",
|
|
1113
|
+
"declare reduction", "declare_induction", "requires", "threadprivate",
|
|
1114
|
+
"groupprivate", "allocate", "assumes", "begin assumes",
|
|
1115
|
+
"begin declare target", "begin declare variant", "end assumes",
|
|
1116
|
+
"end declare target", "end declare variant", "error", "nothing",
|
|
1117
|
+
"metadirective", "unknown", "scan", "section", "assume"}
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def _is_construct(tree: ProgramTree, i: int) -> bool:
|
|
1121
|
+
return tree.directives[i].name not in _NOT_CONSTRUCT
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def _extent(tree: ProgramTree, i: int) -> tuple:
|
|
1125
|
+
a = tree.assoc[i]
|
|
1126
|
+
return a.line, (a.end_line if a.end_line is not None else a.line)
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
def _body(tree: ProgramTree, i: int, strict: bool = False) -> list:
|
|
1130
|
+
"""(line, statement) of the base-language statements in the lexical
|
|
1131
|
+
extent of directive i (a loop construct: its DO nest; strict: not
|
|
1132
|
+
inside a nested construct's extent)."""
|
|
1133
|
+
a = tree.assoc[i]
|
|
1134
|
+
if a.kind == "standalone":
|
|
1135
|
+
return []
|
|
1136
|
+
lo, hi = _extent(tree, i)
|
|
1137
|
+
nested = [_extent(tree, c) for c in tree.descendants(i)
|
|
1138
|
+
if tree.assoc[c].kind != "standalone"] if strict else []
|
|
1139
|
+
return [(ln, st) for ln, st, _ in tree.statements
|
|
1140
|
+
if lo < ln <= hi and not any(a_ <= ln <= b_ for a_, b_ in nested)]
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
_STRING = re.compile(r"'[^']*'|\"[^\"]*\"")
|
|
1144
|
+
_API_CALL = re.compile(r"\b(omp_\w+)\s*\(|\bcall\s+(omp_\w+)")
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
def _api_calls(stmt: str) -> list[str]:
|
|
1148
|
+
"""Names of OpenMP runtime routines referenced (called) in a
|
|
1149
|
+
statement: omp_name(or CALL omp_name."""
|
|
1150
|
+
return [a or b for a, b in _API_CALL.findall(_STRING.sub("", stmt))]
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
def _outer_leaves(tree: ProgramTree, i: int):
|
|
1154
|
+
"""(directive index, leaf) of the constructs enclosing directive i,
|
|
1155
|
+
innermost first (the leaves of each enclosing compound from its
|
|
1156
|
+
innermost leaf outward)."""
|
|
1157
|
+
for p in _ancestors(tree, i):
|
|
1158
|
+
for leaf in reversed(_leaves(tree, p)):
|
|
1159
|
+
yield p, leaf
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
def _assoc_count(tree: ProgramTree, i: int) -> int | None:
|
|
1163
|
+
"""Number of loops associated with loop directive i: collapse(n) (1
|
|
1164
|
+
without), or ordered(n) when larger (5.2 §4.4.4: the loops associated
|
|
1165
|
+
with the construct); None when collapse is not an integer literal."""
|
|
1166
|
+
n = _collapse(tree, i)
|
|
1167
|
+
if n is None:
|
|
1168
|
+
return None
|
|
1169
|
+
o = _items(_clauses(tree, i, "ordered"))
|
|
1170
|
+
if o and _int(o[0]) is not None:
|
|
1171
|
+
n = max(n, _int(o[0]))
|
|
1172
|
+
return n
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
def _order_concurrent(tree: ProgramTree, i: int) -> bool:
|
|
1176
|
+
"""A construct on which order(concurrent) is specified, or with a loop
|
|
1177
|
+
leaf and no order clause (5.2 §11.7, 6.0 §13.8: as if order(concurrent)
|
|
1178
|
+
is present)."""
|
|
1179
|
+
orders = _clauses(tree, i, "order")
|
|
1180
|
+
if orders:
|
|
1181
|
+
return "concurrent" in _items(orders)
|
|
1182
|
+
return "loop" in _leaves(tree, i)
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
# --- predicates: nesting and placement ---------------------------------------
|
|
1186
|
+
|
|
1187
|
+
@predicate
|
|
1188
|
+
def teams_strictly_nested_in_target(tree, version):
|
|
1189
|
+
"""A teams constituent whose immediately enclosing construct (in this
|
|
1190
|
+
source) is not a target construct."""
|
|
1191
|
+
return _each(tree, lambda i: "teams" in _leaves(tree, i) and
|
|
1192
|
+
_enclosing_leaf(tree, i, "teams") not in (None, "target"))
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
@predicate
|
|
1196
|
+
def target_contains_only_teams(tree, version):
|
|
1197
|
+
"""A target construct with a nested teams construct that also contains a
|
|
1198
|
+
statement or another directive outside the teams construct."""
|
|
1199
|
+
def bad(i):
|
|
1200
|
+
if tree.directives[i].name != "target":
|
|
1201
|
+
return False
|
|
1202
|
+
kids = tree.children(i)
|
|
1203
|
+
return any(_leaves(tree, k)[0] == "teams" for k in kids) and (
|
|
1204
|
+
tree.assoc[i].stmts > 0 or len(kids) > 1)
|
|
1205
|
+
return _each(tree, bad)
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
@predicate
|
|
1209
|
+
def teams_nested_regions(tree, version):
|
|
1210
|
+
"""A directive nested directly in a teams construct (or a compound
|
|
1211
|
+
construct ending in teams) whose outermost leaf is not distribute,
|
|
1212
|
+
parallel or loop (6.0: or a loop-transforming construct, or
|
|
1213
|
+
workdistribute, which 6.0 §13.5 requires to be closely nested in teams
|
|
1214
|
+
although its properties omit teams-nestable)."""
|
|
1215
|
+
ok = {"distribute", "parallel", "loop"}
|
|
1216
|
+
if version >= 60:
|
|
1217
|
+
ok |= _LOOP_TRANSFORMS | {"workdistribute"}
|
|
1218
|
+
teams = [i for i in range(len(tree.directives))
|
|
1219
|
+
if _leaves(tree, i)[-1] == "teams"]
|
|
1220
|
+
out = {c for i in teams for c in tree.children(i)
|
|
1221
|
+
if _leaves(tree, c)[0] not in ok}
|
|
1222
|
+
# Routine regions strictly nested in teams: only
|
|
1223
|
+
# omp_get_num_teams and omp_get_team_num (5.2 §10.2; 6.0: the
|
|
1224
|
+
# teams-nestable routines, §22.1, §22.3) may be called in the teams
|
|
1225
|
+
# body outside nested constructs
|
|
1226
|
+
routines = {"omp_get_num_teams", "omp_get_team_num"}
|
|
1227
|
+
out |= {i for i in teams for _, st in _body(tree, i, strict=True)
|
|
1228
|
+
if set(_api_calls(st)) - routines}
|
|
1229
|
+
return sorted(out)
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
def _in_main_program(tree: ProgramTree, i: int) -> bool:
|
|
1233
|
+
"""Directive i is in a main program's scoping unit (no caller can put
|
|
1234
|
+
it into another region)."""
|
|
1235
|
+
return tree.units.get(tree.assoc[i].scope[-1], ("", None))[0] \
|
|
1236
|
+
== "program"
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
def _outside(tree, i, leaf, want) -> bool:
|
|
1240
|
+
"""The constituent `leaf` of directive i is not immediately enclosed by
|
|
1241
|
+
`want`: another enclosing construct, or none in a main program (Task
|
|
1242
|
+
16: an orphaned construct of a main program is in the implicit parallel
|
|
1243
|
+
region)."""
|
|
1244
|
+
enc = _enclosing_leaf(tree, i, leaf)
|
|
1245
|
+
return enc != want and (enc is not None or _in_main_program(tree, i))
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
@predicate
|
|
1249
|
+
def distribute_strictly_nested_in_teams(tree, version):
|
|
1250
|
+
"""A distribute constituent whose immediately enclosing construct (in
|
|
1251
|
+
this source) is not a teams construct, or that has none in a main
|
|
1252
|
+
program."""
|
|
1253
|
+
return _each(tree, lambda i: "distribute" in _leaves(tree, i) and
|
|
1254
|
+
_outside(tree, i, "distribute", "teams"))
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
@predicate
|
|
1258
|
+
def loop_orphan_has_bind(tree, version):
|
|
1259
|
+
"""A loop directive not nested in any construct, without a bind
|
|
1260
|
+
clause (5.0: only when it appears in a procedure, 5.0 §2.9.5 p.131)."""
|
|
1261
|
+
def in_procedure(i):
|
|
1262
|
+
return any(tree.units.get(s, ("", None))[0] in ("subroutine",
|
|
1263
|
+
"function")
|
|
1264
|
+
for s in tree.assoc[i].scope)
|
|
1265
|
+
return _each(tree, lambda i: tree.directives[i].name == "loop"
|
|
1266
|
+
and tree.parent(i) is None
|
|
1267
|
+
and not _clauses(tree, i, "bind")
|
|
1268
|
+
and (version > 50 or in_procedure(i)))
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
@predicate
|
|
1272
|
+
def loop_bind_teams_in_teams(tree, version):
|
|
1273
|
+
"""A loop constituent with bind(teams) whose immediately enclosing
|
|
1274
|
+
construct (in this source) is not a teams construct, or that has none
|
|
1275
|
+
in a main program."""
|
|
1276
|
+
return _each(tree, lambda i: "loop" in _leaves(tree, i)
|
|
1277
|
+
and "teams" in _items(_clauses(tree, i, "bind"))
|
|
1278
|
+
and _outside(tree, i, "loop", "teams"))
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
@predicate
|
|
1282
|
+
def device_construct_in_target(tree, version):
|
|
1283
|
+
"""A device-affecting construct (target, target data, target enter/exit
|
|
1284
|
+
data, target update) lexically inside a construct with a target leaf,
|
|
1285
|
+
unless it is a target construct with device(ancestor: ...)."""
|
|
1286
|
+
def bad(i):
|
|
1287
|
+
if _leaves(tree, i)[0] not in _DEVICE_CONSTRUCTS:
|
|
1288
|
+
return False
|
|
1289
|
+
if _leaves(tree, i)[0] == "target" and _has_ancestor_device(tree, i):
|
|
1290
|
+
return False
|
|
1291
|
+
return any("target" in _leaves(tree, a) for a in _ancestors(tree, i))
|
|
1292
|
+
return _each(tree, bad)
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
@predicate
|
|
1296
|
+
def order_concurrent_nesting(tree, version):
|
|
1297
|
+
"""A directive nested in a construct with order(concurrent) whose
|
|
1298
|
+
outermost leaf is not loop, parallel or simd (5.2: any nested directive;
|
|
1299
|
+
6.0: directly nested ones, and also atomic and loop-transforming
|
|
1300
|
+
constructs)."""
|
|
1301
|
+
if version >= 60:
|
|
1302
|
+
ok, nested = ({"loop", "parallel", "simd", "atomic"}
|
|
1303
|
+
| _LOOP_TRANSFORMS), tree.children
|
|
1304
|
+
else:
|
|
1305
|
+
ok, nested = {"loop", "parallel", "simd"}, tree.descendants
|
|
1306
|
+
# A loop construct without an order clause behaves as if
|
|
1307
|
+
# order(concurrent) is present (5.2 §11.7, p.257; 6.0 §13.8, p.423)
|
|
1308
|
+
return sorted({c for i in range(len(tree.directives))
|
|
1309
|
+
if _order_concurrent(tree, i)
|
|
1310
|
+
for c in nested(i) if _is_construct(tree, c)
|
|
1311
|
+
and _leaves(tree, c)[0] not in ok})
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
@predicate
|
|
1315
|
+
def declare_target_in_specification_part(tree, version):
|
|
1316
|
+
"""A declare target directive lexically inside an OpenMP construct (so
|
|
1317
|
+
in an execution part, not a specification part)."""
|
|
1318
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
1319
|
+
and tree.parent(i) is not None)
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
@predicate
|
|
1323
|
+
def requires_in_specification_part(tree, version):
|
|
1324
|
+
"""A requires directive lexically inside an OpenMP construct."""
|
|
1325
|
+
return _each(tree, lambda i: tree.directives[i].name == "requires"
|
|
1326
|
+
and tree.parent(i) is not None)
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
@predicate
|
|
1330
|
+
def requires_before_device_constructs(tree, version):
|
|
1331
|
+
"""A requires directive with reverse_offload, unified_address or
|
|
1332
|
+
unified_shared_memory after a device construct of the same program unit
|
|
1333
|
+
(the Fortran compilation unit)."""
|
|
1334
|
+
req = {"reverse_offload", "unified_address", "unified_shared_memory"}
|
|
1335
|
+
|
|
1336
|
+
def bad(i):
|
|
1337
|
+
return tree.directives[i].name == "requires" \
|
|
1338
|
+
and bool(_clauses(tree, i, *req)) and any(
|
|
1339
|
+
_leaves(tree, j)[0] in _DEVICE_CONSTRUCTS
|
|
1340
|
+
and tree.assoc[j].unit == tree.assoc[i].unit
|
|
1341
|
+
for j in range(i))
|
|
1342
|
+
return _each(tree, bad)
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
@predicate
|
|
1346
|
+
def requires_same_memory_order(tree, version):
|
|
1347
|
+
"""A requires directive whose atomic_default_mem_order argument differs
|
|
1348
|
+
from that of an earlier requires directive of the same program unit
|
|
1349
|
+
(5.0: any second one, 5.0 §2.4 p.63)."""
|
|
1350
|
+
seen, out = {}, []
|
|
1351
|
+
for i in range(len(tree.directives)):
|
|
1352
|
+
unit = tree.assoc[i].unit
|
|
1353
|
+
for o in _items(_clauses(tree, i, "atomic_default_mem_order")):
|
|
1354
|
+
if unit in seen and (version == 50 or seen[unit] != o):
|
|
1355
|
+
out.append(i)
|
|
1356
|
+
seen.setdefault(unit, o)
|
|
1357
|
+
return out
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
# --- predicates: loop association --------------------------------------------
|
|
1361
|
+
|
|
1362
|
+
def _collapse(tree, i) -> int | None:
|
|
1363
|
+
args = _items(_clauses(tree, i, "collapse"))
|
|
1364
|
+
return _int(args[0]) if args else 1
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
@predicate
|
|
1368
|
+
def loop_nest_follows(tree, version):
|
|
1369
|
+
"""A loop-nest-associated directive not followed by a DO loop of the
|
|
1370
|
+
form DO var = ... (or DO CONCURRENT)."""
|
|
1371
|
+
return _each(tree, lambda i: tree.assoc[i].kind == "loop"
|
|
1372
|
+
and tree.assoc[i].loop_depth == 0)
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
@predicate
|
|
1376
|
+
def collapse_within_depth(tree, version):
|
|
1377
|
+
"""collapse(n) with an integer literal n greater than the depth of the
|
|
1378
|
+
DO nest that follows."""
|
|
1379
|
+
def bad(i):
|
|
1380
|
+
a = tree.assoc[i]
|
|
1381
|
+
n = _collapse(tree, i) if _clauses(tree, i, "collapse") else None
|
|
1382
|
+
return a.kind == "loop" and a.loop_depth > 0 and a.depth_known \
|
|
1383
|
+
and n is not None and n > a.loop_depth
|
|
1384
|
+
return _each(tree, bad)
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
@predicate
|
|
1388
|
+
def loop_lastprivate_iteration_variable(tree, version):
|
|
1389
|
+
"""A lastprivate list item on a directive with a loop leaf that is not
|
|
1390
|
+
the iteration variable of one of the collapsed loops."""
|
|
1391
|
+
def bad(i):
|
|
1392
|
+
a = tree.assoc[i]
|
|
1393
|
+
if "loop" not in _leaves(tree, i) or a.loop_depth == 0 \
|
|
1394
|
+
or not a.depth_known:
|
|
1395
|
+
return False
|
|
1396
|
+
n = _collapse(tree, i)
|
|
1397
|
+
loop_vars = a.loop_vars[:n] if n is not None else a.loop_vars
|
|
1398
|
+
return any(x not in loop_vars
|
|
1399
|
+
for x in _items(_clauses(tree, i, "lastprivate")))
|
|
1400
|
+
return _each(tree, bad)
|
|
1401
|
+
|
|
1402
|
+
|
|
1403
|
+
# --- predicates: data mapping ------------------------------------------------
|
|
1404
|
+
|
|
1405
|
+
_MAP_TYPES = {52: {"alloc", "delete", "from", "release", "to", "tofrom"},
|
|
1406
|
+
60: {"alloc", "from", "release", "storage", "to", "tofrom"}}
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
def _map_types(tree, i, version) -> list[str | None]:
|
|
1410
|
+
types = _MAP_TYPES[52 if version < 60 else 60]
|
|
1411
|
+
out = []
|
|
1412
|
+
for c in _clauses(tree, i, "map"):
|
|
1413
|
+
t = [m for m in c.modifiers if m in types]
|
|
1414
|
+
out.append(t[0] if t else None)
|
|
1415
|
+
return out
|
|
1416
|
+
|
|
1417
|
+
|
|
1418
|
+
def _map_type_not_in(allowed):
|
|
1419
|
+
def check(tree, i, version):
|
|
1420
|
+
return any(t not in allowed for t in _map_types(tree, i, version))
|
|
1421
|
+
return check
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
@predicate
|
|
1425
|
+
def target_map_types(tree, version):
|
|
1426
|
+
"""A map clause on a directive with a target leaf whose map-type is not
|
|
1427
|
+
to, from, tofrom or alloc (5.2)."""
|
|
1428
|
+
bad = _map_type_not_in({None, "to", "from", "tofrom", "alloc"})
|
|
1429
|
+
return _each(tree, lambda i: "target" in _leaves(tree, i)
|
|
1430
|
+
and bad(tree, i, version))
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
@predicate
|
|
1434
|
+
def target_data_map_types(tree, version):
|
|
1435
|
+
"""A map clause on target data whose map-type is not to, from, tofrom
|
|
1436
|
+
or alloc (5.2)."""
|
|
1437
|
+
bad = _map_type_not_in({None, "to", "from", "tofrom", "alloc"})
|
|
1438
|
+
return _each(tree, lambda i: tree.directives[i].name == "target data"
|
|
1439
|
+
and bad(tree, i, version))
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
@predicate
|
|
1443
|
+
def enter_data_map_entering(tree, version):
|
|
1444
|
+
"""A map clause on target enter data that is not map-entering: 5.2
|
|
1445
|
+
map-type other than to, tofrom, alloc; 6.0 other than to, tofrom,
|
|
1446
|
+
storage or alloc (alloc stands for storage on map-entering constructs).
|
|
1447
|
+
A clause without a map-type defaults to tofrom (6.0: storage with the
|
|
1448
|
+
delete modifier), both map-entering."""
|
|
1449
|
+
allowed = {None, "to", "tofrom", "alloc"} | (
|
|
1450
|
+
{"storage"} if version >= 60 else set())
|
|
1451
|
+
if version == 50: # 5.0 §2.12.3 p.166: a map-type, to or alloc
|
|
1452
|
+
allowed = {"to", "alloc"}
|
|
1453
|
+
bad = _map_type_not_in(allowed)
|
|
1454
|
+
return _each(tree, lambda i: tree.directives[i].name == "target enter data"
|
|
1455
|
+
and bad(tree, i, version))
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
@predicate
|
|
1459
|
+
def exit_data_map_exiting(tree, version):
|
|
1460
|
+
"""A map clause on target exit data that is not map-exiting: 5.2
|
|
1461
|
+
map-type other than from, tofrom, release, delete; 6.0 other than from,
|
|
1462
|
+
tofrom, storage or release (release stands for storage on map-exiting
|
|
1463
|
+
constructs; delete is a modifier there)."""
|
|
1464
|
+
allowed = {None, "from", "tofrom", "release"} | (
|
|
1465
|
+
{"storage"} if version >= 60 else {"delete"})
|
|
1466
|
+
if version == 50: # 5.0 §2.12.4 p.169: a map-type, from/release/delete
|
|
1467
|
+
allowed = {"from", "release", "delete"}
|
|
1468
|
+
bad = _map_type_not_in(allowed)
|
|
1469
|
+
return _each(tree, lambda i: tree.directives[i].name == "target exit data"
|
|
1470
|
+
and bad(tree, i, version))
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
def _mapper(clause) -> str:
|
|
1474
|
+
for m in clause.modifiers:
|
|
1475
|
+
mm = re.fullmatch(r"mapper\s*\((.*)\)", m)
|
|
1476
|
+
if mm:
|
|
1477
|
+
return _norm(mm.group(1))
|
|
1478
|
+
return "default"
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
@predicate
|
|
1482
|
+
def map_same_item_same_mapper(tree, version):
|
|
1483
|
+
"""The same list item (same text) in map clauses of one directive with
|
|
1484
|
+
different mapper modifiers (no modifier means mapper(default))."""
|
|
1485
|
+
def bad(i):
|
|
1486
|
+
mappers = {}
|
|
1487
|
+
for c in _clauses(tree, i, "map"):
|
|
1488
|
+
for x in _items([c]):
|
|
1489
|
+
mappers.setdefault(x, set()).add(_mapper(c))
|
|
1490
|
+
return any(len(v) > 1 for v in mappers.values())
|
|
1491
|
+
return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
@predicate
|
|
1495
|
+
def map_whole_and_part(tree, version):
|
|
1496
|
+
"""Map clauses of one directive that name a variable and also an array
|
|
1497
|
+
section or element of it (x and x(...)): different list items that
|
|
1498
|
+
share storage, neither the containing structure of the other. Only when
|
|
1499
|
+
x has a visible non-POINTER declaration in the source
|
|
1500
|
+
(ProgramTree.designator_attribute; for s%c the declared type of s must
|
|
1501
|
+
be defined in the source with c non-POINTER): a pointer x maps the
|
|
1502
|
+
pointer and x(...) its target, and an undeclared or possibly
|
|
1503
|
+
use-associated x (or type) is not checked."""
|
|
1504
|
+
def not_pointer(i, x):
|
|
1505
|
+
return tree.designator_attribute(i, x) in ("allocatable", "plain")
|
|
1506
|
+
|
|
1507
|
+
def bad(i):
|
|
1508
|
+
xs = set(_items(_clauses(tree, i, "map")))
|
|
1509
|
+
return any((y.startswith(x + "(") or y.startswith(x + "["))
|
|
1510
|
+
and not_pointer(i, x) for x in xs for y in xs)
|
|
1511
|
+
return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
@predicate
|
|
1515
|
+
def map_self_consistent(tree, version):
|
|
1516
|
+
"""A map list item with the self modifier and another map list item of
|
|
1517
|
+
the same base variable without it, on one directive."""
|
|
1518
|
+
def bad(i):
|
|
1519
|
+
cs = _clauses(tree, i, "map")
|
|
1520
|
+
selfish = {_base(x) for c in cs if "self" in c.modifiers
|
|
1521
|
+
for x in _items([c])}
|
|
1522
|
+
return any(_base(x) in selfish for c in cs
|
|
1523
|
+
if "self" not in c.modifiers for x in _items([c]))
|
|
1524
|
+
return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
@predicate
|
|
1528
|
+
def target_dsa_not_mapped(tree, version):
|
|
1529
|
+
"""On a target construct, a list item of private, firstprivate,
|
|
1530
|
+
is_device_ptr or has_device_addr with the same base variable as a map
|
|
1531
|
+
list item."""
|
|
1532
|
+
def bad(i):
|
|
1533
|
+
if tree.directives[i].name != "target":
|
|
1534
|
+
return False
|
|
1535
|
+
dsa = {_base(x) for x in _items(_clauses(
|
|
1536
|
+
tree, i, "private", "firstprivate", "is_device_ptr",
|
|
1537
|
+
"has_device_addr"))}
|
|
1538
|
+
return bool(dsa & {_base(x) for x in _items(_clauses(tree, i,
|
|
1539
|
+
"map"))})
|
|
1540
|
+
return _each(tree, bad)
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
def _categories(tree, i) -> list[str]:
|
|
1544
|
+
"""The variable-category of each defaultmap clause of directive i; a
|
|
1545
|
+
clause without one counts as all ("If no variable-category is specified
|
|
1546
|
+
in the clause then the effect is as if all was specified", 5.2 §5.8.7
|
|
1547
|
+
p.161 l.17-18; in 5.0 such a clause applies to every category)."""
|
|
1548
|
+
return [(_norm(c.modifiers[0]) if c.modifiers else "all")
|
|
1549
|
+
for c in _clauses(tree, i, "defaultmap")]
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
@predicate
|
|
1553
|
+
def defaultmap_category_once(tree, version):
|
|
1554
|
+
"""Two defaultmap clauses on one directive whose variable-categories
|
|
1555
|
+
overlap: the same category twice, or all (a clause without a category)
|
|
1556
|
+
beside any other defaultmap clause."""
|
|
1557
|
+
def bad(i):
|
|
1558
|
+
cats = _categories(tree, i)
|
|
1559
|
+
# from 5.2 defaultmap-map-002 refuses all beside another clause
|
|
1560
|
+
return len(cats) != len(set(cats)) or (
|
|
1561
|
+
version < 52 and "all" in cats and len(cats) > 1)
|
|
1562
|
+
return _each(tree, bad)
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
@predicate
|
|
1566
|
+
def defaultmap_all_alone(tree, version):
|
|
1567
|
+
"""A defaultmap clause with the all variable-category and another
|
|
1568
|
+
defaultmap clause on the same directive."""
|
|
1569
|
+
return _each(tree, lambda i: "all" in _categories(tree, i)
|
|
1570
|
+
and len(_clauses(tree, i, "defaultmap")) > 1)
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
# --- predicates: clauses -----------------------------------------------------
|
|
1574
|
+
|
|
1575
|
+
@predicate
|
|
1576
|
+
def dnm_names_constituent(tree, version):
|
|
1577
|
+
"""A directive-name modifier that names neither the directive nor one
|
|
1578
|
+
of its constituent directives."""
|
|
1579
|
+
def bad(i):
|
|
1580
|
+
key = tree.directives[i].name
|
|
1581
|
+
ok = _constituents(key, version)
|
|
1582
|
+
return any(_names()[_compact(m)] not in ok
|
|
1583
|
+
for c in _clauses(tree, i) for m in _dnm(c, key, version))
|
|
1584
|
+
return _each(tree, bad)
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
@predicate
|
|
1588
|
+
def if_once_per_constituent(tree, version):
|
|
1589
|
+
"""Two if clauses that apply to a common leaf construct: an if clause
|
|
1590
|
+
without a directive-name modifier applies to every leaf, one with a
|
|
1591
|
+
modifier naming a constituent to that constituent's leaves."""
|
|
1592
|
+
def bad(i):
|
|
1593
|
+
key = tree.directives[i].name
|
|
1594
|
+
sets = []
|
|
1595
|
+
for c in _clauses(tree, i, "if"):
|
|
1596
|
+
dnm = _dnm(c, key, version)
|
|
1597
|
+
if not dnm:
|
|
1598
|
+
sets.append(set(_rule_leaves(key, version)))
|
|
1599
|
+
continue
|
|
1600
|
+
named = _names()[_compact(dnm[0])]
|
|
1601
|
+
if named in _constituents(key, version):
|
|
1602
|
+
sets.append(set(_rule_leaves(named, version)))
|
|
1603
|
+
return any(a & b for k, a in enumerate(sets) for b in sets[k + 1:])
|
|
1604
|
+
return _each(tree, bad)
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
def _not_positive(values) -> bool:
|
|
1608
|
+
return any(v is not None and v <= 0 for v in map(_int, values))
|
|
1609
|
+
|
|
1610
|
+
|
|
1611
|
+
@predicate
|
|
1612
|
+
def thread_limit_positive(tree, version):
|
|
1613
|
+
"""thread_limit with an integer literal argument less than one."""
|
|
1614
|
+
return _each(tree, lambda i: _not_positive(
|
|
1615
|
+
[c.args[-1] for c in _clauses(tree, i, "thread_limit")]))
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
def _num_teams_bounds(tree, i, version):
|
|
1619
|
+
"""(lower, upper) texts of each num_teams clause (lower None if absent)."""
|
|
1620
|
+
key = tree.directives[i].name
|
|
1621
|
+
out = []
|
|
1622
|
+
for c in _clauses(tree, i, "num_teams"):
|
|
1623
|
+
dnm = set(_dnm(c, key, version))
|
|
1624
|
+
lower = [m for m in c.modifiers if m not in dnm]
|
|
1625
|
+
out.append((lower[0] if lower else None, c.args[-1]))
|
|
1626
|
+
return out
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
@predicate
|
|
1630
|
+
def num_teams_positive(tree, version):
|
|
1631
|
+
"""num_teams with an integer literal lower-bound or upper-bound less
|
|
1632
|
+
than one."""
|
|
1633
|
+
return _each(tree, lambda i: any(
|
|
1634
|
+
_not_positive([lo, up] if lo is not None else [up])
|
|
1635
|
+
for lo, up in _num_teams_bounds(tree, i, version)))
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
@predicate
|
|
1639
|
+
def num_teams_bounds_ordered(tree, version):
|
|
1640
|
+
"""num_teams with integer literal bounds where lower-bound exceeds
|
|
1641
|
+
upper-bound."""
|
|
1642
|
+
def bad(i):
|
|
1643
|
+
for lo, up in _num_teams_bounds(tree, i, version):
|
|
1644
|
+
lo, up = (_int(lo) if lo is not None else None), _int(up)
|
|
1645
|
+
if lo is not None and up is not None and lo > up:
|
|
1646
|
+
return True
|
|
1647
|
+
return False
|
|
1648
|
+
return _each(tree, bad)
|
|
1649
|
+
|
|
1650
|
+
|
|
1651
|
+
@predicate
|
|
1652
|
+
def dist_schedule_chunk_positive(tree, version):
|
|
1653
|
+
"""dist_schedule with an integer literal chunk_size less than one."""
|
|
1654
|
+
return _each(tree, lambda i: _not_positive(
|
|
1655
|
+
[c.args[1] for c in _clauses(tree, i, "dist_schedule")
|
|
1656
|
+
if len(c.args) > 1]))
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
def _reductions(tree, i, modifier):
|
|
1660
|
+
return [c for c in _clauses(tree, i, "reduction") if modifier in
|
|
1661
|
+
c.modifiers]
|
|
1662
|
+
|
|
1663
|
+
|
|
1664
|
+
@predicate
|
|
1665
|
+
def reduction_task_placement(tree, version):
|
|
1666
|
+
"""reduction(task, ...) on a directive without a parallel or
|
|
1667
|
+
worksharing leaf, or with a simd or loop leaf."""
|
|
1668
|
+
def bad(i):
|
|
1669
|
+
ls = set(_leaves(tree, i))
|
|
1670
|
+
return bool(_reductions(tree, i, "task")) and (
|
|
1671
|
+
not ls & ({"parallel"} | _WORKSHARING) or ls & {"simd", "loop"})
|
|
1672
|
+
return _each(tree, bad)
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
@predicate
|
|
1676
|
+
def reduction_inscan_placement(tree, version):
|
|
1677
|
+
"""reduction(inscan, ...) on a directive without a do or simd leaf, or
|
|
1678
|
+
with a distribute leaf (6.0: or taskloop; 5.0: on any directive but do,
|
|
1679
|
+
do simd, simd, parallel do and parallel do simd, 5.0 §2.19.5.4
|
|
1680
|
+
p.302)."""
|
|
1681
|
+
banned = {"distribute"} | ({"taskloop"} if version >= 60 else set())
|
|
1682
|
+
ok50 = {"do", "do simd", "simd", "parallel do", "parallel do simd",
|
|
1683
|
+
"for", "for simd", "parallel for", "parallel for simd"}
|
|
1684
|
+
|
|
1685
|
+
def bad(i):
|
|
1686
|
+
ls = set(_leaves(tree, i))
|
|
1687
|
+
if version == 50:
|
|
1688
|
+
return bool(_reductions(tree, i, "inscan")) and \
|
|
1689
|
+
tree.directives[i].name not in ok50
|
|
1690
|
+
return bool(_reductions(tree, i, "inscan")) and (
|
|
1691
|
+
not ls & {"do", "for", "simd"} or ls & banned)
|
|
1692
|
+
return _each(tree, bad)
|
|
1693
|
+
|
|
1694
|
+
|
|
1695
|
+
@predicate
|
|
1696
|
+
def reduction_inscan_not_mixed(tree, version):
|
|
1697
|
+
"""A reduction clause with inscan and one without it on one
|
|
1698
|
+
directive."""
|
|
1699
|
+
return _each(tree, lambda i: bool(_reductions(tree, i, "inscan")) and any(
|
|
1700
|
+
"inscan" not in c.modifiers for c in _clauses(tree, i, "reduction")))
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
@predicate
|
|
1704
|
+
def reduction_inscan_no_ordered_schedule(tree, version):
|
|
1705
|
+
"""reduction(inscan, ...) on a directive with an ordered or schedule
|
|
1706
|
+
clause."""
|
|
1707
|
+
return _each(tree, lambda i: bool(_reductions(tree, i, "inscan"))
|
|
1708
|
+
and bool(_clauses(tree, i, "ordered", "schedule")))
|
|
1709
|
+
|
|
1710
|
+
|
|
1711
|
+
@predicate
|
|
1712
|
+
def reduction_task_no_nowait(tree, version):
|
|
1713
|
+
"""reduction(task, ...) on a directive with nowait (on the directive or
|
|
1714
|
+
its end directive)."""
|
|
1715
|
+
return _each(tree, lambda i: bool(_reductions(tree, i, "task"))
|
|
1716
|
+
and bool(_clauses(tree, i, "nowait", end=True)))
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
@predicate
|
|
1720
|
+
def distribute_firstprivate_lastprivate(tree, version):
|
|
1721
|
+
"""A list item in both firstprivate and lastprivate on a directive with
|
|
1722
|
+
a distribute leaf."""
|
|
1723
|
+
return _each(tree, lambda i: "distribute" in _leaves(tree, i) and bool(
|
|
1724
|
+
set(_items(_clauses(tree, i, "firstprivate")))
|
|
1725
|
+
& set(_items(_clauses(tree, i, "lastprivate")))))
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
@predicate
|
|
1729
|
+
def distribute_no_conditional_lastprivate(tree, version):
|
|
1730
|
+
"""lastprivate(conditional: ...) on a directive with a distribute
|
|
1731
|
+
leaf."""
|
|
1732
|
+
return _each(tree, lambda i: "distribute" in _leaves(tree, i) and any(
|
|
1733
|
+
"conditional" in c.modifiers
|
|
1734
|
+
for c in _clauses(tree, i, "lastprivate")))
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
@lru_cache(maxsize=None)
|
|
1738
|
+
def _allowed(leaf: str) -> dict:
|
|
1739
|
+
return default_grammar().allowed(default_skeleton(), leaf)
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
def _permits(leaf: str, clause: str, version: int) -> bool:
|
|
1743
|
+
rule = _allowed(leaf).get(clause)
|
|
1744
|
+
return rule is not None and rule_valid(rule, version)
|
|
1745
|
+
|
|
1746
|
+
|
|
1747
|
+
def _dsa_split(tree, i, version) -> list[dict]:
|
|
1748
|
+
"""Explicit data-sharing clauses of directive i split onto its leaf
|
|
1749
|
+
constructs per 5.2 §17.2 / 6.0 §19.2: for each leaf, list item -> the
|
|
1750
|
+
clause names applied to it there.
|
|
1751
|
+
|
|
1752
|
+
private and linear: the innermost leaf permitting the clause;
|
|
1753
|
+
firstprivate: the listed leaves (distribute; teams without distribute; a
|
|
1754
|
+
worksharing leaf that accepts it; taskloop; parallel without taskloop or
|
|
1755
|
+
an accepting worksharing leaf; target unless the item is in lastprivate
|
|
1756
|
+
or is the base of a map list item), or the only permitting leaf;
|
|
1757
|
+
reduction: every permitting leaf except parallel combined with
|
|
1758
|
+
sections, do, loop or taskloop and teams combined with loop; every
|
|
1759
|
+
other clause (shared, lastprivate, in_reduction, is_device_ptr, ...):
|
|
1760
|
+
every permitting leaf; a clause no leaf permits but the directive does
|
|
1761
|
+
(6.0 target_data's use_device_*) stays on the directive. Not modeled
|
|
1762
|
+
(so never a violation by
|
|
1763
|
+
themselves): the implicit shared clauses the split adds on parallel and
|
|
1764
|
+
teams, the implicit map clauses on target, and the firstprivate /
|
|
1765
|
+
lastprivate effect of linear on the outer leaves."""
|
|
1766
|
+
key = tree.directives[i].name
|
|
1767
|
+
ls = _rule_leaves(key, version)
|
|
1768
|
+
out = [dict() for _ in ls] + [dict()] # last: the directive itself
|
|
1769
|
+
lastp = set(_items(_clauses(tree, i, "lastprivate")))
|
|
1770
|
+
mapped = {_base(x) for x in _items(_clauses(tree, i, "map"))}
|
|
1771
|
+
for c in _clauses(tree, i, *_DSA):
|
|
1772
|
+
ks = [k for k, leaf in enumerate(ls) if _permits(leaf, c.name,
|
|
1773
|
+
version)]
|
|
1774
|
+
if not ks: # no leaf takes it (6.0 target_data: use_device_*)
|
|
1775
|
+
if _permits(key, c.name, version):
|
|
1776
|
+
for x in set(_items([c])):
|
|
1777
|
+
out[-1].setdefault(x, []).append(c.name)
|
|
1778
|
+
continue
|
|
1779
|
+
for x in set(_items([c])):
|
|
1780
|
+
if c.name in ("private", "linear"):
|
|
1781
|
+
targets = [ks[-1]]
|
|
1782
|
+
elif c.name == "firstprivate" and len(ks) > 1:
|
|
1783
|
+
ws = any(ls[k] in _WORKSHARING for k in ks)
|
|
1784
|
+
targets = [k for k in ks if (
|
|
1785
|
+
ls[k] == "distribute"
|
|
1786
|
+
or (ls[k] == "teams" and "distribute" not in ls)
|
|
1787
|
+
or ls[k] in _WORKSHARING or ls[k] == "taskloop"
|
|
1788
|
+
or (ls[k] == "parallel" and not ws
|
|
1789
|
+
and "taskloop" not in ls)
|
|
1790
|
+
or (ls[k] == "target" and x not in lastp
|
|
1791
|
+
and _base(x) not in mapped))]
|
|
1792
|
+
elif c.name == "reduction":
|
|
1793
|
+
targets = [k for k in ks if not (
|
|
1794
|
+
(ls[k] == "parallel" and set(ls) & {
|
|
1795
|
+
"sections", "do", "for", "loop", "taskloop"})
|
|
1796
|
+
or (ls[k] == "teams" and "loop" in ls))]
|
|
1797
|
+
else:
|
|
1798
|
+
targets = ks
|
|
1799
|
+
for k in targets:
|
|
1800
|
+
out[k].setdefault(x, []).append(c.name)
|
|
1801
|
+
return out
|
|
1802
|
+
|
|
1803
|
+
|
|
1804
|
+
@predicate
|
|
1805
|
+
def dsa_list_item_once(tree, version):
|
|
1806
|
+
"""After splitting the explicit data-sharing clauses of a directive onto
|
|
1807
|
+
its leaf constructs (see _dsa_split: private, firstprivate, lastprivate,
|
|
1808
|
+
shared, linear, reduction, in_reduction, is_device_ptr, use_device_ptr,
|
|
1809
|
+
has_device_addr, use_device_addr), a list item (same text) in two
|
|
1810
|
+
clauses on one leaf, other than exactly one firstprivate and one
|
|
1811
|
+
lastprivate. Implicit clauses created by the split are not counted."""
|
|
1812
|
+
def bad(i):
|
|
1813
|
+
return any(len(v) > 1 and sorted(v) != ["firstprivate", "lastprivate"]
|
|
1814
|
+
for leaf in _dsa_split(tree, i, version)
|
|
1815
|
+
for v in leaf.values())
|
|
1816
|
+
return _each(tree, bad)
|
|
1817
|
+
|
|
1818
|
+
|
|
1819
|
+
@predicate
|
|
1820
|
+
def list_item_not_part_of_variable(tree, version):
|
|
1821
|
+
"""An array element, array section or structure component (a list item
|
|
1822
|
+
with "(" or "%") in a variable list: declare target's extended list and
|
|
1823
|
+
its to/enter/link/local clauses, and private, firstprivate,
|
|
1824
|
+
lastprivate, shared."""
|
|
1825
|
+
def bad(i):
|
|
1826
|
+
d = tree.directives[i]
|
|
1827
|
+
xs = _items(_clauses(tree, i, "private", "firstprivate",
|
|
1828
|
+
"lastprivate", "shared"))
|
|
1829
|
+
if d.name == "declare target":
|
|
1830
|
+
xs += [_norm(a) for a in d.args]
|
|
1831
|
+
xs += _items(_clauses(tree, i, "to", "enter", "link", "local"))
|
|
1832
|
+
return any(_is_part(x) for x in xs)
|
|
1833
|
+
return _each(tree, bad)
|
|
1834
|
+
|
|
1835
|
+
|
|
1836
|
+
@predicate
|
|
1837
|
+
def use_device_no_structure_element(tree, version):
|
|
1838
|
+
"""A structure component (a list item with "%") in use_device_ptr or
|
|
1839
|
+
use_device_addr (5.2)."""
|
|
1840
|
+
return _each(tree, lambda i: any(_is_component(x) for x in _items(
|
|
1841
|
+
_clauses(tree, i, "use_device_ptr", "use_device_addr"))))
|
|
1842
|
+
|
|
1843
|
+
|
|
1844
|
+
@predicate
|
|
1845
|
+
def use_device_addr_section_base(tree, version):
|
|
1846
|
+
"""An array section in use_device_addr whose array base is not a name
|
|
1847
|
+
(6.0), e.g. s%a(1:n)."""
|
|
1848
|
+
return _each(tree, lambda i: any(
|
|
1849
|
+
_section_base(x) is not None
|
|
1850
|
+
and not re.fullmatch(r"[a-z_]\w*", _section_base(x))
|
|
1851
|
+
for x in _items(_clauses(tree, i, "use_device_addr"))))
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
@predicate
|
|
1855
|
+
def device_ancestor_only_on_target(tree, version):
|
|
1856
|
+
"""device(ancestor: ...) on a directive without a target leaf."""
|
|
1857
|
+
return _each(tree, lambda i: _has_ancestor_device(tree, i)
|
|
1858
|
+
and "target" not in _leaves(tree, i))
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
@predicate
|
|
1862
|
+
def device_ancestor_is_one(tree, version):
|
|
1863
|
+
"""device(ancestor: n) with an integer literal n other than 1."""
|
|
1864
|
+
return _each(tree, lambda i: any(
|
|
1865
|
+
"ancestor" in c.modifiers and _int(c.args[-1]) not in (None, 1)
|
|
1866
|
+
for c in _clauses(tree, i, "device")))
|
|
1867
|
+
|
|
1868
|
+
|
|
1869
|
+
@predicate
|
|
1870
|
+
def device_ancestor_needs_reverse_offload(tree, version):
|
|
1871
|
+
"""device(ancestor: ...) in a source without a requires directive that
|
|
1872
|
+
has the reverse_offload clause (anywhere in the source: a requires in a
|
|
1873
|
+
module that the program unit references also counts)."""
|
|
1874
|
+
if any(d.name == "requires" and _clauses(tree, i, "reverse_offload")
|
|
1875
|
+
for i, d in enumerate(tree.directives)):
|
|
1876
|
+
return []
|
|
1877
|
+
return _each(tree, lambda i: _has_ancestor_device(tree, i))
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
@predicate
|
|
1881
|
+
def target_ancestor_limits(tree, version):
|
|
1882
|
+
"""A construct with a target leaf and device(ancestor: ...) that has a
|
|
1883
|
+
clause other than device, firstprivate, private, defaultmap, map (6.0:
|
|
1884
|
+
nowait), a leaf other than target, or a nested directive."""
|
|
1885
|
+
ok = {"device", "firstprivate", "private", "defaultmap", "map"} | (
|
|
1886
|
+
{"nowait"} if version >= 60 else set())
|
|
1887
|
+
|
|
1888
|
+
def bad(i):
|
|
1889
|
+
if "target" not in _leaves(tree, i) or not _has_ancestor_device(
|
|
1890
|
+
tree, i):
|
|
1891
|
+
return False
|
|
1892
|
+
return (any(c.name not in ok for c in _clauses(tree, i, end=True))
|
|
1893
|
+
or len(_leaves(tree, i)) > 1 or bool(tree.children(i))
|
|
1894
|
+
# Calls to runtime routines in the region
|
|
1895
|
+
or any(_api_calls(st) for _, st in _body(tree, i)))
|
|
1896
|
+
return _each(tree, bad)
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
# --- predicates: declare target ----------------------------------------------
|
|
1900
|
+
|
|
1901
|
+
@predicate
|
|
1902
|
+
def declare_target_extended_list_alone(tree, version):
|
|
1903
|
+
"""declare target with both an extended-list argument and clauses."""
|
|
1904
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
1905
|
+
and bool(tree.directives[i].args)
|
|
1906
|
+
and bool(tree.directives[i].clauses))
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
@predicate
|
|
1910
|
+
def declare_target_enter_or_link(tree, version):
|
|
1911
|
+
"""declare target with clauses but no enter, to (its 5.2 synonym) or
|
|
1912
|
+
link clause."""
|
|
1913
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
1914
|
+
and bool(tree.directives[i].clauses)
|
|
1915
|
+
and not _clauses(tree, i, "enter", "to", "link"))
|
|
1916
|
+
|
|
1917
|
+
|
|
1918
|
+
@predicate
|
|
1919
|
+
def declare_target_nohost_link(tree, version):
|
|
1920
|
+
"""declare target with device_type(nohost) and a link clause."""
|
|
1921
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
1922
|
+
and "nohost" in _items(_clauses(tree, i, "device_type"))
|
|
1923
|
+
and bool(_clauses(tree, i, "link")))
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
# --- default(none) ---------------------------------------------------------
|
|
1927
|
+
|
|
1928
|
+
# Clauses that give a list item an explicitly determined data-sharing
|
|
1929
|
+
# attribute (5.2 §5.4, 6.0 §7.5).
|
|
1930
|
+
_EXPLICIT_DSA = ("private", "firstprivate", "lastprivate", "shared", "linear",
|
|
1931
|
+
"induction",
|
|
1932
|
+
"reduction", "in_reduction", "is_device_ptr",
|
|
1933
|
+
"has_device_addr", "use_device_ptr", "use_device_addr")
|
|
1934
|
+
_DO_VAR = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?do\s+(?:\d+\s*,?\s*)?"
|
|
1935
|
+
r"([a-z_]\w*)\s*=")
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
@predicate
|
|
1939
|
+
def default_none_listed(tree, version):
|
|
1940
|
+
"""A construct with default(none) whose structured block or loop nest
|
|
1941
|
+
references, in a base-language statement, a variable declared in the
|
|
1942
|
+
source (seen from the directive, ProgramTree.attribute: not a named
|
|
1943
|
+
constant, not supplied by a USE) that is listed in no data-sharing
|
|
1944
|
+
attribute clause of the directive and has no predetermined attribute
|
|
1945
|
+
the scanner can see: DO variables of loops in the construct are
|
|
1946
|
+
predetermined private, threadprivate variables are predetermined (5.2
|
|
1947
|
+
§5.1.1, 6.0 §7.1.1). References in nested
|
|
1948
|
+
directives' clauses are not counted (lenient), nor names a USE may
|
|
1949
|
+
supply."""
|
|
1950
|
+
if getattr(tree, "lang", "f90") == "c":
|
|
1951
|
+
return _c_default_none(tree, version)
|
|
1952
|
+
out = []
|
|
1953
|
+
# threadprivate variables are predetermined (5.2 §5.1.1, 6.0 §7.1.1)
|
|
1954
|
+
tprivate = {_base(_norm(x)) for d in tree.directives
|
|
1955
|
+
if d.name == "threadprivate" for x in d.args}
|
|
1956
|
+
for i, d in enumerate(tree.directives):
|
|
1957
|
+
cl = [c for c in d.clauses if c.name == "default"]
|
|
1958
|
+
if not cl or not any(_norm(a).startswith("none")
|
|
1959
|
+
for c in cl for a in c.args):
|
|
1960
|
+
continue
|
|
1961
|
+
a = tree.assoc[i]
|
|
1962
|
+
if a.kind not in ("block", "loop") or a.end_line is None:
|
|
1963
|
+
continue
|
|
1964
|
+
listed = {_base(x) for x in _items(_clauses(tree, i,
|
|
1965
|
+
*_EXPLICIT_DSA))}
|
|
1966
|
+
body = [(ln, st) for ln, st, _ in tree.statements
|
|
1967
|
+
if a.line < ln < a.end_line
|
|
1968
|
+
or (a.kind == "loop" and ln == a.end_line)]
|
|
1969
|
+
dovars = {m.group(1) for _, st in body
|
|
1970
|
+
if (m := _DO_VAR.match(st))}
|
|
1971
|
+
if version == 50 and "teams" in leaves(d.name) and not \
|
|
1972
|
+
{"parallel", "task", "taskloop"} & set(leaves(d.name)):
|
|
1973
|
+
# 5.0 §2.19.1.1 p.271: a sequential loop's variable is private
|
|
1974
|
+
# only in a parallel or task generating construct (5.2 adds
|
|
1975
|
+
# teams), so in a teams region only the DO variables of loops
|
|
1976
|
+
# in a nested parallel or task construct, or of loops a loop
|
|
1977
|
+
# directive is associated with, are predetermined
|
|
1978
|
+
dovars = set(a.loop_vars or ()) if a.kind == "loop" else set()
|
|
1979
|
+
for j in tree.descendants(i):
|
|
1980
|
+
b = tree.assoc[j]
|
|
1981
|
+
if b.kind == "loop":
|
|
1982
|
+
dovars |= set(b.loop_vars or ())
|
|
1983
|
+
if {"parallel", "task", "taskloop"} & set(_leaves(tree, j)) \
|
|
1984
|
+
and b.end_line is not None:
|
|
1985
|
+
dovars |= {m.group(1) for ln, st, _ in tree.statements
|
|
1986
|
+
if b.line < ln < b.end_line
|
|
1987
|
+
and (m := _DO_VAR.match(st))}
|
|
1988
|
+
for _, st in body:
|
|
1989
|
+
names = set(re.findall(r"(?<![%\w])([a-z_]\w*)",
|
|
1990
|
+
_STRING.sub("", st)))
|
|
1991
|
+
hit = [x for x in names - listed - dovars - tprivate
|
|
1992
|
+
if tree.attribute(i, x) is not None
|
|
1993
|
+
and not any((sid, x) in tree.params
|
|
1994
|
+
for sid in a.scope)]
|
|
1995
|
+
if hit:
|
|
1996
|
+
out.append(i)
|
|
1997
|
+
break
|
|
1998
|
+
return out
|
|
1999
|
+
|
|
2000
|
+
|
|
2001
|
+
def _c_names(st: str) -> set:
|
|
2002
|
+
"""Identifiers of a C statement (not member names after . or ->)."""
|
|
2003
|
+
st = _STRING.sub("", st)
|
|
2004
|
+
return set(re.findall(r"(?<![\w.>])([a-z_]\w*)", st)) - {
|
|
2005
|
+
"for", "if", "while", "do", "int", "bool", "double", "return",
|
|
2006
|
+
"struct", "const", "static", "sizeof", "true", "false", "else",
|
|
2007
|
+
"switch", "case", "break", "continue", "void", "char", "long",
|
|
2008
|
+
"short", "unsigned", "signed", "float"}
|
|
2009
|
+
|
|
2010
|
+
|
|
2011
|
+
def _c_outside(tree, i, scopes, name) -> bool:
|
|
2012
|
+
"""Whether `name`, referenced in a statement with scope chain
|
|
2013
|
+
`scopes` inside construct i, is declared (visibly) outside the
|
|
2014
|
+
construct: its declaring scope, seen from the statement, is on the
|
|
2015
|
+
directive's own scope chain. A name declared inside the construct
|
|
2016
|
+
(a block or for-loop scope of its own) is private (5.2 §5.1.1: variables
|
|
2017
|
+
with automatic storage duration declared in a scope inside the
|
|
2018
|
+
construct)."""
|
|
2019
|
+
hit = tree._find(scopes, name, tree.decls)
|
|
2020
|
+
if hit is None:
|
|
2021
|
+
return False
|
|
2022
|
+
return hit[0][-1] in tree.assoc[i].scope
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
def _c_construct_refs(tree, i) -> set:
|
|
2026
|
+
"""(name) of the variables declared outside construct i that its
|
|
2027
|
+
statements reference (C); the iteration variables of the
|
|
2028
|
+
loops the construct is associated with are not counted (predetermined
|
|
2029
|
+
private, 5.2 §5.1.1)."""
|
|
2030
|
+
a = tree.assoc[i]
|
|
2031
|
+
if a.kind not in ("block", "loop") or a.end_line is None:
|
|
2032
|
+
return set()
|
|
2033
|
+
loopv = set(a.loop_vars or ())
|
|
2034
|
+
out = set()
|
|
2035
|
+
for ln, st, scopes in tree.statements:
|
|
2036
|
+
if not (a.line < ln <= a.end_line):
|
|
2037
|
+
continue
|
|
2038
|
+
for x in _c_names(st):
|
|
2039
|
+
if x in loopv or not _c_outside(tree, i, scopes, x):
|
|
2040
|
+
continue
|
|
2041
|
+
if any((s, x) in tree.params for s in scopes):
|
|
2042
|
+
continue
|
|
2043
|
+
key = tree._find(scopes, x, tree.decls)
|
|
2044
|
+
# a function name is not a variable
|
|
2045
|
+
if key is not None and tree.units and any(
|
|
2046
|
+
n == x for _, n in tree.units.values()) and \
|
|
2047
|
+
key[0][-1] == 0:
|
|
2048
|
+
continue
|
|
2049
|
+
out.add(x)
|
|
2050
|
+
return out
|
|
2051
|
+
|
|
2052
|
+
|
|
2053
|
+
def _c_default_none(tree, version):
|
|
2054
|
+
"""default(none) in C: a variable declared outside the
|
|
2055
|
+
construct that its statements reference, not listed in a data-sharing
|
|
2056
|
+
attribute clause, not a loop iteration variable of the construct, not
|
|
2057
|
+
threadprivate, not a named (enumeration) constant."""
|
|
2058
|
+
out = []
|
|
2059
|
+
tprivate = {_base(_norm(x)) for d in tree.directives
|
|
2060
|
+
if d.name == "threadprivate" for x in d.args}
|
|
2061
|
+
for i, d in enumerate(tree.directives):
|
|
2062
|
+
cl = [c for c in d.clauses if c.name == "default"]
|
|
2063
|
+
if not cl or not any(_norm(a).startswith("none")
|
|
2064
|
+
for c in cl for a in c.args):
|
|
2065
|
+
continue
|
|
2066
|
+
listed = {_base(x) for x in _items(_clauses(tree, i,
|
|
2067
|
+
*_EXPLICIT_DSA))}
|
|
2068
|
+
if _c_construct_refs(tree, i) - listed - tprivate:
|
|
2069
|
+
out.append(i)
|
|
2070
|
+
return out
|
|
2071
|
+
|
|
2072
|
+
|
|
2073
|
+
# =============================================================================
|
|
2074
|
+
# The remaining static restrictions
|
|
2075
|
+
# =============================================================================
|
|
2076
|
+
|
|
2077
|
+
def _decl_scope(tree: ProgramTree, i: int, name: str):
|
|
2078
|
+
"""The scoping unit that declares `name` as seen from directive i, or
|
|
2079
|
+
None (not declared, or a USE may supply it)."""
|
|
2080
|
+
hit = tree._find(tree.assoc[i].scope, name, tree.decls)
|
|
2081
|
+
return None if hit is None else hit[0][-1]
|
|
2082
|
+
|
|
2083
|
+
|
|
2084
|
+
def _vtype(tree: ProgramTree, i: int, name: str) -> str | None:
|
|
2085
|
+
s = _decl_scope(tree, i, name)
|
|
2086
|
+
return None if s is None else tree.vtypes.get((s, name))
|
|
2087
|
+
|
|
2088
|
+
|
|
2089
|
+
def _is_array(tree: ProgramTree, i: int, name: str) -> bool | None:
|
|
2090
|
+
s = _decl_scope(tree, i, name)
|
|
2091
|
+
return None if s is None else (s, name) in tree.arrays
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
def _outer_leaf_slots(tree: ProgramTree, i: int):
|
|
2095
|
+
"""(directive index, leaf position, leaf) of the enclosing constructs,
|
|
2096
|
+
innermost first."""
|
|
2097
|
+
for p in _ancestors(tree, i):
|
|
2098
|
+
ls = _leaves(tree, p)
|
|
2099
|
+
for k in range(len(ls) - 1, -1, -1):
|
|
2100
|
+
yield p, k, ls[k]
|
|
2101
|
+
|
|
2102
|
+
|
|
2103
|
+
def _on_leaf(tree, i, k, version, names) -> set:
|
|
2104
|
+
"""List items of directive i that the clause split puts on its leaf k
|
|
2105
|
+
in one of the clauses `names`."""
|
|
2106
|
+
split = _dsa_split(tree, i, version)
|
|
2107
|
+
return {x for x, cs in split[k].items() if set(cs) & set(names)} \
|
|
2108
|
+
if k < len(split) - 1 else set()
|
|
2109
|
+
|
|
2110
|
+
|
|
2111
|
+
_PRIVATIZING = ("private", "firstprivate", "lastprivate", "linear")
|
|
2112
|
+
_WS_LEAVES = {"do", "for", "sections", "single", "workshare", "scope"}
|
|
2113
|
+
|
|
2114
|
+
|
|
2115
|
+
def _ws_leaf(tree, i, leaf) -> bool:
|
|
2116
|
+
"""A worksharing leaf (5.2 §11: do, for, sections, single, workshare,
|
|
2117
|
+
scope; loop only with bind(parallel), when its binding region is the
|
|
2118
|
+
parallel region)."""
|
|
2119
|
+
return leaf in _WS_LEAVES or (
|
|
2120
|
+
leaf == "loop" and "parallel" in _items(_clauses(tree, i, "bind")))
|
|
2121
|
+
|
|
2122
|
+
|
|
2123
|
+
# --- nesting of regions --------------------------------------------------------
|
|
2124
|
+
|
|
2125
|
+
@predicate
|
|
2126
|
+
def worksharing_closely_nested(tree, version):
|
|
2127
|
+
"""A construct whose outermost leaf is a worksharing construct and whose
|
|
2128
|
+
closest enclosing construct, up to the nearest parallel, teams or target
|
|
2129
|
+
leaf, is a worksharing, task, taskloop, critical, ordered, atomic or
|
|
2130
|
+
masked construct (taskgroup and other constructs are passed through)."""
|
|
2131
|
+
bad_outer = {"task", "taskloop", "critical", "ordered (Block)", "atomic",
|
|
2132
|
+
"masked", "master"} | ({"loop"} if version == 50 else set())
|
|
2133
|
+
|
|
2134
|
+
def bad(i):
|
|
2135
|
+
if not _ws_leaf(tree, i, _leaves(tree, i)[0]):
|
|
2136
|
+
return False
|
|
2137
|
+
for p, leaf in _outer_leaves(tree, i):
|
|
2138
|
+
if leaf in ("parallel", "teams", "target"):
|
|
2139
|
+
return False
|
|
2140
|
+
if leaf in bad_outer or _ws_leaf(tree, p, leaf):
|
|
2141
|
+
return True
|
|
2142
|
+
return False
|
|
2143
|
+
return _each(tree, bad)
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
def _simdizable(tree, c, version) -> bool:
|
|
2147
|
+
name = tree.directives[c].name
|
|
2148
|
+
if not _is_construct(tree, c) or name in ("simd", "atomic"):
|
|
2149
|
+
return True
|
|
2150
|
+
if version >= 60 and name in _LOOP_TRANSFORMS:
|
|
2151
|
+
return True
|
|
2152
|
+
if name == "loop": # SIMDizable iff its binding region is not defined
|
|
2153
|
+
return not set(_items(_clauses(tree, c, "bind"))) & {"teams",
|
|
2154
|
+
"parallel"}
|
|
2155
|
+
if name == "ordered (Block)":
|
|
2156
|
+
return bool(_clauses(tree, c, "simd"))
|
|
2157
|
+
return False
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
@predicate
|
|
2161
|
+
def simd_simdizable_only(tree, version):
|
|
2162
|
+
"""A construct lexically inside a construct with a simd leaf that is not
|
|
2163
|
+
SIMDizable: simd, atomic, loop without bind(teams/parallel), ordered
|
|
2164
|
+
with the simd clause (6.0: and loop-transforming constructs)."""
|
|
2165
|
+
return sorted({c for i in range(len(tree.directives))
|
|
2166
|
+
if "simd" in _leaves(tree, i)
|
|
2167
|
+
for c in tree.descendants(i)
|
|
2168
|
+
if not _simdizable(tree, c, version)})
|
|
2169
|
+
|
|
2170
|
+
|
|
2171
|
+
@predicate
|
|
2172
|
+
def order_concurrent_no_api_calls(tree, version):
|
|
2173
|
+
"""A construct with order(concurrent) (a loop leaf without an order
|
|
2174
|
+
clause counts) whose lexical extent calls an OpenMP runtime routine
|
|
2175
|
+
(5.2: none may be called; 6.0: only order-concurrent-nestable routines,
|
|
2176
|
+
and no routine has that property)."""
|
|
2177
|
+
return _each(tree, lambda i: _order_concurrent(tree, i) and any(
|
|
2178
|
+
_api_calls(st) for _, st in _body(tree, i)))
|
|
2179
|
+
|
|
2180
|
+
|
|
2181
|
+
def _ordered_parent(tree, i):
|
|
2182
|
+
for p in _ancestors(tree, i):
|
|
2183
|
+
if tree.directives[p].name != "taskgroup":
|
|
2184
|
+
return p
|
|
2185
|
+
return None
|
|
2186
|
+
|
|
2187
|
+
|
|
2188
|
+
def _block_ordered(tree, i, simd: bool) -> bool:
|
|
2189
|
+
return tree.directives[i].name == "ordered (Block)" and (
|
|
2190
|
+
bool(_clauses(tree, i, "simd")) == simd)
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
@predicate
|
|
2194
|
+
def ordered_threads_in_worksharing_loop(tree, version):
|
|
2195
|
+
"""A block-associated ordered construct without the simd clause whose
|
|
2196
|
+
closest enclosing construct (taskgroup passed through) has no do leaf.
|
|
2197
|
+
An orphaned ordered construct is not flagged."""
|
|
2198
|
+
def bad(i):
|
|
2199
|
+
p = _ordered_parent(tree, i) if _block_ordered(tree, i, False) \
|
|
2200
|
+
else None
|
|
2201
|
+
return p is not None and not set(_leaves(tree, p)) & {"do", "for"}
|
|
2202
|
+
return _each(tree, bad)
|
|
2203
|
+
|
|
2204
|
+
|
|
2205
|
+
@predicate
|
|
2206
|
+
def ordered_simd_in_simd(tree, version):
|
|
2207
|
+
"""A block-associated ordered construct with the simd clause whose
|
|
2208
|
+
closest enclosing construct has no simd leaf (orphans not flagged)."""
|
|
2209
|
+
def bad(i):
|
|
2210
|
+
p = tree.parent(i) if _block_ordered(tree, i, True) else None
|
|
2211
|
+
return p is not None and "simd" not in _leaves(tree, p)
|
|
2212
|
+
return _each(tree, bad)
|
|
2213
|
+
|
|
2214
|
+
|
|
2215
|
+
@predicate
|
|
2216
|
+
def ordered_binding_has_ordered_clause(tree, version):
|
|
2217
|
+
"""A block-associated ordered construct without the simd clause closely
|
|
2218
|
+
nested in a construct with a do leaf that has no ordered clause, or an
|
|
2219
|
+
ordered(n) whose literal n is not 1 (5.2) / not the collapse value
|
|
2220
|
+
(6.0)."""
|
|
2221
|
+
def bad(i):
|
|
2222
|
+
p = _ordered_parent(tree, i) if _block_ordered(tree, i, False) \
|
|
2223
|
+
else None
|
|
2224
|
+
if p is None or not set(_leaves(tree, p)) & {"do", "for"}:
|
|
2225
|
+
return False
|
|
2226
|
+
cl = _clauses(tree, p, "ordered")
|
|
2227
|
+
if not cl:
|
|
2228
|
+
return True
|
|
2229
|
+
n = _int(cl[0].args[0]) if cl[0].args else None
|
|
2230
|
+
want = 1 if version < 60 else _collapse(tree, p)
|
|
2231
|
+
return n is not None and want is not None and n != want
|
|
2232
|
+
return _each(tree, bad)
|
|
2233
|
+
|
|
2234
|
+
|
|
2235
|
+
@predicate
|
|
2236
|
+
def workdistribute_in_teams(tree, version):
|
|
2237
|
+
"""A workdistribute constituent whose immediately enclosing construct is
|
|
2238
|
+
not a teams construct (orphaned: flagged, the restriction names the
|
|
2239
|
+
construct)."""
|
|
2240
|
+
return _each(tree, lambda i: "workdistribute" in _leaves(tree, i)
|
|
2241
|
+
and _enclosing_leaf(tree, i, "workdistribute") != "teams")
|
|
2242
|
+
|
|
2243
|
+
|
|
2244
|
+
@predicate
|
|
2245
|
+
def workdistribute_no_nested_region(tree, version):
|
|
2246
|
+
"""A construct lexically inside a workdistribute construct."""
|
|
2247
|
+
return sorted({c for i in range(len(tree.directives))
|
|
2248
|
+
if "workdistribute" in _leaves(tree, i)
|
|
2249
|
+
for c in tree.descendants(i) if _is_construct(tree, c)})
|
|
2250
|
+
|
|
2251
|
+
|
|
2252
|
+
_ASSIGNMENT = re.compile(r"^[a-z_]\w*\s*(?:\([^=]*\))?(?:\s*%\s*[a-z_]\w*\s*"
|
|
2253
|
+
r"(?:\([^=]*\))?)*\s*=(?!=)")
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
@predicate
|
|
2257
|
+
def workdistribute_statements(tree, version):
|
|
2258
|
+
"""A base-language statement in a workdistribute construct that is not
|
|
2259
|
+
an assignment or a CALL (BLOCK / END BLOCK delimiters aside); whether a
|
|
2260
|
+
called procedure is pure and elemental is not checked."""
|
|
2261
|
+
def bad(i):
|
|
2262
|
+
return "workdistribute" in _leaves(tree, i) and any(
|
|
2263
|
+
not (_ASSIGNMENT.match(st) or re.match(r"^call\b", st)
|
|
2264
|
+
or _BLOCK.match(st) or _END_BLOCK.match(st))
|
|
2265
|
+
for _, st in _body(tree, i))
|
|
2266
|
+
return _each(tree, bad)
|
|
2267
|
+
|
|
2268
|
+
|
|
2269
|
+
# --- compound constructs and clause values --------------------------------------
|
|
2270
|
+
|
|
2271
|
+
def _assoc_vars(tree, i) -> tuple | None:
|
|
2272
|
+
"""Iteration variables of the loops associated with loop directive i,
|
|
2273
|
+
or None when unknown."""
|
|
2274
|
+
a = tree.assoc[i]
|
|
2275
|
+
n = _assoc_count(tree, i)
|
|
2276
|
+
if a.kind != "loop" or not a.loop_depth or n is None:
|
|
2277
|
+
return None
|
|
2278
|
+
if n > a.loop_depth and a.depth_known:
|
|
2279
|
+
return None # collapse-loop-001 reports it
|
|
2280
|
+
return a.loop_vars[:n] if n <= a.loop_depth else None
|
|
2281
|
+
|
|
2282
|
+
|
|
2283
|
+
@predicate
|
|
2284
|
+
def distribute_linear_loop_vars(tree, version):
|
|
2285
|
+
"""A compound with a distribute leaf and a linear list item that is not
|
|
2286
|
+
the iteration variable of an associated loop."""
|
|
2287
|
+
def bad(i):
|
|
2288
|
+
ls = _leaves(tree, i)
|
|
2289
|
+
vs = _assoc_vars(tree, i)
|
|
2290
|
+
return "distribute" in ls and len(ls) > 1 and vs is not None and any(
|
|
2291
|
+
_base(x) not in vs for x in _items(_clauses(tree, i, "linear")))
|
|
2292
|
+
return _each(tree, bad)
|
|
2293
|
+
|
|
2294
|
+
|
|
2295
|
+
@predicate
|
|
2296
|
+
def schedule_runtime_auto_no_chunk(tree, version):
|
|
2297
|
+
"""schedule(runtime, chunk) or schedule(auto, chunk)."""
|
|
2298
|
+
return _each(tree, lambda i: any(
|
|
2299
|
+
c.args and _norm(c.args[0]) in ("runtime", "auto") and len(c.args) > 1
|
|
2300
|
+
for c in _clauses(tree, i, "schedule")))
|
|
2301
|
+
|
|
2302
|
+
|
|
2303
|
+
@predicate
|
|
2304
|
+
def schedule_nonmonotonic_no_ordered(tree, version):
|
|
2305
|
+
"""schedule with the nonmonotonic modifier and an ordered clause on the
|
|
2306
|
+
same directive."""
|
|
2307
|
+
return _each(tree, lambda i: any(
|
|
2308
|
+
"nonmonotonic" in c.modifiers for c in _clauses(tree, i, "schedule"))
|
|
2309
|
+
and bool(_clauses(tree, i, "ordered")))
|
|
2310
|
+
|
|
2311
|
+
|
|
2312
|
+
def _lit(tree, i, name) -> int | None:
|
|
2313
|
+
cs = _clauses(tree, i, name)
|
|
2314
|
+
return _int(cs[0].args[0]) if cs and cs[0].args else None
|
|
2315
|
+
|
|
2316
|
+
|
|
2317
|
+
@predicate
|
|
2318
|
+
def simdlen_le_safelen(tree, version):
|
|
2319
|
+
"""simdlen and safelen with integer literals, simdlen > safelen."""
|
|
2320
|
+
def bad(i):
|
|
2321
|
+
a, b = _lit(tree, i, "simdlen"), _lit(tree, i, "safelen")
|
|
2322
|
+
return a is not None and b is not None and a > b
|
|
2323
|
+
return _each(tree, bad)
|
|
2324
|
+
|
|
2325
|
+
|
|
2326
|
+
@predicate
|
|
2327
|
+
def simd_order_concurrent_no_safelen(tree, version):
|
|
2328
|
+
"""A directive with a simd leaf, order(concurrent) and safelen."""
|
|
2329
|
+
return _each(tree, lambda i: "simd" in _leaves(tree, i)
|
|
2330
|
+
and "concurrent" in _items(_clauses(tree, i, "order"))
|
|
2331
|
+
and bool(_clauses(tree, i, "safelen")))
|
|
2332
|
+
|
|
2333
|
+
|
|
2334
|
+
@predicate
|
|
2335
|
+
def positive_arguments(tree, version):
|
|
2336
|
+
"""An integer literal less than one as an argument with the positive
|
|
2337
|
+
property: num_threads, safelen, simdlen, the schedule chunk_size, the
|
|
2338
|
+
ordered parameter, the aligned alignment (thread_limit, num_teams and
|
|
2339
|
+
dist_schedule have their own rules)."""
|
|
2340
|
+
def values(i):
|
|
2341
|
+
out = []
|
|
2342
|
+
for c in _clauses(tree, i, "num_threads", "safelen", "simdlen",
|
|
2343
|
+
"ordered"):
|
|
2344
|
+
out += c.args if c.name == "num_threads" else c.args[:1]
|
|
2345
|
+
for c in _clauses(tree, i, "schedule"):
|
|
2346
|
+
out += c.args[1:2]
|
|
2347
|
+
for c in _clauses(tree, i, "aligned"):
|
|
2348
|
+
out += [m for m in c.modifiers if _compact(m) not in _names()]
|
|
2349
|
+
return out
|
|
2350
|
+
return _each(tree, lambda i: _not_positive(values(i)))
|
|
2351
|
+
|
|
2352
|
+
|
|
2353
|
+
@predicate
|
|
2354
|
+
def constant_arguments(tree, version):
|
|
2355
|
+
"""safelen, simdlen or ordered(n) whose argument is a variable declared
|
|
2356
|
+
in the source (not a named constant)."""
|
|
2357
|
+
def bad(i):
|
|
2358
|
+
for c in _clauses(tree, i, "safelen", "simdlen", "ordered"):
|
|
2359
|
+
x = _norm(c.args[0]) if c.args else ""
|
|
2360
|
+
if re.fullmatch(r"[a-z_]\w*", x) \
|
|
2361
|
+
and tree.attribute(i, x) is not None \
|
|
2362
|
+
and not any((s, x) in tree.params
|
|
2363
|
+
for s in tree.assoc[i].scope):
|
|
2364
|
+
return True
|
|
2365
|
+
return False
|
|
2366
|
+
return _each(tree, bad)
|
|
2367
|
+
|
|
2368
|
+
|
|
2369
|
+
# clause sets (5.2 §3.2, 6.0 §5.2) the skeleton does not enforce
|
|
2370
|
+
_REQUIRED_SETS = {
|
|
2371
|
+
52: {"target update": {"to", "from"}, "scan": {"inclusive", "exclusive"},
|
|
2372
|
+
"depobj": {"depend", "destroy", "update"}},
|
|
2373
|
+
60: {"target update": {"to", "from"},
|
|
2374
|
+
"scan": {"inclusive", "exclusive", "init_complete"},
|
|
2375
|
+
"depobj": {"destroy", "init", "update", "depend"}},
|
|
2376
|
+
}
|
|
2377
|
+
_EXCLUSIVE_SETS = {
|
|
2378
|
+
52: {"scan": [{"inclusive", "exclusive"}],
|
|
2379
|
+
"depobj": [{"depend", "destroy", "update"}],
|
|
2380
|
+
"task": [{"detach", "mergeable"}],
|
|
2381
|
+
"taskloop": [{"nogroup", "reduction"}, {"grainsize", "num_tasks"}]},
|
|
2382
|
+
60: {"scan": [{"inclusive", "exclusive", "init_complete"}],
|
|
2383
|
+
"single": [{"copyprivate", "nowait"}],
|
|
2384
|
+
"task": [{"detach", "mergeable"}],
|
|
2385
|
+
"taskloop": [{"nogroup", "reduction"}, {"grainsize", "num_tasks"}]},
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
|
|
2389
|
+
@predicate
|
|
2390
|
+
def clause_set_required(tree, version):
|
|
2391
|
+
"""target update without to/from, scan without inclusive/exclusive,
|
|
2392
|
+
depobj without depend/destroy/update (6.0: init/destroy/update)."""
|
|
2393
|
+
sets = _REQUIRED_SETS[52 if version < 60 else 60]
|
|
2394
|
+
return _each(tree, lambda i: tree.directives[i].name in sets and not (
|
|
2395
|
+
{c.name for c in _clauses(tree, i, end=True)}
|
|
2396
|
+
& sets[tree.directives[i].name]))
|
|
2397
|
+
|
|
2398
|
+
|
|
2399
|
+
@predicate
|
|
2400
|
+
def clause_set_exclusive(tree, version):
|
|
2401
|
+
"""Two clauses with different names from one exclusive clause set on a
|
|
2402
|
+
directive (scan, depobj (5.2), single (6.0), task, taskloop)."""
|
|
2403
|
+
sets = _EXCLUSIVE_SETS[52 if version < 60 else 60]
|
|
2404
|
+
return _each(tree, lambda i: any(
|
|
2405
|
+
len({c.name for c in _clauses(tree, i, end=True)} & s) > 1
|
|
2406
|
+
for s in sets.get(tree.directives[i].name, ())))
|
|
2407
|
+
|
|
2408
|
+
|
|
2409
|
+
@predicate
|
|
2410
|
+
def single_copyprivate_nowait(tree, version):
|
|
2411
|
+
"""single with copyprivate and nowait (on the directive or its end
|
|
2412
|
+
directive)."""
|
|
2413
|
+
return _each(tree, lambda i: tree.directives[i].name == "single"
|
|
2414
|
+
and bool(_clauses(tree, i, "copyprivate", end=True))
|
|
2415
|
+
and bool(_clauses(tree, i, "nowait", end=True)))
|
|
2416
|
+
|
|
2417
|
+
|
|
2418
|
+
_UNIQUE_LISTS = set(_DSA) | {"to", "from", "enter", "link", "local",
|
|
2419
|
+
"aligned", "nontemporal", "copyin",
|
|
2420
|
+
"copyprivate", "allocate", "inclusive",
|
|
2421
|
+
"exclusive"}
|
|
2422
|
+
|
|
2423
|
+
|
|
2424
|
+
@predicate
|
|
2425
|
+
def list_item_directive_wide_unique(tree, version):
|
|
2426
|
+
"""A list item twice in one clause, in two clauses of the same name
|
|
2427
|
+
(not data-sharing clauses, which datasharing-dsa-001 checks after the
|
|
2428
|
+
split; not map or depend, where the spec allows repeats), in both to
|
|
2429
|
+
and from, or in two of enter, link and local, of one directive."""
|
|
2430
|
+
def bad(i):
|
|
2431
|
+
cs = [c for c in _clauses(tree, i, end=True)
|
|
2432
|
+
if c.name in _UNIQUE_LISTS]
|
|
2433
|
+
if tree.directives[i].name == "declare target" \
|
|
2434
|
+
and len(set(map(_norm, tree.directives[i].args))) \
|
|
2435
|
+
< len(tree.directives[i].args):
|
|
2436
|
+
return True
|
|
2437
|
+
by = {}
|
|
2438
|
+
for c in cs:
|
|
2439
|
+
xs = _items([c])
|
|
2440
|
+
if len(xs) != len(set(xs)):
|
|
2441
|
+
return True
|
|
2442
|
+
by.setdefault(c.name, []).extend(set(xs))
|
|
2443
|
+
if any(len(xs) != len(set(xs)) for n, xs in by.items()
|
|
2444
|
+
if n not in _DSA):
|
|
2445
|
+
return True
|
|
2446
|
+
pairs = [("to", "from"), ("enter", "link"), ("enter", "local"),
|
|
2447
|
+
("link", "local"), ("to", "link"), ("to", "local")]
|
|
2448
|
+
return any(set(by.get(a, ())) & set(by.get(b, ())) for a, b in pairs)
|
|
2449
|
+
return _each(tree, bad)
|
|
2450
|
+
|
|
2451
|
+
|
|
2452
|
+
@predicate
|
|
2453
|
+
def linear_inscan_loop_vars(tree, version):
|
|
2454
|
+
"""reduction(inscan, ...) and a linear list item that is not the
|
|
2455
|
+
iteration variable of an associated loop, on one directive."""
|
|
2456
|
+
def bad(i):
|
|
2457
|
+
vs = _assoc_vars(tree, i)
|
|
2458
|
+
return bool(_reductions(tree, i, "inscan")) and vs is not None and any(
|
|
2459
|
+
_base(x) not in vs for x in _items(_clauses(tree, i, "linear")))
|
|
2460
|
+
return _each(tree, bad)
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
def _linear_ref_uval(c) -> bool:
|
|
2464
|
+
return any(re.match(r"^(?:ref|uval)\s*\(", _norm(a)) for a in c.args) \
|
|
2465
|
+
or bool({"ref", "uval"} & set(map(_norm, c.modifiers)))
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
@predicate
|
|
2469
|
+
def linear_ref_uval_declare_simd(tree, version):
|
|
2470
|
+
"""A linear clause with the ref or uval modifier on a directive other
|
|
2471
|
+
than declare simd (5.0: any modifier, val included, 5.0 §2.9.2 p.107,
|
|
2472
|
+
§2.9.3.1 p.113)."""
|
|
2473
|
+
def has_mod(c):
|
|
2474
|
+
if version == 50:
|
|
2475
|
+
return bool({"ref", "uval", "val"} & set(map(_norm, c.modifiers)))
|
|
2476
|
+
return _linear_ref_uval(c)
|
|
2477
|
+
return _each(tree, lambda i: tree.directives[i].name != "declare simd"
|
|
2478
|
+
and any(has_mod(c) for c in _clauses(tree, i, "linear")))
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
def _linear_names(c) -> list[str]:
|
|
2482
|
+
return [_base(re.sub(r"^(?:ref|uval|val)\((.*)\)$", r"\1", _norm(a)))
|
|
2483
|
+
for a in c.args]
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
@predicate
|
|
2487
|
+
def linear_not_pointer(tree, version):
|
|
2488
|
+
"""A linear list item declared with the POINTER attribute."""
|
|
2489
|
+
return _each(tree, lambda i: any(
|
|
2490
|
+
tree.attribute(i, x) == "pointer"
|
|
2491
|
+
for c in _clauses(tree, i, "linear") for x in _linear_names(c)))
|
|
2492
|
+
|
|
2493
|
+
|
|
2494
|
+
@predicate
|
|
2495
|
+
def linear_integer(tree, version):
|
|
2496
|
+
"""A linear list item (without ref) declared with an intrinsic type
|
|
2497
|
+
other than integer, or a derived type."""
|
|
2498
|
+
return _each(tree, lambda i: any(
|
|
2499
|
+
_vtype(tree, i, x) not in (None, "integer")
|
|
2500
|
+
for c in _clauses(tree, i, "linear") if not _linear_ref_uval(c)
|
|
2501
|
+
for x in _linear_names(c)))
|
|
2502
|
+
|
|
2503
|
+
|
|
2504
|
+
@predicate
|
|
2505
|
+
def loop_var_predetermined_dsa(tree, version):
|
|
2506
|
+
"""After the clause split, the iteration variable of an associated loop
|
|
2507
|
+
in a data-sharing clause on a loop leaf (do, simd, distribute, loop,
|
|
2508
|
+
taskloop) other than private or lastprivate (or linear on a simd leaf
|
|
2509
|
+
with one associated loop)."""
|
|
2510
|
+
loop_leaves = {"do", "for", "simd", "distribute", "loop", "taskloop"}
|
|
2511
|
+
|
|
2512
|
+
def bad(i):
|
|
2513
|
+
vs = _assoc_vars(tree, i)
|
|
2514
|
+
if not vs:
|
|
2515
|
+
return False
|
|
2516
|
+
ls = _rule_leaves(tree.directives[i].name, version)
|
|
2517
|
+
split = _dsa_split(tree, i, version)
|
|
2518
|
+
for k, leaf in enumerate(ls):
|
|
2519
|
+
if leaf not in loop_leaves:
|
|
2520
|
+
continue
|
|
2521
|
+
for x, names in split[k].items():
|
|
2522
|
+
if x not in vs:
|
|
2523
|
+
continue
|
|
2524
|
+
for nm in names:
|
|
2525
|
+
if nm in ("private", "lastprivate") or (
|
|
2526
|
+
nm == "linear" and leaf == "simd"
|
|
2527
|
+
and len(vs) == 1):
|
|
2528
|
+
continue
|
|
2529
|
+
return True
|
|
2530
|
+
return False
|
|
2531
|
+
return _each(tree, bad)
|
|
2532
|
+
|
|
2533
|
+
|
|
2534
|
+
# --- data-sharing across nested constructs ---------------------------------------
|
|
2535
|
+
|
|
2536
|
+
def _binding(tree, i, kinds, stop) -> tuple | None:
|
|
2537
|
+
"""(directive, leaf position, leaf) of the closest enclosing leaf in
|
|
2538
|
+
`kinds`, if no leaf in `stop` comes first."""
|
|
2539
|
+
for p, k, leaf in _outer_leaf_slots(tree, i):
|
|
2540
|
+
if leaf in kinds:
|
|
2541
|
+
return p, k, leaf
|
|
2542
|
+
if leaf in stop:
|
|
2543
|
+
return None
|
|
2544
|
+
return None
|
|
2545
|
+
|
|
2546
|
+
|
|
2547
|
+
_GENERATING = {"parallel", "teams", "target", "task", "taskloop"}
|
|
2548
|
+
|
|
2549
|
+
|
|
2550
|
+
def _items_on(tree, i, version, leaf, names) -> set:
|
|
2551
|
+
ls = _rule_leaves(tree.directives[i].name, version)
|
|
2552
|
+
return {_base(x) for k, lf in enumerate(ls) if lf == leaf
|
|
2553
|
+
for x in _on_leaf(tree, i, k, version, names)}
|
|
2554
|
+
|
|
2555
|
+
|
|
2556
|
+
def _private_in(tree, p, k, version, names=_PRIVATIZING) -> set:
|
|
2557
|
+
return {_base(x) for x in _on_leaf(tree, p, k, version, names)}
|
|
2558
|
+
|
|
2559
|
+
|
|
2560
|
+
def _nested_dsa_rule(inner_leaves, inner_clauses, outer, outer_clauses,
|
|
2561
|
+
doc):
|
|
2562
|
+
"""A predicate: an item in `inner_clauses` on an inner leaf in
|
|
2563
|
+
`inner_leaves` (worksharing leaves for "ws") that is in `outer_clauses`
|
|
2564
|
+
on the closest enclosing `outer` leaf (no other generating leaf
|
|
2565
|
+
between)."""
|
|
2566
|
+
def fn(tree, version):
|
|
2567
|
+
def bad(i):
|
|
2568
|
+
ls = _rule_leaves(tree.directives[i].name, version)
|
|
2569
|
+
if ls[0] not in inner_leaves and not (
|
|
2570
|
+
"ws" in inner_leaves and _ws_leaf(tree, i, ls[0])):
|
|
2571
|
+
return False
|
|
2572
|
+
items = _items_on(tree, i, version, ls[0], inner_clauses)
|
|
2573
|
+
if not items:
|
|
2574
|
+
return False
|
|
2575
|
+
hit = _binding(tree, i, {outer}, _GENERATING - {outer})
|
|
2576
|
+
if hit is None:
|
|
2577
|
+
return False
|
|
2578
|
+
p, k, _ = hit
|
|
2579
|
+
return bool(items & _private_in(tree, p, k, version,
|
|
2580
|
+
outer_clauses))
|
|
2581
|
+
return _each(tree, bad)
|
|
2582
|
+
fn.__doc__ = doc
|
|
2583
|
+
return fn
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
def _register(name, fn):
|
|
2587
|
+
fn.__name__ = name
|
|
2588
|
+
return predicate(fn)
|
|
2589
|
+
|
|
2590
|
+
|
|
2591
|
+
_register("firstprivate_private_in_parallel", _nested_dsa_rule(
|
|
2592
|
+
{"ws"}, ("firstprivate",), "parallel", _PRIVATIZING,
|
|
2593
|
+
"""firstprivate on a worksharing construct of an item that is private
|
|
2594
|
+
(private, firstprivate, lastprivate, linear) on the closest enclosing
|
|
2595
|
+
parallel leaf."""))
|
|
2596
|
+
_register("firstprivate_private_in_teams", _nested_dsa_rule(
|
|
2597
|
+
{"distribute"}, ("firstprivate",), "teams", _PRIVATIZING,
|
|
2598
|
+
"""firstprivate on a distribute construct of an item that is private on
|
|
2599
|
+
the closest enclosing teams leaf."""))
|
|
2600
|
+
_register("firstprivate_reduction_in_parallel_ws", _nested_dsa_rule(
|
|
2601
|
+
{"ws", "task", "taskloop"}, ("firstprivate",), "parallel",
|
|
2602
|
+
("reduction",),
|
|
2603
|
+
"""firstprivate on a worksharing, task or taskloop construct of an item
|
|
2604
|
+
in a reduction clause of the closest enclosing parallel leaf (5.2)."""))
|
|
2605
|
+
_register("firstprivate_reduction_in_parallel_task", _nested_dsa_rule(
|
|
2606
|
+
{"task", "taskloop"}, ("firstprivate",), "parallel", ("reduction",),
|
|
2607
|
+
"""firstprivate on a task or taskloop construct of an item in a
|
|
2608
|
+
reduction clause of the closest enclosing parallel leaf (6.0)."""))
|
|
2609
|
+
_register("firstprivate_reduction_in_teams", _nested_dsa_rule(
|
|
2610
|
+
{"distribute"}, ("firstprivate",), "teams", ("reduction",),
|
|
2611
|
+
"""firstprivate on a distribute construct of an item in a reduction
|
|
2612
|
+
clause of the closest enclosing teams leaf (5.2)."""))
|
|
2613
|
+
_register("reduction_shared_in_parallel", _nested_dsa_rule(
|
|
2614
|
+
{"ws"}, ("reduction",), "parallel", _PRIVATIZING + ("reduction",),
|
|
2615
|
+
"""reduction on a worksharing construct of an item that is private
|
|
2616
|
+
(private, firstprivate, lastprivate, linear, reduction) on the closest
|
|
2617
|
+
enclosing parallel leaf."""))
|
|
2618
|
+
_register("reduction_shared_in_teams", _nested_dsa_rule(
|
|
2619
|
+
{"distribute", "loop"}, ("reduction",), "teams",
|
|
2620
|
+
_PRIVATIZING + ("reduction",),
|
|
2621
|
+
"""reduction on a distribute or loop construct of an item that is
|
|
2622
|
+
private on the closest enclosing teams leaf (6.0)."""))
|
|
2623
|
+
|
|
2624
|
+
|
|
2625
|
+
@predicate
|
|
2626
|
+
def lastprivate_private_in_binding(tree, version):
|
|
2627
|
+
"""lastprivate on a work-distribution leaf (worksharing leaves,
|
|
2628
|
+
distribute, loop) of an item that is private on the closest enclosing
|
|
2629
|
+
parallel (distribute: teams) leaf."""
|
|
2630
|
+
def bad(i):
|
|
2631
|
+
ls = _rule_leaves(tree.directives[i].name, version)
|
|
2632
|
+
first = ls[0]
|
|
2633
|
+
if first == "distribute":
|
|
2634
|
+
outer = "teams"
|
|
2635
|
+
elif _ws_leaf(tree, i, first) or first == "loop":
|
|
2636
|
+
outer = "parallel"
|
|
2637
|
+
else:
|
|
2638
|
+
return False
|
|
2639
|
+
items = _items_on(tree, i, version, first, ("lastprivate",))
|
|
2640
|
+
hit = _binding(tree, i, {outer}, _GENERATING - {outer}) \
|
|
2641
|
+
if items else None
|
|
2642
|
+
return hit is not None and bool(
|
|
2643
|
+
items & _private_in(tree, hit[0], hit[1], version))
|
|
2644
|
+
return _each(tree, bad)
|
|
2645
|
+
|
|
2646
|
+
|
|
2647
|
+
@predicate
|
|
2648
|
+
def lastprivate_conditional_scalar(tree, version):
|
|
2649
|
+
"""lastprivate(conditional: x) with x declared as an array."""
|
|
2650
|
+
return _each(tree, lambda i: any(
|
|
2651
|
+
_is_array(tree, i, _base(x))
|
|
2652
|
+
for c in _clauses(tree, i, "lastprivate")
|
|
2653
|
+
if "conditional" in c.modifiers for x in _items([c])))
|
|
2654
|
+
|
|
2655
|
+
|
|
2656
|
+
def _tp_keys(tree) -> set:
|
|
2657
|
+
"""(declaring scope, name) of threadprivate variables."""
|
|
2658
|
+
out = set()
|
|
2659
|
+
for j, d in enumerate(tree.directives):
|
|
2660
|
+
if d.name == "threadprivate":
|
|
2661
|
+
for x in d.args:
|
|
2662
|
+
x = _norm(x)
|
|
2663
|
+
if not x.startswith("/"):
|
|
2664
|
+
s = _decl_scope(tree, j, x)
|
|
2665
|
+
out.add((s if s is not None else
|
|
2666
|
+
tree.assoc[j].scope[-1], x))
|
|
2667
|
+
return out
|
|
2668
|
+
|
|
2669
|
+
|
|
2670
|
+
def _key(tree, i, x) -> tuple:
|
|
2671
|
+
s = _decl_scope(tree, i, x)
|
|
2672
|
+
return (s if s is not None else tree.assoc[i].scope[-1], x)
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
@predicate
|
|
2676
|
+
def copyin_threadprivate(tree, version):
|
|
2677
|
+
"""A copyin list item (not a common block) declared in the source,
|
|
2678
|
+
visible from the directive, that no threadprivate directive names."""
|
|
2679
|
+
def bad(i):
|
|
2680
|
+
tp = _tp_keys(tree)
|
|
2681
|
+
return any(not x.startswith("/") and tree.attribute(i, x) is not None
|
|
2682
|
+
and _key(tree, i, x) not in tp
|
|
2683
|
+
for x in map(_base, _items(_clauses(tree, i, "copyin"))))
|
|
2684
|
+
return _each(tree, bad)
|
|
2685
|
+
|
|
2686
|
+
|
|
2687
|
+
@predicate
|
|
2688
|
+
def threadprivate_only_copyin(tree, version):
|
|
2689
|
+
"""A threadprivate variable as the base of a list item of a clause
|
|
2690
|
+
other than copyin and copyprivate."""
|
|
2691
|
+
def bad(i):
|
|
2692
|
+
tp = _tp_keys(tree)
|
|
2693
|
+
return tree.directives[i].name != "threadprivate" and any(
|
|
2694
|
+
_key(tree, i, _base(x)) in tp
|
|
2695
|
+
for c in _clauses(tree, i, end=True)
|
|
2696
|
+
if c.name not in ("copyin", "copyprivate")
|
|
2697
|
+
and c.name in _UNIQUE_LISTS | {"map", "depend"}
|
|
2698
|
+
for x in _items([c]))
|
|
2699
|
+
return _each(tree, bad)
|
|
2700
|
+
|
|
2701
|
+
|
|
2702
|
+
@predicate
|
|
2703
|
+
def threadprivate_in_specification_part(tree, version):
|
|
2704
|
+
"""A threadprivate directive lexically inside a construct (an execution
|
|
2705
|
+
part)."""
|
|
2706
|
+
return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
|
|
2707
|
+
and tree.parent(i) is not None)
|
|
2708
|
+
|
|
2709
|
+
|
|
2710
|
+
def _foreign_scope(tree, i, x) -> bool:
|
|
2711
|
+
"""x is declared visibly from directive i, in another scoping unit."""
|
|
2712
|
+
s = _decl_scope(tree, i, _norm(x))
|
|
2713
|
+
return s is not None and s != tree.assoc[i].scope[-1]
|
|
2714
|
+
|
|
2715
|
+
|
|
2716
|
+
@predicate
|
|
2717
|
+
def threadprivate_declaring_scope(tree, version):
|
|
2718
|
+
"""A threadprivate variable declared (visibly) in another scoping unit
|
|
2719
|
+
than the directive's."""
|
|
2720
|
+
return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
|
|
2721
|
+
and any(_foreign_scope(tree, i, x) for x in
|
|
2722
|
+
tree.directives[i].args
|
|
2723
|
+
if not _norm(x).startswith("/")))
|
|
2724
|
+
|
|
2725
|
+
|
|
2726
|
+
def _not_saved(tree, i, x) -> bool:
|
|
2727
|
+
"""x is declared in a subprogram (not a program, module or submodule)
|
|
2728
|
+
without SAVE (attribute, statement, bare SAVE, or initializer); a
|
|
2729
|
+
procedure of the source (a function's name is also its result
|
|
2730
|
+
variable's) is not a variable."""
|
|
2731
|
+
s = _decl_scope(tree, i, x)
|
|
2732
|
+
if s is None or s == 0 or any(n == x for _, n in tree.units.values()):
|
|
2733
|
+
return False
|
|
2734
|
+
if getattr(tree, "lang", "f90") == "c":
|
|
2735
|
+
# C: static storage duration = file scope or static
|
|
2736
|
+
return (s, x) not in tree.saved
|
|
2737
|
+
kind = tree.units.get(s, ("", None))[0]
|
|
2738
|
+
if kind in ("program", "module", "submodule", "blockdata", ""):
|
|
2739
|
+
return False
|
|
2740
|
+
return (s, x) not in tree.saved and (s, "*") not in tree.saved
|
|
2741
|
+
|
|
2742
|
+
|
|
2743
|
+
@predicate
|
|
2744
|
+
def threadprivate_saved(tree, version):
|
|
2745
|
+
"""A threadprivate variable declared in a subprogram without the SAVE
|
|
2746
|
+
attribute."""
|
|
2747
|
+
return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
|
|
2748
|
+
and any(_not_saved(tree, i, _norm(x))
|
|
2749
|
+
for x in tree.directives[i].args
|
|
2750
|
+
if not _norm(x).startswith("/")))
|
|
2751
|
+
|
|
2752
|
+
|
|
2753
|
+
def _dt_items(tree, i, *names) -> list[str]:
|
|
2754
|
+
d = tree.directives[i]
|
|
2755
|
+
xs = _items(_clauses(tree, i, *names))
|
|
2756
|
+
if "enter" in names:
|
|
2757
|
+
xs += [_norm(a) for a in d.args]
|
|
2758
|
+
return [x for x in xs if not x.startswith("/")]
|
|
2759
|
+
|
|
2760
|
+
|
|
2761
|
+
@predicate
|
|
2762
|
+
def declare_target_enter_saved(tree, version):
|
|
2763
|
+
"""A declare target variable (5.2: any list item; 6.0: in enter or
|
|
2764
|
+
the extended list) declared in a subprogram without SAVE."""
|
|
2765
|
+
names = ("enter", "to", "link", "local") if version < 60 else (
|
|
2766
|
+
"enter", "to")
|
|
2767
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
2768
|
+
and any(_not_saved(tree, i, x)
|
|
2769
|
+
for x in _dt_items(tree, i, *names)))
|
|
2770
|
+
|
|
2771
|
+
|
|
2772
|
+
@predicate
|
|
2773
|
+
def declare_target_link_saved(tree, version):
|
|
2774
|
+
"""A link list item declared in a subprogram without SAVE (6.0)."""
|
|
2775
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
2776
|
+
and any(_not_saved(tree, i, x)
|
|
2777
|
+
for x in _dt_items(tree, i, "link")))
|
|
2778
|
+
|
|
2779
|
+
|
|
2780
|
+
@predicate
|
|
2781
|
+
def declare_target_declaring_scope(tree, version):
|
|
2782
|
+
"""A declare target variable declared (visibly) in another scoping
|
|
2783
|
+
unit than the directive's."""
|
|
2784
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
2785
|
+
and any(_foreign_scope(tree, i, x) for x in _dt_items(
|
|
2786
|
+
tree, i, "enter", "to", "link", "local")))
|
|
2787
|
+
|
|
2788
|
+
|
|
2789
|
+
@predicate
|
|
2790
|
+
def declare_target_procedure_placement(tree, version):
|
|
2791
|
+
"""A declare target without an extended list and without enter, to,
|
|
2792
|
+
link or local clauses (5.2: no clauses or only device_type/indirect;
|
|
2793
|
+
6.0: a declaration-associated directive; 5.0: no clauses at all, 5.0
|
|
2794
|
+
§2.12.7 p.184) in a program, module or submodule scoping unit."""
|
|
2795
|
+
def bad(i):
|
|
2796
|
+
d = tree.directives[i]
|
|
2797
|
+
if d.name != "declare target" or d.args or _clauses(
|
|
2798
|
+
tree, i, "enter", "to", "link", "local"):
|
|
2799
|
+
return False
|
|
2800
|
+
if version == 50 and d.clauses:
|
|
2801
|
+
return False
|
|
2802
|
+
kind = tree.units.get(tree.assoc[i].scope[-1], ("", None))[0]
|
|
2803
|
+
return kind in ("program", "module", "submodule", "blockdata")
|
|
2804
|
+
return _each(tree, bad)
|
|
2805
|
+
|
|
2806
|
+
|
|
2807
|
+
def _cross_directive(tree, first: tuple, second: tuple) -> list[int]:
|
|
2808
|
+
"""declare target directives naming an item in a clause of `second`
|
|
2809
|
+
that another declare target directive names in `first` (the extended
|
|
2810
|
+
list counts as enter)."""
|
|
2811
|
+
seen = {}
|
|
2812
|
+
for i, d in enumerate(tree.directives):
|
|
2813
|
+
if d.name != "declare target":
|
|
2814
|
+
continue
|
|
2815
|
+
for kind, names in (("a", first), ("b", second)):
|
|
2816
|
+
for x in _dt_items(tree, i, *names) if "enter" in names else [
|
|
2817
|
+
x for x in _items(_clauses(tree, i, *names))]:
|
|
2818
|
+
seen.setdefault(_key(tree, i, _base(x)), []).append((kind, i))
|
|
2819
|
+
out = set()
|
|
2820
|
+
for hits in seen.values():
|
|
2821
|
+
a = {i for k, i in hits if k == "a"}
|
|
2822
|
+
b = {i for k, i in hits if k == "b"}
|
|
2823
|
+
if any(x != y for x in a for y in b):
|
|
2824
|
+
out.add(max(a | b))
|
|
2825
|
+
return sorted(out)
|
|
2826
|
+
|
|
2827
|
+
|
|
2828
|
+
@predicate
|
|
2829
|
+
def declare_target_enter_link(tree, version):
|
|
2830
|
+
"""The same item in enter (or to, or the extended list) on one declare
|
|
2831
|
+
target directive and in link (6.0: or local) on another."""
|
|
2832
|
+
second = ("link",) if version < 60 else ("link", "local")
|
|
2833
|
+
return _cross_directive(tree, ("enter", "to"), second)
|
|
2834
|
+
|
|
2835
|
+
|
|
2836
|
+
@predicate
|
|
2837
|
+
def declare_target_link_local(tree, version):
|
|
2838
|
+
"""The same item in link on one declare target directive and local on
|
|
2839
|
+
another (6.0)."""
|
|
2840
|
+
return _cross_directive(tree, ("link",), ("local",))
|
|
2841
|
+
|
|
2842
|
+
|
|
2843
|
+
@predicate
|
|
2844
|
+
def indirect_device_type_any(tree, version):
|
|
2845
|
+
"""indirect (or indirect(.true.)) with device_type other than any."""
|
|
2846
|
+
def bad(i):
|
|
2847
|
+
ind = _clauses(tree, i, "indirect")
|
|
2848
|
+
on = ind and (not ind[0].args or _norm(ind[0].args[0]) == ".true.")
|
|
2849
|
+
return bool(on) and any(x != "any" for x in _items(
|
|
2850
|
+
_clauses(tree, i, "device_type")))
|
|
2851
|
+
return _each(tree, bad)
|
|
2852
|
+
|
|
2853
|
+
|
|
2854
|
+
@predicate
|
|
2855
|
+
def map_device_local(tree, version):
|
|
2856
|
+
"""A map list item that a declare target local clause of the source
|
|
2857
|
+
names (a device-local variable, 6.0)."""
|
|
2858
|
+
def bad(i):
|
|
2859
|
+
local = {_key(tree, j, _base(x)) for j, d in enumerate(tree.directives)
|
|
2860
|
+
if d.name == "declare target"
|
|
2861
|
+
for x in _items(_clauses(tree, j, "local"))}
|
|
2862
|
+
return _map_construct(tree, i) and any(
|
|
2863
|
+
_key(tree, i, _base(x)) in local
|
|
2864
|
+
for x in _items(_clauses(tree, i, "map")))
|
|
2865
|
+
return _each(tree, bad)
|
|
2866
|
+
|
|
2867
|
+
|
|
2868
|
+
@predicate
|
|
2869
|
+
def target_map_structure_elements(tree, version):
|
|
2870
|
+
"""On a construct with a target leaf that maps s%c (and not s), a
|
|
2871
|
+
statement in the construct referencing another component s%d that no
|
|
2872
|
+
map clause names."""
|
|
2873
|
+
def bad(i):
|
|
2874
|
+
if "target" not in _leaves(tree, i):
|
|
2875
|
+
return False
|
|
2876
|
+
# C structure elements s.c / p->c read as s%c
|
|
2877
|
+
maps = {re.sub(r"\([^()]*\)|\[[^\[\]]*\]", "",
|
|
2878
|
+
x.replace("->", "%").replace(".", "%"))
|
|
2879
|
+
for x in _items(_clauses(tree, i, "map"))}
|
|
2880
|
+
out = False
|
|
2881
|
+
for x in maps:
|
|
2882
|
+
if "%" not in x:
|
|
2883
|
+
continue
|
|
2884
|
+
s = x.split("%")[0]
|
|
2885
|
+
if s in maps:
|
|
2886
|
+
continue
|
|
2887
|
+
for _, st in _body(tree, i):
|
|
2888
|
+
st = re.sub(r"->|\.(?=[a-z_])", "%", st)
|
|
2889
|
+
for comp in re.findall(rf"(?<![%\w]){s}\s*(?:\([^()]*\)|"
|
|
2890
|
+
r"\[[^\[\]]*\])?\s*"
|
|
2891
|
+
r"%\s*([a-z_]\w*)", st):
|
|
2892
|
+
el = f"{s}%{comp}"
|
|
2893
|
+
# mapped: the element itself or a part of it (s%c%d)
|
|
2894
|
+
if not any(m == el or m.startswith(el + "%")
|
|
2895
|
+
for m in maps):
|
|
2896
|
+
out = True
|
|
2897
|
+
return out
|
|
2898
|
+
return _each(tree, bad)
|
|
2899
|
+
|
|
2900
|
+
|
|
2901
|
+
@predicate
|
|
2902
|
+
def defaultmap_none_listed(tree, version):
|
|
2903
|
+
"""A construct with defaultmap(none) (or defaultmap(none: all)) whose
|
|
2904
|
+
lexical extent references a variable declared in the source that is
|
|
2905
|
+
not a named constant, not a DO variable of a loop in the construct,
|
|
2906
|
+
not threadprivate, not in a declare target enter/to/link clause and
|
|
2907
|
+
not listed in a data-environment attribute clause of the directive."""
|
|
2908
|
+
attr_clauses = _EXPLICIT_DSA + ("map",)
|
|
2909
|
+
dt = None
|
|
2910
|
+
out = []
|
|
2911
|
+
for i, d in enumerate(tree.directives):
|
|
2912
|
+
cl = [c for c in _clauses(tree, i, "defaultmap")
|
|
2913
|
+
if _items([c])[:1] == ["none"]
|
|
2914
|
+
and (not c.modifiers or _norm(c.modifiers[0]) == "all")]
|
|
2915
|
+
if not cl:
|
|
2916
|
+
continue
|
|
2917
|
+
if dt is None:
|
|
2918
|
+
dt = {_key(tree, j, _base(x)) for j, e in enumerate(
|
|
2919
|
+
tree.directives) if e.name == "declare target"
|
|
2920
|
+
for x in _dt_items(tree, j, "enter", "to", "link")}
|
|
2921
|
+
dt |= _tp_keys(tree)
|
|
2922
|
+
listed = {_base(x) for x in _items(_clauses(tree, i, *attr_clauses))}
|
|
2923
|
+
if getattr(tree, "lang", "f90") == "c":
|
|
2924
|
+
if any(_key(tree, i, x) not in dt
|
|
2925
|
+
for x in _c_construct_refs(tree, i) - listed):
|
|
2926
|
+
out.append(i)
|
|
2927
|
+
continue
|
|
2928
|
+
body = _body(tree, i)
|
|
2929
|
+
dovars = {m.group(1) for _, st in body if (m := _DO_VAR.match(st))}
|
|
2930
|
+
for _, st in body:
|
|
2931
|
+
names = set(re.findall(r"(?<![%\w])([a-z_]\w*)",
|
|
2932
|
+
_STRING.sub("", st)))
|
|
2933
|
+
if any(tree.attribute(i, x) is not None
|
|
2934
|
+
and not any((s, x) in tree.params
|
|
2935
|
+
for s in tree.assoc[i].scope)
|
|
2936
|
+
and _key(tree, i, x) not in dt
|
|
2937
|
+
for x in names - listed - dovars):
|
|
2938
|
+
out.append(i)
|
|
2939
|
+
break
|
|
2940
|
+
return out
|
|
2941
|
+
|
|
2942
|
+
|
|
2943
|
+
_ALLOC_PRIVATE = ("private", "firstprivate", "lastprivate", "linear",
|
|
2944
|
+
"reduction", "in_reduction", "induction")
|
|
2945
|
+
|
|
2946
|
+
|
|
2947
|
+
@predicate
|
|
2948
|
+
def allocate_needs_private_clause(tree, version):
|
|
2949
|
+
"""An allocate list item (not on allocators) that no private,
|
|
2950
|
+
firstprivate, lastprivate, linear, reduction, in_reduction or induction
|
|
2951
|
+
clause of the directive names."""
|
|
2952
|
+
def bad(i):
|
|
2953
|
+
if tree.directives[i].name == "allocators":
|
|
2954
|
+
return False
|
|
2955
|
+
priv = {_base(x) for x in _items(_clauses(tree, i, *_ALLOC_PRIVATE))}
|
|
2956
|
+
return any(_base(x) not in priv
|
|
2957
|
+
for x in _items(_clauses(tree, i, "allocate")))
|
|
2958
|
+
return _each(tree, bad)
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
def _has_requires(tree, i, clause) -> bool:
|
|
2962
|
+
unit = tree.assoc[i].unit
|
|
2963
|
+
return any(d.name == "requires" and tree.assoc[j].unit == unit
|
|
2964
|
+
and _clauses(tree, j, clause)
|
|
2965
|
+
for j, d in enumerate(tree.directives))
|
|
2966
|
+
|
|
2967
|
+
|
|
2968
|
+
@predicate
|
|
2969
|
+
def allocate_target_allocator(tree, version):
|
|
2970
|
+
"""An allocate clause without an allocator modifier on a construct with
|
|
2971
|
+
a target leaf or inside one, without requires dynamic_allocators in
|
|
2972
|
+
the program unit."""
|
|
2973
|
+
def bad(i):
|
|
2974
|
+
cs = [c for c in _clauses(tree, i, "allocate")
|
|
2975
|
+
if not [m for m in c.modifiers
|
|
2976
|
+
if not re.match(r"^align\s*\(", _norm(m))]]
|
|
2977
|
+
in_target = "target" in _leaves(tree, i) or any(
|
|
2978
|
+
"target" in _leaves(tree, a) for a in _ancestors(tree, i))
|
|
2979
|
+
return bool(cs) and in_target and not _has_requires(
|
|
2980
|
+
tree, i, "dynamic_allocators")
|
|
2981
|
+
return _each(tree, bad)
|
|
2982
|
+
|
|
2983
|
+
|
|
2984
|
+
_PREDEFINED_ALLOCATORS = {
|
|
2985
|
+
"omp_default_mem_alloc", "omp_large_cap_mem_alloc", "omp_const_mem_alloc",
|
|
2986
|
+
"omp_high_bw_mem_alloc", "omp_low_lat_mem_alloc", "omp_cgroup_mem_alloc",
|
|
2987
|
+
"omp_pteam_mem_alloc", "omp_thread_mem_alloc", "omp_null_allocator"}
|
|
2988
|
+
_PREDEFINED_MEMSPACES = {
|
|
2989
|
+
"omp_default_mem_space", "omp_large_cap_mem_space",
|
|
2990
|
+
"omp_const_mem_space", "omp_high_bw_mem_space", "omp_low_lat_mem_space",
|
|
2991
|
+
"omp_null_mem_space"}
|
|
2992
|
+
|
|
2993
|
+
|
|
2994
|
+
@predicate
|
|
2995
|
+
def uses_allocators_not_in_other_clauses(tree, version):
|
|
2996
|
+
"""A uses_allocators allocator that is also in a data-sharing or map
|
|
2997
|
+
clause of the directive."""
|
|
2998
|
+
def bad(i):
|
|
2999
|
+
al = {_base(x) for x in _items(_clauses(tree, i, "uses_allocators"))}
|
|
3000
|
+
return bool(al & {_base(x) for x in _items(
|
|
3001
|
+
_clauses(tree, i, *(_DSA + ("map",))))})
|
|
3002
|
+
return _each(tree, bad)
|
|
3003
|
+
|
|
3004
|
+
|
|
3005
|
+
def _modifiers(c) -> list[str]:
|
|
3006
|
+
"""The modifiers of a clause other than directive-name modifiers."""
|
|
3007
|
+
return [m for m in c.modifiers if _compact(_norm(m)) not in _names()]
|
|
3008
|
+
|
|
3009
|
+
|
|
3010
|
+
@predicate
|
|
3011
|
+
def uses_allocators_predefined_no_modifiers(tree, version):
|
|
3012
|
+
"""uses_allocators of a predefined allocator with modifiers (a
|
|
3013
|
+
directive-name modifier aside)."""
|
|
3014
|
+
return _each(tree, lambda i: any(
|
|
3015
|
+
_modifiers(c) and set(_items([c])) & _PREDEFINED_ALLOCATORS
|
|
3016
|
+
for c in _clauses(tree, i, "uses_allocators")))
|
|
3017
|
+
|
|
3018
|
+
|
|
3019
|
+
@predicate
|
|
3020
|
+
def uses_allocators_predefined_memspace(tree, version):
|
|
3021
|
+
"""A memspace(...) modifier whose argument is not a predefined memory
|
|
3022
|
+
space name."""
|
|
3023
|
+
def bad(i):
|
|
3024
|
+
for c in _clauses(tree, i, "uses_allocators"):
|
|
3025
|
+
for m in c.modifiers:
|
|
3026
|
+
mm = re.fullmatch(r"memspace\s*\((.*)\)", _norm(m))
|
|
3027
|
+
if mm and mm.group(1) not in _PREDEFINED_MEMSPACES:
|
|
3028
|
+
return True
|
|
3029
|
+
return False
|
|
3030
|
+
return _each(tree, bad)
|
|
3031
|
+
|
|
3032
|
+
|
|
3033
|
+
@predicate
|
|
3034
|
+
def uses_allocators_traits_constant(tree, version):
|
|
3035
|
+
"""A traits(t) modifier (5.0: an allocator(t) item) whose t is declared
|
|
3036
|
+
in the source as a variable that is not a named constant."""
|
|
3037
|
+
def bad(i):
|
|
3038
|
+
for c in _clauses(tree, i, "uses_allocators"):
|
|
3039
|
+
for m in c.modifiers:
|
|
3040
|
+
mm = re.fullmatch(r"traits\s*\(([a-z_]\w*)\)", _norm(m)) \
|
|
3041
|
+
or (version == 50 and re.fullmatch(
|
|
3042
|
+
r"[a-z_]\w*\(([a-z_]\w*)\)", _norm(m)))
|
|
3043
|
+
if mm and tree.attribute(i, mm.group(1)) is not None and \
|
|
3044
|
+
not any((s, mm.group(1)) in tree.params
|
|
3045
|
+
for s in tree.assoc[i].scope) and \
|
|
3046
|
+
not _c_const(tree, i, mm.group(1)):
|
|
3047
|
+
return True
|
|
3048
|
+
return False
|
|
3049
|
+
return _each(tree, bad)
|
|
3050
|
+
|
|
3051
|
+
|
|
3052
|
+
def _c_const(tree, i, name) -> bool:
|
|
3053
|
+
"""A C variable declared const (a constant array of the
|
|
3054
|
+
uses_allocators traits)."""
|
|
3055
|
+
s = _decl_scope(tree, i, name)
|
|
3056
|
+
return s is not None and "const" in tree.extra.get("ctypes", {}).get(
|
|
3057
|
+
(s, name), "").split()
|
|
3058
|
+
|
|
3059
|
+
|
|
3060
|
+
_MEMORY_ORDER = ("seq_cst", "acq_rel", "release", "acquire", "relaxed")
|
|
3061
|
+
|
|
3062
|
+
|
|
3063
|
+
@predicate
|
|
3064
|
+
def requires_mem_order_before_atomic(tree, version):
|
|
3065
|
+
"""requires atomic_default_mem_order after an atomic construct without
|
|
3066
|
+
a memory-order clause in the same program unit."""
|
|
3067
|
+
def bad(i):
|
|
3068
|
+
if tree.directives[i].name != "requires" or not _clauses(
|
|
3069
|
+
tree, i, "atomic_default_mem_order"):
|
|
3070
|
+
return False
|
|
3071
|
+
return any(tree.directives[j].name == "atomic"
|
|
3072
|
+
and tree.assoc[j].unit == tree.assoc[i].unit
|
|
3073
|
+
and not _clauses(tree, j, *_MEMORY_ORDER)
|
|
3074
|
+
for j in range(i))
|
|
3075
|
+
return _each(tree, bad)
|
|
3076
|
+
|
|
3077
|
+
|
|
3078
|
+
_DEVICE_GLOBAL = {52: ("reverse_offload", "unified_address",
|
|
3079
|
+
"unified_shared_memory"),
|
|
3080
|
+
60: ("reverse_offload", "unified_address",
|
|
3081
|
+
"unified_shared_memory", "self_maps")}
|
|
3082
|
+
|
|
3083
|
+
|
|
3084
|
+
@predicate
|
|
3085
|
+
def requires_same_in_all_units(tree, version):
|
|
3086
|
+
"""Program units of the source that contain declare target directives
|
|
3087
|
+
or device constructs whose device requirements differ (5.2: some
|
|
3088
|
+
specify reverse_offload, unified_address or unified_shared_memory,
|
|
3089
|
+
some none; 6.0: different sets, self_maps included). A unit inherits
|
|
3090
|
+
the requirements of the modules of the source it uses."""
|
|
3091
|
+
if getattr(tree, "lang", "f90") == "c":
|
|
3092
|
+
return [] # C: one source is one translation unit (5.2 §1.2.2)
|
|
3093
|
+
req_names = _DEVICE_GLOBAL[52 if version < 60 else 60]
|
|
3094
|
+
units, names, reqs, uses = {}, {}, {}, {}
|
|
3095
|
+
for ln, st, scopes in tree.statements:
|
|
3096
|
+
if len(scopes) > 1:
|
|
3097
|
+
units.setdefault(scopes[1], None)
|
|
3098
|
+
top_of = {}
|
|
3099
|
+
for _, _, scopes in tree.statements:
|
|
3100
|
+
for s in scopes[1:]:
|
|
3101
|
+
top_of[s] = scopes[1] if len(scopes) > 1 else None
|
|
3102
|
+
for s, (kind, name) in tree.units.items():
|
|
3103
|
+
if top_of.get(s) == s and kind == "module":
|
|
3104
|
+
names[name] = s
|
|
3105
|
+
for s, mods in tree.use_modules.items():
|
|
3106
|
+
if top_of.get(s):
|
|
3107
|
+
uses.setdefault(top_of[s], set()).update(mods)
|
|
3108
|
+
device = {}
|
|
3109
|
+
for i, d in enumerate(tree.directives):
|
|
3110
|
+
sc = tree.assoc[i].scope
|
|
3111
|
+
top = sc[1] if len(sc) > 1 else None
|
|
3112
|
+
if top is None:
|
|
3113
|
+
continue
|
|
3114
|
+
if d.name == "requires":
|
|
3115
|
+
reqs.setdefault(top, set()).update(
|
|
3116
|
+
c.name for c in d.clauses if c.name in req_names)
|
|
3117
|
+
if d.name == "declare target" or (
|
|
3118
|
+
_leaves(tree, i)[0] in _DEVICE_CONSTRUCTS):
|
|
3119
|
+
device.setdefault(top, i)
|
|
3120
|
+
|
|
3121
|
+
def effective(top):
|
|
3122
|
+
out = set(reqs.get(top, ()))
|
|
3123
|
+
for m in uses.get(top, ()):
|
|
3124
|
+
if m in names:
|
|
3125
|
+
out |= reqs.get(names[m], set())
|
|
3126
|
+
return out
|
|
3127
|
+
sets = {top: effective(top) for top in device}
|
|
3128
|
+
if version < 60:
|
|
3129
|
+
kinds = {bool(v) for v in sets.values()}
|
|
3130
|
+
if len(kinds) > 1:
|
|
3131
|
+
return sorted(device[t] for t, v in sets.items() if not v)
|
|
3132
|
+
return []
|
|
3133
|
+
if len({frozenset(v) for v in sets.values()}) > 1:
|
|
3134
|
+
common = max(sets.values(), key=len)
|
|
3135
|
+
return sorted(device[t] for t, v in sets.items() if v != common)
|
|
3136
|
+
return []
|
|
3137
|
+
|
|
3138
|
+
|
|
3139
|
+
def _dep_type(c) -> str | None:
|
|
3140
|
+
kinds = {"in", "out", "inout", "mutexinoutset", "inoutset", "depobj",
|
|
3141
|
+
"source", "sink"}
|
|
3142
|
+
return next((m for m in map(_norm, c.modifiers) if m in kinds), None)
|
|
3143
|
+
|
|
3144
|
+
|
|
3145
|
+
@predicate
|
|
3146
|
+
def depend_all_memory_out_inout(tree, version):
|
|
3147
|
+
"""omp_all_memory in a depend clause whose type is not out or inout."""
|
|
3148
|
+
return _each(tree, lambda i: any(
|
|
3149
|
+
"omp_all_memory" in _items([c]) and _dep_type(c) not in ("out",
|
|
3150
|
+
"inout")
|
|
3151
|
+
for c in _clauses(tree, i, "depend")))
|
|
3152
|
+
|
|
3153
|
+
|
|
3154
|
+
@predicate
|
|
3155
|
+
def depend_depobj_no_section(tree, version):
|
|
3156
|
+
"""An array section in a depend clause with the depobj type."""
|
|
3157
|
+
return _each(tree, lambda i: any(
|
|
3158
|
+
_dep_type(c) == "depobj" and any(
|
|
3159
|
+
re.search(r"\(.*:.*\)|\[.*:.*\]", x) for x in _items([c]))
|
|
3160
|
+
for c in _clauses(tree, i, "depend")))
|
|
3161
|
+
|
|
3162
|
+
|
|
3163
|
+
@predicate
|
|
3164
|
+
def depend_no_common_block(tree, version):
|
|
3165
|
+
"""A common block name (/name/) in a depend clause."""
|
|
3166
|
+
return _each(tree, lambda i: any(
|
|
3167
|
+
x.startswith("/") for x in _items(_clauses(tree, i, "depend"))))
|
|
3168
|
+
|
|
3169
|
+
|
|
3170
|
+
def _zero_length(item: str) -> bool:
|
|
3171
|
+
"""An array section with integer literal bounds lb > ub in some
|
|
3172
|
+
dimension (or a literal length of zero is not Fortran syntax; C, Task
|
|
3173
|
+
20: a literal length of 0)."""
|
|
3174
|
+
for dims in _c_sections(item):
|
|
3175
|
+
if any(len(t) >= 2 and _int(t[1] or "x") == 0 for t in dims):
|
|
3176
|
+
return True
|
|
3177
|
+
m = re.search(r"\((.*)\)$", item)
|
|
3178
|
+
if not m:
|
|
3179
|
+
return False
|
|
3180
|
+
for dim in _split_top(m.group(1), ","):
|
|
3181
|
+
parts = _split_top(dim, ":")
|
|
3182
|
+
if len(parts) >= 2:
|
|
3183
|
+
lo, hi = _int(parts[0] or "x"), _int(parts[1] or "x")
|
|
3184
|
+
if lo is not None and hi is not None and lo > hi:
|
|
3185
|
+
return True
|
|
3186
|
+
return False
|
|
3187
|
+
|
|
3188
|
+
|
|
3189
|
+
@predicate
|
|
3190
|
+
def depend_no_zero_length(tree, version):
|
|
3191
|
+
"""A depend list item that is an array section with literal bounds
|
|
3192
|
+
lb > ub (zero length)."""
|
|
3193
|
+
return _each(tree, lambda i: any(
|
|
3194
|
+
_zero_length(x) for x in _items(_clauses(tree, i, "depend"))))
|
|
3195
|
+
|
|
3196
|
+
|
|
3197
|
+
@predicate
|
|
3198
|
+
def taskwait_no_mutexinoutset(tree, version):
|
|
3199
|
+
"""taskwait with depend(mutexinoutset: ...)."""
|
|
3200
|
+
return _each(tree, lambda i: tree.directives[i].name == "taskwait" and any(
|
|
3201
|
+
_dep_type(c) == "mutexinoutset" for c in _clauses(tree, i, "depend")))
|
|
3202
|
+
|
|
3203
|
+
|
|
3204
|
+
@predicate
|
|
3205
|
+
def taskwait_nowait_needs_depend(tree, version):
|
|
3206
|
+
"""taskwait with nowait and no depend clause."""
|
|
3207
|
+
return _each(tree, lambda i: tree.directives[i].name == "taskwait"
|
|
3208
|
+
and bool(_clauses(tree, i, "nowait"))
|
|
3209
|
+
and not _clauses(tree, i, "depend"))
|
|
3210
|
+
|
|
3211
|
+
|
|
3212
|
+
@predicate
|
|
3213
|
+
def taskwait_replayable_needs_depend(tree, version):
|
|
3214
|
+
"""taskwait with replayable and no depend clause (6.0)."""
|
|
3215
|
+
return _each(tree, lambda i: tree.directives[i].name == "taskwait"
|
|
3216
|
+
and bool(_clauses(tree, i, "replayable"))
|
|
3217
|
+
and not _clauses(tree, i, "depend"))
|
|
3218
|
+
|
|
3219
|
+
|
|
3220
|
+
@predicate
|
|
3221
|
+
def depobj_one_locator(tree, version):
|
|
3222
|
+
"""A depend clause on depobj with more than one list item."""
|
|
3223
|
+
return _each(tree, lambda i: tree.directives[i].name == "depobj" and any(
|
|
3224
|
+
len(_items([c])) > 1 for c in _clauses(tree, i, "depend")))
|
|
3225
|
+
|
|
3226
|
+
|
|
3227
|
+
@predicate
|
|
3228
|
+
def depobj_update_not_depobj(tree, version):
|
|
3229
|
+
"""update(depobj) on a depobj directive."""
|
|
3230
|
+
return _each(tree, lambda i: "depobj" in _items(
|
|
3231
|
+
_clauses(tree, i, "update")))
|
|
3232
|
+
|
|
3233
|
+
|
|
3234
|
+
def _scans(tree, i) -> list[int]:
|
|
3235
|
+
return [c for c in tree.children(i) if tree.directives[c].name == "scan"
|
|
3236
|
+
and _clauses(tree, c, "inclusive", "exclusive")]
|
|
3237
|
+
|
|
3238
|
+
|
|
3239
|
+
@predicate
|
|
3240
|
+
def scan_at_most_one(tree, version):
|
|
3241
|
+
"""A construct with more than one scan directive (with inclusive or
|
|
3242
|
+
exclusive) as its separating directives."""
|
|
3243
|
+
return sorted(s for i in range(len(tree.directives))
|
|
3244
|
+
for s in _scans(tree, i)[1:])
|
|
3245
|
+
|
|
3246
|
+
|
|
3247
|
+
@predicate
|
|
3248
|
+
def scan_not_orphaned(tree, version):
|
|
3249
|
+
"""A scan directive not directly in the loop nest of a construct with a
|
|
3250
|
+
do or simd leaf."""
|
|
3251
|
+
def bad(i):
|
|
3252
|
+
if tree.directives[i].name != "scan":
|
|
3253
|
+
return False
|
|
3254
|
+
p = tree.parent(i)
|
|
3255
|
+
return p is None or not set(_leaves(tree, p)) & {"do", "for", "simd"}
|
|
3256
|
+
return _each(tree, bad)
|
|
3257
|
+
|
|
3258
|
+
|
|
3259
|
+
@predicate
|
|
3260
|
+
def scan_items_in_inscan_reduction(tree, version):
|
|
3261
|
+
"""A scan inclusive/exclusive list item that is not in a
|
|
3262
|
+
reduction(inscan, ...) clause of the enclosing construct."""
|
|
3263
|
+
def bad(i):
|
|
3264
|
+
if tree.directives[i].name != "scan" or tree.parent(i) is None:
|
|
3265
|
+
return False
|
|
3266
|
+
red = set(_items(_reductions(tree, tree.parent(i), "inscan")))
|
|
3267
|
+
return any(x not in red for x in _items(
|
|
3268
|
+
_clauses(tree, i, "inclusive", "exclusive")))
|
|
3269
|
+
return _each(tree, bad)
|
|
3270
|
+
|
|
3271
|
+
|
|
3272
|
+
@predicate
|
|
3273
|
+
def inscan_items_on_scan(tree, version):
|
|
3274
|
+
"""A reduction(inscan, ...) list item that no inclusive or exclusive
|
|
3275
|
+
clause of a scan directive in the construct names."""
|
|
3276
|
+
def bad(i):
|
|
3277
|
+
red = set(_items(_reductions(tree, i, "inscan")))
|
|
3278
|
+
if not red:
|
|
3279
|
+
return False
|
|
3280
|
+
on = set().union(*[set(_items(_clauses(tree, s, "inclusive",
|
|
3281
|
+
"exclusive")))
|
|
3282
|
+
for s in _scans(tree, i)])
|
|
3283
|
+
return bool(red - on)
|
|
3284
|
+
return _each(tree, bad)
|
|
3285
|
+
|
|
3286
|
+
|
|
3287
|
+
@predicate
|
|
3288
|
+
def scan_perfectly_nested(tree, version):
|
|
3289
|
+
"""A construct with a scan directive whose associated loops are not
|
|
3290
|
+
perfectly nested (intervening statements)."""
|
|
3291
|
+
def bad(i):
|
|
3292
|
+
a, n = tree.assoc[i], _assoc_count(tree, i)
|
|
3293
|
+
return bool(_scans(tree, i)) and n is not None and n > 1 and any(
|
|
3294
|
+
a.intervening[:n - 1])
|
|
3295
|
+
return _each(tree, bad)
|
|
3296
|
+
|
|
3297
|
+
|
|
3298
|
+
@predicate
|
|
3299
|
+
def declare_simd_in_subprogram(tree, version):
|
|
3300
|
+
"""declare simd outside the specification part of a subroutine or
|
|
3301
|
+
function (in a program, module or submodule scoping unit, or inside a
|
|
3302
|
+
construct)."""
|
|
3303
|
+
def bad(i):
|
|
3304
|
+
if tree.directives[i].name != "declare simd":
|
|
3305
|
+
return False
|
|
3306
|
+
kind = tree.units.get(tree.assoc[i].scope[-1], ("", None))[0]
|
|
3307
|
+
return tree.parent(i) is not None or kind in (
|
|
3308
|
+
"program", "module", "submodule", "blockdata")
|
|
3309
|
+
return _each(tree, bad)
|
|
3310
|
+
|
|
3311
|
+
|
|
3312
|
+
def _mapper_spec(d) -> tuple:
|
|
3313
|
+
"""(mapper identifier, type, variable) of a declare mapper argument
|
|
3314
|
+
(Fortran ``[id:] type :: var``; C ``[id:] type var``, the
|
|
3315
|
+
type with its blanks removed)."""
|
|
3316
|
+
raw = d.args[0] if d.args else ""
|
|
3317
|
+
if "::" not in raw and raw:
|
|
3318
|
+
m = re.match(r"^\s*(?:(\w+)\s*:(?!:))?\s*(.*\S)\s+(\w+)\s*$", raw)
|
|
3319
|
+
if m:
|
|
3320
|
+
return (m.group(1) or "default").lower(), _norm(m.group(2)), \
|
|
3321
|
+
m.group(3).lower()
|
|
3322
|
+
arg = _norm(raw)
|
|
3323
|
+
head, _, var = arg.partition("::")
|
|
3324
|
+
ident, _, typ = head.rpartition(":")
|
|
3325
|
+
return ident or "default", typ, var
|
|
3326
|
+
|
|
3327
|
+
|
|
3328
|
+
@predicate
|
|
3329
|
+
def declare_mapper_no_release_delete(tree, version):
|
|
3330
|
+
"""release or delete map-type on a declare mapper map clause (5.2)."""
|
|
3331
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare mapper"
|
|
3332
|
+
and any(set(c.modifiers) & {"release", "delete"}
|
|
3333
|
+
for c in _clauses(tree, i, "map")))
|
|
3334
|
+
|
|
3335
|
+
|
|
3336
|
+
@predicate
|
|
3337
|
+
def declare_mapper_modifier_default(tree, version):
|
|
3338
|
+
"""A mapper(m) modifier with m other than default on a declare mapper
|
|
3339
|
+
map clause."""
|
|
3340
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare mapper"
|
|
3341
|
+
and any(_mapper(c) != "default"
|
|
3342
|
+
for c in _clauses(tree, i, "map")))
|
|
3343
|
+
|
|
3344
|
+
|
|
3345
|
+
@predicate
|
|
3346
|
+
def declare_mapper_unique(tree, version):
|
|
3347
|
+
"""Two declare mapper directives in one scoping unit with the same
|
|
3348
|
+
mapper identifier and type."""
|
|
3349
|
+
seen, out = set(), []
|
|
3350
|
+
for i, d in enumerate(tree.directives):
|
|
3351
|
+
if d.name == "declare mapper":
|
|
3352
|
+
ident, typ, _ = _mapper_spec(d)
|
|
3353
|
+
key = (tree.assoc[i].scope[-1], ident, typ)
|
|
3354
|
+
if key in seen:
|
|
3355
|
+
out.append(i)
|
|
3356
|
+
seen.add(key)
|
|
3357
|
+
return out
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
@predicate
|
|
3361
|
+
def declare_mapper_type_not_intrinsic(tree, version):
|
|
3362
|
+
"""A declare mapper for an intrinsic type; for an abstract type the
|
|
3363
|
+
source defines (5.0 §2.19.7.3 p.328, 5.2, 6.0); (6.0) for a
|
|
3364
|
+
parameterized derived type the source defines."""
|
|
3365
|
+
def bad(i):
|
|
3366
|
+
if tree.directives[i].name != "declare mapper":
|
|
3367
|
+
return False
|
|
3368
|
+
typ = _mapper_spec(tree.directives[i])[1]
|
|
3369
|
+
if re.match(r"^(?:integer|real|doubleprecision|complex|logical|"
|
|
3370
|
+
r"character)\b", typ):
|
|
3371
|
+
return True
|
|
3372
|
+
if tree.lang != "f90":
|
|
3373
|
+
return False
|
|
3374
|
+
m = re.fullmatch(r"(?:type|class)\(([a-z_]\w*)\)|([a-z_]\w*)", typ)
|
|
3375
|
+
t = _oop(tree)["types"].get(m and (m.group(1) or m.group(2)))
|
|
3376
|
+
return bool(t and (t["abstract"] or (version >= 60 and t["pdt"])))
|
|
3377
|
+
return _each(tree, bad)
|
|
3378
|
+
|
|
3379
|
+
|
|
3380
|
+
@predicate
|
|
3381
|
+
def declare_mapper_maps_var(tree, version):
|
|
3382
|
+
"""A declare mapper none of whose map clauses maps the variable or an
|
|
3383
|
+
element of it."""
|
|
3384
|
+
def bad(i):
|
|
3385
|
+
if tree.directives[i].name != "declare mapper":
|
|
3386
|
+
return False
|
|
3387
|
+
var = _mapper_spec(tree.directives[i])[2]
|
|
3388
|
+
return bool(var) and not any(_base(x) == var for x in _items(
|
|
3389
|
+
_clauses(tree, i, "map")))
|
|
3390
|
+
return _each(tree, bad)
|
|
3391
|
+
|
|
3392
|
+
|
|
3393
|
+
# --- loop nest shape --------------------------------------------------------------
|
|
3394
|
+
|
|
3395
|
+
_BRANCH = re.compile(r"^(?:if\s*\(.*\)\s*)?(exit|cycle)\b\s*([a-z_]\w*)?$")
|
|
3396
|
+
_DO_NAME = re.compile(r"^(?:([a-z_]\w*)\s*:\s*)?do\b")
|
|
3397
|
+
|
|
3398
|
+
|
|
3399
|
+
def _branches(tree, i) -> list[tuple]:
|
|
3400
|
+
"""(exit|cycle, depth of the DO it applies to, line) in the DO nest of
|
|
3401
|
+
loop directive i; depth 1 is the outermost loop of the nest."""
|
|
3402
|
+
out, stack = [], []
|
|
3403
|
+
for ln, st in _body(tree, i):
|
|
3404
|
+
if _is_do(st):
|
|
3405
|
+
m = _DO_NAME.match(st)
|
|
3406
|
+
stack.append(m.group(1) if m else None)
|
|
3407
|
+
continue
|
|
3408
|
+
if _END_DO.match(st):
|
|
3409
|
+
if stack:
|
|
3410
|
+
stack.pop()
|
|
3411
|
+
continue
|
|
3412
|
+
m = _BRANCH.match(st)
|
|
3413
|
+
if m and stack:
|
|
3414
|
+
name = m.group(2)
|
|
3415
|
+
if name:
|
|
3416
|
+
depth = next((k + 1 for k in range(len(stack) - 1, -1, -1)
|
|
3417
|
+
if stack[k] == name), None)
|
|
3418
|
+
else:
|
|
3419
|
+
depth = len(stack)
|
|
3420
|
+
if depth:
|
|
3421
|
+
out.append((m.group(1), depth, ln))
|
|
3422
|
+
return out
|
|
3423
|
+
|
|
3424
|
+
|
|
3425
|
+
def _n_assoc(tree, i) -> int:
|
|
3426
|
+
a = tree.assoc[i]
|
|
3427
|
+
n = _assoc_count(tree, i) or 1
|
|
3428
|
+
return min(n, a.loop_depth) if a.loop_depth else n
|
|
3429
|
+
|
|
3430
|
+
|
|
3431
|
+
@predicate
|
|
3432
|
+
def exit_from_associated_loop(tree, version):
|
|
3433
|
+
"""An EXIT statement that terminates one of the associated loops."""
|
|
3434
|
+
return _each(tree, lambda i: tree.assoc[i].kind == "loop" and any(
|
|
3435
|
+
k == "exit" and d <= _n_assoc(tree, i)
|
|
3436
|
+
for k, d, _ in _branches(tree, i)))
|
|
3437
|
+
|
|
3438
|
+
|
|
3439
|
+
@predicate
|
|
3440
|
+
def cycle_in_intervening_code(tree, version):
|
|
3441
|
+
"""A CYCLE statement that applies to an associated loop other than the
|
|
3442
|
+
innermost one (it is in intervening code or branches out of the inner
|
|
3443
|
+
loop body)."""
|
|
3444
|
+
return _each(tree, lambda i: tree.assoc[i].kind == "loop" and any(
|
|
3445
|
+
k == "cycle" and d < _n_assoc(tree, i)
|
|
3446
|
+
for k, d, _ in _branches(tree, i)))
|
|
3447
|
+
|
|
3448
|
+
|
|
3449
|
+
@predicate
|
|
3450
|
+
def loop_var_not_modified(tree, version):
|
|
3451
|
+
"""An assignment to the iteration variable of an associated loop, or a
|
|
3452
|
+
nested DO loop with that variable, in the loop nest."""
|
|
3453
|
+
def bad(i):
|
|
3454
|
+
vs = _assoc_vars(tree, i)
|
|
3455
|
+
if not vs:
|
|
3456
|
+
return False
|
|
3457
|
+
if getattr(tree, "lang", "f90") == "c":
|
|
3458
|
+
return _c_loop_var_modified(tree, i, vs)
|
|
3459
|
+
dos = [m.group(1) for _, st in _body(tree, i)
|
|
3460
|
+
if (m := _DO_VAR.match(st))]
|
|
3461
|
+
if any(dos.count(v) > 1 for v in vs):
|
|
3462
|
+
return True
|
|
3463
|
+
return any(re.match(rf"^{v}\s*=(?!=)", st)
|
|
3464
|
+
for _, st in _body(tree, i) for v in vs)
|
|
3465
|
+
return _each(tree, bad)
|
|
3466
|
+
|
|
3467
|
+
|
|
3468
|
+
def _c_loop_var_modified(tree, i, vs) -> bool:
|
|
3469
|
+
"""C: an assignment, increment or decrement of an associated
|
|
3470
|
+
loop's iteration variable in the nest, or a nested for loop on it,
|
|
3471
|
+
other than the associated loops' own headers."""
|
|
3472
|
+
own = set(tree.extra["loops"].get(i, {}).get("lines", ()))
|
|
3473
|
+
for ln, st in _body(tree, i):
|
|
3474
|
+
if st.startswith("for (") and ln in own:
|
|
3475
|
+
continue
|
|
3476
|
+
for v in vs:
|
|
3477
|
+
if re.search(rf"(?<![\w.>*]){v}\s*(?:=(?!=)|\+\+|--|"
|
|
3478
|
+
r"[-+*/%&|^]=|<<=|>>=)|(?:\+\+|--)\s*"
|
|
3479
|
+
rf"{v}\b", st):
|
|
3480
|
+
return True
|
|
3481
|
+
return False
|
|
3482
|
+
|
|
3483
|
+
|
|
3484
|
+
@predicate
|
|
3485
|
+
def loop_var_not_threadprivate(tree, version):
|
|
3486
|
+
"""The iteration variable of an associated loop is threadprivate."""
|
|
3487
|
+
def bad(i):
|
|
3488
|
+
vs = _assoc_vars(tree, i)
|
|
3489
|
+
tp = _tp_keys(tree)
|
|
3490
|
+
return bool(vs) and any(_key(tree, i, v) in tp for v in vs)
|
|
3491
|
+
return _each(tree, bad)
|
|
3492
|
+
|
|
3493
|
+
|
|
3494
|
+
@predicate
|
|
3495
|
+
def loop_var_integer(tree, version):
|
|
3496
|
+
"""An associated loop whose iteration variable is declared with a type
|
|
3497
|
+
other than integer."""
|
|
3498
|
+
def bad(i):
|
|
3499
|
+
vs = _assoc_vars(tree, i)
|
|
3500
|
+
return bool(vs) and any(_vtype(tree, i, v) not in (None, "integer")
|
|
3501
|
+
for v in vs)
|
|
3502
|
+
return _each(tree, bad)
|
|
3503
|
+
|
|
3504
|
+
|
|
3505
|
+
def _refs(expr: str, names) -> set:
|
|
3506
|
+
return {x for x in re.findall(r"(?<![%\w])([a-z_]\w*)", expr)
|
|
3507
|
+
if x in names}
|
|
3508
|
+
|
|
3509
|
+
|
|
3510
|
+
def _nonrect(tree, i) -> list[tuple]:
|
|
3511
|
+
"""(lb refs, ub refs) of each associated loop after the first whose
|
|
3512
|
+
bounds reference an outer associated loop variable."""
|
|
3513
|
+
vs = _assoc_vars(tree, i)
|
|
3514
|
+
if not vs:
|
|
3515
|
+
return []
|
|
3516
|
+
out = []
|
|
3517
|
+
for k in range(1, len(vs)):
|
|
3518
|
+
lb, ub, _ = tree.assoc[i].loop_bounds[k]
|
|
3519
|
+
lr, ur = _refs(lb, vs[:k]), _refs(ub, vs[:k])
|
|
3520
|
+
if lr or ur:
|
|
3521
|
+
out.append((lr, ur))
|
|
3522
|
+
return out
|
|
3523
|
+
|
|
3524
|
+
|
|
3525
|
+
@predicate
|
|
3526
|
+
def nonrect_same_outer_var(tree, version):
|
|
3527
|
+
"""A non-rectangular loop whose lb and ub reference different outer
|
|
3528
|
+
loop variables (or more than one)."""
|
|
3529
|
+
return _each(tree, lambda i: any(
|
|
3530
|
+
len(lr | ur) > 1 for lr, ur in _nonrect(tree, i)))
|
|
3531
|
+
|
|
3532
|
+
|
|
3533
|
+
def _nonrect_with(*clauses):
|
|
3534
|
+
def fn(tree, version):
|
|
3535
|
+
return _each(tree, lambda i: bool(_clauses(tree, i, *clauses))
|
|
3536
|
+
and bool(_nonrect(tree, i)))
|
|
3537
|
+
fn.__doc__ = (f"A {' or '.join(clauses)} clause on a construct whose "
|
|
3538
|
+
"associated loops include a non-rectangular loop (a bound "
|
|
3539
|
+
"referencing an outer associated loop variable).")
|
|
3540
|
+
return fn
|
|
3541
|
+
|
|
3542
|
+
|
|
3543
|
+
_register("schedule_rectangular", _nonrect_with("schedule"))
|
|
3544
|
+
_register("dist_schedule_rectangular", _nonrect_with("dist_schedule"))
|
|
3545
|
+
_register("ordered_rectangular", _nonrect_with("ordered"))
|
|
3546
|
+
_register("linear_rectangular", _nonrect_with("linear"))
|
|
3547
|
+
_register("induction_rectangular", _nonrect_with("induction"))
|
|
3548
|
+
|
|
3549
|
+
|
|
3550
|
+
@predicate
|
|
3551
|
+
def ordered_n_within_depth(tree, version):
|
|
3552
|
+
"""ordered(n) with an integer literal n greater than the depth of the
|
|
3553
|
+
DO nest that follows."""
|
|
3554
|
+
def bad(i):
|
|
3555
|
+
a, n = tree.assoc[i], _lit(tree, i, "ordered")
|
|
3556
|
+
return a.kind == "loop" and a.loop_depth > 0 and a.depth_known \
|
|
3557
|
+
and n is not None and n > a.loop_depth
|
|
3558
|
+
return _each(tree, bad)
|
|
3559
|
+
|
|
3560
|
+
|
|
3561
|
+
@predicate
|
|
3562
|
+
def ordered_n_ge_collapse(tree, version):
|
|
3563
|
+
"""ordered(n) and collapse(m) with integer literals, n < m."""
|
|
3564
|
+
def bad(i):
|
|
3565
|
+
n = _lit(tree, i, "ordered")
|
|
3566
|
+
m = _lit(tree, i, "collapse")
|
|
3567
|
+
return n is not None and m is not None and n < m
|
|
3568
|
+
return _each(tree, bad)
|
|
3569
|
+
|
|
3570
|
+
|
|
3571
|
+
@predicate
|
|
3572
|
+
def ordered_n_no_linear(tree, version):
|
|
3573
|
+
"""ordered(n) and a linear clause on one directive (5.2)."""
|
|
3574
|
+
return _each(tree, lambda i: any(c.args for c in _clauses(
|
|
3575
|
+
tree, i, "ordered")) and bool(_clauses(tree, i, "linear")))
|
|
3576
|
+
|
|
3577
|
+
|
|
3578
|
+
@predicate
|
|
3579
|
+
def ordered_n_perfectly_nested(tree, version):
|
|
3580
|
+
"""ordered(n) with the n associated loops not perfectly nested
|
|
3581
|
+
(intervening statements; 5.2; 5.0: any ordered clause, the loops the
|
|
3582
|
+
construct is associated with, 5.0 §2.9.2 p.107)."""
|
|
3583
|
+
def bad(i):
|
|
3584
|
+
a, n = tree.assoc[i], _lit(tree, i, "ordered")
|
|
3585
|
+
if version == 50 and n is None and _clauses(tree, i, "ordered"):
|
|
3586
|
+
n = _lit(tree, i, "collapse") or 1
|
|
3587
|
+
return n is not None and a.kind == "loop" and len(a.intervening) \
|
|
3588
|
+
>= n and any(a.intervening[:n - 1])
|
|
3589
|
+
return _each(tree, bad)
|
|
3590
|
+
|
|
3591
|
+
|
|
3592
|
+
@predicate
|
|
3593
|
+
def loop_do_concurrent_clauses(tree, version):
|
|
3594
|
+
"""A construct with a loop leaf followed by a DO CONCURRENT loop with a
|
|
3595
|
+
data-sharing clause or collapse (6.0)."""
|
|
3596
|
+
def bad(i):
|
|
3597
|
+
if "loop" not in _leaves(tree, i):
|
|
3598
|
+
return False
|
|
3599
|
+
first = next((st for _, st in _body(tree, i) if _is_do(st)), "")
|
|
3600
|
+
return bool(re.match(r"^(?:[a-z_]\w*\s*:\s*)?do\s*concurrent\b",
|
|
3601
|
+
first)) and bool(_clauses(
|
|
3602
|
+
tree, i, "private", "lastprivate",
|
|
3603
|
+
"reduction", "collapse"))
|
|
3604
|
+
return _each(tree, bad)
|
|
3605
|
+
|
|
3606
|
+
|
|
3607
|
+
@predicate
|
|
3608
|
+
def reduction_section_base(tree, version):
|
|
3609
|
+
"""A reduction, in_reduction or induction list item that is an array
|
|
3610
|
+
section whose base is not a name, or with literal zero length (5.2:
|
|
3611
|
+
or a literal stride other than 1)."""
|
|
3612
|
+
def bad_item(x):
|
|
3613
|
+
if _c_sections(x): # C: b[lb:len[:stride]]
|
|
3614
|
+
base = _section_base(x)
|
|
3615
|
+
if base is None or not re.fullmatch(r"[a-z_]\w*", base):
|
|
3616
|
+
return True
|
|
3617
|
+
if _zero_length(x):
|
|
3618
|
+
return True
|
|
3619
|
+
return version < 60 and any(
|
|
3620
|
+
len(t) == 3 and _int(t[2] or "1") not in (None, 1)
|
|
3621
|
+
for dims in _c_sections(x) for t in dims)
|
|
3622
|
+
if not re.search(r"\(.*:.*\)$", x):
|
|
3623
|
+
return False
|
|
3624
|
+
if not re.fullmatch(r"[a-z_]\w*", x.split("(")[0]):
|
|
3625
|
+
return True
|
|
3626
|
+
if _zero_length(x):
|
|
3627
|
+
return True
|
|
3628
|
+
if version < 60:
|
|
3629
|
+
inner = re.search(r"\((.*)\)$", x).group(1)
|
|
3630
|
+
return any(len(p) == 3 and _int(p[2] or "1") not in (None, 1)
|
|
3631
|
+
for p in (_split_top(d, ":")
|
|
3632
|
+
for d in _split_top(inner, ",")))
|
|
3633
|
+
return False
|
|
3634
|
+
return _each(tree, lambda i: any(bad_item(x) for x in _items(
|
|
3635
|
+
_clauses(tree, i, "reduction", "in_reduction", "induction"))))
|
|
3636
|
+
|
|
3637
|
+
|
|
3638
|
+
@predicate
|
|
3639
|
+
def aligned_pointer_or_allocatable(tree, version):
|
|
3640
|
+
"""An aligned list item declared without POINTER or ALLOCATABLE and not
|
|
3641
|
+
of type(c_ptr) (5.2)."""
|
|
3642
|
+
def bad(i):
|
|
3643
|
+
for x in map(_base, _items(_clauses(tree, i, "aligned"))):
|
|
3644
|
+
s = _decl_scope(tree, i, x)
|
|
3645
|
+
if s is not None and tree.attribute(i, x) == "plain" \
|
|
3646
|
+
and tree.vartypes.get((s, x)) != "c_ptr":
|
|
3647
|
+
return True
|
|
3648
|
+
return False
|
|
3649
|
+
return _each(tree, bad)
|
|
3650
|
+
|
|
3651
|
+
|
|
3652
|
+
@predicate
|
|
3653
|
+
def aligned_array(tree, version):
|
|
3654
|
+
"""An aligned list item declared as a scalar (6.0)."""
|
|
3655
|
+
return _each(tree, lambda i: any(
|
|
3656
|
+
_is_array(tree, i, _base(x)) is False
|
|
3657
|
+
for x in _items(_clauses(tree, i, "aligned"))))
|
|
3658
|
+
|
|
3659
|
+
|
|
3660
|
+
@predicate
|
|
3661
|
+
def in_reduction_matching_item(tree, version):
|
|
3662
|
+
"""An in_reduction list item whose closest enclosing construct with the
|
|
3663
|
+
item in task_reduction or reduction(task, ...) uses another reduction
|
|
3664
|
+
identifier, or (in a main program, where no caller can supply one)
|
|
3665
|
+
that no enclosing construct has."""
|
|
3666
|
+
def ident(c):
|
|
3667
|
+
return next((_norm(m) for m in _modifiers(c)
|
|
3668
|
+
if _norm(m) not in ("task", "inscan", "default")), None)
|
|
3669
|
+
|
|
3670
|
+
def bad(i):
|
|
3671
|
+
for c in _clauses(tree, i, "in_reduction"):
|
|
3672
|
+
for x in _items([c]):
|
|
3673
|
+
found = None
|
|
3674
|
+
for p in _ancestors(tree, i):
|
|
3675
|
+
for oc in _clauses(tree, p, "task_reduction",
|
|
3676
|
+
"reduction"):
|
|
3677
|
+
if (oc.name == "task_reduction"
|
|
3678
|
+
or "task" in oc.modifiers) \
|
|
3679
|
+
and x in _items([oc]):
|
|
3680
|
+
found = oc
|
|
3681
|
+
if found:
|
|
3682
|
+
break
|
|
3683
|
+
if found is not None and ident(found) != ident(c):
|
|
3684
|
+
return True
|
|
3685
|
+
kind = tree.units.get(tree.assoc[i].scope[-1],
|
|
3686
|
+
("", None))[0]
|
|
3687
|
+
if found is None and kind == "program":
|
|
3688
|
+
return True
|
|
3689
|
+
return False
|
|
3690
|
+
return _each(tree, bad)
|
|
3691
|
+
|
|
3692
|
+
|
|
3693
|
+
# --- Second pass ------------------------------------------------------------
|
|
3694
|
+
|
|
3695
|
+
_LIST_CLAUSES = set(_UNIQUE_LISTS) | {"map", "depend", "use_device_ptr",
|
|
3696
|
+
"use_device_addr", "is_device_ptr",
|
|
3697
|
+
"has_device_addr", "in_reduction",
|
|
3698
|
+
"task_reduction", "induction"}
|
|
3699
|
+
|
|
3700
|
+
|
|
3701
|
+
def _c_sections(x: str) -> list[list[list[str]]]:
|
|
3702
|
+
"""C array sections: per [..] subscript with a ':', its
|
|
3703
|
+
[lb, length[, stride]] texts, as one dimension list."""
|
|
3704
|
+
dims = [_split_top(m.group(1), ":")
|
|
3705
|
+
for m in re.finditer(r"\[([^\[\]]*)\]", x)]
|
|
3706
|
+
return [dims] if any(len(d) > 1 for d in dims) else []
|
|
3707
|
+
|
|
3708
|
+
|
|
3709
|
+
def _sections(x: str) -> list[list[str]]:
|
|
3710
|
+
"""The subscript triplets (lb, ub[, stride]) of the parenthesised parts
|
|
3711
|
+
of a list item that contain a ':'."""
|
|
3712
|
+
out = []
|
|
3713
|
+
for m in re.finditer(r"\(([^()]*(?:\([^()]*\)[^()]*)*)\)", x):
|
|
3714
|
+
dims = _split_top(m.group(1), ",")
|
|
3715
|
+
if any(":" in d for d in dims):
|
|
3716
|
+
out.append([_split_top(d, ":") for d in dims])
|
|
3717
|
+
return out
|
|
3718
|
+
|
|
3719
|
+
|
|
3720
|
+
@predicate
|
|
3721
|
+
def section_stride_positive(tree, version):
|
|
3722
|
+
"""An array section with an integer literal stride less than one."""
|
|
3723
|
+
return _each(tree, lambda i: any(
|
|
3724
|
+
len(t) == 3 and _int(t[2] or "1") is not None and _int(t[2]) < 1
|
|
3725
|
+
for c in _clauses(tree, i, end=True) if c.name in _LIST_CLAUSES
|
|
3726
|
+
for x in _items([c]) for dims in _sections(x) for t in dims))
|
|
3727
|
+
|
|
3728
|
+
|
|
3729
|
+
@predicate
|
|
3730
|
+
def section_last_part_ref(tree, version):
|
|
3731
|
+
"""A list item with a section subscript in a part-ref other than the
|
|
3732
|
+
last (a(1:n)%c)."""
|
|
3733
|
+
return _each(tree, lambda i: any(
|
|
3734
|
+
re.search(r"\([^()]*:[^()]*\)\s*%", x)
|
|
3735
|
+
for c in _clauses(tree, i, end=True) if c.name in _LIST_CLAUSES
|
|
3736
|
+
for x in _items([c])))
|
|
3737
|
+
|
|
3738
|
+
|
|
3739
|
+
@predicate
|
|
3740
|
+
def map_section_contiguous(tree, version):
|
|
3741
|
+
"""A map list item that is an array section with an integer literal
|
|
3742
|
+
stride other than 1 (not contiguous)."""
|
|
3743
|
+
return _each(tree, lambda i: _map_construct(tree, i) and any(
|
|
3744
|
+
len(t) == 3 and _int(t[2] or "1") not in (None, 1)
|
|
3745
|
+
for x in _items(_clauses(tree, i, "map"))
|
|
3746
|
+
for dims in _sections(x) + _c_sections(x) for t in dims))
|
|
3747
|
+
|
|
3748
|
+
|
|
3749
|
+
def _iterator_values(spec: str, c_lang: bool, consts: dict | None = None
|
|
3750
|
+
) -> tuple[str, int | None]:
|
|
3751
|
+
"""(identifier, number of values) of one iterator-specifier
|
|
3752
|
+
``[type] id = begin:end[:step]`` (None when a bound is not an integer
|
|
3753
|
+
literal). C/C++ ranges exclude the end, Fortran ranges include it
|
|
3754
|
+
(5.2 §3.2.6 p.67)."""
|
|
3755
|
+
m = re.match(r"\s*(?:[\w\s:*]*?(?:::)?\s+)?([a-z_]\w*)\s*=\s*(.+)$",
|
|
3756
|
+
spec.strip(), re.I)
|
|
3757
|
+
if not m:
|
|
3758
|
+
return "", None
|
|
3759
|
+
parts = _split_top(m.group(2), ":")
|
|
3760
|
+
nums = [_int(x) if _int(x) is not None else
|
|
3761
|
+
(consts or {}).get(x.strip()) for x in parts]
|
|
3762
|
+
if len(parts) not in (2, 3) or any(n is None for n in nums):
|
|
3763
|
+
return m.group(1).lower(), None
|
|
3764
|
+
b, e = nums[0], nums[1]
|
|
3765
|
+
s = nums[2] if len(nums) == 3 else 1
|
|
3766
|
+
if s == 0:
|
|
3767
|
+
return m.group(1).lower(), None
|
|
3768
|
+
if c_lang:
|
|
3769
|
+
n = max(0, -(-(e - b) // s)) if s > 0 else max(0, -(-(b - e) // -s))
|
|
3770
|
+
else:
|
|
3771
|
+
n = max(0, (e - b) // s + 1) if s > 0 else max(0, (b - e) // -s + 1)
|
|
3772
|
+
return m.group(1).lower(), n
|
|
3773
|
+
|
|
3774
|
+
|
|
3775
|
+
@predicate
|
|
3776
|
+
def map_iterator_shared_containing_array(tree, version):
|
|
3777
|
+
"""A map clause whose iterator modifier gives two or more values (integer
|
|
3778
|
+
literal bounds) to an identifier used only in the subscript applied to a
|
|
3779
|
+
named variable: every instance has that variable as containing array
|
|
3780
|
+
(or base pointer), 5.2 §5.8.3 p.156."""
|
|
3781
|
+
def bad(i):
|
|
3782
|
+
for c in _clauses(tree, i, "map"):
|
|
3783
|
+
for mod in c.modifiers:
|
|
3784
|
+
m = re.match(r"\s*iterator\s*\((.*)\)\s*$", mod, re.I | re.S)
|
|
3785
|
+
if not m:
|
|
3786
|
+
continue
|
|
3787
|
+
c_lang = getattr(tree, "lang", "") == "c"
|
|
3788
|
+
multi = set()
|
|
3789
|
+
for spec in _split_top(m.group(1), ","):
|
|
3790
|
+
macros = tree.extra.get("macros") or {}
|
|
3791
|
+
ident, n = _iterator_values(
|
|
3792
|
+
spec, c_lang, {**{k.lower(): v for k, v in
|
|
3793
|
+
macros.items()}, **macros})
|
|
3794
|
+
if ident and n is not None and n >= 2:
|
|
3795
|
+
multi.add(ident)
|
|
3796
|
+
for x in c.args:
|
|
3797
|
+
x = _norm(x)
|
|
3798
|
+
mm = (re.fullmatch(r"([a-z_]\w*)\s*\[([^\[\]]*)\]", x)
|
|
3799
|
+
or re.fullmatch(r"([a-z_]\w*)\s*\(([^()]*)\)", x))
|
|
3800
|
+
if mm and multi & set(re.findall(r"[a-z_]\w*",
|
|
3801
|
+
mm.group(2))):
|
|
3802
|
+
return True
|
|
3803
|
+
return False
|
|
3804
|
+
return _each(tree, bad)
|
|
3805
|
+
|
|
3806
|
+
|
|
3807
|
+
@predicate
|
|
3808
|
+
def task_reduction_item_needs_in_reduction(tree, version):
|
|
3809
|
+
"""A task construct whose body references an item of a reduction clause
|
|
3810
|
+
of the innermost enclosing worksharing or parallel construct (a leaf
|
|
3811
|
+
of it) without naming it in an in_reduction clause."""
|
|
3812
|
+
def bad(i):
|
|
3813
|
+
if tree.directives[i].name != "task":
|
|
3814
|
+
return False
|
|
3815
|
+
hit = None
|
|
3816
|
+
for p, k, leaf in _outer_leaf_slots(tree, i):
|
|
3817
|
+
if leaf == "parallel" or _ws_leaf(tree, p, leaf):
|
|
3818
|
+
hit = (p, k)
|
|
3819
|
+
break
|
|
3820
|
+
if leaf in ("teams", "target"):
|
|
3821
|
+
return False
|
|
3822
|
+
if hit is None:
|
|
3823
|
+
return False
|
|
3824
|
+
red = {_base(x) for x in _on_leaf(tree, hit[0], hit[1], version,
|
|
3825
|
+
("reduction",))}
|
|
3826
|
+
red -= {_base(x) for x in _items(_clauses(tree, i, "in_reduction"))}
|
|
3827
|
+
if not red:
|
|
3828
|
+
return False
|
|
3829
|
+
names = set()
|
|
3830
|
+
for _, st in _body(tree, i):
|
|
3831
|
+
names |= set(re.findall(r"(?<![%\w])([a-z_]\w*)",
|
|
3832
|
+
_STRING.sub("", st)))
|
|
3833
|
+
names |= {_base(x) for x in _items(_clauses(tree, i))}
|
|
3834
|
+
return bool(red & names)
|
|
3835
|
+
return _each(tree, bad)
|
|
3836
|
+
|
|
3837
|
+
|
|
3838
|
+
@predicate
|
|
3839
|
+
def ordered_simd_threads_in_worksharing_simd(tree, version):
|
|
3840
|
+
"""A block-associated ordered construct with both simd and threads
|
|
3841
|
+
whose closest enclosing construct has no simd leaf, or has one but no
|
|
3842
|
+
do leaf of its own or of its closest enclosing construct (5.2)."""
|
|
3843
|
+
def bad(i):
|
|
3844
|
+
d = tree.directives[i]
|
|
3845
|
+
if d.name != "ordered (Block)" or not (
|
|
3846
|
+
_clauses(tree, i, "simd") and _clauses(tree, i, "threads")):
|
|
3847
|
+
return False
|
|
3848
|
+
p = tree.parent(i)
|
|
3849
|
+
if p is None:
|
|
3850
|
+
return False
|
|
3851
|
+
ls = set(_leaves(tree, p))
|
|
3852
|
+
if "simd" not in ls:
|
|
3853
|
+
return True
|
|
3854
|
+
if ls & {"do", "for"}:
|
|
3855
|
+
return False
|
|
3856
|
+
q = tree.parent(p)
|
|
3857
|
+
return q is None or not set(_leaves(tree, q)) & {"do", "for"}
|
|
3858
|
+
return _each(tree, bad)
|
|
3859
|
+
|
|
3860
|
+
|
|
3861
|
+
_DECLARATIVE = {"declare target", "declare simd", "declare mapper",
|
|
3862
|
+
"declare reduction", "declare_induction", "declare variant",
|
|
3863
|
+
"requires", "threadprivate", "groupprivate", "allocate"}
|
|
3864
|
+
_USE_IMPLICIT = re.compile(r"^(?:use\b|import\b|implicit\b)")
|
|
3865
|
+
|
|
3866
|
+
|
|
3867
|
+
@predicate
|
|
3868
|
+
def declarative_after_use(tree, version):
|
|
3869
|
+
"""A declarative directive (5.2: requires) before a USE, IMPORT or
|
|
3870
|
+
IMPLICIT statement of its own scoping unit."""
|
|
3871
|
+
names = _DECLARATIVE if version >= 60 else {"requires"}
|
|
3872
|
+
|
|
3873
|
+
def bad(i):
|
|
3874
|
+
if tree.directives[i].name not in names:
|
|
3875
|
+
return False
|
|
3876
|
+
a = tree.assoc[i]
|
|
3877
|
+
return any(ln > a.line and sc == a.scope and _USE_IMPLICIT.match(st)
|
|
3878
|
+
for ln, st, sc in tree.statements)
|
|
3879
|
+
return _each(tree, bad)
|
|
3880
|
+
|
|
3881
|
+
|
|
3882
|
+
@predicate
|
|
3883
|
+
def named_constant_list_item(tree, version):
|
|
3884
|
+
"""A named constant (PARAMETER, visible from the directive) as a list
|
|
3885
|
+
item of a clause other than shared or firstprivate (5.2: of a
|
|
3886
|
+
data-sharing attribute clause; 6.0: of any clause)."""
|
|
3887
|
+
clauses = set(_DSA) if version < 60 else _LIST_CLAUSES
|
|
3888
|
+
|
|
3889
|
+
def bad(i):
|
|
3890
|
+
for c in _clauses(tree, i, end=True):
|
|
3891
|
+
if c.name not in clauses or c.name in ("shared", "firstprivate"):
|
|
3892
|
+
continue
|
|
3893
|
+
for x in map(_base, _items([c])):
|
|
3894
|
+
s = _decl_scope(tree, i, x)
|
|
3895
|
+
if s is not None and (s, x) in tree.params:
|
|
3896
|
+
return True
|
|
3897
|
+
return False
|
|
3898
|
+
return _each(tree, bad)
|
|
3899
|
+
|
|
3900
|
+
|
|
3901
|
+
# --- Fortran derived types and type-bound procedures (fortran-oop) ---------------
|
|
3902
|
+
# The scanner's facts (``types``,
|
|
3903
|
+
# ``vartypes``, ``vtypes``, ``units``, ``statements``) plus a pass over the
|
|
3904
|
+
# statements of each derived-type definition: its parent (EXTENDS), whether
|
|
3905
|
+
# it is abstract or parameterized, and its type-bound procedure part
|
|
3906
|
+
# (specific bindings, generic bindings, final subroutines). Types are
|
|
3907
|
+
# matched by name across the source (the programs judged here define each
|
|
3908
|
+
# type once); a binding whose procedure is not defined in the source is
|
|
3909
|
+
# never judged.
|
|
3910
|
+
|
|
3911
|
+
_TYPE_HEAD = re.compile(r"^type\b(?!\s*\()(?!\s*is\b)\s*(,[^:]*)?(?:::)?\s*"
|
|
3912
|
+
r"([a-z_]\w*)\s*(\(.*\))?$")
|
|
3913
|
+
_TBP_SPECIFIC = re.compile(r"^procedure\b\s*(?:\(\s*\w*\s*\))?\s*"
|
|
3914
|
+
r"(?:,([^:]*))?(?:::)?\s*(.*)$")
|
|
3915
|
+
_TBP_GENERIC = re.compile(r"^generic\b\s*(?:,[^:]*)?::\s*(.+?)\s*=>\s*(.*)$")
|
|
3916
|
+
_TBP_FINAL = re.compile(r"^final\b\s*(?:::)?\s*(.*)$")
|
|
3917
|
+
_GENERIC_IFACE = re.compile(r"^interface\s+([a-z_]\w*)\s*$")
|
|
3918
|
+
_PROC_POINTER = re.compile(r"^procedure\s*\([^)]*\)\s*,([^:]*)::\s*(.*)$")
|
|
3919
|
+
_CHAIN = r"[a-z_]\w*(?:\s*\([^()]*\))?(?:\s*%\s*[a-z_]\w*(?:\s*\([^()]*\))?)*"
|
|
3920
|
+
_TBP_REF = re.compile(rf"({_CHAIN})\s*%\s*([a-z_]\w*)\s*(?=\(|$)")
|
|
3921
|
+
_ASSIGN_TO = re.compile(rf"^({_CHAIN})\s*=(?![=>])")
|
|
3922
|
+
_ANY_CHAIN = re.compile(_CHAIN)
|
|
3923
|
+
_LOCAL_DECL = re.compile(r"^(type|class)\s*\(\s*([a-z_]\w*)\s*\)(.*)$")
|
|
3924
|
+
|
|
3925
|
+
|
|
3926
|
+
def _oop(tree: ProgramTree) -> dict:
|
|
3927
|
+
"""{type name: {parent, abstract, pdt, comps, bindings, generics,
|
|
3928
|
+
finals}}, procedure names defined in the source, generic interface
|
|
3929
|
+
names and procedure pointer names (cached in tree.extra)."""
|
|
3930
|
+
if "oop" in tree.extra:
|
|
3931
|
+
return tree.extra["oop"]
|
|
3932
|
+
types, generic_names, proc_ptrs = {}, set(), set()
|
|
3933
|
+
cur, tbp = None, False
|
|
3934
|
+
for _, st, _sc in tree.statements:
|
|
3935
|
+
m = _TYPE_HEAD.match(st)
|
|
3936
|
+
if cur is None and m:
|
|
3937
|
+
attrs = m.group(1) or ""
|
|
3938
|
+
par = re.search(r"\bextends\s*\(\s*([a-z_]\w*)\s*\)", attrs)
|
|
3939
|
+
cur, tbp = m.group(2), False
|
|
3940
|
+
types[cur] = {"parent": par.group(1) if par else None,
|
|
3941
|
+
"abstract": bool(re.search(r"\babstract\b",
|
|
3942
|
+
attrs)),
|
|
3943
|
+
"pdt": bool(m.group(3)), "comps": {},
|
|
3944
|
+
"bindings": {}, "generics": {}, "finals": []}
|
|
3945
|
+
continue
|
|
3946
|
+
if cur is not None:
|
|
3947
|
+
if _END_TYPE.match(st):
|
|
3948
|
+
cur = None
|
|
3949
|
+
elif st == "contains":
|
|
3950
|
+
tbp = True
|
|
3951
|
+
elif tbp and (g := _TBP_GENERIC.match(st)):
|
|
3952
|
+
key = _norm(g.group(1))
|
|
3953
|
+
types[cur]["generics"].setdefault(key, []).extend(
|
|
3954
|
+
_norm(x) for x in _split_statements_commas(g.group(2)))
|
|
3955
|
+
elif tbp and (f := _TBP_FINAL.match(st)):
|
|
3956
|
+
types[cur]["finals"] += [_norm(x) for x in
|
|
3957
|
+
_split_statements_commas(f.group(1))]
|
|
3958
|
+
elif tbp and (p := _TBP_SPECIFIC.match(st)):
|
|
3959
|
+
deferred = "deferred" in (p.group(1) or "")
|
|
3960
|
+
for ent in _split_statements_commas(p.group(2)):
|
|
3961
|
+
b, _, impl = ent.partition("=>")
|
|
3962
|
+
b, impl = _norm(b), _norm(impl) or _norm(b)
|
|
3963
|
+
if b:
|
|
3964
|
+
types[cur]["bindings"][b] = None if deferred \
|
|
3965
|
+
else impl
|
|
3966
|
+
continue
|
|
3967
|
+
g = _GENERIC_IFACE.match(st)
|
|
3968
|
+
if g:
|
|
3969
|
+
generic_names.add(g.group(1))
|
|
3970
|
+
pp = _PROC_POINTER.match(st)
|
|
3971
|
+
if pp and re.search(r"\bpointer\b", pp.group(1)):
|
|
3972
|
+
proc_ptrs |= set(_entities(pp.group(2)))
|
|
3973
|
+
for (_scope, tname), comps in tree.types.items():
|
|
3974
|
+
if tname in types:
|
|
3975
|
+
types[tname]["comps"].update(comps)
|
|
3976
|
+
procs = {}
|
|
3977
|
+
for uid, (kind, name) in tree.units.items():
|
|
3978
|
+
if kind in ("subroutine", "function") and name:
|
|
3979
|
+
procs.setdefault(name, []).append(uid)
|
|
3980
|
+
out = {"types": types, "procs": procs, "generic_names": generic_names,
|
|
3981
|
+
"proc_pointers": proc_ptrs}
|
|
3982
|
+
tree.extra["oop"] = out
|
|
3983
|
+
return out
|
|
3984
|
+
|
|
3985
|
+
|
|
3986
|
+
def _ancestors_of(types: dict, t: str) -> list[str]:
|
|
3987
|
+
out = []
|
|
3988
|
+
while t in types and t not in out:
|
|
3989
|
+
out.append(t)
|
|
3990
|
+
t = types[t]["parent"]
|
|
3991
|
+
return out
|
|
3992
|
+
|
|
3993
|
+
|
|
3994
|
+
def _descendants_of(types: dict, t: str) -> list[str]:
|
|
3995
|
+
return [d for d in types if d != t and t in _ancestors_of(types, d)]
|
|
3996
|
+
|
|
3997
|
+
|
|
3998
|
+
def _scoped(table: dict, scopes: tuple, name: str):
|
|
3999
|
+
"""The entry of `name` declared in the innermost of `scopes` that
|
|
4000
|
+
declares it (a local declaration; no USE shadowing check: a name
|
|
4001
|
+
declared in a scoping unit cannot also be use-associated there)."""
|
|
4002
|
+
for sid in reversed(scopes):
|
|
4003
|
+
if (sid, name) in table:
|
|
4004
|
+
return table[(sid, name)]
|
|
4005
|
+
return None
|
|
4006
|
+
|
|
4007
|
+
|
|
4008
|
+
def _chain_type(tree, oop, scopes, chain: str):
|
|
4009
|
+
"""(type name, polymorphic) of a designator `a%b(i)%c`, None when a
|
|
4010
|
+
name on the path is not a derived-type entity the source defines."""
|
|
4011
|
+
parts = [re.sub(r"\(.*", "", x).strip()
|
|
4012
|
+
for x in re.sub(r"\([^()]*\)", "", chain).split("%")]
|
|
4013
|
+
t = _scoped(tree.vartypes, scopes, parts[0])
|
|
4014
|
+
poly = _scoped(tree.vtypes, scopes, parts[0]) == "class"
|
|
4015
|
+
for comp in parts[1:]:
|
|
4016
|
+
found = None
|
|
4017
|
+
for a in _ancestors_of(oop["types"], t or ""):
|
|
4018
|
+
if comp in oop["types"][a]["comps"]:
|
|
4019
|
+
found = oop["types"][a]["comps"][comp]
|
|
4020
|
+
break
|
|
4021
|
+
if found is None:
|
|
4022
|
+
return None
|
|
4023
|
+
t, poly = found[1], False
|
|
4024
|
+
return (t, poly) if t in oop["types"] else None
|
|
4025
|
+
|
|
4026
|
+
|
|
4027
|
+
def _binding_procs(oop, t: str, b: str, poly: bool, seen=None) -> set:
|
|
4028
|
+
"""Procedures a reference to binding (or generic) `b` of an object of
|
|
4029
|
+
declared type `t` may invoke: the inherited or own binding, and, for a
|
|
4030
|
+
polymorphic object, every override in an extension of `t`."""
|
|
4031
|
+
seen = set() if seen is None else seen
|
|
4032
|
+
if (t, b) in seen:
|
|
4033
|
+
return set()
|
|
4034
|
+
seen.add((t, b))
|
|
4035
|
+
types, out = oop["types"], set()
|
|
4036
|
+
for a in _ancestors_of(types, t):
|
|
4037
|
+
if b in types[a]["bindings"]:
|
|
4038
|
+
if types[a]["bindings"][b]:
|
|
4039
|
+
out.add(types[a]["bindings"][b])
|
|
4040
|
+
break
|
|
4041
|
+
if b in types[a]["generics"]:
|
|
4042
|
+
for sp in types[a]["generics"][b]:
|
|
4043
|
+
out |= _binding_procs(oop, t, sp, poly, seen)
|
|
4044
|
+
break
|
|
4045
|
+
if poly:
|
|
4046
|
+
for d in _descendants_of(types, t):
|
|
4047
|
+
impl = types[d]["bindings"].get(b)
|
|
4048
|
+
if impl:
|
|
4049
|
+
out.add(impl)
|
|
4050
|
+
return out
|
|
4051
|
+
|
|
4052
|
+
|
|
4053
|
+
def _finals(oop, t: str) -> set:
|
|
4054
|
+
return {f for a in _ancestors_of(oop["types"], t)
|
|
4055
|
+
for f in oop["types"][a]["finals"]}
|
|
4056
|
+
|
|
4057
|
+
|
|
4058
|
+
_OPERATOR = re.compile(r"^operator\((.+)\)$")
|
|
4059
|
+
|
|
4060
|
+
|
|
4061
|
+
def _stmt_accesses(tree, oop, st: str, scopes: tuple) -> set:
|
|
4062
|
+
"""Type-bound procedures a statement accesses: binding references,
|
|
4063
|
+
a defined assignment to a derived-type designator, a defined
|
|
4064
|
+
operator on one, and the final subroutines of a local derived-type
|
|
4065
|
+
object it declares."""
|
|
4066
|
+
out = set()
|
|
4067
|
+
for m in _TBP_REF.finditer(st):
|
|
4068
|
+
ty = _chain_type(tree, oop, scopes, m.group(1))
|
|
4069
|
+
if ty:
|
|
4070
|
+
out |= _binding_procs(oop, ty[0], m.group(2), ty[1])
|
|
4071
|
+
a = _ASSIGN_TO.match(st)
|
|
4072
|
+
if a:
|
|
4073
|
+
ty = _chain_type(tree, oop, scopes, a.group(1))
|
|
4074
|
+
if ty:
|
|
4075
|
+
out |= _binding_procs(oop, ty[0], "assignment(=)", ty[1])
|
|
4076
|
+
for m in _ANY_CHAIN.finditer(st):
|
|
4077
|
+
ty = _chain_type(tree, oop, scopes, m.group(0))
|
|
4078
|
+
if not ty:
|
|
4079
|
+
continue
|
|
4080
|
+
for a_ in _ancestors_of(oop["types"], ty[0]):
|
|
4081
|
+
for g in oop["types"][a_]["generics"]:
|
|
4082
|
+
op = _OPERATOR.match(g)
|
|
4083
|
+
if op and op.group(1) in st.replace(m.group(0), " "):
|
|
4084
|
+
out |= _binding_procs(oop, ty[0], g, ty[1])
|
|
4085
|
+
d = _LOCAL_DECL.match(st)
|
|
4086
|
+
attrs = _norm(d.group(3).split("::")[0]) if d else ""
|
|
4087
|
+
if d and "pointer" not in attrs and not re.search(
|
|
4088
|
+
r"intent\(in(?:out)?\)", attrs):
|
|
4089
|
+
# a local object or an INTENT(OUT) dummy is finalized (F2018
|
|
4090
|
+
# 7.5.6.3); an INTENT(IN)/(INOUT) dummy or a pointer is not
|
|
4091
|
+
out |= _finals(oop, d.group(2))
|
|
4092
|
+
return out
|
|
4093
|
+
|
|
4094
|
+
|
|
4095
|
+
def _declared_target_procs(tree) -> set:
|
|
4096
|
+
"""Procedure names that appear in a declare target directive: the
|
|
4097
|
+
procedure a declaration-associated directive (no list, no enter/to
|
|
4098
|
+
clause) sits in, and the names of its extended list or enter/to
|
|
4099
|
+
clauses."""
|
|
4100
|
+
out = set()
|
|
4101
|
+
for i, d in enumerate(tree.directives):
|
|
4102
|
+
if d.name != "declare target":
|
|
4103
|
+
continue
|
|
4104
|
+
listed = _dt_items(tree, i, "enter", "to")
|
|
4105
|
+
if listed:
|
|
4106
|
+
out |= {_base(x) for x in listed}
|
|
4107
|
+
else:
|
|
4108
|
+
kind, name = tree.units.get(tree.assoc[i].scope[-1], ("", None))
|
|
4109
|
+
if kind in ("subroutine", "function") and name:
|
|
4110
|
+
out.add(name)
|
|
4111
|
+
return out
|
|
4112
|
+
|
|
4113
|
+
|
|
4114
|
+
_NOT_REGION = {"target data", "target enter data", "target exit data",
|
|
4115
|
+
"target update"}
|
|
4116
|
+
|
|
4117
|
+
|
|
4118
|
+
@predicate
|
|
4119
|
+
def type_bound_declare_target(tree, version):
|
|
4120
|
+
"""A type-bound procedure (a specific binding, a generic binding's
|
|
4121
|
+
specific, a defined assignment or operator, a final subroutine)
|
|
4122
|
+
accessed in a target region whose procedure, defined in the source,
|
|
4123
|
+
is in no declare target directive. The region: the statements of
|
|
4124
|
+
target constructs and of the procedures that run on the device (in a
|
|
4125
|
+
declare target directive, referenced from the region, or reached as
|
|
4126
|
+
a type-bound procedure); firstprivate and private items of target
|
|
4127
|
+
constructs reach defined assignments and final subroutines."""
|
|
4128
|
+
if tree.lang != "f90":
|
|
4129
|
+
return []
|
|
4130
|
+
oop = _oop(tree)
|
|
4131
|
+
if not any(t["bindings"] or t["generics"] or t["finals"]
|
|
4132
|
+
for t in oop["types"].values()):
|
|
4133
|
+
return []
|
|
4134
|
+
declared = _declared_target_procs(tree)
|
|
4135
|
+
procs = oop["procs"]
|
|
4136
|
+
by_unit = {}
|
|
4137
|
+
for ln, st, sc in tree.statements:
|
|
4138
|
+
if sc:
|
|
4139
|
+
by_unit.setdefault(sc[-1], []).append((ln, st, sc))
|
|
4140
|
+
roots = [] # (origin directive, statements)
|
|
4141
|
+
for i, d in enumerate(tree.directives):
|
|
4142
|
+
if d.name.split()[0] == "target" and d.name not in _NOT_REGION:
|
|
4143
|
+
body = [(ln, st, sc) for ln, st, sc in tree.statements
|
|
4144
|
+
if _extent(tree, i)[0] < ln <= _extent(tree, i)[1]]
|
|
4145
|
+
roots.append((i, body))
|
|
4146
|
+
for c in _clauses(tree, i, "private", "firstprivate"):
|
|
4147
|
+
for x in _items([c]):
|
|
4148
|
+
ty = _chain_type(tree, oop, tree.assoc[i].scope, x)
|
|
4149
|
+
if ty:
|
|
4150
|
+
acc = _finals(oop, ty[0])
|
|
4151
|
+
if c.name == "firstprivate":
|
|
4152
|
+
acc |= _binding_procs(oop, ty[0],
|
|
4153
|
+
"assignment(=)", ty[1])
|
|
4154
|
+
roots.append((i, [], acc))
|
|
4155
|
+
elif d.name == "declare target":
|
|
4156
|
+
for name in _declared_target_procs_at(tree, i):
|
|
4157
|
+
for uid in procs.get(name, []):
|
|
4158
|
+
roots.append((i, by_unit.get(uid, [])))
|
|
4159
|
+
bad, done = set(), set()
|
|
4160
|
+
work = [(r[0], r[1], r[2] if len(r) > 2 else set()) for r in roots]
|
|
4161
|
+
while work:
|
|
4162
|
+
origin, stmts, extra = work.pop()
|
|
4163
|
+
reached = set(extra)
|
|
4164
|
+
for _ln, st, sc in stmts:
|
|
4165
|
+
reached |= _stmt_accesses(tree, oop, st, sc)
|
|
4166
|
+
for name in re.findall(r"\b([a-z_]\w*)\s*(?=\(|$)", st):
|
|
4167
|
+
if name in procs and not st.startswith(("subroutine",
|
|
4168
|
+
"function")):
|
|
4169
|
+
reached.add(("call", name))
|
|
4170
|
+
for r in reached:
|
|
4171
|
+
called = isinstance(r, tuple)
|
|
4172
|
+
name = r[1] if called else r
|
|
4173
|
+
if not called and name in procs and name not in declared:
|
|
4174
|
+
bad.add(origin)
|
|
4175
|
+
if name in procs and name not in done:
|
|
4176
|
+
done.add(name)
|
|
4177
|
+
for uid in procs[name]:
|
|
4178
|
+
work.append((origin, by_unit.get(uid, []), set()))
|
|
4179
|
+
return sorted(bad)
|
|
4180
|
+
|
|
4181
|
+
|
|
4182
|
+
def _declared_target_procs_at(tree, i: int) -> set:
|
|
4183
|
+
"""The procedure names declare target directive i declares (as
|
|
4184
|
+
_declared_target_procs, for one directive)."""
|
|
4185
|
+
listed = _dt_items(tree, i, "enter", "to")
|
|
4186
|
+
if listed:
|
|
4187
|
+
return {_base(x) for x in listed}
|
|
4188
|
+
kind, name = tree.units.get(tree.assoc[i].scope[-1], ("", None))
|
|
4189
|
+
return {name} if kind in ("subroutine", "function") and name else set()
|
|
4190
|
+
|
|
4191
|
+
|
|
4192
|
+
def _type_name_bad(oop, spec: str) -> bool:
|
|
4193
|
+
"""A type-name list item that is CLASS(*), TYPE(*) or an abstract
|
|
4194
|
+
type defined in the source."""
|
|
4195
|
+
spec = _norm(spec)
|
|
4196
|
+
if spec in ("class(*)", "type(*)"):
|
|
4197
|
+
return True
|
|
4198
|
+
m = re.fullmatch(r"(?:type|class)\(([a-z_]\w*)\)|([a-z_]\w*)", spec)
|
|
4199
|
+
name = m and (m.group(1) or m.group(2))
|
|
4200
|
+
t = oop["types"].get(name) if name else None
|
|
4201
|
+
return bool(t and t["abstract"])
|
|
4202
|
+
|
|
4203
|
+
|
|
4204
|
+
def _reduction_types(d) -> list[str]:
|
|
4205
|
+
"""The type-name list of a declare reduction argument
|
|
4206
|
+
``id : types : combiner``."""
|
|
4207
|
+
raw = d.args[0] if d.args else ""
|
|
4208
|
+
parts, cur, depth = [], "", 0
|
|
4209
|
+
for ch in raw:
|
|
4210
|
+
if ch == "(":
|
|
4211
|
+
depth += 1
|
|
4212
|
+
elif ch == ")":
|
|
4213
|
+
depth -= 1
|
|
4214
|
+
if ch == ":" and depth == 0 and len(parts) < 2:
|
|
4215
|
+
parts.append(cur)
|
|
4216
|
+
cur = ""
|
|
4217
|
+
continue
|
|
4218
|
+
cur += ch
|
|
4219
|
+
parts.append(cur)
|
|
4220
|
+
return _split_statements_commas(parts[1]) if len(parts) == 3 else []
|
|
4221
|
+
|
|
4222
|
+
|
|
4223
|
+
@predicate
|
|
4224
|
+
def type_name_list_item(tree, version):
|
|
4225
|
+
"""A declare reduction type-name list item (and a declare mapper type)
|
|
4226
|
+
that is CLASS(*) or (6.0) TYPE(*), or an abstract type; the abstract
|
|
4227
|
+
type of a declare mapper is declaremapper-clause-004's."""
|
|
4228
|
+
if tree.lang != "f90":
|
|
4229
|
+
return []
|
|
4230
|
+
oop = _oop(tree)
|
|
4231
|
+
|
|
4232
|
+
def bad(i):
|
|
4233
|
+
d = tree.directives[i]
|
|
4234
|
+
if d.name == "declare reduction":
|
|
4235
|
+
return any(_type_name_bad(oop, t) for t in _reduction_types(d))
|
|
4236
|
+
if d.name == "declare mapper":
|
|
4237
|
+
return _norm(_mapper_spec(d)[1]) in ("class(*)", "type(*)")
|
|
4238
|
+
return False
|
|
4239
|
+
return _each(tree, bad)
|
|
4240
|
+
|
|
4241
|
+
|
|
4242
|
+
@predicate
|
|
4243
|
+
def declare_target_procedure_name(tree, version):
|
|
4244
|
+
"""A declare target list item that names a generic interface or a
|
|
4245
|
+
procedure pointer of the source."""
|
|
4246
|
+
if tree.lang != "f90":
|
|
4247
|
+
return []
|
|
4248
|
+
oop = _oop(tree)
|
|
4249
|
+
names = oop["generic_names"] | oop["proc_pointers"]
|
|
4250
|
+
return _each(tree, lambda i: tree.directives[i].name == "declare target"
|
|
4251
|
+
and any(_base(x) in names for x in _dt_items(
|
|
4252
|
+
tree, i, "enter", "to", "link", "local")))
|
|
4253
|
+
|
|
4254
|
+
|
|
4255
|
+
# The C rules (catalog/omp/rules.yaml entries with lang: c)
|
|
4256
|
+
from offload.engine import c_rules # noqa: E402,F401
|