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/report.py
ADDED
|
@@ -0,0 +1,3374 @@
|
|
|
1
|
+
"""Support table and vendor-claims comparison.
|
|
2
|
+
|
|
3
|
+
Reads the matrix (results/matrix.jsonl, one Result per program, toolchain and
|
|
4
|
+
memory mode), the oracle audit summary (tests/data/oracle_audit_summary.json)
|
|
5
|
+
and the vendor claims (catalog/omp/claims.yaml), and renders
|
|
6
|
+
results/support_table.md and results/support_table.csv
|
|
7
|
+
(command line: tools/derive/support_table.py).
|
|
8
|
+
One table row per matrix cell (program x toolchain x HSA_XNACK). The headline
|
|
9
|
+
outcome is HSA_XNACK=1 with THP; the copy-mode column (HSA_XNACK=0) is filled
|
|
10
|
+
where the program ran in copy mode (``COPY_MARK`` marks copy_mode_only). Each cell carries its
|
|
11
|
+
evidence level and its atom's audit disposition. Cells without a separating
|
|
12
|
+
oracle read ``NO_ORACLE_TEXT``, ``LEGALITY_TEXT`` or ``UNVERIFIED_TEXT`` and
|
|
13
|
+
never count as supported. A wrong answer is "confirmed" only when the atom's
|
|
14
|
+
oracle is audited valid. A dagger marks modes that ran on different nodes.
|
|
15
|
+
The omp_vv suite is a second source. OpenACC matrices use the OpenACC catalog."""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import csv
|
|
19
|
+
import io
|
|
20
|
+
import json
|
|
21
|
+
import re
|
|
22
|
+
from collections import Counter, defaultdict
|
|
23
|
+
from dataclasses import dataclass, field, replace
|
|
24
|
+
from fnmatch import fnmatchcase
|
|
25
|
+
from functools import lru_cache
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
import yaml
|
|
29
|
+
|
|
30
|
+
from offload.engine.textclean import scrub as _public
|
|
31
|
+
from offload.engine.toolchains import arch_of, base_name, family
|
|
32
|
+
# Report.family (below) is the label category; the toolchain
|
|
33
|
+
# family (message tables: every CCE release is cce19; a gfx90a twin is its
|
|
34
|
+
# compiler) is this one
|
|
35
|
+
from offload.engine.toolchains import family as _tc_family
|
|
36
|
+
|
|
37
|
+
ROOT = Path(__file__).resolve().parent.parent.parent
|
|
38
|
+
MATRIX = ROOT / "results" / "matrix.jsonl"
|
|
39
|
+
BASELINE = ROOT / "results" / "baseline.jsonl"
|
|
40
|
+
AUDIT = ROOT / "tests" / "data" / "oracle_audit_summary.json"
|
|
41
|
+
# The generic claims ingested from the vendor documents
|
|
42
|
+
# (tools/derive/claims.py ingest; mapping in catalog/<model>/claims.yaml)
|
|
43
|
+
CLAIMS = ROOT / "catalog" / "vendor_claims_generic.yaml"
|
|
44
|
+
CLAUSES = ROOT / "catalog" / "omp" / "clauses.yaml"
|
|
45
|
+
RULES = ROOT / "catalog" / "omp" / "rules.yaml"
|
|
46
|
+
OVERRIDES = ROOT / "catalog" / "omp" / "spec_overrides.yaml"
|
|
47
|
+
|
|
48
|
+
# Links in the Markdown are relative to results/support_table.md.
|
|
49
|
+
LINK = {"clauses": "../catalog/omp/clauses.yaml",
|
|
50
|
+
"skeleton": "../catalog/omp/skeleton.yaml",
|
|
51
|
+
"rules": "../catalog/omp/rules.yaml",
|
|
52
|
+
"claims": "../catalog/vendor_claims_generic.yaml",
|
|
53
|
+
"semantic": "../catalog/omp/semantic_atoms.yaml",
|
|
54
|
+
"audit": "../reports/oracle-audit.md"}
|
|
55
|
+
# The OpenACC catalog and audit
|
|
56
|
+
ACC_AUDIT = ROOT / "tests" / "data" / "acc_audit_summary.json"
|
|
57
|
+
ACC_CLAIMS = CLAIMS
|
|
58
|
+
ACC_MATRIX = ROOT / "results" / "acc_matrix.jsonl"
|
|
59
|
+
# The C programs' matrix, audit summary and omp_vv C baseline
|
|
60
|
+
C_MATRIX = ROOT / "results" / "matrix_c.jsonl"
|
|
61
|
+
C_AUDIT = ROOT / "tests" / "data" / "oracle_audit_summary_c.json"
|
|
62
|
+
C_BASELINE = ROOT / "results" / "baseline_c.jsonl"
|
|
63
|
+
# The OpenACC C slice (NVHPC only; its audits are NV_AUDIT's)
|
|
64
|
+
ACC_C_MATRIX = ROOT / "results" / "acc_matrix_c.jsonl"
|
|
65
|
+
ACC_C_AUDIT = ROOT / "tests" / "data" / "acc_audit_summary_c.json"
|
|
66
|
+
LINK_ACC = {k: v.replace("/omp/", "/acc/") for k, v in LINK.items()}
|
|
67
|
+
LINK_ACC["audit"] = "../reports/acc-oracle-audit.md"
|
|
68
|
+
# the OpenACC section of each directive (3.4)
|
|
69
|
+
ACC_DIRECTIVE_SECTION = {
|
|
70
|
+
"parallel": "2.5.1", "serial": "2.5.2", "kernels": "2.5.3",
|
|
71
|
+
"data": "2.6.5", "enter_data": "2.6.6", "exit_data": "2.6.6",
|
|
72
|
+
"host_data": "2.8", "loop": "2.9", "cache": "2.10",
|
|
73
|
+
"parallel_loop": "2.11", "serial_loop": "2.11", "kernels_loop": "2.11",
|
|
74
|
+
"atomic": "2.12", "declare": "2.13", "init": "2.14.1",
|
|
75
|
+
"shutdown": "2.14.2", "set": "2.14.3", "update": "2.14.4",
|
|
76
|
+
"routine": "2.15.1", "wait": "2.16.3"}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def model_of_atom(atom: str) -> str:
|
|
80
|
+
return "acc" if atom.startswith("acc.") else "omp"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _paths(model: str) -> tuple[Path, Path, Path, dict]:
|
|
84
|
+
"""(clauses, rules, overrides, links) of a model's catalog."""
|
|
85
|
+
d = ROOT / "catalog" / model
|
|
86
|
+
return (d / "clauses.yaml", d / "rules.yaml", d / "spec_overrides.yaml",
|
|
87
|
+
LINK_ACC if model == "acc" else LINK)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def table_model(table) -> str:
|
|
91
|
+
""""acc" when every row is an OpenACC atom, else "omp"."""
|
|
92
|
+
return "acc" if table and all(r.atom.startswith("acc.")
|
|
93
|
+
for r in table) else "omp"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def table_lang(table) -> str:
|
|
97
|
+
""""c" when every row is a C program, else "f90"."""
|
|
98
|
+
return "c" if table and all(getattr(r, "lang", "f90") == "c"
|
|
99
|
+
for r in table) else "f90"
|
|
100
|
+
|
|
101
|
+
# "unverified" only where a deterministic oracle is possible and
|
|
102
|
+
# missing; the forms no run can separate read as their category.
|
|
103
|
+
UNVERIFIED_TEXT = "accepted; unverified (an oracle is possible but missing)"
|
|
104
|
+
NO_ORACLE_TEXT = {
|
|
105
|
+
"hint_only": "accepted; conforms vacuously",
|
|
106
|
+
"implementation_defined": "implementation-defined",
|
|
107
|
+
"equivalent_default": "equivalent to the default (no separable effect)"}
|
|
108
|
+
LEGALITY_TEXT = "accepted (its oracle is the illegal variant)"
|
|
109
|
+
NO_ORACLE_NAME = {"hint_only": "hint only",
|
|
110
|
+
"implementation_defined": "implementation-defined",
|
|
111
|
+
"equivalent_default": "equivalent default"}
|
|
112
|
+
EVIDENCE_LEVELS = ("construct", "probe", "copy_mode_only", "hint_only",
|
|
113
|
+
"implementation_defined", "equivalent_default",
|
|
114
|
+
"legality", "unverified")
|
|
115
|
+
# evidence whose accepted run is never credited as support
|
|
116
|
+
UNCREDITED = ("unverified", "legality") + tuple(NO_ORACLE_TEXT)
|
|
117
|
+
# the toolchains and their names come from catalog/toolchains.yaml
|
|
118
|
+
# (offload.engine.toolchains): the AMD columns (each with its HSA_XNACK modes,
|
|
119
|
+
# then its twins), the NVIDIA machine's toolchains get columns of their own
|
|
120
|
+
from offload.engine.toolchains import NVIDIA_TOOLCHAINS # noqa: E402
|
|
121
|
+
from offload.engine.toolchains import ARCH_SUFFIX as _SUFFIX, registry as _reg # noqa: E402
|
|
122
|
+
# the AMD columns: every AMD toolchain with a report column that is neither a
|
|
123
|
+
# twin nor a regression release (AFAR, CCE, GCC)
|
|
124
|
+
ALL_TOOLCHAINS = _reg().of_gpu("amd")
|
|
125
|
+
# the toolchains with rows in the matrix being rendered (build_table narrows
|
|
126
|
+
# it); a column for a compiler that was never run would only say "not run"
|
|
127
|
+
TOOLCHAINS = list(ALL_TOOLCHAINS)
|
|
128
|
+
HEADLINE, COPY = 1, 0
|
|
129
|
+
MODES = (HEADLINE, COPY) # headline HSA_XNACK=1 first, then copy mode
|
|
130
|
+
# The gfx90a twin of each toolchain (Tioga MI250X, a discrete GPU,
|
|
131
|
+
# HSA_XNACK=0 only): a third column per compiler, after its two MI300A
|
|
132
|
+
# modes. G90 holds the twins with rows in the matrix being rendered.
|
|
133
|
+
G90_SUFFIX = _SUFFIX["gfx90a"]
|
|
134
|
+
G90: list[str] = []
|
|
135
|
+
# Every twin with rows in the matrix being rendered, in catalog order: the
|
|
136
|
+
# gfx90a ones and those on other machines (MI300X, MI350X, copy memory only).
|
|
137
|
+
# Each is a cell column after its compiler's MI300A modes; only the gfx90a
|
|
138
|
+
# cell enters the portable verdict (portable3).
|
|
139
|
+
TWINS: list[str] = []
|
|
140
|
+
# the AMD columns' toolchains with MI300A rows of their own (a compiler whose
|
|
141
|
+
# MI300A rows are left out, GCC, has only its twins' columns)
|
|
142
|
+
MI300A: list[str] = []
|
|
143
|
+
# Toolchains of other machines (NVIDIA: nvhpc on wingtip, A100,
|
|
144
|
+
# sm_80) get columns of their own after the AMD ones, one per memory mode
|
|
145
|
+
# of their machine (catalog/machines.yaml: headline mode first); the AMD
|
|
146
|
+
# columns, cells and verdicts are computed as before and never read an
|
|
147
|
+
# NVIDIA row. NV holds those with rows in the matrix being rendered.
|
|
148
|
+
NV: list[str] = []
|
|
149
|
+
# toolchains left out of the support tables on purpose (catalog
|
|
150
|
+
# ``report_column: false``): their rows are dropped, never an error
|
|
151
|
+
REPORT_EXCLUDED = tuple(n for n, t in _reg().toolchains.items()
|
|
152
|
+
if not t.report_column)
|
|
153
|
+
# the column names: every column (and the excluded) by its catalog label
|
|
154
|
+
TC_NAME = {n: _reg().label(n) for n in (*ALL_TOOLCHAINS,
|
|
155
|
+
*(w for t in ALL_TOOLCHAINS for w in _reg().twins_of(t)),
|
|
156
|
+
*NVIDIA_TOOLCHAINS, *REPORT_EXCLUDED)
|
|
157
|
+
if n in _reg().toolchains} # a gfx942 toolchain may have no twin yet
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def nv_modes(tc: str) -> tuple[int, ...]:
|
|
161
|
+
"""The row keys of an NVIDIA toolchain's modes, headline first (those
|
|
162
|
+
the toolchain has: GCC runs separate memory only)."""
|
|
163
|
+
from offload.engine.confirm.machines import machine_of
|
|
164
|
+
from offload.engine.toolchains import TOOLCHAINS as _TC
|
|
165
|
+
m = machine_of(tc)
|
|
166
|
+
keys = (m.row_key(m.headline_mode),) + tuple(
|
|
167
|
+
m.row_key(x) for x in m.other_modes(m.headline_mode))
|
|
168
|
+
return tuple(k for k in keys if k in _TC[tc].xnack_modes)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def nv_mode_name(tc: str, x: int) -> str:
|
|
172
|
+
"""The mode's name for an NVIDIA row ("separate", "managed"): the
|
|
173
|
+
machine's mode, ``copy`` named after NVHPC's -gpu=mem:separate."""
|
|
174
|
+
from offload.engine.confirm.machines import machine_of
|
|
175
|
+
m = machine_of(tc).mode_of_row_key(x)
|
|
176
|
+
return "separate" if m == "copy" else m
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _mode_word(tc: str, x: int) -> str:
|
|
180
|
+
"""How a cell's memory mode is named in a label."""
|
|
181
|
+
return nv_mode_name(tc, x) if tc in NVIDIA_TOOLCHAINS \
|
|
182
|
+
else f"HSA_XNACK={x}"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def g90_of(tc: str) -> str:
|
|
186
|
+
"""The gfx90a twin's name of a gfx942 toolchain."""
|
|
187
|
+
return f"{tc}{G90_SUFFIX}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def twins_of(tc: str) -> list[str]:
|
|
191
|
+
"""The twins of a compiler with rows in the matrix being rendered."""
|
|
192
|
+
return [w for w in _reg().twins_of(tc) if w in TWINS]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def columns() -> list[tuple[str, int]]:
|
|
196
|
+
"""The (toolchain, xnack) cell columns, per compiler: HSA_XNACK=1 and
|
|
197
|
+
0 on MI300A, then each twin with rows (HSA_XNACK=0)."""
|
|
198
|
+
out = []
|
|
199
|
+
for tc in TOOLCHAINS:
|
|
200
|
+
out += [(tc, x) for x in MODES] if tc in MI300A else []
|
|
201
|
+
out += [(w, COPY) for w in twins_of(tc)]
|
|
202
|
+
for tc in NV:
|
|
203
|
+
out += [(tc, x) for x in nv_modes(tc)]
|
|
204
|
+
return out
|
|
205
|
+
COPY_MARK = "◊"
|
|
206
|
+
NOT_RUN = "not run"
|
|
207
|
+
COPY_NOT_RUN = "copy-mode check not run"
|
|
208
|
+
# outcomes the memory mode cannot change: no copy-mode pointer needed
|
|
209
|
+
_COMPILE_TIME = ("rejected", "harness_error")
|
|
210
|
+
CLAIM_VALUES = ("supported", "unsupported", "partial")
|
|
211
|
+
CLAIM_FORMS = (None, "compile_error", "accepted")
|
|
212
|
+
|
|
213
|
+
# How a rejected legal cell is attributed:
|
|
214
|
+
# - NOT COUNTED (against the atom or in the claims): a listed confound
|
|
215
|
+
# (CONFOUNDS: the message shows that the compiler rejected another feature
|
|
216
|
+
# the program needs), or a diagnostic at an OpenMP directive line that
|
|
217
|
+
# does not carry the atom (_line_confound; e.g. AFAR stops at the
|
|
218
|
+
# uses_allocators line of an allocate program).
|
|
219
|
+
# - COUNTED WITH A CAVEAT (‡): a listed caveat (CAVEATS: the rejection is
|
|
220
|
+
# the atom's own, but the message needs context), or a diagnostic at a
|
|
221
|
+
# non-directive statement that does not carry the atom (the compiler's
|
|
222
|
+
# check of the atom points at a statement that uses a variable).
|
|
223
|
+
# Each entry: (atom patterns, toolchain, message regex, text); it applies
|
|
224
|
+
# only to a rejected cell whose toolchain and reason match.
|
|
225
|
+
_ALLOC = ("the allocate programs also use uses_allocators("
|
|
226
|
+
"omp_default_mem_alloc); AFAR rejects them with 'USES_ALLOCATORS "
|
|
227
|
+
"clause is not implemented yet', so they measure uses_allocators, "
|
|
228
|
+
"not allocate")
|
|
229
|
+
_DSIMD = ("the linear(ref) and linear(uval) programs put the clause on "
|
|
230
|
+
"declare simd; AFAR stops with 'not yet implemented: "
|
|
231
|
+
"omp.declare_simd', so they measure declare simd, not the modifier")
|
|
232
|
+
CONFOUNDS = (
|
|
233
|
+
(("omp.clause.allocate", "omp.clause.allocate.a*"), "afar",
|
|
234
|
+
r"USES_ALLOCATORS", _ALLOC),
|
|
235
|
+
(("omp.clause.linear.ref", "omp.clause.linear.uval"), "afar",
|
|
236
|
+
r"omp\.declare_simd", _DSIMD),
|
|
237
|
+
# 5.0 allows a linear modifier only on declare simd (5.0
|
|
238
|
+
# §2.9.2 p.107), so the 5.0 linear(val) program is a declare simd one
|
|
239
|
+
(("omp.clause.linear.val",), "afar", r"omp\.declare_simd",
|
|
240
|
+
"at 5.0 the linear(val(x)) program puts the clause on declare simd "
|
|
241
|
+
"(5.0 allows no linear modifier on the worksharing-loop and simd "
|
|
242
|
+
"constructs); AFAR stops with 'not yet implemented: omp.declare_simd', "
|
|
243
|
+
"so it measures declare simd, not the modifier"),
|
|
244
|
+
(("omp.clause.allocate",), "cce19",
|
|
245
|
+
r"DYNAMIC_ALLOCATORS",
|
|
246
|
+
"the allocate(t) program "
|
|
247
|
+
"has requires dynamic_allocators (an allocate clause in a target "
|
|
248
|
+
"region without an allocator needs it, 5.2 §6.6 p.179, 6.0 §8.6 "
|
|
249
|
+
"p.313); CCE 19 does not implement that requirement, so it measures "
|
|
250
|
+
"dynamic_allocators, not allocate"),
|
|
251
|
+
(("omp.clause.device_type*", "omp.clause.indirect*"), "cce19",
|
|
252
|
+
r"INDIRECT, LINK, TO or DEVICE_TYPE",
|
|
253
|
+
"CCE 19 rejects the enter spelling on declare target (ftn-1725 "
|
|
254
|
+
"'\"INDIRECT, LINK, TO or DEVICE_TYPE\" was expected'); the 6.0 program "
|
|
255
|
+
"must use enter (to is not allowed on declare target at 6.0), so it "
|
|
256
|
+
"measures enter, not the clause under test"),
|
|
257
|
+
(("omp.directive.target_teams_distribute_simd",), "afar",
|
|
258
|
+
r"DEFAULT clause is not allowed",
|
|
259
|
+
"AFAR rejected the default(none) hygiene clause, which only a "
|
|
260
|
+
"provisional spec override allows on this directive at 5.2"),
|
|
261
|
+
(("omp.clause.default.shared", "omp.clause.default.all"), None,
|
|
262
|
+
r"^ice:",
|
|
263
|
+
"the program needs an explicit task in a target region (default(shared) "
|
|
264
|
+
"differs from the default only on a task); both toolchains fail to "
|
|
265
|
+
"compile any task in a target region, with or without the default "
|
|
266
|
+
"clause (AFAR: segmentation fault in OpenMPIRBuilder::createTask; CCE "
|
|
267
|
+
"19: internal compiler error 'unhandled construct type'), so "
|
|
268
|
+
"it measures the task construct on the device, not "
|
|
269
|
+
"default(shared)"),
|
|
270
|
+
(("omp.clause.uses_allocators.memspace", "omp.clause.uses_allocators.traits"),
|
|
271
|
+
"cce19", r"ftn-1510",
|
|
272
|
+
"CCE 19 rejected the allocator handle my_alloc for lacking a "
|
|
273
|
+
"data-sharing attribute under default(none) (ftn-1510), not the "
|
|
274
|
+
"uses_allocators modifier"),
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
CAVEATS = (
|
|
279
|
+
(("omp.clause.in_reduction.*",), "afar",
|
|
280
|
+
r"no symbol found for|minus reduction operator is deprecated",
|
|
281
|
+
"AFAR's error is on the companion task_reduction line that the spec "
|
|
282
|
+
"requires for in_reduction (same reduction identifier), not on the "
|
|
283
|
+
"in_reduction clause"),
|
|
284
|
+
(("omp.clause.transparent*",), None, r"(?i)omp_impex",
|
|
285
|
+
"the cause is omp_impex missing from the compiler's omp_lib module (a "
|
|
286
|
+
"6.0 runtime constant the clause needs), not a parse of the clause"),
|
|
287
|
+
(("omp.clause.device_type.nohost",), "cce19", r"f\$gen_mod_",
|
|
288
|
+
"link failure: undefined `f$gen_mod_` from the host build (the host "
|
|
289
|
+
"version of the target region references the nohost procedure); "
|
|
290
|
+
"whether that is a CCE gap or a non-conforming program is open"),
|
|
291
|
+
# Omp_vv cross-check, decided by results/task14/probe runs
|
|
292
|
+
(("omp.clause.order", "omp.clause.order.*"), "afar",
|
|
293
|
+
r"Unhandled clause order in omp\.wsloop",
|
|
294
|
+
"construct-specific: AFAR rejects order on the worksharing-loop "
|
|
295
|
+
"construct this program uses (parallel do; also on loop: 'Unhandled "
|
|
296
|
+
"clause order in omp.loop'), but compiles and runs order(concurrent) "
|
|
297
|
+
"on simd and target simd in both memory modes (probe and "
|
|
298
|
+
"omp_vv test_target_simd_order_concurrent)"),
|
|
299
|
+
(("omp.clause.map.iterator",), "cce19", r"OmpIt\$",
|
|
300
|
+
"construct-specific: CCE 19 fails to link map(iterator) on target "
|
|
301
|
+
"enter data, which this program uses (undefined `OmpIt$k`); on the "
|
|
302
|
+
"target construct it links, runs correctly at HSA_XNACK=0 and fails at "
|
|
303
|
+
"HSA_XNACK=1 in the runtime (hipFree hipErrorInvalidValue; "
|
|
304
|
+
"omp_vv test_target_map_iterators)"),
|
|
305
|
+
)
|
|
306
|
+
VARIABLE_STATEMENT = ("the compiler points at a statement that uses a "
|
|
307
|
+
"variable, not at the clause")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def confound(atom: str, toolchain: str | None = None,
|
|
311
|
+
reason: str | None = None, table=None) -> str | None:
|
|
312
|
+
"""The listed text for a rejected cell (atom, toolchain, reason) from
|
|
313
|
+
`table` (CONFOUNDS by default); with only `atom`, any entry for it."""
|
|
314
|
+
for pats, tc, rx, text in (CONFOUNDS if table is None else table):
|
|
315
|
+
if not any(fnmatchcase(atom, p) for p in pats):
|
|
316
|
+
continue
|
|
317
|
+
if toolchain is None or (tc in (None, _tc_family(toolchain))
|
|
318
|
+
and reason is not None
|
|
319
|
+
and re.search(rx, reason)):
|
|
320
|
+
return text
|
|
321
|
+
return None
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
_LINE_RX = (re.compile(r'\.f90"?:(\d+):\d+'), re.compile(r"Line = (\d+)"),
|
|
325
|
+
# Clang diagnostics of the C programs
|
|
326
|
+
re.compile(r'\.c:(\d+):\d+'),
|
|
327
|
+
# NVHPC, "NVFORTRAN-S-0034-... (<file>.f90: 12)" and
|
|
328
|
+
# nvc's '"<file>.c", line 12: error: ...'
|
|
329
|
+
re.compile(r'\.f90: (\d+)\)'), re.compile(r'\.c", line (\d+):'))
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def diagnosed_line(reason: str) -> int | None:
|
|
333
|
+
"""The source line a compiler diagnostic in `reason` points at."""
|
|
334
|
+
for rx in _LINE_RX:
|
|
335
|
+
m = rx.search(reason)
|
|
336
|
+
if m:
|
|
337
|
+
return int(m.group(1))
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _statement(source: str, n: int) -> str | None:
|
|
342
|
+
"""Line n (1-based) of `source` with its & continuation lines (C: its
|
|
343
|
+
backslash continuation lines)."""
|
|
344
|
+
lines = source.splitlines()
|
|
345
|
+
if not 1 <= n <= len(lines):
|
|
346
|
+
return None
|
|
347
|
+
cont = "\\" if re.search(r"^\s*#\s*pragma\s+omp", source, re.M) \
|
|
348
|
+
else "&"
|
|
349
|
+
i = j = n - 1
|
|
350
|
+
while i > 0 and lines[i - 1].rstrip().endswith(cont):
|
|
351
|
+
i -= 1
|
|
352
|
+
while j < len(lines) - 1 and lines[j].rstrip().endswith(cont):
|
|
353
|
+
j += 1
|
|
354
|
+
return " ".join(lines[i:j + 1]).lower()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def carries(atom: str, text: str) -> bool:
|
|
358
|
+
"""Whether a statement carries the atom: its clause name followed by
|
|
359
|
+
`(` or a word end, or its directive name. A semantic atom
|
|
360
|
+
(catalog/<model>/semantic_atoms.yaml, e.g.
|
|
361
|
+
``omp.clause.defaultmap.character``) is exercised through the
|
|
362
|
+
program's data, not through a clause, so every statement of its
|
|
363
|
+
program carries it: a rejection at a directive line is the atom's own
|
|
364
|
+
failure."""
|
|
365
|
+
from offload.engine.catalog import semantic_atoms
|
|
366
|
+
if atom in semantic_atoms():
|
|
367
|
+
return True
|
|
368
|
+
parts = atom.split(".")
|
|
369
|
+
if len(parts) >= 3 and parts[1] == "clause":
|
|
370
|
+
return re.search(rf"(?<![\w]){re.escape(parts[2])}\b", text) is not None
|
|
371
|
+
if len(parts) >= 3 and parts[1] == "directive":
|
|
372
|
+
words = parts[2].split("_")
|
|
373
|
+
return re.search(r"\b" + r"[\s_]*".join(map(re.escape, words))
|
|
374
|
+
+ r"\b", text) is not None
|
|
375
|
+
return True
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _line_confound(atom: str, reason: str, source: str | None
|
|
379
|
+
) -> tuple[str, bool]:
|
|
380
|
+
"""(text, excluded) for a diagnostic at a line that does not carry the
|
|
381
|
+
atom: excluded when that line is an OpenMP directive, counted with a
|
|
382
|
+
caveat otherwise; ("", False) when the line carries the atom or cannot
|
|
383
|
+
be found."""
|
|
384
|
+
n = diagnosed_line(reason)
|
|
385
|
+
if n is None or source is None:
|
|
386
|
+
return "", False
|
|
387
|
+
text = _statement(source, n)
|
|
388
|
+
if text is None or carries(atom, text):
|
|
389
|
+
return "", False
|
|
390
|
+
if text.lstrip().startswith(("!$omp", "!$acc")) or re.match(
|
|
391
|
+
r"\s*#\s*pragma\s+omp\b", text):
|
|
392
|
+
return (f"the diagnosed directive line {n} does not carry the atom",
|
|
393
|
+
True)
|
|
394
|
+
return f"{VARIABLE_STATEMENT} (line {n})", False
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@lru_cache(maxsize=None)
|
|
398
|
+
def _programs(version: int, lang: str = "f90") -> dict:
|
|
399
|
+
from offload.engine.catalog import model_of, slice_atoms
|
|
400
|
+
if lang == "c" and model_of(version) == "acc":
|
|
401
|
+
from offload.engine.acc_c_gen import all_acc_c_programs
|
|
402
|
+
return {p.id: p.source for p in all_acc_c_programs()}
|
|
403
|
+
if lang == "c":
|
|
404
|
+
from offload.engine.c_gen import all_c_programs
|
|
405
|
+
return {p.id: p.source for p in all_c_programs(version)}
|
|
406
|
+
from offload.engine.gen import generate
|
|
407
|
+
return {p.id: p.source for p in generate(slice_atoms(version), version)}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def generated_source(program_id: str, atom: str, version: int) -> str | None:
|
|
411
|
+
"""The current generator's source of a matrix program (None if it is no
|
|
412
|
+
longer generated); a C program (``ompNN-c-...``) from
|
|
413
|
+
offload.engine.c_gen."""
|
|
414
|
+
lang = "c" if re.match(r"(omp|acc)\d\d-c-", program_id) else "f90"
|
|
415
|
+
try:
|
|
416
|
+
return _programs(version, lang).get(program_id)
|
|
417
|
+
except Exception:
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _root_reason(rec: dict) -> str:
|
|
422
|
+
"""A CCE reason quoting a module summary (ftn-855/894) is replaced by
|
|
423
|
+
the root error from the stored stderr."""
|
|
424
|
+
reason = rec.get("reason", "")
|
|
425
|
+
if family(rec.get("toolchain", "")) != "cce19" or not re.search(
|
|
426
|
+
r"ftn-(855|894) ftn:", reason):
|
|
427
|
+
return reason
|
|
428
|
+
from offload.engine.harness import classify_message
|
|
429
|
+
found = classify_message("cce19", rec.get("stderr", ""))
|
|
430
|
+
if not found or re.search(r"ftn-(855|894) ftn:", found[1]):
|
|
431
|
+
return reason
|
|
432
|
+
prefix = reason.split(":", 1)[0]
|
|
433
|
+
return f"{prefix}: {found[1]}"
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
AUDIT_TEXT = {
|
|
437
|
+
"valid": "audited (valid)",
|
|
438
|
+
"unaudited_no_passing_original": "oracle unaudited",
|
|
439
|
+
"hint_only": "no oracle (hint only)",
|
|
440
|
+
"implementation_defined": "no oracle (implementation-defined)",
|
|
441
|
+
"equivalent_default": "no oracle (equivalent default)",
|
|
442
|
+
"legality": "legality (illegal variant)",
|
|
443
|
+
"unverified_oracle_not_written": "unverified (oracle not written)",
|
|
444
|
+
"unverified_needs_other_infrastructure": "unverified (needs other "
|
|
445
|
+
"infrastructure)",
|
|
446
|
+
"not_observable": "not observable",
|
|
447
|
+
"": "not in audit",
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# --- the table -----------------------------------------------------------------------
|
|
452
|
+
|
|
453
|
+
@dataclass(frozen=True)
|
|
454
|
+
class Row:
|
|
455
|
+
atom: str
|
|
456
|
+
toolchain: str
|
|
457
|
+
xnack: int
|
|
458
|
+
outcome: str # harness outcome; illegal cells: rejected | not_rejected
|
|
459
|
+
flaky: bool
|
|
460
|
+
version: int = 0
|
|
461
|
+
program_id: str = ""
|
|
462
|
+
legal: bool = True
|
|
463
|
+
violates: str | None = None
|
|
464
|
+
evidence: str = "construct"
|
|
465
|
+
audit: str = "" # audit disposition of (version, atom), "" if none
|
|
466
|
+
label: str = "" # what the report prints for the cell
|
|
467
|
+
reason: str = ""
|
|
468
|
+
needs: tuple = ()
|
|
469
|
+
thp: str = ""
|
|
470
|
+
confound: str = "" # caveat or exclusion reason of a rejection
|
|
471
|
+
excluded: bool = False # rejection not counted (another feature)
|
|
472
|
+
copy_label: str = "" # headline rows: the copy-mode label, or NOT_RUN
|
|
473
|
+
basis: str = "" # no-oracle programs: '<cite>: "<spec sentence>"'
|
|
474
|
+
lang: str = "f90" # "c" for a C program
|
|
475
|
+
# "device" (the atom's headline program) or "host+device" (a
|
|
476
|
+
# host construct feeding a target region, reported apart) and, for the
|
|
477
|
+
# latter, the realistic use its program header names
|
|
478
|
+
scope: str = "device"
|
|
479
|
+
use: str = ""
|
|
480
|
+
# Headline legal rows: the portable verdict (PORTABLE) and,
|
|
481
|
+
# when the two modes differ, the class of the difference (MODE_CLASSES,
|
|
482
|
+
# catalog/mode_differences.yaml; "unclassified" if no entry)
|
|
483
|
+
portable: str = ""
|
|
484
|
+
mode_class: str = ""
|
|
485
|
+
# The outcome of the program's presence checks run at
|
|
486
|
+
# HSA_XNACK=1 (tools/campaigns/presence_x1.py), "" if it has none
|
|
487
|
+
presence_x1: str = ""
|
|
488
|
+
# Headline legal rows: the class of a difference between the
|
|
489
|
+
# MI300A copy-mode cell and the gfx90a cell (ARCH_CLASSES,
|
|
490
|
+
# catalog/mode_differences.yaml arch_entries; "unclassified" if none)
|
|
491
|
+
arch_class: str = ""
|
|
492
|
+
# Failing legal cells: the reproduction gate's status
|
|
493
|
+
# (GATE_STATES, annotate_gate); "" for every other cell
|
|
494
|
+
gate: str = ""
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _audit_map(path: Path | None) -> dict[tuple[int, str], str]:
|
|
498
|
+
if path is None or not Path(path).is_file():
|
|
499
|
+
return {}
|
|
500
|
+
data = json.loads(Path(path).read_text())
|
|
501
|
+
return {(int(v), atom): e.get("disposition", "")
|
|
502
|
+
for v, atoms in data.get("versions", {}).items()
|
|
503
|
+
for atom, e in atoms.items()}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# The NVIDIA toolchains' oracle audits (tools/campaigns/oracle_audit.py
|
|
507
|
+
# --toolchains nvhpc, one run per memory mode), per model and language:
|
|
508
|
+
# {"modes": {"<row key>": <an audit summary>}}
|
|
509
|
+
NV_AUDIT = {("omp", "f90"): ROOT / "tests" / "data" /
|
|
510
|
+
"oracle_audit_summary_nvhpc.json",
|
|
511
|
+
("omp", "c"): ROOT / "tests" / "data" /
|
|
512
|
+
"oracle_audit_summary_nvhpc_c.json",
|
|
513
|
+
("acc", "f90"): ROOT / "tests" / "data" /
|
|
514
|
+
"acc_audit_summary_nvhpc.json",
|
|
515
|
+
# The OpenACC C slice
|
|
516
|
+
("acc", "c"): ROOT / "tests" / "data" /
|
|
517
|
+
"acc_audit_summary_nvhpc_c.json"}
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
@lru_cache(maxsize=None)
|
|
521
|
+
def _nv_audit_file(path: str) -> dict:
|
|
522
|
+
p = Path(path)
|
|
523
|
+
if not p.is_file():
|
|
524
|
+
return {}
|
|
525
|
+
return {int(x): {(int(v), atom): e.get("disposition", "")
|
|
526
|
+
for v, atoms in doc.get("versions", {}).items()
|
|
527
|
+
for atom, e in atoms.items()}
|
|
528
|
+
for x, doc in json.loads(p.read_text()).get("modes", {}).items()}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def nv_audit_path(toolchain: str, model: str, lang: str) -> Path | None:
|
|
532
|
+
"""The NVIDIA audit summary of a toolchain (NV_AUDIT is nvhpc's;
|
|
533
|
+
another release's is the same file name with its toolchain, e.g.
|
|
534
|
+
oracle_audit_summary_nvhpc269.json); a release without its own audit
|
|
535
|
+
reuses nvhpc's (ruling: the programs are unchanged)."""
|
|
536
|
+
path = NV_AUDIT.get((model, lang))
|
|
537
|
+
if path is None or toolchain == "nvhpc":
|
|
538
|
+
return path
|
|
539
|
+
own = path.with_name(path.name.replace("_nvhpc", f"_{toolchain}"))
|
|
540
|
+
return own if own.is_file() else path
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def _nv_audit(rec: dict) -> dict:
|
|
544
|
+
path = nv_audit_path(rec["toolchain"], rec.get("model", "omp"),
|
|
545
|
+
"c" if rec.get("lang") == "c" else "f90")
|
|
546
|
+
return _nv_audit_file(str(path)).get(int(rec["xnack"]), {}) \
|
|
547
|
+
if path else {}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _is_crash(rec: dict | None) -> bool:
|
|
551
|
+
return (rec is not None and rec["outcome"] == "wrong_answer"
|
|
552
|
+
and rec.get("reason", "").startswith("crash:"))
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _crash_kind(reason: str) -> str:
|
|
556
|
+
return reason[len("crash:"):].split(";")[0].strip()
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def _label(rec: dict, other: dict | None, disposition: str) -> str:
|
|
560
|
+
"""The report text for one legal or illegal cell (see module docstring)."""
|
|
561
|
+
o = rec["outcome"]
|
|
562
|
+
if not rec.get("legal", True):
|
|
563
|
+
return {"rejected": "rejected",
|
|
564
|
+
"harness_error": "harness error"}.get(o, "not rejected")
|
|
565
|
+
if o == "harness_error":
|
|
566
|
+
return "harness error"
|
|
567
|
+
ev = rec.get("evidence", "construct")
|
|
568
|
+
unaudited = disposition.startswith("unaudited")
|
|
569
|
+
mark = " (oracle unaudited)" if unaudited else ""
|
|
570
|
+
flaky = " [flaky]" if rec.get("flaky") else ""
|
|
571
|
+
nv = rec["toolchain"] in NVIDIA_TOOLCHAINS
|
|
572
|
+
if ("zero_copy" in rec.get("needs", ()) and (rec["xnack"] == 0 or nv)
|
|
573
|
+
and o in ("wrong_answer", "fell_back_to_host")):
|
|
574
|
+
how = _crash_kind(rec["reason"]) if _is_crash(rec) else rec["reason"]
|
|
575
|
+
# No NVHPC mode on wingtip is unified memory
|
|
576
|
+
need = (f"{nv_mode_name(rec['toolchain'], rec['xnack'])} memory, no "
|
|
577
|
+
"unified memory on this machine" if nv else "HSA_XNACK=1")
|
|
578
|
+
return ("fails in copy mode: needs zero-copy memory "
|
|
579
|
+
f"({need}); {how.split(';')[0]}{flaky}")
|
|
580
|
+
if _is_crash(rec):
|
|
581
|
+
text = f"crash: {_crash_kind(rec['reason'])}"
|
|
582
|
+
if other is not None and not _is_crash(other):
|
|
583
|
+
text += f", {_mode_word(rec['toolchain'], rec['xnack'])} only"
|
|
584
|
+
return text + mark + flaky
|
|
585
|
+
if o == "rejected":
|
|
586
|
+
return "rejected" + flaky
|
|
587
|
+
if o == "accepted_ignored":
|
|
588
|
+
return "accepted, ignored (warning)" + flaky
|
|
589
|
+
if o == "fell_back_to_host":
|
|
590
|
+
return "fell back to host" + mark + flaky
|
|
591
|
+
if o == "wrong_answer":
|
|
592
|
+
if ev == "probe" and "compile-time" in rec["reason"]:
|
|
593
|
+
return ("compile-time probe failed" + (
|
|
594
|
+
" (confirmed)" if disposition == "valid" else "")
|
|
595
|
+
+ mark + flaky)
|
|
596
|
+
if ev == "probe" and "no_error_termination" in rec["reason"]:
|
|
597
|
+
return ("no error termination (negative probe" + (
|
|
598
|
+
", confirmed" if disposition == "valid" else "")
|
|
599
|
+
+ ")" + mark + flaky)
|
|
600
|
+
if unaudited:
|
|
601
|
+
return "wrong answer (oracle unaudited)" + flaky
|
|
602
|
+
if ev == "unverified":
|
|
603
|
+
return "wrong answer (evidence unverified)" + flaky
|
|
604
|
+
if ev in NO_ORACLE_TEXT:
|
|
605
|
+
return f"wrong answer (no oracle: {NO_ORACLE_NAME[ev]})" + flaky
|
|
606
|
+
if ev == "legality":
|
|
607
|
+
return "wrong answer (legality program's kernel)" + flaky
|
|
608
|
+
if disposition == "valid":
|
|
609
|
+
return "wrong answer (confirmed)" + flaky
|
|
610
|
+
return "wrong answer" + flaky
|
|
611
|
+
if ev in UNCREDITED and o in ("correct_on_device", "unobservable"):
|
|
612
|
+
return {"unverified": UNVERIFIED_TEXT, "legality": LEGALITY_TEXT,
|
|
613
|
+
**NO_ORACLE_TEXT}[ev] + flaky
|
|
614
|
+
if o == "unobservable":
|
|
615
|
+
return "compiled and ran; nothing checked (unobservable)" + flaky
|
|
616
|
+
if o == "correct_on_device":
|
|
617
|
+
if unaudited:
|
|
618
|
+
return "correct on device (oracle unaudited)" + flaky
|
|
619
|
+
if ev == "copy_mode_only" and rec["xnack"] == 1 and not nv:
|
|
620
|
+
return ("correct on device (copy-mode evidence; not separable at "
|
|
621
|
+
"HSA_XNACK=1)" + flaky)
|
|
622
|
+
if ev == "copy_mode_only" and nv and rec["xnack"] != nv_modes(
|
|
623
|
+
rec["toolchain"])[0]: # Managed shares the heap
|
|
624
|
+
return ("correct on device (copy-mode evidence; not separable in "
|
|
625
|
+
f"{nv_mode_name(rec['toolchain'], rec['xnack'])} memory)"
|
|
626
|
+
+ flaky)
|
|
627
|
+
if ev == "probe" and rec["reason"].startswith(
|
|
628
|
+
"compile_error_termination"):
|
|
629
|
+
return ("compile-time error termination as required (probe)"
|
|
630
|
+
+ flaky)
|
|
631
|
+
if ev == "probe" and "compile message: " in rec["reason"]:
|
|
632
|
+
return ("correct on device; compile-time message as required "
|
|
633
|
+
"(probe)" + flaky)
|
|
634
|
+
if ev == "probe":
|
|
635
|
+
return "error termination as required (negative probe)" + flaky
|
|
636
|
+
return "correct on device" + flaky
|
|
637
|
+
return o + flaky
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
COMPANION_SUFFIX = "-binop" # offload.engine.acc_c_gen.BINOP_SUFFIX
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def is_companion(program_id: str) -> bool:
|
|
644
|
+
"""A companion form of an atom's program (the
|
|
645
|
+
OpenACC C port's atomic update as written, beside the atom's program),
|
|
646
|
+
not the atom's program: kept out of the one-row-per-atom tables."""
|
|
647
|
+
return program_id.endswith(COMPANION_SUFFIX)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def companion_section(results: Path) -> list[str]:
|
|
651
|
+
"""Markdown lines listing the companion programs' cells of `results`
|
|
652
|
+
(none: no lines)."""
|
|
653
|
+
results = Path(results)
|
|
654
|
+
if not results.is_file():
|
|
655
|
+
return []
|
|
656
|
+
recs = [json.loads(line) for line in results.read_text().splitlines()
|
|
657
|
+
if line.strip()]
|
|
658
|
+
recs = sorted((r for r in recs if is_companion(r["program_id"])),
|
|
659
|
+
key=lambda r: (r["program_id"], r["toolchain"],
|
|
660
|
+
r["xnack"]))
|
|
661
|
+
if not recs:
|
|
662
|
+
return []
|
|
663
|
+
out = ["## Companion forms (not in the per-atom counts)", "",
|
|
664
|
+
"A companion program is another legal form of an atom's program "
|
|
665
|
+
"(the OpenACC C port's atomic update "
|
|
666
|
+
"x = x + e1 + e2, legal by OpenACC 3.4 §2.12, beside the atom's "
|
|
667
|
+
"program, which writes x += e1 + e2). Its cells are listed here "
|
|
668
|
+
"and go through the confirmer; they are not in the tables above.",
|
|
669
|
+
"", "| program | atom | toolchain | mode | outcome | reason |",
|
|
670
|
+
"|---|---|---|---|---|---|"]
|
|
671
|
+
for r in recs:
|
|
672
|
+
out.append(f"| {r['program_id']} | `{r['primary_atom']}` | "
|
|
673
|
+
f"{TC_NAME.get(r['toolchain'], r['toolchain'])} | "
|
|
674
|
+
f"{r['xnack']} | {r['outcome']} | "
|
|
675
|
+
f"{_md(r.get('reason', '')[:120])} |")
|
|
676
|
+
return out + [""]
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def build_table(results: Path, audit: Path | None = AUDIT,
|
|
680
|
+
source_of=generated_source) -> list[Row]:
|
|
681
|
+
"""One Row per matrix cell (program, toolchain, HSA_XNACK).
|
|
682
|
+
|
|
683
|
+
Raises ValueError on a duplicate cell. ``audit`` is the committed audit
|
|
684
|
+
summary (dispositions per version and atom); ``source_of(program_id,
|
|
685
|
+
atom, version)`` gives a program's source for the diagnosed-line check.
|
|
686
|
+
"""
|
|
687
|
+
recs = [json.loads(line) for line in Path(results).read_text().splitlines()
|
|
688
|
+
if line.strip()]
|
|
689
|
+
# One program per atom; the companion forms (the
|
|
690
|
+
# OpenACC C -binop programs) are listed by companion_section
|
|
691
|
+
recs = [r for r in recs if not is_companion(r["program_id"])]
|
|
692
|
+
# GCC: toolchains kept out of the support tables on purpose
|
|
693
|
+
# (REPORT_EXCLUDED); their rows are dropped here, never an error
|
|
694
|
+
recs = [r for r in recs if r["toolchain"] not in REPORT_EXCLUDED]
|
|
695
|
+
present = {r["toolchain"] for r in recs}
|
|
696
|
+
# Rows of a toolchain without a column fail loudly
|
|
697
|
+
# (they were dropped)
|
|
698
|
+
every_twin = [w for t in ALL_TOOLCHAINS for w in _reg().twins_of(t)]
|
|
699
|
+
unknown = sorted(present - set(ALL_TOOLCHAINS) - set(every_twin) -
|
|
700
|
+
set(NVIDIA_TOOLCHAINS))
|
|
701
|
+
if unknown:
|
|
702
|
+
raise ValueError(f"{results}: rows of toolchains with no support-"
|
|
703
|
+
f"table column: {unknown} (offload.engine.report."
|
|
704
|
+
"ALL_TOOLCHAINS)")
|
|
705
|
+
TOOLCHAINS[:] = [t for t in ALL_TOOLCHAINS if t in present
|
|
706
|
+
or present & set(_reg().twins_of(t))]
|
|
707
|
+
G90[:] = [g90_of(t) for t in ALL_TOOLCHAINS if g90_of(t) in present]
|
|
708
|
+
TWINS[:] = [w for w in every_twin if w in present]
|
|
709
|
+
MI300A[:] = [t for t in ALL_TOOLCHAINS if t in present]
|
|
710
|
+
NV[:] = [t for t in NVIDIA_TOOLCHAINS if t in present]
|
|
711
|
+
seen: set[tuple] = set()
|
|
712
|
+
by_cell: dict[tuple, dict] = {}
|
|
713
|
+
for r in recs:
|
|
714
|
+
key = (r["program_id"], r["toolchain"], r["xnack"], r.get("opt", ""))
|
|
715
|
+
if key in seen:
|
|
716
|
+
raise ValueError(f"duplicate matrix cell {key}")
|
|
717
|
+
seen.add(key)
|
|
718
|
+
by_cell[key] = r
|
|
719
|
+
disp = _audit_map(audit)
|
|
720
|
+
rows = []
|
|
721
|
+
for (pid, tc, x, opt), r in by_cell.items():
|
|
722
|
+
if tc in NVIDIA_TOOLCHAINS: # Its other mode
|
|
723
|
+
other = next((by_cell[k] for k in ((pid, tc, y, opt)
|
|
724
|
+
for y in nv_modes(tc)
|
|
725
|
+
if y != x) if k in by_cell),
|
|
726
|
+
None)
|
|
727
|
+
else:
|
|
728
|
+
other = by_cell.get((pid, tc, 1 - x, opt))
|
|
729
|
+
d = disp.get((r.get("version", 0), r["primary_atom"]), "")
|
|
730
|
+
if tc in NVIDIA_TOOLCHAINS:
|
|
731
|
+
# Dispositions are per toolchain: an NVIDIA cell
|
|
732
|
+
# reads the audit of its own toolchain and mode (none: no
|
|
733
|
+
# disposition, so no failure reads as confirmed)
|
|
734
|
+
d = _nv_audit(r).get((r.get("version", 0), r["primary_atom"]),
|
|
735
|
+
"")
|
|
736
|
+
if pid.endswith("-hostdev") or r.get("scope") == "host+device":
|
|
737
|
+
d = "" # A pattern program is not the atom's oracle
|
|
738
|
+
legal = r.get("legal", True)
|
|
739
|
+
if legal:
|
|
740
|
+
outcome = r["outcome"]
|
|
741
|
+
else:
|
|
742
|
+
outcome = r["outcome"] if r["outcome"] in (
|
|
743
|
+
"rejected", "harness_error") else "not_rejected"
|
|
744
|
+
reason = _root_reason(r)
|
|
745
|
+
label = _label(r, other, d)
|
|
746
|
+
if (x == HEADLINE and legal and other is None
|
|
747
|
+
and r.get("evidence") == "copy_mode_only"
|
|
748
|
+
and r["outcome"] not in _COMPILE_TIME):
|
|
749
|
+
label += f"; {COPY_NOT_RUN}"
|
|
750
|
+
why, excluded = "", False
|
|
751
|
+
if legal and outcome == "rejected":
|
|
752
|
+
atom = r["primary_atom"]
|
|
753
|
+
listed = confound(atom, tc, reason)
|
|
754
|
+
caveat = confound(atom, tc, reason, CAVEATS)
|
|
755
|
+
line, at_directive = ("", False) if listed or caveat else \
|
|
756
|
+
_line_confound(atom, reason,
|
|
757
|
+
source_of(pid, atom, r.get("version", 0)))
|
|
758
|
+
if listed or at_directive:
|
|
759
|
+
why, excluded = listed or line, True
|
|
760
|
+
label = f"rejected (not counted: {why})"
|
|
761
|
+
elif caveat or line:
|
|
762
|
+
why = caveat or line
|
|
763
|
+
label = "rejected ‡"
|
|
764
|
+
scope = r.get("scope") or ("host+device" if pid.endswith(
|
|
765
|
+
"-hostdev") else "device")
|
|
766
|
+
use = ""
|
|
767
|
+
if scope != "device":
|
|
768
|
+
from offload.engine.offload_scope import scope_of
|
|
769
|
+
src = source_of(pid, r["primary_atom"], r.get("version", 0))
|
|
770
|
+
use = scope_of(src)[1] if src else ""
|
|
771
|
+
basis = ""
|
|
772
|
+
if legal and r.get("evidence") in NO_ORACLE_TEXT:
|
|
773
|
+
from offload.engine.gen import no_oracle_of
|
|
774
|
+
src = source_of(pid, r["primary_atom"], r.get("version", 0))
|
|
775
|
+
noo = no_oracle_of(src) if src else None
|
|
776
|
+
basis = noo[2] if noo else ""
|
|
777
|
+
rows.append(Row(
|
|
778
|
+
atom=r["primary_atom"], toolchain=tc, xnack=x, outcome=outcome,
|
|
779
|
+
flaky=bool(r.get("flaky")), version=r.get("version", 0),
|
|
780
|
+
program_id=pid, legal=legal, violates=r.get("violates"),
|
|
781
|
+
evidence=r.get("evidence", "construct"), audit=d,
|
|
782
|
+
label=label, reason=reason, confound=why, excluded=excluded,
|
|
783
|
+
needs=tuple(r.get("needs", ())), thp=r.get("thp", ""),
|
|
784
|
+
basis=basis, lang=r.get("lang", "f90"), scope=scope, use=use))
|
|
785
|
+
labels = {(r.program_id, r.toolchain, r.xnack): r.label for r in rows}
|
|
786
|
+
cells = {(r.program_id, r.toolchain, r.xnack): r for r in rows}
|
|
787
|
+
rows = [replace(r, presence_x1=(PRESENCE_X1.get(r.program_id) or {})
|
|
788
|
+
.get(r.toolchain, "")) if r.xnack == HEADLINE and r.legal
|
|
789
|
+
else r for r in rows]
|
|
790
|
+
def three(r):
|
|
791
|
+
"""The portable verdict over the three cells."""
|
|
792
|
+
copy = cells.get((r.program_id, r.toolchain, COPY))
|
|
793
|
+
if _mode_class(r, copy) == "requirement_refused":
|
|
794
|
+
return "requirement_refused"
|
|
795
|
+
v2 = _portable(r, copy)
|
|
796
|
+
if not G90: # no gfx90a run in this matrix: the two-mode verdict
|
|
797
|
+
return v2
|
|
798
|
+
g = cells.get((r.program_id, g90_of(r.toolchain), COPY))
|
|
799
|
+
return portable3(v2, g.label if g else None)
|
|
800
|
+
rows = [replace(r, copy_label=labels.get(
|
|
801
|
+
(r.program_id, r.toolchain, COPY), NOT_RUN),
|
|
802
|
+
portable=three(r),
|
|
803
|
+
mode_class=_mode_class(r, cells.get((r.program_id, r.toolchain,
|
|
804
|
+
COPY))),
|
|
805
|
+
arch_class=_arch_class(r, cells.get((r.program_id, r.toolchain,
|
|
806
|
+
COPY)),
|
|
807
|
+
cells.get((r.program_id, g90_of(r.toolchain),
|
|
808
|
+
COPY))))
|
|
809
|
+
if r.xnack == HEADLINE and r.toolchain in TOOLCHAINS else r
|
|
810
|
+
for r in rows]
|
|
811
|
+
rows = [_nv_headline(r, cells) if r.toolchain in NV else r
|
|
812
|
+
for r in rows]
|
|
813
|
+
# A managed cell relabelled by its headline row's class
|
|
814
|
+
rows = [cells[(r.program_id, r.toolchain, r.xnack)]
|
|
815
|
+
if r.toolchain in NV and r.xnack != nv_modes(r.toolchain)[0]
|
|
816
|
+
else r for r in rows]
|
|
817
|
+
order = {t: i for i, (t, _) in enumerate(columns())}
|
|
818
|
+
|
|
819
|
+
def mode_rank(r):
|
|
820
|
+
if r.toolchain in NV:
|
|
821
|
+
return nv_modes(r.toolchain).index(r.xnack)
|
|
822
|
+
return -r.xnack
|
|
823
|
+
rows.sort(key=lambda r: (r.version, not r.legal, r.atom, r.program_id,
|
|
824
|
+
order.get(r.toolchain, 99), mode_rank(r)))
|
|
825
|
+
return annotate_gate(rows)
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def _nv_headline(r: "Row", cells: dict) -> "Row":
|
|
829
|
+
"""An NVIDIA headline row (the machine's headline mode,
|
|
830
|
+
separate memory) with its other mode's label (managed), its portable
|
|
831
|
+
verdict over both modes and the class of a difference between them;
|
|
832
|
+
other rows unchanged."""
|
|
833
|
+
modes = nv_modes(r.toolchain)
|
|
834
|
+
if r.xnack != modes[0]:
|
|
835
|
+
return r
|
|
836
|
+
other = cells.get((r.program_id, r.toolchain, modes[1])) \
|
|
837
|
+
if len(modes) > 1 else None
|
|
838
|
+
cls = _nv_mode_class(r, other)
|
|
839
|
+
if cls == "managed_shares_storage" and other is not None:
|
|
840
|
+
other = replace(other, label=NV_SHARED_TEXT)
|
|
841
|
+
cells[(other.program_id, other.toolchain, other.xnack)] = other
|
|
842
|
+
port = _nv_portable(r, other) if len(modes) > 1 else _one_mode(r)
|
|
843
|
+
if cls == "managed_shares_storage" and _ok(r.label):
|
|
844
|
+
port = "managed_unverified"
|
|
845
|
+
return replace(r, copy_label=other.label if other else NOT_RUN,
|
|
846
|
+
portable=port, mode_class=cls)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
# The portable verdict of an NVIDIA toolchain: correct in both of
|
|
850
|
+
# its machine's modes (separate and managed memory on wingtip). The keys
|
|
851
|
+
# are PORTABLE's, the copy-mode role taken by separate memory (x0) and the
|
|
852
|
+
# shared one by managed memory (x1).
|
|
853
|
+
NV_PORTABLE = {
|
|
854
|
+
"portable": "correct in separate and in managed memory",
|
|
855
|
+
"x1_only": "correct in managed memory only",
|
|
856
|
+
"x0_only": "correct in separate memory only",
|
|
857
|
+
"neither": "an observed failure in both modes",
|
|
858
|
+
"x0_fail_x1_unverified": "failed in separate memory; managed not "
|
|
859
|
+
"verifiable",
|
|
860
|
+
"x1_fail_x0_unverified": "failed in managed memory; separate not "
|
|
861
|
+
"verifiable",
|
|
862
|
+
"not_verifiable": "neither mode gives an observable verdict",
|
|
863
|
+
"needs_zero_copy": "the program needs zero-copy memory (unified "
|
|
864
|
+
"memory, which wingtip does not have) by design",
|
|
865
|
+
"managed_unverified": "correct in separate memory; the managed-memory "
|
|
866
|
+
"cell is not verifiable (managed_shares_storage)",
|
|
867
|
+
"managed_not_run": "no managed-memory run",
|
|
868
|
+
# a toolchain that runs one memory mode only (GCC: separate memory)
|
|
869
|
+
"one_mode_correct": "correct in the one memory mode the toolchain runs",
|
|
870
|
+
"one_mode_failure": "an observed failure in the one memory mode the toolchain runs",
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def _one_mode(r: "Row") -> str:
|
|
875
|
+
"""The verdict of a toolchain that runs one memory mode only."""
|
|
876
|
+
if not r.legal:
|
|
877
|
+
return ""
|
|
878
|
+
if "zero_copy" in r.needs and not _ok(r.label):
|
|
879
|
+
return "needs_zero_copy"
|
|
880
|
+
return "one_mode_correct" if _ok(r.label) else \
|
|
881
|
+
"one_mode_failure" if _failure(r.label) else "not_verifiable"
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def _nv_portable(r: "Row", man: "Row | None") -> str:
|
|
885
|
+
if not r.legal:
|
|
886
|
+
return ""
|
|
887
|
+
if "zero_copy" in r.needs and not _ok(r.label):
|
|
888
|
+
return "needs_zero_copy"
|
|
889
|
+
if man is None:
|
|
890
|
+
return "managed_not_run"
|
|
891
|
+
return portable_of(man.label, r.label, False,
|
|
892
|
+
r.evidence == "copy_mode_only")
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
# Classes of a difference between an NVIDIA toolchain's separate
|
|
896
|
+
# and managed memory cells (catalog/mode_differences.yaml nvhpc_entries)
|
|
897
|
+
NV_MODE_CLASSES = {
|
|
898
|
+
"separate_failure": "correct in managed memory, wrong in separate "
|
|
899
|
+
"memory, the program's mapping conforming: a "
|
|
900
|
+
"compiler failure with separate host and device "
|
|
901
|
+
"memory (the copy-mode failure), with a finding",
|
|
902
|
+
"managed_failure": "correct in separate memory, wrong in managed "
|
|
903
|
+
"memory: a failure specific to managed memory, with a "
|
|
904
|
+
"finding",
|
|
905
|
+
"both_wrong": "wrong in both modes, in different ways: not a "
|
|
906
|
+
"portability result",
|
|
907
|
+
"needs_unified_memory": "the program requires unified memory by design "
|
|
908
|
+
"(requires unified_shared_memory, self_maps): "
|
|
909
|
+
"failing without it is not a portability result",
|
|
910
|
+
"requirement_refused": "the runtime or compiler refuses a requires "
|
|
911
|
+
"directive in one mode (supporting a requirement "
|
|
912
|
+
"is implementation defined): conforming",
|
|
913
|
+
"intermittent": "a failure that shows in one mode by chance (a race "
|
|
914
|
+
"or a flaky run), not the mode",
|
|
915
|
+
"not_verifiable": "neither cell gives a verdict the other contradicts "
|
|
916
|
+
"(unobservable, ignored or a harness error on one "
|
|
917
|
+
"side): not a portability result",
|
|
918
|
+
"managed_shares_storage": "a copy_mode_only program whose shared-memory "
|
|
919
|
+
"probe is local data (separate in managed "
|
|
920
|
+
"memory) while its checked arrays are "
|
|
921
|
+
"allocatable (managed memory: the device "
|
|
922
|
+
"shares their storage, which the spec allows): "
|
|
923
|
+
"the copy-semantics check says nothing in "
|
|
924
|
+
"managed memory; rerun with the probe on the "
|
|
925
|
+
"heap it passes (tools/campaigns/nvhpc_managed_probe.py)",
|
|
926
|
+
}
|
|
927
|
+
# the managed-memory cell of a managed_shares_storage difference
|
|
928
|
+
NV_SHARED_TEXT = ("not verifiable in managed memory (the copy-mode check "
|
|
929
|
+
"reads storage managed memory shares)")
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
def nv_mode_entry(program_id: str, toolchain: str) -> dict | None:
|
|
933
|
+
"""The catalog/mode_differences.yaml nvhpc_entries entry of a
|
|
934
|
+
(program, NVIDIA toolchain)."""
|
|
935
|
+
from fnmatch import fnmatchcase
|
|
936
|
+
for e in MODE_DIFFERENCES.get("nvhpc_entries") or []:
|
|
937
|
+
if any(fnmatchcase(program_id, p) for p in e["programs"]) and any(
|
|
938
|
+
fnmatchcase(toolchain, t) for t in e["toolchains"]):
|
|
939
|
+
return e
|
|
940
|
+
return None
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def _nv_mode_class(r: "Row", other: "Row | None") -> str:
|
|
944
|
+
if not r.legal or other is None or _mode_key(r) == _mode_key(other):
|
|
945
|
+
return ""
|
|
946
|
+
e = nv_mode_entry(r.program_id, r.toolchain)
|
|
947
|
+
return e["class"] if e else "unclassified"
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
def nv_mode_class_problem(cls: str, sep_label: str, man_label: str
|
|
951
|
+
) -> str | None:
|
|
952
|
+
"""Why an NVIDIA mode class does not fit the separate and managed
|
|
953
|
+
labels of its cell (None when it fits)."""
|
|
954
|
+
fs, fm = _failure(sep_label), _failure(man_label)
|
|
955
|
+
need = {"separate_failure": (True, False),
|
|
956
|
+
"managed_failure": (False, True),
|
|
957
|
+
"both_wrong": (True, True)}.get(cls)
|
|
958
|
+
if need is not None and (fs, fm) != need:
|
|
959
|
+
return (f"class {cls} needs separate "
|
|
960
|
+
f"{'failing' if need[0] else 'not failing'} and managed "
|
|
961
|
+
f"{'failing' if need[1] else 'not failing'}; the cells are "
|
|
962
|
+
f"{sep_label!r} / {man_label!r}")
|
|
963
|
+
if cls in ("needs_unified_memory", "requirement_refused") and not (
|
|
964
|
+
fs or fm):
|
|
965
|
+
return f"class {cls} needs a failing cell"
|
|
966
|
+
return None
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
# The verdict a user can rely on for portable code is "correct in
|
|
970
|
+
# both memory modes" (HSA_XNACK=0: separate host and device memory, as on a
|
|
971
|
+
# discrete GPU; HSA_XNACK=1: unified memory, where a missing or wrong map
|
|
972
|
+
# often still gives the right answer). Values of Row.portable (headline
|
|
973
|
+
# legal rows; "" elsewhere):
|
|
974
|
+
PORTABLE = {
|
|
975
|
+
"portable": "correct at HSA_XNACK=0 and at HSA_XNACK=1",
|
|
976
|
+
"x1_only": "correct at HSA_XNACK=1 only (unified memory)",
|
|
977
|
+
"x0_only": "correct at HSA_XNACK=0 only (copy mode)",
|
|
978
|
+
"neither": "an observed failure in both modes (rejected, wrong "
|
|
979
|
+
"answer, crash, host fallback, a probe that did not "
|
|
980
|
+
"terminate, or a presence check failing at HSA_XNACK=1)",
|
|
981
|
+
"x0_fail_x1_unverified": "failed at HSA_XNACK=0; HSA_XNACK=1 not "
|
|
982
|
+
"verifiable (nothing observable there, or a "
|
|
983
|
+
"copy_mode_only program whose HSA_XNACK=1 "
|
|
984
|
+
"check skips what the copy mode tests)",
|
|
985
|
+
"x1_fail_x0_unverified": "failed at HSA_XNACK=1; HSA_XNACK=0 not "
|
|
986
|
+
"verifiable",
|
|
987
|
+
"requirement_refused": "the runtime refuses a requires directive in one "
|
|
988
|
+
"mode (supporting a requirement is "
|
|
989
|
+
"implementation defined, 5.2 §8.2): a conforming "
|
|
990
|
+
"refusal, not a failure",
|
|
991
|
+
"not_verifiable": "neither mode gives an observable verdict "
|
|
992
|
+
"(unobservable, accepted and ignored, no oracle, "
|
|
993
|
+
"unverified, harness error, a rejection not counted)",
|
|
994
|
+
"needs_zero_copy": "the program needs zero-copy memory (HSA_XNACK=1) "
|
|
995
|
+
"by design; not a portability result",
|
|
996
|
+
"x0_not_run": "no HSA_XNACK=0 run",
|
|
997
|
+
# The third cell, gfx90a (Tioga MI250X, a discrete GPU, at
|
|
998
|
+
# HSA_XNACK=0): "portable" needs it correct too
|
|
999
|
+
"gfx942_only": "correct in both MI300A modes, an observed failure on "
|
|
1000
|
+
"the discrete gfx90a (MI250X)",
|
|
1001
|
+
"gfx90a_unverified": "correct in both MI300A modes; the gfx90a cell "
|
|
1002
|
+
"gives no verdict (unobservable, ignored, harness "
|
|
1003
|
+
"error, ...)",
|
|
1004
|
+
"gfx90a_not_run": "correct in both MI300A modes; no gfx90a run",
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
def portable3(v2: str, g90_label: str | None) -> str:
|
|
1009
|
+
"""The portable verdict over the three cells of a compiler:
|
|
1010
|
+
`v2`, the two-mode MI300A verdict (portable_of), and the gfx90a cell's
|
|
1011
|
+
label (None: no gfx90a row). Only a cell correct in both MI300A modes
|
|
1012
|
+
can be portable; it stays so when the gfx90a cell is correct too, else
|
|
1013
|
+
it is gfx942_only (an observed failure on gfx90a), gfx90a_unverified or
|
|
1014
|
+
gfx90a_not_run. Every other two-mode verdict already says the cell is
|
|
1015
|
+
not portable and stands; the gfx90a column and the arch-difference
|
|
1016
|
+
class (ARCH_CLASSES) say what gfx90a adds."""
|
|
1017
|
+
if v2 != "portable":
|
|
1018
|
+
return v2
|
|
1019
|
+
if g90_label is None:
|
|
1020
|
+
return "gfx90a_not_run"
|
|
1021
|
+
if _failure(g90_label):
|
|
1022
|
+
return "gfx942_only"
|
|
1023
|
+
return "portable" if _ok(g90_label) else "gfx90a_unverified"
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
MODE_DIFF_FILE = Path(__file__).resolve().parent.parent.parent / "catalog" / \
|
|
1027
|
+
"mode_differences.yaml"
|
|
1028
|
+
# Classes of a difference between the memory modes
|
|
1029
|
+
MODE_CLASSES = {
|
|
1030
|
+
"copy_mode_failure": "correct at HSA_XNACK=1, wrong at 0, and the "
|
|
1031
|
+
"program's mapping is conforming (it would be "
|
|
1032
|
+
"correct with separate host and device memory): a "
|
|
1033
|
+
"compiler copy-mode failure, with a finding",
|
|
1034
|
+
"unified_memory_failure": "correct at HSA_XNACK=0, wrong at 1: a "
|
|
1035
|
+
"failure specific to unified memory",
|
|
1036
|
+
"needs_unified_memory": "the program requires unified memory by design "
|
|
1037
|
+
"(requires unified_shared_memory or self_maps, "
|
|
1038
|
+
"the self map-type modifier): failing or "
|
|
1039
|
+
"refusing to run at HSA_XNACK=0 is not a "
|
|
1040
|
+
"portability result",
|
|
1041
|
+
"requirement_refused": "the runtime refuses a requires directive at "
|
|
1042
|
+
"HSA_XNACK=0 and terminates before the region "
|
|
1043
|
+
"(supporting a requirement is implementation "
|
|
1044
|
+
"defined, 5.2 §8.2): conforming, not a wrong "
|
|
1045
|
+
"answer",
|
|
1046
|
+
"both_wrong": "wrong in both modes, in different ways: not a "
|
|
1047
|
+
"portability result",
|
|
1048
|
+
"no_finding_yet": "a difference between the modes that no finding explains yet: shown "
|
|
1049
|
+
"in the table and not counted as a compiler failure",
|
|
1050
|
+
"x1_oracle_blind": "the program's check runs only in copy mode (its "
|
|
1051
|
+
"HSA_XNACK=1 cell reads correct with copy-mode "
|
|
1052
|
+
"evidence, which checks nothing the mode hides) and "
|
|
1053
|
+
"a copy-independent check (tools/campaigns/presence_x1.py, the "
|
|
1054
|
+
"reproducer) shows the compiler wrong at "
|
|
1055
|
+
"HSA_XNACK=1 too: a failure in both modes, not a "
|
|
1056
|
+
"copy-mode failure",
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
# Classes of a difference between a compiler's MI300A copy-mode
|
|
1061
|
+
# cell (gfx942, HSA_XNACK=0) and its gfx90a cell (MI250X, HSA_XNACK=0):
|
|
1062
|
+
# both have separate host and device memory, so a difference is the GPU,
|
|
1063
|
+
# its runtime path or what the APU's copy mode does not emulate
|
|
1064
|
+
ARCH_CLASSES = {
|
|
1065
|
+
"gfx90a_failure": "correct on MI300A, wrong on gfx90a, the program's "
|
|
1066
|
+
"mapping is conforming: a compiler or runtime failure "
|
|
1067
|
+
"specific to gfx90a (or to the discrete GPU), with a "
|
|
1068
|
+
"finding",
|
|
1069
|
+
"x0_emulation_gap": "correct at HSA_XNACK=0 on MI300A, wrong on the "
|
|
1070
|
+
"discrete gfx90a because something reaches device "
|
|
1071
|
+
"code without a map that the APU's copy mode still "
|
|
1072
|
+
"makes accessible: the compiler fails to map it "
|
|
1073
|
+
"(a mapping failure HSA_XNACK=0 on MI300A hides), "
|
|
1074
|
+
"with a finding",
|
|
1075
|
+
"gfx942_failure": "wrong at HSA_XNACK=0 on MI300A, correct on gfx90a: "
|
|
1076
|
+
"a failure specific to the MI300A (APU) copy mode, "
|
|
1077
|
+
"with a finding",
|
|
1078
|
+
"both_wrong": "wrong on both GPUs, in different ways: not a "
|
|
1079
|
+
"portability result",
|
|
1080
|
+
"needs_unified_memory": "the program requires unified memory by "
|
|
1081
|
+
"design; failing or refusing without it is not "
|
|
1082
|
+
"a portability result",
|
|
1083
|
+
"requirement_refused": "the runtime refuses a requires directive on "
|
|
1084
|
+
"one GPU (supporting a requirement is "
|
|
1085
|
+
"implementation defined, 5.2 §8.2): conforming",
|
|
1086
|
+
"no_finding_yet": "a difference between the GPUs that no finding explains yet: shown "
|
|
1087
|
+
"in the table and not counted as a compiler failure",
|
|
1088
|
+
"intermittent": "a failure the finding shows to be intermittent (a "
|
|
1089
|
+
"race): it showed in one cell and not in the other by "
|
|
1090
|
+
"chance; the same program fails on both GPUs in other "
|
|
1091
|
+
"runs",
|
|
1092
|
+
"not_verifiable": "neither cell gives a verdict the other contradicts "
|
|
1093
|
+
"(e.g. unobservable on one GPU, ignored or a harness "
|
|
1094
|
+
"error on the other): not a portability result",
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
def load_mode_differences(path: Path = MODE_DIFF_FILE) -> dict:
|
|
1099
|
+
"""catalog/mode_differences.yaml (arch_entries),
|
|
1100
|
+
checked."""
|
|
1101
|
+
import yaml
|
|
1102
|
+
doc = yaml.safe_load(Path(path).read_text()) or {}
|
|
1103
|
+
for e in doc.get("entries") or []:
|
|
1104
|
+
if e.get("class") not in MODE_CLASSES:
|
|
1105
|
+
raise ValueError(f"{path}: unknown class in {e}")
|
|
1106
|
+
for e in doc.get("arch_entries") or []:
|
|
1107
|
+
if e.get("class") not in ARCH_CLASSES:
|
|
1108
|
+
raise ValueError(f"{path}: unknown arch class in {e}")
|
|
1109
|
+
for e in doc.get("nvhpc_entries") or []:
|
|
1110
|
+
if e.get("class") not in NV_MODE_CLASSES:
|
|
1111
|
+
raise ValueError(f"{path}: unknown nvhpc class in {e}")
|
|
1112
|
+
return doc
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
MODE_DIFFERENCES = load_mode_differences() if MODE_DIFF_FILE.is_file() \
|
|
1116
|
+
else {"entries": []}
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def mode_entry(program_id: str, toolchain: str) -> dict | None:
|
|
1120
|
+
"""The catalog/mode_differences.yaml entry of a (program, toolchain)."""
|
|
1121
|
+
from fnmatch import fnmatchcase
|
|
1122
|
+
for e in MODE_DIFFERENCES.get("entries") or []:
|
|
1123
|
+
if any(fnmatchcase(program_id, p) for p in e["programs"]) and any(
|
|
1124
|
+
fnmatchcase(toolchain, t) for t in e["toolchains"]):
|
|
1125
|
+
return e
|
|
1126
|
+
return None
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
def arch_entry(program_id: str, toolchain: str) -> dict | None:
|
|
1130
|
+
"""The catalog/mode_differences.yaml arch_entries entry of a (program,
|
|
1131
|
+
gfx942 toolchain)."""
|
|
1132
|
+
from fnmatch import fnmatchcase
|
|
1133
|
+
for e in MODE_DIFFERENCES.get("arch_entries") or []:
|
|
1134
|
+
if any(fnmatchcase(program_id, p) for p in e["programs"]) and any(
|
|
1135
|
+
fnmatchcase(base_name(toolchain), t)
|
|
1136
|
+
for t in e["toolchains"]):
|
|
1137
|
+
return e
|
|
1138
|
+
return None
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
def _arch_class(r: "Row", copy: "Row | None", g90: "Row | None") -> str:
|
|
1142
|
+
"""The class of a difference between the MI300A copy-mode
|
|
1143
|
+
cell and the gfx90a cell of a legal headline row ("" when they agree
|
|
1144
|
+
or one is missing)."""
|
|
1145
|
+
if r.xnack != HEADLINE or not r.legal or copy is None or g90 is None \
|
|
1146
|
+
or _mode_key(copy) == _mode_key(g90):
|
|
1147
|
+
return ""
|
|
1148
|
+
e = arch_entry(r.program_id, r.toolchain)
|
|
1149
|
+
return e["class"] if e else "unclassified"
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
def _mode_class(r: "Row", copy: "Row | None") -> str:
|
|
1153
|
+
if r.xnack != HEADLINE or not r.legal or copy is None or \
|
|
1154
|
+
_mode_key(r) == _mode_key(copy):
|
|
1155
|
+
return ""
|
|
1156
|
+
e = mode_entry(r.program_id, r.toolchain)
|
|
1157
|
+
return e["class"] if e else "unclassified"
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
PRESENCE_X1_FILE = Path(__file__).resolve().parent.parent.parent / "tests" / \
|
|
1161
|
+
"data" / "presence_x1_summary.json"
|
|
1162
|
+
# {program id: {toolchain: outcome}}: the copy-mode programs' presence
|
|
1163
|
+
# checks run at HSA_XNACK=1 (tools/campaigns/presence_x1.py)
|
|
1164
|
+
PRESENCE_X1 = (json.loads(PRESENCE_X1_FILE.read_text())["programs"]
|
|
1165
|
+
if PRESENCE_X1_FILE.is_file() else {})
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
def _ok(label: str) -> bool:
|
|
1169
|
+
"""A cell the dashboard counts as correct (status ok); a
|
|
1170
|
+
compile-time probe that terminated as required is a pass too."""
|
|
1171
|
+
return label.startswith(("correct on device", "error termination",
|
|
1172
|
+
"compile-time error termination as required"))
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
COPY_EVIDENCE_X1 = "correct on device (copy-mode evidence"
|
|
1176
|
+
FAILURE_PREFIXES = ("rejected", "wrong answer", "crash", "fell back",
|
|
1177
|
+
"no error termination", "compile-time probe failed",
|
|
1178
|
+
"fails in copy mode")
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
def _failure(label: str) -> bool:
|
|
1182
|
+
"""An observed failure (a counted rejection, a wrong answer, a crash,
|
|
1183
|
+
a host fallback, a probe that did not terminate)."""
|
|
1184
|
+
return label.startswith(FAILURE_PREFIXES) and \
|
|
1185
|
+
not label.startswith("rejected (not counted")
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
def mode_class_problem(cls: str, x1_label: str, x0_label: str
|
|
1189
|
+
) -> str | None:
|
|
1190
|
+
"""Why a memory-mode class does not fit the
|
|
1191
|
+
headline (HSA_XNACK=1) and copy-mode labels of its cell (None when it
|
|
1192
|
+
fits): the class says which modes fail."""
|
|
1193
|
+
f1, f0 = _failure(x1_label), _failure(x0_label)
|
|
1194
|
+
need = {"copy_mode_failure": (False, True),
|
|
1195
|
+
"x1_oracle_blind": (False, True),
|
|
1196
|
+
"unified_memory_failure": (True, False),
|
|
1197
|
+
"both_wrong": (True, True)}.get(cls)
|
|
1198
|
+
if need is not None and (f1, f0) != need:
|
|
1199
|
+
return (f"class {cls} needs HSA_XNACK=1 "
|
|
1200
|
+
f"{'failing' if need[0] else 'not failing'} and HSA_XNACK=0 "
|
|
1201
|
+
f"{'failing' if need[1] else 'not failing'}; the cells are "
|
|
1202
|
+
f"{x1_label!r} / {x0_label!r}")
|
|
1203
|
+
if cls in ("needs_unified_memory", "requirement_refused") and not f0:
|
|
1204
|
+
return (f"class {cls} needs a failing HSA_XNACK=0 cell; it is "
|
|
1205
|
+
f"{x0_label!r}")
|
|
1206
|
+
return None
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
def portable_of(x1_label: str, x0_label: str,
|
|
1210
|
+
x1_hidden_failure: bool = False,
|
|
1211
|
+
x1_copy_only: bool | None = None) -> str:
|
|
1212
|
+
"""The portable verdict of a headline label and its copy-mode label.
|
|
1213
|
+
A copy_mode_only cell's HSA_XNACK=1 check is skipped under unified
|
|
1214
|
+
memory: it counts as correct only next to a correct copy-mode cell,
|
|
1215
|
+
never as evidence on its own (`x1_copy_only` comes from
|
|
1216
|
+
the program's evidence, not from the label, which an unaudited oracle
|
|
1217
|
+
changes; without it the label decides). `x1_hidden_failure`: the
|
|
1218
|
+
program's presence checks fail when run at HSA_XNACK=1."""
|
|
1219
|
+
if x1_copy_only is None:
|
|
1220
|
+
x1_copy_only = x1_label.startswith(COPY_EVIDENCE_X1)
|
|
1221
|
+
|
|
1222
|
+
def state(label, other_ok, copy_only, hidden):
|
|
1223
|
+
if hidden or _failure(label):
|
|
1224
|
+
return "fail"
|
|
1225
|
+
if _ok(label) and (other_ok or not copy_only):
|
|
1226
|
+
return "ok"
|
|
1227
|
+
return "unv"
|
|
1228
|
+
b = state(x0_label, True, False, False)
|
|
1229
|
+
a = state(x1_label, b == "ok", x1_copy_only, x1_hidden_failure)
|
|
1230
|
+
return {("ok", "ok"): "portable", ("ok", "fail"): "x1_only",
|
|
1231
|
+
("ok", "unv"): "x1_only", ("fail", "ok"): "x0_only",
|
|
1232
|
+
("unv", "ok"): "x0_only", ("fail", "fail"): "neither",
|
|
1233
|
+
("unv", "fail"): "x0_fail_x1_unverified",
|
|
1234
|
+
("fail", "unv"): "x1_fail_x0_unverified"}.get(
|
|
1235
|
+
(a, b), "not_verifiable")
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def _portable(r: "Row", copy: "Row | None") -> str:
|
|
1239
|
+
if r.xnack != HEADLINE or not r.legal:
|
|
1240
|
+
return ""
|
|
1241
|
+
if copy is None:
|
|
1242
|
+
return "x0_not_run"
|
|
1243
|
+
if "zero_copy" in r.needs and not _ok(copy.label):
|
|
1244
|
+
return "needs_zero_copy"
|
|
1245
|
+
hidden = r.presence_x1 == "wrong_answer"
|
|
1246
|
+
return portable_of(r.label, copy.label, hidden,
|
|
1247
|
+
r.evidence == "copy_mode_only")
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
NV_COPY_EVIDENCE_FAMILY = ("correct on device (copy-mode evidence; not "
|
|
1251
|
+
"separable in managed memory)")
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
def family(label: str) -> str:
|
|
1255
|
+
"""Coarse category of a cell label, for the summary counts."""
|
|
1256
|
+
for prefix, fam in (
|
|
1257
|
+
("correct on device (oracle unaudited)",
|
|
1258
|
+
"correct on device (oracle unaudited)"),
|
|
1259
|
+
# An NVIDIA managed-memory cell
|
|
1260
|
+
# is no HSA_XNACK=1 cell
|
|
1261
|
+
("correct on device (copy-mode evidence; not separable in ",
|
|
1262
|
+
NV_COPY_EVIDENCE_FAMILY),
|
|
1263
|
+
("correct on device (copy-mode", "correct on device (copy-mode evidence, HSA_XNACK=1)"),
|
|
1264
|
+
("correct on device", "correct on device"),
|
|
1265
|
+
(NO_ORACLE_TEXT["hint_only"],
|
|
1266
|
+
"accepted; conforms vacuously (no oracle)"),
|
|
1267
|
+
(NO_ORACLE_TEXT["implementation_defined"],
|
|
1268
|
+
"implementation-defined (no oracle)"),
|
|
1269
|
+
(NO_ORACLE_TEXT["equivalent_default"],
|
|
1270
|
+
"equivalent to the default (no oracle)"),
|
|
1271
|
+
(LEGALITY_TEXT, "accepted; oracle is the illegal variant"),
|
|
1272
|
+
(UNVERIFIED_TEXT, "accepted; unverified"),
|
|
1273
|
+
("compiled and ran", "unobservable"),
|
|
1274
|
+
("rejected (not counted",
|
|
1275
|
+
"rejected, not counted (another feature)"),
|
|
1276
|
+
("rejected", "rejected"),
|
|
1277
|
+
("accepted, ignored", "accepted, ignored"),
|
|
1278
|
+
("fell back", "fell back to host"),
|
|
1279
|
+
("wrong answer (confirmed)", "wrong answer (confirmed)"),
|
|
1280
|
+
("wrong answer (oracle unaudited)", "wrong answer (oracle unaudited)"),
|
|
1281
|
+
("wrong answer", "wrong answer (other)"),
|
|
1282
|
+
("crash", "crash"),
|
|
1283
|
+
("fails in copy mode", "needs zero-copy; failed in copy mode"),
|
|
1284
|
+
("harness error", "harness error"),
|
|
1285
|
+
(NV_SHARED_TEXT, "not verifiable (managed memory shares the "
|
|
1286
|
+
"checked storage)"),
|
|
1287
|
+
("not rejected", "not rejected")):
|
|
1288
|
+
if label.startswith(prefix):
|
|
1289
|
+
return fam
|
|
1290
|
+
return label
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
# --- claims --------------------------------------------------------------------------
|
|
1294
|
+
|
|
1295
|
+
@dataclass(frozen=True)
|
|
1296
|
+
class Claim:
|
|
1297
|
+
source: str
|
|
1298
|
+
url: str
|
|
1299
|
+
toolchain: str
|
|
1300
|
+
claim: str
|
|
1301
|
+
version_note: str
|
|
1302
|
+
quote: str = ""
|
|
1303
|
+
atoms: tuple = ()
|
|
1304
|
+
pattern: str | None = None
|
|
1305
|
+
form: str | None = None # compile_error | accepted | None
|
|
1306
|
+
general: bool = False # release-level statement: specific claims win
|
|
1307
|
+
versions: tuple | None = None
|
|
1308
|
+
status: str = "provisional"
|
|
1309
|
+
language: str = "Fortran"
|
|
1310
|
+
note: str = ""
|
|
1311
|
+
# None for the old per-model claim files (a Fortran row is
|
|
1312
|
+
# compared with a claim of any language); a generic claim covers the
|
|
1313
|
+
# languages it names unless its document is cross-language
|
|
1314
|
+
cross_language: bool | None = None
|
|
1315
|
+
spec_version: str = ""
|
|
1316
|
+
page: str = ""
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
@dataclass(frozen=True)
|
|
1320
|
+
class Mismatch:
|
|
1321
|
+
atom: str
|
|
1322
|
+
toolchain: str
|
|
1323
|
+
claimed: str
|
|
1324
|
+
measured: str
|
|
1325
|
+
version: int = 0
|
|
1326
|
+
xnack: tuple = ()
|
|
1327
|
+
source: str = ""
|
|
1328
|
+
url: str = ""
|
|
1329
|
+
quote: str = ""
|
|
1330
|
+
oracle_unaudited: bool = False
|
|
1331
|
+
general: bool = False
|
|
1332
|
+
reason: str = "" # harness reason of the first contradicting cell
|
|
1333
|
+
confound: str = "" # listed caveat of a contradicting cell
|
|
1334
|
+
language: str = "" # language the vendor's statement is about
|
|
1335
|
+
# The contradicting cells (program_id, toolchain, xnack,
|
|
1336
|
+
# outcome), every memory mode and GPU, and the rows' language
|
|
1337
|
+
cells: tuple = ()
|
|
1338
|
+
lang: str = "f90"
|
|
1339
|
+
spec_version: str = ""
|
|
1340
|
+
page: str = ""
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
def _from_generic(d: dict) -> Claim:
|
|
1344
|
+
"""A report Claim of one generic record (offload.engine.confirm.claims)."""
|
|
1345
|
+
atom = d["atom"]
|
|
1346
|
+
pat = any(ch in atom for ch in "*?[")
|
|
1347
|
+
page = d.get("page", "")
|
|
1348
|
+
m = re.search(r"\[([^\]]*)\]\s*$", page)
|
|
1349
|
+
return Claim(source=d["source"], url=d["url"], toolchain=d["toolchain"],
|
|
1350
|
+
claim=d["claim"], version_note=m.group(1) if m else page,
|
|
1351
|
+
quote=d["quote"], atoms=() if pat else (atom,),
|
|
1352
|
+
pattern=atom if pat else None, form=d.get("form") or None,
|
|
1353
|
+
general=bool(d.get("general")),
|
|
1354
|
+
versions=tuple(int(v) for v in str(d.get("versions") or "")
|
|
1355
|
+
.split(",") if v) or None,
|
|
1356
|
+
language=d["language"], note=d.get("note", ""),
|
|
1357
|
+
cross_language=bool(d.get("cross_language")),
|
|
1358
|
+
spec_version=str(d.get("spec_version") or ""), page=page)
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
def load_claims(path: Path) -> list[Claim]:
|
|
1362
|
+
"""The claims of a file, parsed once per version of it."""
|
|
1363
|
+
return list(_claims(str(path), Path(path).stat().st_mtime))
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
@lru_cache(maxsize=None)
|
|
1367
|
+
def _claims(path: str, mtime: float) -> tuple:
|
|
1368
|
+
return tuple(_parse_claims(Path(path)))
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def _parse_claims(path: Path) -> list[Claim]:
|
|
1372
|
+
data = yaml.safe_load(Path(path).read_text()) or {}
|
|
1373
|
+
entries = data.get("claims") or []
|
|
1374
|
+
if entries and "vendor" in entries[0]:
|
|
1375
|
+
return [_from_generic(e) for e in entries]
|
|
1376
|
+
out = []
|
|
1377
|
+
for i, e in enumerate(entries):
|
|
1378
|
+
atoms = list(e.get("atoms") or [])
|
|
1379
|
+
if e.get("atom"):
|
|
1380
|
+
atoms.append(e["atom"])
|
|
1381
|
+
c = Claim(source=e["source"], url=e["url"], toolchain=e["toolchain"],
|
|
1382
|
+
claim=e["claim"], version_note=e.get("version_note", ""),
|
|
1383
|
+
quote=e.get("quote", ""), atoms=tuple(atoms),
|
|
1384
|
+
pattern=e.get("pattern"), form=e.get("form"),
|
|
1385
|
+
general=bool(e.get("general", False)),
|
|
1386
|
+
versions=tuple(e["versions"]) if e.get("versions") else None,
|
|
1387
|
+
status=e.get("status", "provisional"),
|
|
1388
|
+
language=e.get("language", "Fortran"),
|
|
1389
|
+
note=e.get("note", ""))
|
|
1390
|
+
if c.claim not in CLAIM_VALUES:
|
|
1391
|
+
raise ValueError(f"claim {i}: claim must be one of {CLAIM_VALUES}")
|
|
1392
|
+
if c.form not in CLAIM_FORMS:
|
|
1393
|
+
raise ValueError(f"claim {i}: form must be one of {CLAIM_FORMS}")
|
|
1394
|
+
if not (c.atoms or c.pattern):
|
|
1395
|
+
raise ValueError(f"claim {i}: needs atom, atoms or pattern")
|
|
1396
|
+
out.append(c)
|
|
1397
|
+
return out
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
def _matches(c: Claim, row: Row) -> bool:
|
|
1401
|
+
# A claim about a compiler covers its gfx90a twin too
|
|
1402
|
+
if c.toolchain != base_name(row.toolchain):
|
|
1403
|
+
return False
|
|
1404
|
+
if c.versions is not None and row.version not in c.versions:
|
|
1405
|
+
return False
|
|
1406
|
+
if c.versions is None and row.version >= 60 and c.spec_version and \
|
|
1407
|
+
c.spec_version != "6.0":
|
|
1408
|
+
return False # A 5.x feature claim, not the 6.0 form
|
|
1409
|
+
if row.lang != "c" and c.cross_language is False and \
|
|
1410
|
+
"Fortran" not in c.language:
|
|
1411
|
+
return False
|
|
1412
|
+
atom = row.atom
|
|
1413
|
+
if row.lang == "c":
|
|
1414
|
+
# A C row is compared with the claims about C (the
|
|
1415
|
+
# language field names C), under its Fortran atom name where the
|
|
1416
|
+
# claim lists the do spelling (parallel_for -> parallel_do)
|
|
1417
|
+
if "C" not in c.language.replace("C/C++", "C").split() and \
|
|
1418
|
+
"C/C++" not in c.language:
|
|
1419
|
+
return False
|
|
1420
|
+
from offload.engine.c_gen import fortran_atom
|
|
1421
|
+
if atom not in c.atoms and not (c.pattern and fnmatchcase(
|
|
1422
|
+
atom, c.pattern)):
|
|
1423
|
+
atom = fortran_atom(atom)
|
|
1424
|
+
return atom in c.atoms or (
|
|
1425
|
+
c.pattern is not None and fnmatchcase(atom, c.pattern))
|
|
1426
|
+
|
|
1427
|
+
|
|
1428
|
+
def _state(row: Row) -> str | None:
|
|
1429
|
+
"""What a legal cell says about support, for claim comparison."""
|
|
1430
|
+
if not row.legal or row.outcome == "harness_error":
|
|
1431
|
+
return None
|
|
1432
|
+
if row.label.startswith("fails in copy mode"):
|
|
1433
|
+
return None # expected for zero-copy programs; not a feature result
|
|
1434
|
+
if row.label == NV_SHARED_TEXT:
|
|
1435
|
+
return None # Managed memory shares the checked storage
|
|
1436
|
+
if row.excluded:
|
|
1437
|
+
return None # the compiler rejected another feature
|
|
1438
|
+
return {"rejected": "rejected", "accepted_ignored": "ignored",
|
|
1439
|
+
"fell_back_to_host": "fail", "wrong_answer": "fail",
|
|
1440
|
+
"unobservable": "accepted",
|
|
1441
|
+
"correct_on_device": "accepted" if row.evidence in UNCREDITED
|
|
1442
|
+
else "pass"}.get(row.outcome)
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
def _disagrees(c: Claim, row: Row) -> bool:
|
|
1446
|
+
s = _state(row)
|
|
1447
|
+
if s is None:
|
|
1448
|
+
return False
|
|
1449
|
+
if c.claim == "supported":
|
|
1450
|
+
# A failing cell contradicts a support claim only when
|
|
1451
|
+
# it counts as a compiler failure (a confirmed gate verdict)
|
|
1452
|
+
# (an ungated row keeps its outcome until the gate has seen it,
|
|
1453
|
+
# The rule: claims about rows recorded before the gate)
|
|
1454
|
+
if _failure(row.label) and not (confirmed_failure(row) or
|
|
1455
|
+
row.gate == "ungated"):
|
|
1456
|
+
return False
|
|
1457
|
+
return s in ("rejected", "ignored", "fail")
|
|
1458
|
+
if c.claim == "unsupported":
|
|
1459
|
+
return s != "rejected" if c.form == "compile_error" else s == "pass"
|
|
1460
|
+
return c.form == "accepted" and s == "rejected" # partial
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
def claim_groups(table: list[Row], claims: Path | list[Claim]
|
|
1464
|
+
) -> dict[tuple, list[Row]]:
|
|
1465
|
+
"""(claim index, atom, version, toolchain) -> the legal rows it covers.
|
|
1466
|
+
|
|
1467
|
+
A general (release-level) claim applies to an atom only when no specific
|
|
1468
|
+
claim for the same toolchain covers that atom.
|
|
1469
|
+
"""
|
|
1470
|
+
cl = load_claims(claims) if isinstance(claims, (str, Path)) else claims
|
|
1471
|
+
groups: dict[tuple, list[Row]] = defaultdict(list)
|
|
1472
|
+
by_tc: dict[str, list[tuple[int, Claim]]] = defaultdict(list) # a claim covers one toolchain (and its twin)
|
|
1473
|
+
for i, c in enumerate(cl):
|
|
1474
|
+
by_tc[c.toolchain].append((i, c))
|
|
1475
|
+
table_only = _reg().table_only()
|
|
1476
|
+
for row in table:
|
|
1477
|
+
if not row.legal or row.scope != "device":
|
|
1478
|
+
continue # Host+device patterns are not headline
|
|
1479
|
+
if row.toolchain in table_only:
|
|
1480
|
+
continue # a support-table cell only (MI300X, MI350X): no evidence about a claim
|
|
1481
|
+
base = base_name(row.toolchain)
|
|
1482
|
+
mine = [(i, c) for i, c in by_tc.get(base, ()) if _matches(c, row)]
|
|
1483
|
+
hits = [i for i, c in mine if not c.general] or [i for i, c in mine if c.general]
|
|
1484
|
+
for i in hits:
|
|
1485
|
+
groups[(i, row.atom, row.version, base)].append(row)
|
|
1486
|
+
return groups
|
|
1487
|
+
|
|
1488
|
+
|
|
1489
|
+
def _measured(bad: list[Row], rows: list[Row]) -> str:
|
|
1490
|
+
twins = [r for r in bad if r.toolchain != base_name(r.toolchain)]
|
|
1491
|
+
if twins: # Name the GPU of a twin's cell
|
|
1492
|
+
main = [r for r in bad if r not in twins]
|
|
1493
|
+
mrows = [r for r in rows if r.toolchain == base_name(r.toolchain)]
|
|
1494
|
+
parts = [_measured(main, mrows)] if main else []
|
|
1495
|
+
parts += [f"{_reg().twin_tag(r.toolchain)}: {r.label}" for r in twins]
|
|
1496
|
+
return "; ".join(parts)
|
|
1497
|
+
labels = {r.label for r in bad}
|
|
1498
|
+
if len(labels) == 1 and (len(bad) == len(rows)
|
|
1499
|
+
or "HSA_XNACK=" in bad[0].label):
|
|
1500
|
+
return bad[0].label
|
|
1501
|
+
return "; ".join(r.label if "HSA_XNACK=" in r.label
|
|
1502
|
+
else f"{_mode_word(r.toolchain, r.xnack)}: {r.label}"
|
|
1503
|
+
for r in sorted(bad, key=lambda r: -r.xnack))
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
def _oracle_dependent(row: Row) -> bool:
|
|
1507
|
+
"""A run-time outcome of an atom whose oracle is unaudited. Compile-time
|
|
1508
|
+
results (rejected, ignored with a warning) do not depend on the oracle."""
|
|
1509
|
+
return (row.audit.startswith("unaudited")
|
|
1510
|
+
and row.outcome not in ("rejected", "accepted_ignored"))
|
|
1511
|
+
|
|
1512
|
+
|
|
1513
|
+
def compare_claims(table: list[Row], claims: Path | list[Claim]
|
|
1514
|
+
) -> list[Mismatch]:
|
|
1515
|
+
"""Claims the measurements contradict, one Mismatch per claim, atom,
|
|
1516
|
+
version and toolchain (both memory modes folded into ``measured``)."""
|
|
1517
|
+
cl = load_claims(claims) if isinstance(claims, (str, Path)) else claims
|
|
1518
|
+
out = []
|
|
1519
|
+
for (i, atom, version, tc), rows in claim_groups(table, cl).items():
|
|
1520
|
+
c = cl[i]
|
|
1521
|
+
bad = [r for r in rows if _disagrees(c, r)]
|
|
1522
|
+
if not bad:
|
|
1523
|
+
continue
|
|
1524
|
+
claimed = c.claim + (f" ({c.form.replace('_', ' ')})" if c.form
|
|
1525
|
+
else "")
|
|
1526
|
+
out.append(Mismatch(
|
|
1527
|
+
atom=atom, toolchain=tc, claimed=f"{claimed} [{c.source}]",
|
|
1528
|
+
measured=_measured(bad, rows), version=version,
|
|
1529
|
+
xnack=tuple(sorted({r.xnack for r in bad}, reverse=True)),
|
|
1530
|
+
source=c.source, url=c.url, quote=c.quote,
|
|
1531
|
+
oracle_unaudited=any(_oracle_dependent(r) for r in bad),
|
|
1532
|
+
general=c.general, reason=bad[0].reason, language=c.language,
|
|
1533
|
+
confound=next((r.confound for r in bad if r.confound), ""),
|
|
1534
|
+
cells=tuple((r.program_id, r.toolchain, r.xnack, r.outcome)
|
|
1535
|
+
for r in bad),
|
|
1536
|
+
lang=bad[0].lang, spec_version=c.spec_version, page=c.page))
|
|
1537
|
+
out.sort(key=lambda m: (m.toolchain, m.oracle_unaudited, m.source,
|
|
1538
|
+
m.atom, m.version))
|
|
1539
|
+
return out
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
# --- citations -----------------------------------------------------------------------
|
|
1543
|
+
|
|
1544
|
+
@lru_cache(maxsize=None)
|
|
1545
|
+
def _yaml(path: Path):
|
|
1546
|
+
return yaml.safe_load(Path(path).read_text())
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
def _vstr(version: int) -> str:
|
|
1550
|
+
return f"{version // 10}.{version % 10}"
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
def _spec(version: int) -> str:
|
|
1554
|
+
"""'OpenMP 5.2' / 'OpenACC 3.4'."""
|
|
1555
|
+
from offload.engine.catalog import spec_name
|
|
1556
|
+
return spec_name(version)
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
@lru_cache(maxsize=None)
|
|
1560
|
+
def _acc_section(number: str) -> str | None:
|
|
1561
|
+
"""'OpenACC 3.4 §<number>, p.<first page>'."""
|
|
1562
|
+
try:
|
|
1563
|
+
from offload.engine import spec_text
|
|
1564
|
+
secs = spec_text.sections("acc3.4")
|
|
1565
|
+
except Exception:
|
|
1566
|
+
return None
|
|
1567
|
+
for sec in secs.values():
|
|
1568
|
+
if sec.number == number:
|
|
1569
|
+
return f"OpenACC 3.4 §{number}, p.{sec.first_page}"
|
|
1570
|
+
return None
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
@lru_cache(maxsize=None)
|
|
1574
|
+
def _spec_section(version: int, name: str) -> str | None:
|
|
1575
|
+
"""'OpenMP 5.2 §13.8, p.284' for a construct/directive name, or None."""
|
|
1576
|
+
try:
|
|
1577
|
+
from offload.engine import spec_text
|
|
1578
|
+
secs = spec_text.sections(_vstr(version))
|
|
1579
|
+
except Exception: # spec PDF or pdftotext missing: fall back to catalog
|
|
1580
|
+
return None
|
|
1581
|
+
forms = dict.fromkeys([name, name.replace(" ", "_")])
|
|
1582
|
+
for f in forms:
|
|
1583
|
+
for suffix in ("", " Construct", " Directive", " Clause"):
|
|
1584
|
+
s = secs.get(f + suffix)
|
|
1585
|
+
if s is not None:
|
|
1586
|
+
return f"OpenMP {_vstr(version)} §{s.number}, p.{s.first_page}"
|
|
1587
|
+
return None
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
_COMPOUND = {50: "Combined Constructs",
|
|
1591
|
+
52: "Combined and Composite Directive Names",
|
|
1592
|
+
60: "Compound Directive Names"}
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
# 5.0 clauses without a section of their own, by the construct
|
|
1596
|
+
# section whose syntax lists them (the 5.0 section titles)
|
|
1597
|
+
_HOME_50 = {
|
|
1598
|
+
"aligned": "simd", "safelen": "simd", "simdlen": "simd",
|
|
1599
|
+
"nontemporal": "simd", "collapse": "Worksharing-Loop",
|
|
1600
|
+
"schedule": "Worksharing-Loop", "ordered": "Worksharing-Loop",
|
|
1601
|
+
"num_threads": "parallel", "num_teams": "teams",
|
|
1602
|
+
"thread_limit": "teams", "dist_schedule": "distribute", "bind": "loop",
|
|
1603
|
+
"nowait": "target", "device": "target", "is_device_ptr": "target",
|
|
1604
|
+
"uses_allocators": "target", "device_type": "declare target",
|
|
1605
|
+
"link": "declare target", "use_device_ptr": "target data",
|
|
1606
|
+
"use_device_addr": "target data", "unified_address": "requires",
|
|
1607
|
+
"unified_shared_memory": "requires", "reverse_offload": "requires",
|
|
1608
|
+
"dynamic_allocators": "requires",
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
def atom_citation(atom: str, version: int) -> tuple[str, str]:
|
|
1613
|
+
"""(citation text, link) for a legal program's primary atom."""
|
|
1614
|
+
parts = atom.split(".")
|
|
1615
|
+
clauses_path, _, _, LINK = _paths(model_of_atom(atom))
|
|
1616
|
+
from offload.engine.catalog import semantic_atoms
|
|
1617
|
+
sem = semantic_atoms().get(atom)
|
|
1618
|
+
if sem is not None: # The spec text that defines the behaviour
|
|
1619
|
+
cites = sem.citations
|
|
1620
|
+
best = [v for v in sorted(cites) if v <= version] or sorted(cites)[:1]
|
|
1621
|
+
return cites[best[-1]], LINK["semantic"]
|
|
1622
|
+
if parts[0] == "acc" and len(parts) >= 3 and parts[1] == "directive":
|
|
1623
|
+
cite = _acc_section(ACC_DIRECTIVE_SECTION.get(parts[2], ""))
|
|
1624
|
+
return cite or "TableGen skeleton entry", LINK["skeleton"]
|
|
1625
|
+
if len(parts) >= 3 and parts[1] == "clause":
|
|
1626
|
+
e = (_yaml(clauses_path).get("clauses") or {}).get(parts[2])
|
|
1627
|
+
if e:
|
|
1628
|
+
forms = sorted(e["forms"], key=lambda f: f["since"])
|
|
1629
|
+
best = [f for f in forms if f["since"] <= version]
|
|
1630
|
+
if not best and parts[0] == "omp":
|
|
1631
|
+
# A 5.0 clause whose syntax is the 5.2 one has no
|
|
1632
|
+
# 5.0 form: cite its 5.0 section, or the section of the
|
|
1633
|
+
# construct whose syntax lists it
|
|
1634
|
+
own = _spec_section(version, parts[2]) or (
|
|
1635
|
+
_spec_section(version, _HOME_50[parts[2]])
|
|
1636
|
+
if version == 50 and parts[2] in _HOME_50 else None)
|
|
1637
|
+
if own:
|
|
1638
|
+
return own, LINK["clauses"]
|
|
1639
|
+
best = best or forms[:1]
|
|
1640
|
+
return best[-1]["citation"], LINK["clauses"]
|
|
1641
|
+
return "catalog clause entry", LINK["clauses"]
|
|
1642
|
+
if len(parts) >= 3 and parts[1] == "directive":
|
|
1643
|
+
name = parts[2].replace("_", " ")
|
|
1644
|
+
cite = _spec_section(version, name)
|
|
1645
|
+
if cite is None:
|
|
1646
|
+
leaves = [c for w in name.split()
|
|
1647
|
+
if (c := _spec_section(version, w))]
|
|
1648
|
+
comp = _spec_section(version, _COMPOUND.get(version, ""))
|
|
1649
|
+
if comp and leaves:
|
|
1650
|
+
cite = f"{comp} (compound); leaves " + "; ".join(
|
|
1651
|
+
x.split(" ", 2)[2] for x in leaves)
|
|
1652
|
+
return cite or "TableGen skeleton entry", LINK["skeleton"]
|
|
1653
|
+
return "catalog entry", LINK["skeleton"]
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
def rule_citation(rule_id: str | None) -> tuple[str, str]:
|
|
1657
|
+
"""(citation text, link) for an illegal variant's rule (OMP52-...)."""
|
|
1658
|
+
if not rule_id:
|
|
1659
|
+
return "no rule", LINK["rules"]
|
|
1660
|
+
ver, _, rid = rule_id.partition("-")
|
|
1661
|
+
model = "acc" if ver.startswith("ACC") else "omp"
|
|
1662
|
+
_, rules_path, _, LINK = _paths(model)
|
|
1663
|
+
try:
|
|
1664
|
+
version = int(ver.removeprefix("OMP").removeprefix("ACC"))
|
|
1665
|
+
except ValueError:
|
|
1666
|
+
return rule_id, LINK["rules"]
|
|
1667
|
+
for r in _yaml(rules_path).get("rules", []):
|
|
1668
|
+
if r["id"] == rid:
|
|
1669
|
+
spec = r.get("spec", {}).get(version) or {}
|
|
1670
|
+
return spec.get("citation", rid), LINK["rules"]
|
|
1671
|
+
return rule_id, LINK["rules"]
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
# --- rendering -----------------------------------------------------------------------
|
|
1675
|
+
|
|
1676
|
+
def _md(text: str) -> str:
|
|
1677
|
+
return str(text).replace("|", "\\|").replace("\n", " ")
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
def _clip(text: str, n: int = 170) -> str:
|
|
1681
|
+
text = " ".join(str(text).split())
|
|
1682
|
+
return text if len(text) <= n else text[:n - 1] + "…"
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
# --- The reproduction gate's status of a failing cell ---------------------
|
|
1686
|
+
|
|
1687
|
+
GATE_STATES = ("confirmed", "decided", "unchecked", "nondeterministic",
|
|
1688
|
+
"decided_not_counted", "ours", "environment", "not_reproduced",
|
|
1689
|
+
"illegal", "not_a_failure", "ungated")
|
|
1690
|
+
GATE_TEXT = {
|
|
1691
|
+
"confirmed": "confirmed (own, or inherited from a compile-time "
|
|
1692
|
+
"signature): counted",
|
|
1693
|
+
"decided": "decided, counts (a decision with counts_as: "
|
|
1694
|
+
"failure): counted",
|
|
1695
|
+
"unchecked": "unchecked (not rerun): not counted here",
|
|
1696
|
+
"nondeterministic": "nondeterministic: not counted",
|
|
1697
|
+
"decided_not_counted": "decided, does not count (a decision)",
|
|
1698
|
+
"ours": "ours (a recorded ruling: our build's failure, not the "
|
|
1699
|
+
"compiler's): not counted",
|
|
1700
|
+
"environment": "environment: not counted",
|
|
1701
|
+
"not_reproduced": "not reproduced: not counted",
|
|
1702
|
+
"illegal": "illegal: not counted",
|
|
1703
|
+
"not_a_failure": "not a failure: not counted",
|
|
1704
|
+
"ungated": "ungated (no gate verdict yet): not counted here",
|
|
1705
|
+
}
|
|
1706
|
+
COUNTED_STATES = ("confirmed", "decided")
|
|
1707
|
+
|
|
1708
|
+
|
|
1709
|
+
def gate_status(rec: dict | None, decision: dict | None) -> str:
|
|
1710
|
+
"""The gate column of a failing cell from its verdict record
|
|
1711
|
+
(offload.engine.gate.load_verdicts) and its decision
|
|
1712
|
+
(offload.engine.gate.load_decisions): ``decided`` (counts_as: failure) or
|
|
1713
|
+
``decided_not_counted`` when a decision names the cell; ``confirmed``
|
|
1714
|
+
for its own confirmed verdict or one inherited from a compile-time
|
|
1715
|
+
defect signature (an inherited verdict of any other kind reads
|
|
1716
|
+
``unchecked``); else the verdict, or ``ungated``."""
|
|
1717
|
+
if decision:
|
|
1718
|
+
return "decided" if decision.get("counts_as") == "failure" else \
|
|
1719
|
+
"decided_not_counted"
|
|
1720
|
+
if rec is None:
|
|
1721
|
+
return "ungated"
|
|
1722
|
+
v = rec.get("verdict") or "ungated"
|
|
1723
|
+
if v == "confirmed" and rec.get("inherited"):
|
|
1724
|
+
from offload.engine.gate import COMPILE_CLASSES
|
|
1725
|
+
parts = str(rec.get("signature", "")).split("|")
|
|
1726
|
+
return "confirmed" if len(parts) > 1 and \
|
|
1727
|
+
parts[1] in COMPILE_CLASSES else "unchecked"
|
|
1728
|
+
return v
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
def runtime_ruling(r: Row):
|
|
1732
|
+
"""The recorded ruling (catalog/rulings.yaml, `decides` naming outcome
|
|
1733
|
+
classes) that decides the cell, or None: e.g.
|
|
1734
|
+
RUL-HOST-LIBATOMIC (our_bug) or RUL-MAPPED-FLAG-NOT-COPIED-BACK (a
|
|
1735
|
+
fell-back-to-host cell that ran on the device: a finding's wrong
|
|
1736
|
+
answer)."""
|
|
1737
|
+
import re as _re
|
|
1738
|
+
from fnmatch import fnmatchcase
|
|
1739
|
+
from offload.engine.confirm import registry as REG
|
|
1740
|
+
from offload.engine.confirm.facts import family as _family
|
|
1741
|
+
for e in REG.entries().values():
|
|
1742
|
+
d = e.decides or {}
|
|
1743
|
+
outs = d.get("outcome")
|
|
1744
|
+
if not d.get("verdict") or not isinstance(outs, list):
|
|
1745
|
+
continue
|
|
1746
|
+
if r.outcome not in outs:
|
|
1747
|
+
continue
|
|
1748
|
+
if not any(fnmatchcase(r.program_id, p)
|
|
1749
|
+
for p in e.applies.get("programs", ())):
|
|
1750
|
+
continue
|
|
1751
|
+
if d.get("families") and _family(r.toolchain) not in d["families"]:
|
|
1752
|
+
continue
|
|
1753
|
+
if d.get("toolchains") and r.toolchain not in d["toolchains"]:
|
|
1754
|
+
continue
|
|
1755
|
+
if d.get("reason") and not _re.search(d["reason"], r.reason or ""):
|
|
1756
|
+
continue
|
|
1757
|
+
return e
|
|
1758
|
+
return None
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
def _ours(r: Row) -> bool:
|
|
1762
|
+
"""A recorded ruling says the cell's failure is our build's (verdict
|
|
1763
|
+
our_bug; the withdrawn complex-reduction link)."""
|
|
1764
|
+
e = runtime_ruling(r)
|
|
1765
|
+
return e is not None and e.decides.get("verdict") == "our_bug"
|
|
1766
|
+
|
|
1767
|
+
|
|
1768
|
+
# the GCC lane gates its own rows into verdict files of its own (tools/campaigns/gcc_lane.py,
|
|
1769
|
+
# one per host it ran on), keyed by cell only
|
|
1770
|
+
LANE_VERDICTS = ROOT / "results" / "gcc"
|
|
1771
|
+
|
|
1772
|
+
|
|
1773
|
+
def _lane_index(paths) -> dict:
|
|
1774
|
+
"""(kind, ident, toolchain, row key) -> the lane's verdict record of a catalog cell."""
|
|
1775
|
+
out = {}
|
|
1776
|
+
for p in paths:
|
|
1777
|
+
for ln in Path(p).read_text().splitlines() if Path(p).is_file() else ():
|
|
1778
|
+
if ln.strip():
|
|
1779
|
+
d = json.loads(ln)
|
|
1780
|
+
kind, ident, tc, x = d["cell"].split("|")
|
|
1781
|
+
out[(kind, ident, tc, int(x))] = d
|
|
1782
|
+
return out
|
|
1783
|
+
|
|
1784
|
+
|
|
1785
|
+
def annotate_gate(table: list[Row], verdicts: Path | None = None,
|
|
1786
|
+
decisions: Path | None = None, lanes=None) -> list[Row]:
|
|
1787
|
+
"""The rows with the gate column set on every failing legal cell
|
|
1788
|
+
(_failure of its label); the catalog and the widened catalog are one
|
|
1789
|
+
verdict kind (offload.engine.gate._case_index). `lanes`: the GCC lane's
|
|
1790
|
+
verdict files (default: those under LANE_VERDICTS when `verdicts` is the
|
|
1791
|
+
gate's store); the gate's store wins a cell both hold."""
|
|
1792
|
+
from offload.engine import gate as G
|
|
1793
|
+
if lanes is None:
|
|
1794
|
+
lanes = sorted(LANE_VERDICTS.glob("gate*/verdicts.jsonl")) if verdicts is None else []
|
|
1795
|
+
idx = {**_lane_index(lanes), **G._case_index(verdicts)}
|
|
1796
|
+
dec = G.load_decisions(decisions)
|
|
1797
|
+
out = []
|
|
1798
|
+
for r in table:
|
|
1799
|
+
if not r.legal or not _failure(r.label):
|
|
1800
|
+
out.append(replace(r, gate="") if r.gate else r)
|
|
1801
|
+
continue
|
|
1802
|
+
if _ours(r):
|
|
1803
|
+
out.append(replace(r, gate="ours"))
|
|
1804
|
+
continue
|
|
1805
|
+
rec = idx.get(("catalog", r.program_id, r.toolchain, int(r.xnack)))
|
|
1806
|
+
d = dec.get(rec["cell"]) if rec else None
|
|
1807
|
+
out.append(replace(r, gate=gate_status(rec, d)))
|
|
1808
|
+
return out
|
|
1809
|
+
|
|
1810
|
+
|
|
1811
|
+
def confirmed_failure(r: Row) -> bool:
|
|
1812
|
+
"""Whether a support-table cell counts as a compiler failure (the ruling on): a failing legal cell whose gate
|
|
1813
|
+
status is ``confirmed`` (its own confirmed rerun, or a confirmed
|
|
1814
|
+
verdict inherited from a compile-time signature) or ``decided`` (a
|
|
1815
|
+
Decision with counts_as: failure).
|
|
1816
|
+
|
|
1817
|
+
It differs from offload.engine.gate.counts / counts_as_failure on purpose:
|
|
1818
|
+
those answer "does the row keep counting in a campaign's counts" and
|
|
1819
|
+
keep an ``unchecked`` row (not rerun) and a row the gate has not seen
|
|
1820
|
+
at their previous outcome; the support tables answer "is this failure
|
|
1821
|
+
confirmed", so an unchecked, ungated, nondeterministic or otherwise
|
|
1822
|
+
unconfirmed cell is shown apart and not counted."""
|
|
1823
|
+
return r.legal and _failure(r.label) and r.gate in COUNTED_STATES
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def gate_section(table: list[Row]) -> list[str]:
|
|
1827
|
+
"""The Markdown section of the gate column: failing legal cells by
|
|
1828
|
+
gate status per column, and the failing cells that do not count."""
|
|
1829
|
+
bad = [r for r in table if r.legal and _failure(r.label)]
|
|
1830
|
+
order = columns()
|
|
1831
|
+
cols = sorted({(r.toolchain, r.xnack) for r in table},
|
|
1832
|
+
key=lambda c: (order.index(c) if c in order else
|
|
1833
|
+
len(order), c[0], -c[1]))
|
|
1834
|
+
head = " | ".join(f"{TC_NAME.get(t, t)} X={x}" if t not in NV else
|
|
1835
|
+
f"{TC_NAME.get(t, t)} {nv_mode_name(t, x)}"
|
|
1836
|
+
for t, x in cols)
|
|
1837
|
+
out = ["## Reproduction gate", "",
|
|
1838
|
+
"Each failing legal cell (a counted rejection, a wrong answer, a "
|
|
1839
|
+
"crash, a host fallback, a failed probe) carries the "
|
|
1840
|
+
"reproduction gate's status (offload.engine.gate, "
|
|
1841
|
+
"results/gate/verdicts.jsonl, and the decisions of "
|
|
1842
|
+
"catalog/gate_queue.yaml): **confirmed** (its own rerun verdict, "
|
|
1843
|
+
"or one inherited from a compile-time defect signature), "
|
|
1844
|
+
"**decided** (a decision; it counts when the decision "
|
|
1845
|
+
"says counts_as: failure), **unchecked** (the gate did not rerun "
|
|
1846
|
+
"it), **nondeterministic**, **ours** (a recorded ruling says the "
|
|
1847
|
+
"failure is our build's), the other verdicts, or **ungated** (no "
|
|
1848
|
+
"verdict yet). A cell counts as a compiler failure only when "
|
|
1849
|
+
"confirmed or decided as counting (report.confirmed_failure; the "
|
|
1850
|
+
"campaign counts of offload.engine.gate.counts keep unchecked rows at "
|
|
1851
|
+
"their previous outcome, a different question); every other "
|
|
1852
|
+
"failing cell is listed apart below, and only counted cells (and "
|
|
1853
|
+
"ungated ones) contradict a support claim. The CSV's "
|
|
1854
|
+
"last column, `gate`, has the status of every failing cell.", "",
|
|
1855
|
+
f"| gate | {head} |", "|---|" + "---|" * len(cols)]
|
|
1856
|
+
states = [g for g in GATE_STATES if any(r.gate == g for r in bad)]
|
|
1857
|
+
for g in states:
|
|
1858
|
+
out.append(f"| {GATE_TEXT[g]} | " + " | ".join(
|
|
1859
|
+
str(sum(1 for r in bad if r.gate == g and
|
|
1860
|
+
(r.toolchain, r.xnack) == c)) for c in cols) + " |")
|
|
1861
|
+
out.append("| failing cells | " + " | ".join(
|
|
1862
|
+
str(sum(1 for r in bad if (r.toolchain, r.xnack) == c))
|
|
1863
|
+
for c in cols) + " |")
|
|
1864
|
+
ruled = [(r, e) for r in bad if (e := runtime_ruling(r)) is not None
|
|
1865
|
+
and e.decides.get("verdict") != "our_bug"]
|
|
1866
|
+
if ruled:
|
|
1867
|
+
out += ["", "### Cells a recorded ruling reads otherwise", "",
|
|
1868
|
+
"Counted as above; the label is the harness's outcome, the "
|
|
1869
|
+
"ruling (catalog/rulings.yaml) says what the cell is (review "
|
|
1870
|
+
"M10: a ran_on_host=1 cell whose region ran on the device "
|
|
1871
|
+
"and whose map(tofrom) flag was not copied back is a wrong "
|
|
1872
|
+
"answer, not a host fallback).", "",
|
|
1873
|
+
"| version | program | toolchain | X | report | ruling |",
|
|
1874
|
+
"|---|---|---|---|---|---|"]
|
|
1875
|
+
out += [f"| {_vstr(r.version)} | {r.program_id} | "
|
|
1876
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | {r.xnack} | "
|
|
1877
|
+
f"{_md(_clip(r.label, 60))} | {e.id} |" for r, e in ruled]
|
|
1878
|
+
apart = [r for r in bad if not confirmed_failure(r)]
|
|
1879
|
+
out += ["", "### Not counted: failing cells without a confirmed verdict "
|
|
1880
|
+
"or a counting decision",
|
|
1881
|
+
""]
|
|
1882
|
+
if apart:
|
|
1883
|
+
out += ["| version | program | toolchain | X | report | gate |",
|
|
1884
|
+
"|---|---|---|---|---|---|"]
|
|
1885
|
+
out += [f"| {_vstr(r.version)} | {r.program_id} | "
|
|
1886
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | {r.xnack} | "
|
|
1887
|
+
f"{_md(_clip(r.label, 90))} | {r.gate} |" for r in apart]
|
|
1888
|
+
else:
|
|
1889
|
+
out.append("None.")
|
|
1890
|
+
out.append("")
|
|
1891
|
+
return out
|
|
1892
|
+
|
|
1893
|
+
|
|
1894
|
+
CSV_FIELDS = ("version", "program_id", "atom", "legal", "violates",
|
|
1895
|
+
"toolchain", "xnack", "outcome", "label", "flaky", "evidence",
|
|
1896
|
+
"audit", "needs", "thp", "citation", "reason", "mode",
|
|
1897
|
+
"copy_label", "host", "suite_narrow", "basis",
|
|
1898
|
+
# The program's language (f90 | c); last, so the
|
|
1899
|
+
# earlier columns keep their positions
|
|
1900
|
+
"lang",
|
|
1901
|
+
# Device | host+device (a pattern program, not the
|
|
1902
|
+
# atom's headline)
|
|
1903
|
+
"scope",
|
|
1904
|
+
# Headline legal rows: the portable verdict
|
|
1905
|
+
# (PORTABLE) and the class of a mode difference
|
|
1906
|
+
"portable", "mode_class",
|
|
1907
|
+
# Presence checks at HSA_XNACK=1
|
|
1908
|
+
"presence_x1",
|
|
1909
|
+
# The row's GPU arch (gfx942 | gfx90a) and, on
|
|
1910
|
+
# headline rows, the class of a gfx90a difference
|
|
1911
|
+
"arch", "arch_class",
|
|
1912
|
+
# Failing legal cells: the reproduction gate's
|
|
1913
|
+
# status (counted as a compiler failure only when confirmed)
|
|
1914
|
+
"gate")
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
def _suite_text(suite: dict | None, atom: str, tc: str) -> str:
|
|
1918
|
+
"""'n pass / n fail' of the narrow omp_vv tests of an atom, '' when
|
|
1919
|
+
none."""
|
|
1920
|
+
v = (suite or {}).get((atom, tc))
|
|
1921
|
+
return f"{v[0]} pass / {v[1]} fail" if v and sum(v) else ""
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
def _paths_out(text: str) -> str:
|
|
1925
|
+
"""A compiler message with its install path named by the install ($NVHPC_26_9), not by where it is."""
|
|
1926
|
+
from offload.engine.site.matrix import B
|
|
1927
|
+
return B.scrub(text)
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
def render_csv(table: list[Row], suite: dict | None = None,
|
|
1931
|
+
meta: dict | None = None) -> str:
|
|
1932
|
+
"""One CSV row per cell. `mode`: headline (HSA_XNACK=1) or copy;
|
|
1933
|
+
`copy_label` (headline rows): the same program's copy-mode label or
|
|
1934
|
+
'not run'; `host`: the node per the run metadata; `suite_narrow`: the
|
|
1935
|
+
omp_vv second source for the atom and toolchain."""
|
|
1936
|
+
host = program_hosts(meta or {})
|
|
1937
|
+
g90_host = g90_hosts(meta or {})
|
|
1938
|
+
buf = io.StringIO()
|
|
1939
|
+
w = csv.writer(buf, lineterminator="\n")
|
|
1940
|
+
w.writerow(CSV_FIELDS)
|
|
1941
|
+
for r in table:
|
|
1942
|
+
cite = (atom_citation(r.atom, r.version)[0] if r.legal
|
|
1943
|
+
else rule_citation(r.violates)[0])
|
|
1944
|
+
arch = arch_of(r.toolchain)
|
|
1945
|
+
twin = r.toolchain in TWINS
|
|
1946
|
+
mode = _reg().twin_tag(r.toolchain) if twin else \
|
|
1947
|
+
("headline" if r.xnack == HEADLINE else "copy") \
|
|
1948
|
+
if arch == "gfx942" else arch
|
|
1949
|
+
nvh = r.toolchain in NV and r.xnack == nv_modes(r.toolchain)[0]
|
|
1950
|
+
if r.toolchain in NV: # E.g. "sm_80 separate"
|
|
1951
|
+
mode = f"{arch} {nv_mode_name(r.toolchain, r.xnack)}"
|
|
1952
|
+
w.writerow([r.version, r.program_id, r.atom, int(r.legal),
|
|
1953
|
+
r.violates or "", r.toolchain, r.xnack, r.outcome,
|
|
1954
|
+
r.label, int(r.flaky), r.evidence, r.audit,
|
|
1955
|
+
" ".join(r.needs), r.thp, cite, _clip(_paths_out(r.reason), 300),
|
|
1956
|
+
mode, r.copy_label if r.xnack == HEADLINE and
|
|
1957
|
+
r.toolchain not in NV or nvh else "",
|
|
1958
|
+
(host(r.program_id, r.xnack) if arch == "gfx942" and not twin
|
|
1959
|
+
else g90_host(r.program_id) if r.toolchain in G90
|
|
1960
|
+
else "") or "",
|
|
1961
|
+
_suite_text(suite, r.atom, r.toolchain) if r.legal
|
|
1962
|
+
else "", r.basis, r.lang, r.scope, r.portable,
|
|
1963
|
+
r.mode_class, r.presence_x1, arch, r.arch_class,
|
|
1964
|
+
r.gate])
|
|
1965
|
+
return buf.getvalue()
|
|
1966
|
+
|
|
1967
|
+
|
|
1968
|
+
def program_hosts(meta: dict):
|
|
1969
|
+
"""host(program_id, xnack) -> the node its row ran on per the run
|
|
1970
|
+
metadata (the mode's run, then its reruns in order), None if not
|
|
1971
|
+
recorded."""
|
|
1972
|
+
table: dict[tuple, str] = {}
|
|
1973
|
+
base: dict[int, str] = {}
|
|
1974
|
+
for x in MODES:
|
|
1975
|
+
m = meta.get(x) or {}
|
|
1976
|
+
if m.get("host"):
|
|
1977
|
+
base[x] = m["host"]
|
|
1978
|
+
for rr in m.get("reruns", []):
|
|
1979
|
+
for p in rr.get("programs", []):
|
|
1980
|
+
table[(p, x)] = rr.get("host")
|
|
1981
|
+
return lambda pid, x: table.get((pid, x), base.get(x))
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
def g90_hosts(meta: dict):
|
|
1985
|
+
"""host(program_id) -> the Tioga node of a program's gfx90a row (Task
|
|
1986
|
+
30: the replace runs recorded in meta["g90"]), None if not
|
|
1987
|
+
recorded."""
|
|
1988
|
+
table: dict[str, str] = {}
|
|
1989
|
+
for rr in (meta.get("g90") or {}).get("reruns", []):
|
|
1990
|
+
for p in rr.get("programs", []):
|
|
1991
|
+
table[p] = rr.get("host")
|
|
1992
|
+
return table.get
|
|
1993
|
+
|
|
1994
|
+
|
|
1995
|
+
def _cells(rows: list[Row]) -> dict[tuple, Row]:
|
|
1996
|
+
return {(r.toolchain, r.xnack): r for r in rows}
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
def _mode_key(row: Row) -> tuple:
|
|
2000
|
+
"""What must differ between the two modes for a dagger: the outcome and
|
|
2001
|
+
whether it was a crash, not the label (copy_mode_only labels differ by
|
|
2002
|
+
design)."""
|
|
2003
|
+
return (row.outcome, row.reason.startswith("crash:"))
|
|
2004
|
+
|
|
2005
|
+
|
|
2006
|
+
def _mode_cells(rows: list[Row], host=None) -> list[str]:
|
|
2007
|
+
"""Cell texts in column order (per toolchain: headline, copy mode). A
|
|
2008
|
+
dagger marks a toolchain whose two modes differ and whose two rows ran
|
|
2009
|
+
on different or unrecorded nodes (`host(program_id, xnack)`); a
|
|
2010
|
+
copy_mode_only headline cell with a copy-mode row carries COPY_MARK."""
|
|
2011
|
+
host = host or (lambda pid, x: None)
|
|
2012
|
+
cells = _cells(rows)
|
|
2013
|
+
out = []
|
|
2014
|
+
for tc in TOOLCHAINS:
|
|
2015
|
+
both = [cells.get((tc, x)) for x in MODES] if tc in MI300A else []
|
|
2016
|
+
differ = bool(both) and all(both) and _mode_key(both[0]) != _mode_key(both[1])
|
|
2017
|
+
if differ:
|
|
2018
|
+
h = [host(r.program_id, r.xnack) for r in both]
|
|
2019
|
+
differ = None in h or h[0] != h[1]
|
|
2020
|
+
for x, r in zip(MODES, both):
|
|
2021
|
+
if r is None:
|
|
2022
|
+
out.append(NOT_RUN if x == COPY else "n/a")
|
|
2023
|
+
continue
|
|
2024
|
+
mark = (f" {COPY_MARK}" if x == HEADLINE and r.legal
|
|
2025
|
+
and r.evidence == "copy_mode_only" and both[1]
|
|
2026
|
+
and r.outcome not in _COMPILE_TIME else "")
|
|
2027
|
+
out.append(_md(r.label) + mark + (" †" if differ else ""))
|
|
2028
|
+
for w in twins_of(tc): # The gfx90a, MI300X and MI350X cells
|
|
2029
|
+
g = cells.get((w, COPY))
|
|
2030
|
+
out.append(_md(g.label) if g else NOT_RUN)
|
|
2031
|
+
for tc in NV: # One cell per mode of its machine
|
|
2032
|
+
for x in nv_modes(tc):
|
|
2033
|
+
r = cells.get((tc, x))
|
|
2034
|
+
out.append(_md(r.label) if r else NOT_RUN)
|
|
2035
|
+
return out
|
|
2036
|
+
|
|
2037
|
+
|
|
2038
|
+
def _head_modes() -> str:
|
|
2039
|
+
cols = [f"{TC_NAME[tc]} {nv_mode_name(tc, x)} (A100)" if tc in NV
|
|
2040
|
+
else f"{TC_NAME[tc]} (X=1)" if x == HEADLINE
|
|
2041
|
+
else f"{TC_NAME[tc]} (X=0)" if tc in TWINS
|
|
2042
|
+
else f"{TC_NAME[tc]} copy mode (X=0)"
|
|
2043
|
+
for tc, x in columns()]
|
|
2044
|
+
return " | ".join(cols)
|
|
2045
|
+
|
|
2046
|
+
|
|
2047
|
+
COPY_MARK_TEXT = (f"{COPY_MARK} = copy_mode_only: the check separates the "
|
|
2048
|
+
"construct from a no-op only in copy mode, so the cell's "
|
|
2049
|
+
"real check is its copy-mode column.")
|
|
2050
|
+
DAGGER_TEXT = ("† = the two modes differ for that toolchain and their rows "
|
|
2051
|
+
"ran on different (or unrecorded) nodes, so the node may "
|
|
2052
|
+
"explain the difference as well as the memory mode.")
|
|
2053
|
+
|
|
2054
|
+
|
|
2055
|
+
def _versions_text(table: list[Row]) -> str:
|
|
2056
|
+
"""'5.2 and 6.0' (or '5.0, 5.2 and 6.0') for the versions in a table."""
|
|
2057
|
+
vs = [_vstr(v) for v in sorted({r.version for r in table})] or [
|
|
2058
|
+
"5.2", "6.0"]
|
|
2059
|
+
return vs[0] if len(vs) == 1 else ", ".join(vs[:-1]) + " and " + vs[-1]
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
def _provisional_section(table: list[Row]) -> list[str]:
|
|
2063
|
+
model = table_model(table)
|
|
2064
|
+
CLAUSES, RULES, OVERRIDES, LINK = _paths(model)
|
|
2065
|
+
clauses = _yaml(CLAUSES).get("clauses", {})
|
|
2066
|
+
rules = _yaml(RULES).get("rules", [])
|
|
2067
|
+
ov = _yaml(OVERRIDES) or {}
|
|
2068
|
+
ov_lists = {k: v for k, v in ov.items() if isinstance(v, list)}
|
|
2069
|
+
n_ov = sum(len(v) for v in ov_lists.values())
|
|
2070
|
+
st = Counter(e.get("status") for e in clauses.values())
|
|
2071
|
+
rs = Counter(r.get("status") for r in rules)
|
|
2072
|
+
os_ = Counter(e.get("status") for v in ov_lists.values() for e in v)
|
|
2073
|
+
ov_clauses = sorted({e["clause"] for e in ov.get("clause_rules", [])})
|
|
2074
|
+
ov_dirs = sorted({e["directive"] for k in ("directive_versions",
|
|
2075
|
+
"directive_spellings")
|
|
2076
|
+
for e in ov.get(k, [])})
|
|
2077
|
+
atoms = sorted({r.atom for r in table if r.legal})
|
|
2078
|
+
hit_c = [a for a in atoms if a.startswith(f"{model}.clause.")
|
|
2079
|
+
and a.split(".")[2] in ov_clauses]
|
|
2080
|
+
hit_d = [a for a in atoms if a.startswith(f"{model}.directive.")
|
|
2081
|
+
and a.split(".")[2].replace("_", " ") in ov_dirs]
|
|
2082
|
+
illegal = sorted({r.violates for r in table if not r.legal and r.violates})
|
|
2083
|
+
vers = _versions_text(table)
|
|
2084
|
+
return [
|
|
2085
|
+
"## Results that depend on provisional rules",
|
|
2086
|
+
"",
|
|
2087
|
+
"Nothing in the catalog has been reviewed by the maintainer yet, so every "
|
|
2088
|
+
"result depends on provisional data:",
|
|
2089
|
+
"",
|
|
2090
|
+
f"- **Every legal cell** depends on its atom's clause-grammar entry "
|
|
2091
|
+
f"([clauses.yaml]({LINK['clauses']}): "
|
|
2092
|
+
f"{dict(st)} of {len(clauses)} clauses) and on the generator's "
|
|
2093
|
+
"reading of it.",
|
|
2094
|
+
f"- **Every illegal-variant cell** depends on the one rule it violates "
|
|
2095
|
+
f"([rules.yaml]({LINK['rules']}): {dict(rs)} of {len(rules)} rules; "
|
|
2096
|
+
f"{len(illegal)} rule versions exercised here).",
|
|
2097
|
+
f"- **Spec overrides** of the TableGen skeleton "
|
|
2098
|
+
f"(catalog/{model}/spec_overrides.yaml: {dict(os_)} of {n_ov}) "
|
|
2099
|
+
"decide whether a clause is allowed on a directive"
|
|
2100
|
+
+ (f" at {vers.replace(' and ', ' or ')}" if model == "omp"
|
|
2101
|
+
else "") + ". Atoms whose "
|
|
2102
|
+
f"clause has an override ({len(hit_c)}): "
|
|
2103
|
+
+ ", ".join(f"`{a.removeprefix(model + '.')}`" for a in hit_c) + ". "
|
|
2104
|
+
f"Directive atoms whose version or spelling is an override "
|
|
2105
|
+
f"({len(hit_d)}): "
|
|
2106
|
+
+ (", ".join(f"`{a.removeprefix(model + '.')}`" for a in hit_d)
|
|
2107
|
+
or "none")
|
|
2108
|
+
+ ".",
|
|
2109
|
+
"- **Every vendor claim** in "
|
|
2110
|
+
f"[claims.yaml]({LINK['claims']}) is provisional: the mapping from a "
|
|
2111
|
+
"vendor's prose to an atom is this project's reading.",
|
|
2112
|
+
"",
|
|
2113
|
+
]
|
|
2114
|
+
|
|
2115
|
+
|
|
2116
|
+
def _baseline_section(baseline: list[dict], meta: dict | None = None,
|
|
2117
|
+
c_lang: bool = False) -> list[str]:
|
|
2118
|
+
if not baseline:
|
|
2119
|
+
return ["## Corpus baseline", "", "No baseline results given.", ""]
|
|
2120
|
+
if c_lang: # The omp_vv C tests
|
|
2121
|
+
cnt = Counter((b["toolchain"], b["xnack"], b["status"],
|
|
2122
|
+
b.get("stage", "")) for b in baseline)
|
|
2123
|
+
files = len({(b.get("repo"), b.get("path")) for b in baseline})
|
|
2124
|
+
lines = [
|
|
2125
|
+
"## Corpus baseline (omp_vv C target tests)", "",
|
|
2126
|
+
f"{len(baseline)} results ({files} files x toolchain x memory "
|
|
2127
|
+
f"mode run). {_baseline_modes(baseline, meta or {})} Each test "
|
|
2128
|
+
"is compiled with the compiler's default OpenMP version (5.1) "
|
|
2129
|
+
"when its spec directory is 4.5, 5.0 or 5.1, with "
|
|
2130
|
+
"-fopenmp-version= its directory for 5.2 and 6.0, and with the "
|
|
2131
|
+
"suite's -I ompvv -lm, run once under "
|
|
2132
|
+
"OMP_TARGET_OFFLOAD=mandatory. Pass criterion: exit code 0; the "
|
|
2133
|
+
"tests are **unaudited** (their self-checks were not "
|
|
2134
|
+
"mutation-tested).", "",
|
|
2135
|
+
"| toolchain | HSA_XNACK | pass | fail (compile) | fail (run) | "
|
|
2136
|
+
"harness error |", "|---|---|---|---|---|---|"]
|
|
2137
|
+
for tc, x in columns():
|
|
2138
|
+
if not any(b["toolchain"] == tc and b["xnack"] == x
|
|
2139
|
+
for b in baseline):
|
|
2140
|
+
continue
|
|
2141
|
+
lines.append(
|
|
2142
|
+
f"| {TC_NAME[tc]} | {x} | {cnt[(tc, x, 'pass', 'run')]} | "
|
|
2143
|
+
f"{cnt[(tc, x, 'fail', 'compile')]} | "
|
|
2144
|
+
f"{cnt[(tc, x, 'fail', 'run')]} | "
|
|
2145
|
+
f"{sum(v for k, v in cnt.items() if k[:3] == (tc, x, 'harness_error'))} |")
|
|
2146
|
+
return lines + [""] + _baseline_mode_flags(baseline)
|
|
2147
|
+
cnt = Counter((b["toolchain"], b["xnack"], b["status"], b.get("stage", ""))
|
|
2148
|
+
for b in baseline)
|
|
2149
|
+
host = [b for b in baseline if b["status"] == "pass"
|
|
2150
|
+
and "passed on the host" in b.get("stdout", "").lower()]
|
|
2151
|
+
hc = Counter((b["toolchain"], b["xnack"]) for b in host)
|
|
2152
|
+
files = len({(b.get("repo"), b.get("path")) for b in baseline})
|
|
2153
|
+
lines = [
|
|
2154
|
+
"## Corpus baseline (omp_vv Fortran target tests)",
|
|
2155
|
+
"",
|
|
2156
|
+
f"{len(baseline)} results ({files} files x toolchain x memory mode "
|
|
2157
|
+
f"run). {_baseline_modes(baseline, meta or {})} "
|
|
2158
|
+
"Pass criterion: exit code 0 (Global Constraints); these tests are "
|
|
2159
|
+
"**unaudited**: their self-checks were not mutation-tested, so a pass "
|
|
2160
|
+
"says the program compiled and exited 0, not that the feature works.",
|
|
2161
|
+
"",
|
|
2162
|
+
"| toolchain | HSA_XNACK | pass | fail (compile) | fail (run) | "
|
|
2163
|
+
"of which pass prints 'Test passed on the host' [^host] |",
|
|
2164
|
+
"|---|---|---|---|---|---|",
|
|
2165
|
+
]
|
|
2166
|
+
for tc, x in columns():
|
|
2167
|
+
lines.append(
|
|
2168
|
+
f"| {TC_NAME[tc]} | {x} | {cnt[(tc, x, 'pass', 'run')]} | "
|
|
2169
|
+
f"{cnt[(tc, x, 'fail', 'compile')]} | "
|
|
2170
|
+
f"{cnt[(tc, x, 'fail', 'run')]} | {hc[(tc, x)]} |")
|
|
2171
|
+
lines += [
|
|
2172
|
+
"",
|
|
2173
|
+
f"[^host]: {len(host)} baseline passes print \"Test passed on the "
|
|
2174
|
+
"host\". The omp_vv module (ompvv/ompvv.F90) prints \"host\" when its "
|
|
2175
|
+
"flag ompvv_isHost is true: the flag starts .true. and only the "
|
|
2176
|
+
"test's offloading probe (a target region calling "
|
|
2177
|
+
"omp_is_initial_device) sets it, so the message means either that the "
|
|
2178
|
+
"probe ran on the initial device or that the test never called the "
|
|
2179
|
+
"probe; every run set OMP_TARGET_OFFLOAD=mandatory. Exit code 0 counts "
|
|
2180
|
+
"them as passes under the baseline rule; they are not evidence of "
|
|
2181
|
+
"device execution. "
|
|
2182
|
+
"Files: "
|
|
2183
|
+
+ ", ".join(sorted({b['path'].rsplit('/', 1)[-1] for b in host}))
|
|
2184
|
+
+ ".",
|
|
2185
|
+
"",
|
|
2186
|
+
]
|
|
2187
|
+
return lines + _baseline_mode_flags(baseline)
|
|
2188
|
+
|
|
2189
|
+
|
|
2190
|
+
def _baseline_mode_flags(baseline: list[dict]) -> list[str]:
|
|
2191
|
+
"""Omp_vv tests whose status differs between the two memory
|
|
2192
|
+
modes on a toolchain. The tests are untrusted (not mutation-tested), so
|
|
2193
|
+
these are flags for review, never findings."""
|
|
2194
|
+
by: dict[tuple, dict] = defaultdict(dict)
|
|
2195
|
+
for b in baseline:
|
|
2196
|
+
if arch_of(b["toolchain"]) != "gfx942":
|
|
2197
|
+
continue # _baseline_g90_flags
|
|
2198
|
+
by[(b["path"], b["toolchain"])][b["xnack"]] = b
|
|
2199
|
+
diff = [(p, t, m) for (p, t), m in sorted(by.items())
|
|
2200
|
+
if len(m) == 2 and m[0]["status"] != m[1]["status"]]
|
|
2201
|
+
out = ["### omp_vv tests whose memory modes differ (flags)", "",
|
|
2202
|
+
"omp_vv tests are untrusted (their checks were not "
|
|
2203
|
+
"mutation-tested): a test that passes in one mode only is "
|
|
2204
|
+
"flagged for review, never reported as a compiler finding.", ""]
|
|
2205
|
+
if not diff:
|
|
2206
|
+
return out + ["None.", ""] + _baseline_g90_flags(baseline)
|
|
2207
|
+
c = Counter((t, m[1]["status"] == "pass") for _, t, m in diff)
|
|
2208
|
+
out += ["| toolchain | passes at X=1 only | passes at X=0 only |",
|
|
2209
|
+
"|---|---|---|"]
|
|
2210
|
+
for t in TOOLCHAINS:
|
|
2211
|
+
if c[(t, True)] or c[(t, False)]:
|
|
2212
|
+
out.append(f"| {TC_NAME[t]} | {c[(t, True)]} | "
|
|
2213
|
+
f"{c[(t, False)]} |")
|
|
2214
|
+
out += ["", "| test | toolchain | X=1 | X=0 |", "|---|---|---|---|"]
|
|
2215
|
+
for p, t, m in diff:
|
|
2216
|
+
def cell(b):
|
|
2217
|
+
return _md(_clip(f"{b['status']} ({b.get('stage', '')}: "
|
|
2218
|
+
f"{b.get('reason', '')})", 120))
|
|
2219
|
+
out.append(f"| {p.removeprefix('tests/')} | "
|
|
2220
|
+
f"{TC_NAME.get(t, t)} | {cell(m[1])} | {cell(m[0])} |")
|
|
2221
|
+
return out + [""] + _baseline_g90_flags(baseline)
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
def _baseline_g90_flags(baseline: list[dict]) -> list[str]:
|
|
2225
|
+
"""Omp_vv tests whose gfx90a status differs from the same
|
|
2226
|
+
compiler's MI300A copy-mode (HSA_XNACK=0) status; flags for review,
|
|
2227
|
+
never findings (the tests are untrusted)."""
|
|
2228
|
+
by: dict[tuple, dict] = defaultdict(dict)
|
|
2229
|
+
for b in baseline:
|
|
2230
|
+
by[(b["path"], base_name(b["toolchain"]))][
|
|
2231
|
+
arch_of(b["toolchain"])] = b
|
|
2232
|
+
pairs = [(p, t, m) for (p, t), m in sorted(by.items())
|
|
2233
|
+
if "gfx90a" in m and "gfx942" in m]
|
|
2234
|
+
if not pairs:
|
|
2235
|
+
return []
|
|
2236
|
+
diff = [(p, t, m) for p, t, m in pairs
|
|
2237
|
+
if m["gfx90a"]["status"] != m["gfx942"]["status"]]
|
|
2238
|
+
out = ["### omp_vv tests whose gfx90a result differs from MI300A copy "
|
|
2239
|
+
"mode (flags)", "",
|
|
2240
|
+
f"{len(diff)} of {len(pairs)} (test, compiler) pairs differ "
|
|
2241
|
+
"between MI300A at HSA_XNACK=0 and gfx90a (HSA_XNACK=0). The "
|
|
2242
|
+
"tests are untrusted: flags for review, never findings.", ""]
|
|
2243
|
+
if not diff:
|
|
2244
|
+
return out + ["None.", ""]
|
|
2245
|
+
|
|
2246
|
+
def cell(b):
|
|
2247
|
+
return _md(_clip(f"{b['status']} ({b.get('stage', '')}: "
|
|
2248
|
+
f"{b.get('reason', '')})", 120))
|
|
2249
|
+
out += ["| test | compiler | MI300A X=0 | gfx90a |", "|---|---|---|---|"]
|
|
2250
|
+
for p, t, m in diff:
|
|
2251
|
+
out.append(f"| {p.removeprefix('tests/')} | {TC_NAME.get(t, t)} | "
|
|
2252
|
+
f"{cell(m['gfx942'])} | {cell(m['gfx90a'])} |")
|
|
2253
|
+
return out + [""]
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
def _baseline_modes(baseline: list[dict], meta: dict) -> str:
|
|
2257
|
+
"""Which tests ran in which mode, and on which nodes."""
|
|
2258
|
+
by = defaultdict(set)
|
|
2259
|
+
for b in baseline:
|
|
2260
|
+
by[b["xnack"]].add(b["path"])
|
|
2261
|
+
if by[COPY] == by[HEADLINE]:
|
|
2262
|
+
text = "Every test ran in both modes"
|
|
2263
|
+
else:
|
|
2264
|
+
text = (f"Every test ran at HSA_XNACK=1 ({len(by[HEADLINE])} files); "
|
|
2265
|
+
f"copy mode ran only the {len(by[COPY])} tests whose result "
|
|
2266
|
+
"depends on copy semantics (offload.engine.ompvv.copy_semantics)")
|
|
2267
|
+
nodes = []
|
|
2268
|
+
for x in MODES:
|
|
2269
|
+
m = meta.get(x) or {}
|
|
2270
|
+
if m.get("baseline_rows"):
|
|
2271
|
+
nodes.append(f"HSA_XNACK={x} with the matrix run on "
|
|
2272
|
+
f"{m.get('host', '?')}")
|
|
2273
|
+
for rr in (meta.get(f"baseline{x}") or {}).get("reruns", []):
|
|
2274
|
+
nodes.append(f"HSA_XNACK={x} rows of "
|
|
2275
|
+
f"{len(rr.get('baseline_paths', []))} tests rerun on "
|
|
2276
|
+
f"{rr.get('host', '?')} ({rr.get('started', '?')})")
|
|
2277
|
+
return text + (" (" + "; ".join(nodes) + ")." if nodes else
|
|
2278
|
+
"; nodes not recorded.")
|
|
2279
|
+
|
|
2280
|
+
|
|
2281
|
+
def _claims_section(table: list[Row], mismatches: list[Mismatch],
|
|
2282
|
+
claims: list[Claim]) -> list[str]:
|
|
2283
|
+
groups = claim_groups(table, claims)
|
|
2284
|
+
sources: dict[tuple, Claim] = {}
|
|
2285
|
+
for c in claims: # One row per document edition
|
|
2286
|
+
sources.setdefault((c.source, c.toolchain), c)
|
|
2287
|
+
lines = [
|
|
2288
|
+
"## Vendor claims versus measurements",
|
|
2289
|
+
"",
|
|
2290
|
+
"All claims are **provisional** and cited in "
|
|
2291
|
+
f"[claims.yaml]({LINK['claims']}). A claim covers an atom by name or "
|
|
2292
|
+
"pattern; a release-level (general) claim applies only where no "
|
|
2293
|
+
"specific claim for that toolchain covers the atom. Comparison rules: "
|
|
2294
|
+
"`supported` is contradicted by rejected, ignored, fell back, wrong "
|
|
2295
|
+
"answer or crash; `unsupported` by correct on device (or, for a "
|
|
2296
|
+
"compile-error claim, by anything but rejection); `partial` only when "
|
|
2297
|
+
"the vendor says the form is accepted and it is rejected. Cells that "
|
|
2298
|
+
"cannot separate the construct (no oracle: hint only, "
|
|
2299
|
+
"implementation-defined, equivalent default; legality; unverified; "
|
|
2300
|
+
"unobservable) never contradict a claim, except that a crash of such "
|
|
2301
|
+
"an atom does (a crash needs no oracle), and they never count as "
|
|
2302
|
+
"support; zero-copy programs in copy mode and illegal "
|
|
2303
|
+
"variants are not compared. Both memory modes are compared where both "
|
|
2304
|
+
"ran.",
|
|
2305
|
+
"",
|
|
2306
|
+
"### Sources",
|
|
2307
|
+
"",
|
|
2308
|
+
"| source | toolchain | language of the claim | URL | version note |",
|
|
2309
|
+
"|---|---|---|---|---|",
|
|
2310
|
+
]
|
|
2311
|
+
for (s, _), c in sources.items():
|
|
2312
|
+
lines.append(f"| {s} | {TC_NAME.get(c.toolchain, c.toolchain)} | "
|
|
2313
|
+
f"{c.language} | <{c.url}> | {_md(c.version_note)} |")
|
|
2314
|
+
n_groups = len(groups)
|
|
2315
|
+
confirmed = [m for m in mismatches if not m.oracle_unaudited]
|
|
2316
|
+
unaud = [m for m in mismatches if m.oracle_unaudited]
|
|
2317
|
+
lines += [
|
|
2318
|
+
"",
|
|
2319
|
+
f"{len(claims)} claim entries; {n_groups} (claim, atom, version, "
|
|
2320
|
+
f"toolchain) comparisons; {len(mismatches)} mismatches "
|
|
2321
|
+
f"({len(confirmed)} from a compile result, a crash, or an audited "
|
|
2322
|
+
"oracle, "
|
|
2323
|
+
f"{len(unaud)} from a run-time result of an oracle-unaudited atom). "
|
|
2324
|
+
"A rejection can come from another feature the program also uses; "
|
|
2325
|
+
"the harness reason column shows the compiler's message.",
|
|
2326
|
+
"",
|
|
2327
|
+
]
|
|
2328
|
+
|
|
2329
|
+
def table_of(ms: list[Mismatch]) -> list[str]:
|
|
2330
|
+
out = ["| toolchain | atom | version | claimed [source] | measured | "
|
|
2331
|
+
"harness reason (first contradicting cell) | vendor text |",
|
|
2332
|
+
"|---|---|---|---|---|---|---|"]
|
|
2333
|
+
for m in ms:
|
|
2334
|
+
out.append(
|
|
2335
|
+
f"| {TC_NAME.get(m.toolchain, m.toolchain)} | "
|
|
2336
|
+
f"`{m.atom.removeprefix('omp.')}` | {_vstr(m.version)} | "
|
|
2337
|
+
f"{_md(m.claimed)}{' (general)' if m.general else ''}"
|
|
2338
|
+
f"{' (C/C++ table)' if m.language == 'C/C++' and m.lang != 'c' else ''} | "
|
|
2339
|
+
f"{_md(m.measured)} | {_md(_clip(m.reason, 110))} | "
|
|
2340
|
+
f"[{_md(_clip(m.quote, 90))}]({m.url}) |")
|
|
2341
|
+
return out
|
|
2342
|
+
|
|
2343
|
+
caveats = sorted({m.confound for m in mismatches if m.confound})
|
|
2344
|
+
lines += ["### Mismatches", ""] + (table_of(confirmed) if confirmed
|
|
2345
|
+
else ["None."])
|
|
2346
|
+
if caveats:
|
|
2347
|
+
lines += ["", "‡ Confounded program (generator follow-up): "
|
|
2348
|
+
+ "; ".join(caveats) + "."]
|
|
2349
|
+
lines += ["", "### Mismatches on oracle-unaudited atoms (not confirmed)",
|
|
2350
|
+
"",
|
|
2351
|
+
"Run-time outcomes of atoms whose oracle is unaudited (no "
|
|
2352
|
+
"toolchain passes the original program). The measured outcome "
|
|
2353
|
+
"stands, but these are not confirmed failures.", ""]
|
|
2354
|
+
lines += table_of(unaud) if unaud else ["None."]
|
|
2355
|
+
lines.append("")
|
|
2356
|
+
return lines
|
|
2357
|
+
|
|
2358
|
+
|
|
2359
|
+
def _rerun_winners(modes: dict) -> str:
|
|
2360
|
+
"""Which rerun produced the final rows of a program rerun more than
|
|
2361
|
+
once in one mode (the later run replaced the earlier rows)."""
|
|
2362
|
+
parts = []
|
|
2363
|
+
for x in MODES:
|
|
2364
|
+
last: dict[str, str] = {}
|
|
2365
|
+
seen: Counter = Counter()
|
|
2366
|
+
for rr in modes.get(x, {}).get("reruns", []):
|
|
2367
|
+
for p in rr.get("programs", []):
|
|
2368
|
+
seen[p] += 1
|
|
2369
|
+
last[p] = rr.get("host", "?")
|
|
2370
|
+
multi = sorted(p for p, n in seen.items() if n > 1)
|
|
2371
|
+
if multi:
|
|
2372
|
+
parts.append(f"HSA_XNACK={x}: " + ", ".join(
|
|
2373
|
+
f"`{p}`: {last[p]}" for p in multi))
|
|
2374
|
+
if not parts:
|
|
2375
|
+
return ""
|
|
2376
|
+
return ("- Programs rerun more than once in one mode: their rows come "
|
|
2377
|
+
"from the last rerun, " + "; ".join(parts) + ".")
|
|
2378
|
+
|
|
2379
|
+
|
|
2380
|
+
def _stale_audit_note(legal: list[Row]) -> str:
|
|
2381
|
+
"""One line naming the atoms the audit calls unaudited although a
|
|
2382
|
+
toolchain passes them in the matrix."""
|
|
2383
|
+
stale = sorted({(r.version, r.atom) for r in legal
|
|
2384
|
+
if r.audit.startswith("unaudited")
|
|
2385
|
+
and r.outcome == "correct_on_device"})
|
|
2386
|
+
if not stale:
|
|
2387
|
+
return ("- Audit dispositions come from the committed audit run; no "
|
|
2388
|
+
"unaudited atom passes in the matrix.")
|
|
2389
|
+
names = ", ".join(f"`{a.split('.', 1)[1]}` ({_vstr(v)})"
|
|
2390
|
+
for v, a in stale)
|
|
2391
|
+
if all(a.startswith("acc.") for _, a in stale):
|
|
2392
|
+
return ("- **The OpenACC audit runs on CCE 19 only**: for these "
|
|
2393
|
+
"atoms CCE 19 fails the original while another toolchain "
|
|
2394
|
+
"passes it in the matrix, so 'oracle unaudited' does not "
|
|
2395
|
+
"mean that no compiler runs the program: " + names + ".")
|
|
2396
|
+
return ("- **Audit dispositions predate the explicit -fopenmp-version=60 "
|
|
2397
|
+
"run** of AFAR's 6.0 programs, in which AFAR rejected the "
|
|
2398
|
+
"originals. For these atoms a toolchain passes the original in "
|
|
2399
|
+
"the matrix, so 'oracle unaudited' may not apply (re-audit is a "
|
|
2400
|
+
f"follow-up): {names}.")
|
|
2401
|
+
|
|
2402
|
+
|
|
2403
|
+
def _node_counts(table: list[Row], host) -> tuple[int, int, int]:
|
|
2404
|
+
"""(same node, different nodes, unrecorded) over the (program,
|
|
2405
|
+
toolchain) pairs that have both modes."""
|
|
2406
|
+
pairs = defaultdict(dict)
|
|
2407
|
+
for r in table:
|
|
2408
|
+
if r.toolchain in NVIDIA_TOOLCHAINS: # AMD modes only
|
|
2409
|
+
continue
|
|
2410
|
+
pairs[(r.program_id, r.toolchain)][r.xnack] = r
|
|
2411
|
+
same = diff = unknown = 0
|
|
2412
|
+
for (pid, _), m in pairs.items():
|
|
2413
|
+
if len(m) < 2:
|
|
2414
|
+
continue
|
|
2415
|
+
h = [host(pid, x) for x in MODES]
|
|
2416
|
+
if None in h:
|
|
2417
|
+
unknown += 1
|
|
2418
|
+
elif h[0] == h[1]:
|
|
2419
|
+
same += 1
|
|
2420
|
+
else:
|
|
2421
|
+
diff += 1
|
|
2422
|
+
return same, diff, unknown
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
def _modes_provenance(table: list[Row], meta: dict, host) -> list[str]:
|
|
2426
|
+
modes = {x: m for x, m in meta.items() if isinstance(x, int)}
|
|
2427
|
+
amd = [r for r in table if r.toolchain not in NVIDIA_TOOLCHAINS]
|
|
2428
|
+
progs = {r.program_id for r in amd}
|
|
2429
|
+
copy = {r.program_id for r in amd if r.xnack == COPY}
|
|
2430
|
+
if copy == progs:
|
|
2431
|
+
which = "every program (a full two-mode matrix)"
|
|
2432
|
+
else:
|
|
2433
|
+
which = ("the copy-mode subset only: the copy_mode_only programs and "
|
|
2434
|
+
"the memory-sensitive ones of "
|
|
2435
|
+
"catalog/omp/memory_modes.yaml (offload.engine.run_matrix."
|
|
2436
|
+
"copy_mode_subset)")
|
|
2437
|
+
same, diff, unknown = _node_counts(table, host)
|
|
2438
|
+
if not amd:
|
|
2439
|
+
return [] # An NVIDIA-only table (OpenACC C)
|
|
2440
|
+
|
|
2441
|
+
def run(x):
|
|
2442
|
+
m = modes.get(x, {})
|
|
2443
|
+
return (f"{m.get('host', '?')} (THP {m.get('thp_seen', '?')}, "
|
|
2444
|
+
f"started {m.get('started', '?')})")
|
|
2445
|
+
return [
|
|
2446
|
+
"- **Headline mode: HSA_XNACK=1 with transparent huge pages** (LC's "
|
|
2447
|
+
"example job script) for every program. **Copy mode "
|
|
2448
|
+
f"(HSA_XNACK=0)** for {len(copy)} of {len(progs)} programs: "
|
|
2449
|
+
f"{which}. A copy-mode column reads '{NOT_RUN}' for the others.",
|
|
2450
|
+
f"- HSA_XNACK=1 run on {run(HEADLINE)}; HSA_XNACK=0 run on "
|
|
2451
|
+
f"{run(COPY)}. Of the program x toolchain pairs with both modes, "
|
|
2452
|
+
f"{same} have both rows from one node, {diff} from different nodes "
|
|
2453
|
+
f"and {unknown} from unrecorded nodes; where the modes of the latter "
|
|
2454
|
+
"differ, the cells carry a dagger (†).",
|
|
2455
|
+
]
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
def _g90_provenance(table: list[Row], meta: dict) -> list[str]:
|
|
2459
|
+
"""Where the gfx90a rows ran."""
|
|
2460
|
+
g = [r for r in table if arch_of(r.toolchain) == "gfx90a"]
|
|
2461
|
+
if not g:
|
|
2462
|
+
return []
|
|
2463
|
+
runs = (meta.get("g90") or {}).get("reruns", [])
|
|
2464
|
+
# the machine's name, not the node's (<machine>-gpu<n>.<domain> is <machine>)
|
|
2465
|
+
hosts = sorted({re.sub(r"-gpu\d+$", "", (rr.get("host") or "?").split(".")[0]) for rr in runs})
|
|
2466
|
+
return [
|
|
2467
|
+
f"- **gfx90a (Tioga, AMD Instinct MI250X, a discrete GPU): "
|
|
2468
|
+
f"HSA_XNACK=0 only** (XNACK is not "
|
|
2469
|
+
f"recommended on MI250X), {len({r.program_id for r in g})} "
|
|
2470
|
+
f"programs on " + ", ".join(TC_NAME[t] for t in G90) + ", nodes "
|
|
2471
|
+
+ (", ".join(hosts) if hosts else "not recorded") + "; the same "
|
|
2472
|
+
"compiler releases with gfx90a as the offload arch "
|
|
2473
|
+
"(--offload-arch=gfx90a, craype-accel-amd-gfx90a for CCE, "
|
|
2474
|
+
"-march=gfx90a for GCC's amdgcn offload), the compilers' default "
|
|
2475
|
+
"('xnack any') code objects. Each compiler's third column."]
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
def _gcc_flags_text(c_lang: bool) -> str:
|
|
2479
|
+
"""GCC's flags on the AMD GPUs (its catalog template), when it has a column."""
|
|
2480
|
+
if not any(_reg().toolchains[t].family_id == "gcc" for t in TOOLCHAINS):
|
|
2481
|
+
return ""
|
|
2482
|
+
t = _reg().toolchains["gcc"]
|
|
2483
|
+
flags = t.family.flags["omp"]["c" if c_lang else "f90"].format(
|
|
2484
|
+
target="amdgcn-amdhsa", arch="<the GPU's arch>", latomic="")
|
|
2485
|
+
return f" GCC: {'gcc' if c_lang else 'gfortran'} {flags} -O2, no version flag."
|
|
2486
|
+
|
|
2487
|
+
|
|
2488
|
+
def _toolchains_text() -> str:
|
|
2489
|
+
"""The status line's toolchains: the table's columns, by GPU."""
|
|
2490
|
+
amd = ", ".join(TC_NAME[t] for t in MI300A)
|
|
2491
|
+
other = ", ".join(TC_NAME[t] for t in TOOLCHAINS if t not in MI300A)
|
|
2492
|
+
nv = ", ".join(TC_NAME[t] for t in NV)
|
|
2493
|
+
return (f"Toolchains: {amd} on the MI300A (gfx942) and each one's other GPUs"
|
|
2494
|
+
+ (f"; {other} on its other AMD GPUs only (not the MI300A)" if other else "")
|
|
2495
|
+
+ (f"; {nv} on the A100 (sm_80)" if nv else "")
|
|
2496
|
+
+ "; -O2, three runs per cell.")
|
|
2497
|
+
|
|
2498
|
+
|
|
2499
|
+
def _twins_provenance(table: list[Row], meta: dict) -> list[str]:
|
|
2500
|
+
"""Where the rows of the twins on other machines than Tioga ran (MI300X, MI350X)."""
|
|
2501
|
+
from offload.engine.confirm.machines import load
|
|
2502
|
+
reg, out = _reg(), []
|
|
2503
|
+
for m in load().values():
|
|
2504
|
+
tws = [t for t in TWINS if reg.toolchains[t].machine == m.name and t not in G90]
|
|
2505
|
+
if not tws:
|
|
2506
|
+
continue
|
|
2507
|
+
runs = (meta.get("twins") or {}).get(m.name, [])
|
|
2508
|
+
hosts = sorted({re.sub(r"-gpu\d+$", "", (rr.get("host") or "?").split(".")[0]) for rr in runs})
|
|
2509
|
+
progs = {r.program_id for r in table if r.toolchain in tws}
|
|
2510
|
+
out.append(
|
|
2511
|
+
f"- **{m.arch} ({m.device}, a discrete GPU): copy memory (HSA_XNACK=0) only**, "
|
|
2512
|
+
f"{len(progs)} programs on " + ", ".join(TC_NAME[t] for t in tws) + ", nodes "
|
|
2513
|
+
+ (", ".join(hosts) if hosts else "not recorded") + "; the same compiler releases "
|
|
2514
|
+
f"with {m.arch} as the offload arch. A column of its own after the gfx90a "
|
|
2515
|
+
"one; it does not enter the portable verdict.")
|
|
2516
|
+
return out
|
|
2517
|
+
|
|
2518
|
+
|
|
2519
|
+
def _nv_provenance(table: list[Row], meta: dict) -> list[str]:
|
|
2520
|
+
"""Where the NVIDIA rows ran."""
|
|
2521
|
+
nv = [r for r in table if r.toolchain in NVIDIA_TOOLCHAINS]
|
|
2522
|
+
if not nv:
|
|
2523
|
+
return []
|
|
2524
|
+
runs = (meta.get("nvhpc") or {}).get("reruns", [])
|
|
2525
|
+
# the machine's name, not the node's (<machine>-gpu<n>.<domain> is <machine>)
|
|
2526
|
+
hosts = sorted({re.sub(r"-gpu\d+$", "", (rr.get("host") or "?").split(".")[0]) for rr in runs})
|
|
2527
|
+
comp = sorted({c for rr in runs for c in (rr.get("compilers") or {})
|
|
2528
|
+
.values() if c})
|
|
2529
|
+
return [
|
|
2530
|
+
f"- **NVIDIA A100 (wingtip, sm_80): "
|
|
2531
|
+
+ ", ".join(TC_NAME[t] for t in NV) + "**, "
|
|
2532
|
+
f"{len({r.program_id for r in nv})} programs; NVHPC in separate memory "
|
|
2533
|
+
"(-gpu=mem:separate, the headline mode) and managed memory "
|
|
2534
|
+
"(-gpu=mem:managed), each mode its own build; -mp=gpu (OpenACC: "
|
|
2535
|
+
"-acc=gpu) -gpu=cc80 -O2, no OpenMP version option (every version "
|
|
2536
|
+
"slice compiles with the default)"
|
|
2537
|
+
+ ("; GCC in separate memory only, -foffload=nvptx-none"
|
|
2538
|
+
if any(_reg().toolchains[t].family_id == "gcc" for t in NV) else "")
|
|
2539
|
+
+ "; no unified memory on that node (not applicable). Host "
|
|
2540
|
+
+ (", ".join(hosts) if hosts else "not recorded")
|
|
2541
|
+
+ (f"; {'; '.join(comp)}" if comp else "") + "."]
|
|
2542
|
+
|
|
2543
|
+
|
|
2544
|
+
def render_markdown(table: list[Row], mismatches: list[Mismatch],
|
|
2545
|
+
claims: Path | list[Claim] | None = None,
|
|
2546
|
+
baseline: list[dict] | None = None,
|
|
2547
|
+
meta: dict | None = None,
|
|
2548
|
+
suite: dict | None = None) -> str:
|
|
2549
|
+
cl = (load_claims(claims) if isinstance(claims, (str, Path))
|
|
2550
|
+
else (claims or []))
|
|
2551
|
+
meta = meta or {}
|
|
2552
|
+
model = table_model(table)
|
|
2553
|
+
LINK = _paths(model)[3]
|
|
2554
|
+
acc = model == "acc"
|
|
2555
|
+
show_suite = not acc # omp_vv is an OpenMP suite
|
|
2556
|
+
# The headline tables are device offload only; host+device
|
|
2557
|
+
# pattern programs have their own section
|
|
2558
|
+
legal = [r for r in table if r.legal and r.scope == "device"]
|
|
2559
|
+
hostdev = [r for r in table if r.legal and r.scope != "device"]
|
|
2560
|
+
illegal = [r for r in table if not r.legal]
|
|
2561
|
+
modes = {x: m for x, m in meta.items() if isinstance(x, int)}
|
|
2562
|
+
host = program_hosts(meta)
|
|
2563
|
+
rocm = sorted({r for r in (meta.get("rocm") or [])})
|
|
2564
|
+
intro = [
|
|
2565
|
+
"# Support table: OpenMP target offload, free-form Fortran",
|
|
2566
|
+
"",
|
|
2567
|
+
"Status: **provisional**. Generated by compliance/report.py "
|
|
2568
|
+
"(tools/derive/support_table.py) from results/matrix.jsonl, the committed "
|
|
2569
|
+
f"oracle audit summary ([oracle-audit.md]({LINK['audit']})) and "
|
|
2570
|
+
f"[claims.yaml]({LINK['claims']}). Scope: OpenMP "
|
|
2571
|
+
f"{_versions_text(table)} atoms of "
|
|
2572
|
+
"the first slice, free-form Fortran only (OpenACC and C are outside "
|
|
2573
|
+
f"this slice). {_toolchains_text()}",
|
|
2574
|
+
""]
|
|
2575
|
+
c_lang = table_lang(table) == "c"
|
|
2576
|
+
if c_lang:
|
|
2577
|
+
intro = [
|
|
2578
|
+
"# Support table: OpenMP target offload, C11",
|
|
2579
|
+
"",
|
|
2580
|
+
"Status: **provisional**. Generated by compliance/report.py "
|
|
2581
|
+
"(tools/derive/support_table.py --lang c) from results/matrix_c.jsonl, "
|
|
2582
|
+
"the committed C oracle audit summary "
|
|
2583
|
+
"([oracle-audit-c.md](../reports/oracle-audit-c.md)) and "
|
|
2584
|
+
f"[claims.yaml]({LINK['claims']}) (claims about C or C/C++ "
|
|
2585
|
+
"only; a claim that names a do compound covers its for twin). "
|
|
2586
|
+
f"Scope: OpenMP {_versions_text(table)} atoms of the first "
|
|
2587
|
+
"slice in C: the C ports of the Fortran programs "
|
|
2588
|
+
"(offload.engine.c_gen, offload.engine.f2c) and the C-only illegal "
|
|
2589
|
+
f"variants. {_toolchains_text()} The CSV has the "
|
|
2590
|
+
"Fortran table's columns plus `lang` (c here, f90 there).",
|
|
2591
|
+
""]
|
|
2592
|
+
if acc and c_lang:
|
|
2593
|
+
intro = [
|
|
2594
|
+
"# Support table: OpenACC 3.4, C11 (NVIDIA HPC SDK)",
|
|
2595
|
+
"",
|
|
2596
|
+
"Status: **provisional**. Generated by compliance/report.py "
|
|
2597
|
+
"(tools/derive/support_table.py --model acc --lang c) from "
|
|
2598
|
+
"results/acc_matrix_c.jsonl and the NVIDIA oracle audit "
|
|
2599
|
+
"summaries (tests/data/acc_audit_summary_nvhpc*_c.json). Scope: "
|
|
2600
|
+
"the OpenACC 3.4 atoms of the slice in C: the C "
|
|
2601
|
+
"ports of the Fortran programs (offload.engine.acc_c_gen, "
|
|
2602
|
+
"offload.engine.f2c) without the Fortran-only .eqv./.neqv. "
|
|
2603
|
+
"reductions. Toolchains: NVHPC 25.11 and 26.9 nvc -acc=gpu, "
|
|
2604
|
+
"NVIDIA A100, separate and managed memory; CCE's C compiler and "
|
|
2605
|
+
"AFAR have no OpenACC (results/t37/accprobe) and are not run.",
|
|
2606
|
+
""]
|
|
2607
|
+
elif acc:
|
|
2608
|
+
intro = [
|
|
2609
|
+
"# Support table: OpenACC 3.4, free-form Fortran (CCE)",
|
|
2610
|
+
"",
|
|
2611
|
+
"Status: **provisional**. Generated by compliance/report.py "
|
|
2612
|
+
"(tools/derive/support_table.py --model acc) from "
|
|
2613
|
+
"results/acc_matrix.jsonl and the committed oracle audit summary "
|
|
2614
|
+
f"([acc-oracle-audit.md]({LINK['audit']})). Scope: the OpenACC "
|
|
2615
|
+
"3.4 atoms of the slice (catalog/acc/), free-form Fortran "
|
|
2616
|
+
"only. Toolchains: Cray CCE "
|
|
2617
|
+
+ ", ".join(TC_NAME[t] for t in TOOLCHAINS)
|
|
2618
|
+
+ " (ftn -h acc -O2), MI300A (gfx942); AFAR (amdflang) does not "
|
|
2619
|
+
"support OpenACC and is not run.",
|
|
2620
|
+
""]
|
|
2621
|
+
lines = intro + [
|
|
2622
|
+
"## Provenance and memory modes",
|
|
2623
|
+
"",
|
|
2624
|
+
f"- {len(table)} cells: {len(legal)} legal-program cells, "
|
|
2625
|
+
f"{len(hostdev)} host+device pattern cells and "
|
|
2626
|
+
f"{len(illegal)} illegal-variant cells (program x toolchain x "
|
|
2627
|
+
"HSA_XNACK).",
|
|
2628
|
+
"- Scope: device offload only. A construct "
|
|
2629
|
+
"that runs on the host and whose result never reaches a "
|
|
2630
|
+
+ ("compute" if acc else "target") + " region is out of scope (the "
|
|
2631
|
+
"section 'Out of scope (host execution)' lists those atoms); host "
|
|
2632
|
+
"constructs that feed a "
|
|
2633
|
+
+ ("compute" if acc else "target") + " region the way codes use "
|
|
2634
|
+
"them are host+device patterns, reported in their own section and "
|
|
2635
|
+
"never as an atom's headline result.",
|
|
2636
|
+
] + _modes_provenance(table, meta, host) + _g90_provenance(
|
|
2637
|
+
table, meta) + _twins_provenance(table, meta) + _nv_provenance(table, meta) + [
|
|
2638
|
+
f"- **Rerun rows** (HSA_XNACK={x}): {len(rr.get('programs', []))} "
|
|
2639
|
+
f"programs rerun on {rr.get('host', '?')} (THP "
|
|
2640
|
+
f"{rr.get('thp_seen', '?')}, started {rr.get('started', '?')}) "
|
|
2641
|
+
f"after {_public(rr.get('why', 'a change'))}: "
|
|
2642
|
+
+ (", ".join(rr.get("programs", [])) if len(rr.get("programs", []))
|
|
2643
|
+
<= 40 else "(the full list is in the run metadata)") + "."
|
|
2644
|
+
+ (f" Note: {_public(rr['note'])}." if rr.get("note") else "")
|
|
2645
|
+
for x in MODES for rr in modes.get(x, {}).get("reruns", [])
|
|
2646
|
+
] + [w for w in [_rerun_winners(modes)] if w] + [
|
|
2647
|
+
("" if acc and c_lang else # NVHPC only (its own line)
|
|
2648
|
+
"- CCE compiles with -h acc -O2 (OpenMP off)." if acc else
|
|
2649
|
+
"- Every toolchain compiles a C program with -fopenmp-version= its "
|
|
2650
|
+
"version (both C front ends default to OpenMP 5.1); AFAR: amdclang "
|
|
2651
|
+
"-fopenmp --offload-arch=gfx942; CCE: craycc -fopenmp "
|
|
2652
|
+
"-fopenmp-targets=amdgcn-amd-amdhsa -Xopenmp-target=amdgcn-amd-amdhsa "
|
|
2653
|
+
"-march=gfx942 (the flags the craype-accel-amd-gfx942 module gives "
|
|
2654
|
+
"the cc wrapper; craycc -fopenmp alone builds a host-only "
|
|
2655
|
+
"executable). CCE 19 accepts -fopenmp-version=60 without a "
|
|
2656
|
+
"diagnostic but stays at OpenMP 5.1 (_OPENMP 202011)." if c_lang else
|
|
2657
|
+
"- AFAR compiles 6.0 programs with -fopenmp-version=60"
|
|
2658
|
+
+ (", 5.0 programs with -fopenmp-version=50"
|
|
2659
|
+
if any(r.version == 50 for r in table) else "")
|
|
2660
|
+
+ " and 5.2 "
|
|
2661
|
+
"programs with its default; CCE has no version flag (-fopenmp -O2).")
|
|
2662
|
+
+ _gcc_flags_text(c_lang)
|
|
2663
|
+
+ (f" ROCm loaded with CCE: {', '.join(rocm)}." if rocm else ""),
|
|
2664
|
+
("- One run per trial: OpenACC has no counterpart of "
|
|
2665
|
+
"OMP_TARGET_OFFLOAD, so a failing run is not repeated; host "
|
|
2666
|
+
"execution shows as ran_on_host=1 (acc_on_device(acc_device_host) "
|
|
2667
|
+
"inside the compute region)." if acc else
|
|
2668
|
+
"- `OMP_TARGET_OFFLOAD=mandatory` on the primary run; a failing run "
|
|
2669
|
+
"is repeated without it to tell host fallback from a wrong answer."),
|
|
2670
|
+
"",
|
|
2671
|
+
"## How to read a cell",
|
|
2672
|
+
"",
|
|
2673
|
+
"- **correct on device**: the check passed and the region ran on the "
|
|
2674
|
+
"device; the evidence column says how strong that is: `construct` "
|
|
2675
|
+
"(the check depends on the construct), `probe` (a negative probe: "
|
|
2676
|
+
"the program names an absent item with the present modifier, so "
|
|
2677
|
+
"the run must end in the runtime's error termination; the cell "
|
|
2678
|
+
"reads 'error termination as required (negative probe)', and a run "
|
|
2679
|
+
"that went on reads 'no error termination (negative probe)'; or a "
|
|
2680
|
+
"compile-output probe: an error directive with at(compilation) whose "
|
|
2681
|
+
"message must appear as a warning, or whose fatal severity must stop "
|
|
2682
|
+
"the compile), "
|
|
2683
|
+
"`copy_mode_only` (the check "
|
|
2684
|
+
"separates the construct from a no-op only at HSA_XNACK=0; the "
|
|
2685
|
+
f"headline cell says so and carries {COPY_MARK}: the real check is "
|
|
2686
|
+
f"the copy-mode column, or the cell says '{COPY_NOT_RUN}'); the "
|
|
2687
|
+
"categories below have no separating oracle and are never credited "
|
|
2688
|
+
"as supported.",
|
|
2689
|
+
f"- **{NO_ORACLE_TEXT['hint_only']}** (`hint_only`): the spec lets "
|
|
2690
|
+
"an implementation ignore the form (a hint, an assertion the program "
|
|
2691
|
+
"makes, or a bound one-at-a-time execution meets); "
|
|
2692
|
+
f"**{NO_ORACLE_TEXT['implementation_defined']}** "
|
|
2693
|
+
"(`implementation_defined`): the spec leaves the behaviour to the "
|
|
2694
|
+
f"implementation; **{NO_ORACLE_TEXT['equivalent_default']}** "
|
|
2695
|
+
"(`equivalent_default`): by the spec the form is the behaviour "
|
|
2696
|
+
"without it. The cell reads so when the program compiled and ran "
|
|
2697
|
+
"cleanly; the spec sentence is in the citation column and in the "
|
|
2698
|
+
"section 'Atoms without a separating oracle'. Where the program "
|
|
2699
|
+
"checks what the form requires (e.g. the number of teams within "
|
|
2700
|
+
"the bounds) a failure reads as a wrong answer.",
|
|
2701
|
+
f"- **{LEGALITY_TEXT}** (`legality`): the atom's only effect is a "
|
|
2702
|
+
"compile-time restriction (default(none)); its oracle is the illegal "
|
|
2703
|
+
"variant in the illegal-variants table.",
|
|
2704
|
+
f"- **{UNVERIFIED_TEXT}** (`unverified`): the spec fixes the "
|
|
2705
|
+
"behaviour and a deterministic check is possible, but the program "
|
|
2706
|
+
"does not have it (the audit disposition says why).",
|
|
2707
|
+
"- **wrong answer (confirmed)**: the atom's oracle is audited valid "
|
|
2708
|
+
"(a mutant without the construct is killed on at least one "
|
|
2709
|
+
"toolchain). **wrong answer (oracle unaudited)** / **oracle "
|
|
2710
|
+
"unaudited**: measured outcome, but no toolchain passes the original "
|
|
2711
|
+
"program, so the oracle is unaudited; not a confirmed failure.",
|
|
2712
|
+
"- **crash: SIGABRT, HSA_XNACK=1 only**: the program aborts in that "
|
|
2713
|
+
"memory mode only (shown instead of a plain wrong answer).",
|
|
2714
|
+
"- **fails in copy mode: needs zero-copy memory**: the program needs "
|
|
2715
|
+
"HSA_XNACK=1; failing at HSA_XNACK=0 is expected and is not a "
|
|
2716
|
+
"feature result.",
|
|
2717
|
+
_stale_audit_note(legal),
|
|
2718
|
+
"- Illegal variants: **rejected** / **not rejected** only; acceptance "
|
|
2719
|
+
"of an illegal program is a neutral observation, never a wrong "
|
|
2720
|
+
"answer.",
|
|
2721
|
+
"",
|
|
2722
|
+
]
|
|
2723
|
+
# summary counts
|
|
2724
|
+
n_copy = len({r.program_id for r in legal if r.xnack == COPY
|
|
2725
|
+
and r.toolchain not in NV})
|
|
2726
|
+
n_legal = len({r.program_id for r in legal})
|
|
2727
|
+
lines += ["## Summary (legal-program cells)", "",
|
|
2728
|
+
"Headline columns count every program; copy-mode columns count "
|
|
2729
|
+
f"only the programs run in copy mode ({n_copy} of {n_legal} "
|
|
2730
|
+
"legal programs).", ""]
|
|
2731
|
+
for v in sorted({r.version for r in legal}):
|
|
2732
|
+
rows = [r for r in legal if r.version == v]
|
|
2733
|
+
fams = Counter((family(r.label), r.toolchain, r.xnack) for r in rows)
|
|
2734
|
+
names = sorted({f for f, _, _ in fams},
|
|
2735
|
+
key=lambda f: (-sum(n for (g, _, _), n in fams.items()
|
|
2736
|
+
if g == f), f))
|
|
2737
|
+
n_atoms = len({r.atom for r in rows})
|
|
2738
|
+
lines += [f"### {_spec(v)} ({n_atoms} atoms)", "",
|
|
2739
|
+
f"| outcome | {_head_modes()} |",
|
|
2740
|
+
"|---|" + "---|" * len(columns())]
|
|
2741
|
+
for f in names:
|
|
2742
|
+
lines.append(f"| {f} | " + " | ".join(
|
|
2743
|
+
str(fams[(f, tc, x)]) for tc, x in columns())
|
|
2744
|
+
+ " |")
|
|
2745
|
+
lines.append("")
|
|
2746
|
+
# illegal summary
|
|
2747
|
+
lines += ["### Illegal variants", "",
|
|
2748
|
+
f"| version | {_head_modes()} |",
|
|
2749
|
+
"|---|" + "---|" * len(columns())]
|
|
2750
|
+
for v in sorted({r.version for r in illegal}):
|
|
2751
|
+
rs = [r for r in illegal if r.version == v]
|
|
2752
|
+
lines.append(f"| {_vstr(v)} | " + " | ".join(
|
|
2753
|
+
f"{sum(1 for r in rs if r.toolchain == tc and r.xnack == x and r.outcome == 'rejected')}"
|
|
2754
|
+
f" rejected / "
|
|
2755
|
+
f"{sum(1 for r in rs if r.toolchain == tc and r.xnack == x and r.outcome == 'not_rejected')}"
|
|
2756
|
+
" not" for tc, x in columns()) + " |")
|
|
2757
|
+
lines.append("")
|
|
2758
|
+
lines += gate_section(table)
|
|
2759
|
+
# An NVIDIA-only table (OpenACC C) has no AMD mode sections
|
|
2760
|
+
amd_rows = any(r.toolchain not in NV for r in table)
|
|
2761
|
+
lines += _portable_section(legal) if amd_rows else []
|
|
2762
|
+
lines += _nv_portable_section(legal)
|
|
2763
|
+
lines += _mode_diff_section(table) if amd_rows else []
|
|
2764
|
+
lines += _nv_mode_diff_section(table)
|
|
2765
|
+
lines += _arch_diff_section(table)
|
|
2766
|
+
lines += _presence_x1_section(legal) if amd_rows else []
|
|
2767
|
+
lines += _claims_section(table, mismatches, cl) if cl else []
|
|
2768
|
+
# per-atom tables
|
|
2769
|
+
for v in sorted({r.version for r in legal}):
|
|
2770
|
+
by_atom: dict[str, list[Row]] = defaultdict(list)
|
|
2771
|
+
for r in legal:
|
|
2772
|
+
if r.version == v:
|
|
2773
|
+
by_atom[r.atom].append(r)
|
|
2774
|
+
suite_head = (" | ".join(f"omp_vv {TC_NAME[tc]}"
|
|
2775
|
+
for tc in MI300A) + " | "
|
|
2776
|
+
if show_suite else "")
|
|
2777
|
+
lines += [f"## {_spec(v)}: legal programs, one row per atom",
|
|
2778
|
+
"",
|
|
2779
|
+
COPY_MARK_TEXT + " " + DAGGER_TEXT]
|
|
2780
|
+
if show_suite:
|
|
2781
|
+
lines += ["omp_vv columns (second source, unaudited): narrow "
|
|
2782
|
+
"omp_vv tests that use the atom (at most one base "
|
|
2783
|
+
"clause), passes / fails by exit code, each test read "
|
|
2784
|
+
"at HSA_XNACK=1 or, when its result depends on copy "
|
|
2785
|
+
"semantics, at HSA_XNACK=0; blank when no narrow test "
|
|
2786
|
+
"uses the atom."]
|
|
2787
|
+
nv_head = "".join(f"{TC_NAME[t]} portable ("
|
|
2788
|
+
+ " and ".join(nv_mode_name(t, x) for x in nv_modes(t)) + ") | "
|
|
2789
|
+
for t in NV)
|
|
2790
|
+
lines += ["",
|
|
2791
|
+
f"| atom | evidence | audit | {_head_modes()} | "
|
|
2792
|
+
f"{suite_head}portable ({PORTABLE_HEAD()}) | {nv_head}"
|
|
2793
|
+
"citation |",
|
|
2794
|
+
"|---|---|---|" + "---|" * len(columns())
|
|
2795
|
+
+ ("---|" * len(MI300A) if show_suite else "")
|
|
2796
|
+
+ "---|" * len(NV) + "---|---|"]
|
|
2797
|
+
for atom in sorted(by_atom):
|
|
2798
|
+
rows = by_atom[atom]
|
|
2799
|
+
if len({r.program_id for r in rows}) > 1:
|
|
2800
|
+
raise ValueError(f"several legal programs for {atom} at {v}")
|
|
2801
|
+
r0 = rows[0]
|
|
2802
|
+
cite, link = atom_citation(atom, v)
|
|
2803
|
+
basis = (f"; basis: {_md(r0.basis.split(': ', 1)[0])}"
|
|
2804
|
+
if r0.basis else "")
|
|
2805
|
+
suite_cells = (" | ".join(_suite_text(suite, atom, tc)
|
|
2806
|
+
for tc in MI300A) + " | "
|
|
2807
|
+
if show_suite else "")
|
|
2808
|
+
lines.append(
|
|
2809
|
+
f"| `{atom.removeprefix(model + '.')}` | {r0.evidence} | "
|
|
2810
|
+
f"{AUDIT_TEXT.get(r0.audit, r0.audit)} | "
|
|
2811
|
+
+ " | ".join(_mode_cells(rows, host)) + " | "
|
|
2812
|
+
+ suite_cells + _portable_cell(rows) + " | "
|
|
2813
|
+
+ "".join(_nv_portable_cell(rows, t) + " | " for t in NV)
|
|
2814
|
+
+ f"[{_md(cite)}]({link}){basis} |")
|
|
2815
|
+
lines.append("")
|
|
2816
|
+
lines += _no_oracle_section(legal)
|
|
2817
|
+
lines += _hostdev_section(hostdev, model, host)
|
|
2818
|
+
lines += _out_of_scope_section(table, model)
|
|
2819
|
+
# illegal detail
|
|
2820
|
+
lines += ["## Illegal variants: one row per rule", "",
|
|
2821
|
+
"Each program violates exactly one rule. Cells say only whether "
|
|
2822
|
+
"the compiler rejected it. " + DAGGER_TEXT,
|
|
2823
|
+
"",
|
|
2824
|
+
f"| version | rule | primary atom | {_head_modes()} | rule citation |",
|
|
2825
|
+
"|---|---|---|" + "---|" * len(columns()) + "---|"]
|
|
2826
|
+
by_prog: dict[str, list[Row]] = defaultdict(list)
|
|
2827
|
+
for r in illegal:
|
|
2828
|
+
by_prog[r.program_id].append(r)
|
|
2829
|
+
for pid in sorted(by_prog, key=lambda p: (by_prog[p][0].version,
|
|
2830
|
+
by_prog[p][0].violates or "")):
|
|
2831
|
+
rows = by_prog[pid]
|
|
2832
|
+
r0 = rows[0]
|
|
2833
|
+
cite, link = rule_citation(r0.violates)
|
|
2834
|
+
lines.append(f"| {_vstr(r0.version)} | {r0.violates} | "
|
|
2835
|
+
f"`{r0.atom.removeprefix(model + '.')}` | "
|
|
2836
|
+
+ " | ".join(_mode_cells(rows, host))
|
|
2837
|
+
+ f" | [{_md(cite)}]({link}) |")
|
|
2838
|
+
lines.append("")
|
|
2839
|
+
# failure details
|
|
2840
|
+
bad = [r for r in legal if family(r.label) not in (
|
|
2841
|
+
"correct on device", "correct on device (copy-mode evidence, HSA_XNACK=1)",
|
|
2842
|
+
NV_COPY_EVIDENCE_FAMILY,
|
|
2843
|
+
"accepted; conforms vacuously (no oracle)",
|
|
2844
|
+
"implementation-defined (no oracle)",
|
|
2845
|
+
"equivalent to the default (no oracle)",
|
|
2846
|
+
"accepted; oracle is the illegal variant", "accepted; unverified",
|
|
2847
|
+
"unobservable")]
|
|
2848
|
+
lines += ["## Failure details (legal programs)", "",
|
|
2849
|
+
"Every legal cell that is not a pass or an unseparable "
|
|
2850
|
+
"acceptance, with its gate status ('-' for a cell "
|
|
2851
|
+
"that is no failure) and the harness reason (clipped). "
|
|
2852
|
+
"Copy-mode "
|
|
2853
|
+
"(X=0) rows exist only for programs run in copy mode.", "",
|
|
2854
|
+
"| version | program | toolchain | X | report | gate | reason |",
|
|
2855
|
+
"|---|---|---|---|---|---|---|"]
|
|
2856
|
+
for r in bad:
|
|
2857
|
+
lines.append(f"| {_vstr(r.version)} | {r.program_id} | "
|
|
2858
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | {r.xnack} | "
|
|
2859
|
+
f"{_md(r.label)} | {r.gate or '-'} | "
|
|
2860
|
+
f"{_md(_clip(r.reason))} |")
|
|
2861
|
+
lines.append("")
|
|
2862
|
+
conf = [r for r in legal if r.confound]
|
|
2863
|
+
lines += ["## Confounded rejections", "",
|
|
2864
|
+
"**Not counted** (against the atom or in the claims): the "
|
|
2865
|
+
"compiler rejected another feature the program needs (a listed "
|
|
2866
|
+
"message), or its diagnostic is at an "
|
|
2867
|
+
+ ("OpenACC" if acc else "OpenMP") + " directive line "
|
|
2868
|
+
"that does not carry the atom. **Counted with a caveat (‡)**: "
|
|
2869
|
+
"the rejection is the atom's own but needs context (a listed "
|
|
2870
|
+
"caveat), or the compiler points at a statement that uses a "
|
|
2871
|
+
"variable, not at the clause. Excluded cells are generator "
|
|
2872
|
+
"follow-ups.", ""]
|
|
2873
|
+
if conf:
|
|
2874
|
+
lines += ["| version | program | toolchain | X | counted | why |",
|
|
2875
|
+
"|---|---|---|---|---|---|"]
|
|
2876
|
+
lines += [f"| {_vstr(r.version)} | {r.program_id} | "
|
|
2877
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | {r.xnack} | "
|
|
2878
|
+
f"{'no' if r.excluded else 'yes ‡'} | "
|
|
2879
|
+
f"{_md(r.confound)} |" for r in conf]
|
|
2880
|
+
else:
|
|
2881
|
+
lines.append("None.")
|
|
2882
|
+
lines.append("")
|
|
2883
|
+
lines += _provisional_section(table)
|
|
2884
|
+
if not acc: # the omp_vv corpus baseline is OpenMP
|
|
2885
|
+
lines += _baseline_section(baseline or [], meta, c_lang)
|
|
2886
|
+
return "\n".join(lines) + "\n"
|
|
2887
|
+
|
|
2888
|
+
|
|
2889
|
+
def PORTABLE_HEAD() -> str:
|
|
2890
|
+
"""What the portable column requires (the gfx90a cell too)."""
|
|
2891
|
+
return "X0, X1 and gfx90a" if G90 else "X0 and X1"
|
|
2892
|
+
|
|
2893
|
+
|
|
2894
|
+
def _portable_cell(rows: list[Row]) -> str:
|
|
2895
|
+
"""The toolchains whose headline cell is portable (correct in both
|
|
2896
|
+
modes), then 'X1 only: ...' for those correct at HSA_XNACK=1 only."""
|
|
2897
|
+
head = [r for r in rows if r.xnack == HEADLINE]
|
|
2898
|
+
order = {t: i for i, t in enumerate(TOOLCHAINS)}
|
|
2899
|
+
head.sort(key=lambda r: order.get(r.toolchain, 9))
|
|
2900
|
+
|
|
2901
|
+
def names(v):
|
|
2902
|
+
return ", ".join(TC_NAME.get(r.toolchain, r.toolchain)
|
|
2903
|
+
+ (f" ({r.mode_class})" if r.mode_class and
|
|
2904
|
+
v != "portable" else "")
|
|
2905
|
+
for r in head if r.portable == v)
|
|
2906
|
+
parts = [names("portable") or "none"]
|
|
2907
|
+
if names("x1_only"):
|
|
2908
|
+
parts.append(f"X1 only: {names('x1_only')}")
|
|
2909
|
+
if names("x0_only"):
|
|
2910
|
+
parts.append(f"X0 only: {names('x0_only')}")
|
|
2911
|
+
if names("x0_not_run"):
|
|
2912
|
+
parts.append(f"no X0 run: {names('x0_not_run')}")
|
|
2913
|
+
if names("gfx942_only"):
|
|
2914
|
+
parts.append(f"MI300A only: {names('gfx942_only')}")
|
|
2915
|
+
return "; ".join(parts)
|
|
2916
|
+
|
|
2917
|
+
|
|
2918
|
+
def _nv_portable_cell(rows: list[Row], tc: str) -> str:
|
|
2919
|
+
"""An NVIDIA toolchain's portable verdict (its headline row),
|
|
2920
|
+
with the mode class when the modes differ."""
|
|
2921
|
+
head = [r for r in rows if r.toolchain == tc
|
|
2922
|
+
and r.xnack == nv_modes(tc)[0]]
|
|
2923
|
+
if not head:
|
|
2924
|
+
return NOT_RUN
|
|
2925
|
+
r = head[0]
|
|
2926
|
+
return r.portable + (f" ({r.mode_class})" if r.mode_class else "")
|
|
2927
|
+
|
|
2928
|
+
|
|
2929
|
+
def _nv_portable_section(legal: list[Row]) -> list[str]:
|
|
2930
|
+
"""Per version, an NVIDIA toolchain's headline cells by
|
|
2931
|
+
portable verdict (correct in separate and in managed memory)."""
|
|
2932
|
+
if not NV:
|
|
2933
|
+
return []
|
|
2934
|
+
out = ["## Portable verdict, NVIDIA (separate and managed memory)", "",
|
|
2935
|
+
", ".join(TC_NAME[t] for t in NV)
|
|
2936
|
+
+ " on an NVIDIA A100 (wingtip, sm_80): the headline "
|
|
2937
|
+
"mode is separate memory (-gpu=mem:separate, the compiler's "
|
|
2938
|
+
"default: separate host and device copies, the discrete-GPU "
|
|
2939
|
+
"mode); managed memory (-gpu=mem:managed) puts dynamically "
|
|
2940
|
+
"allocated data in CUDA managed memory. Unified memory "
|
|
2941
|
+
"(-gpu=mem:unified) is not applicable on this machine (no HMM: "
|
|
2942
|
+
"the runtime refuses to start). Portable = correct in both "
|
|
2943
|
+
"modes. Per toolchain, the legal programs' verdicts: "
|
|
2944
|
+
+ "; ".join(f"**{k}**: {v}" for k, v in NV_PORTABLE.items())
|
|
2945
|
+
+ ".", ""]
|
|
2946
|
+
head = [r for r in legal if r.toolchain in NV
|
|
2947
|
+
and r.xnack == nv_modes(r.toolchain)[0]]
|
|
2948
|
+
for v in sorted({r.version for r in head}):
|
|
2949
|
+
rs = [r for r in head if r.version == v]
|
|
2950
|
+
c = Counter((r.portable, r.toolchain) for r in rs)
|
|
2951
|
+
out += [f"### {_spec(v)}", "",
|
|
2952
|
+
"| verdict | " + " | ".join(TC_NAME[t] for t in NV)
|
|
2953
|
+
+ " |", "|---|" + "---|" * len(NV)]
|
|
2954
|
+
for k in NV_PORTABLE:
|
|
2955
|
+
if any(c[(k, t)] for t in NV):
|
|
2956
|
+
out.append(f"| {k} | " + " | ".join(
|
|
2957
|
+
str(c[(k, t)]) for t in NV) + " |")
|
|
2958
|
+
out.append("")
|
|
2959
|
+
return out
|
|
2960
|
+
|
|
2961
|
+
|
|
2962
|
+
def _nv_mode_diff_section(table: list[Row]) -> list[str]:
|
|
2963
|
+
"""Every legal NVIDIA headline cell whose separate and
|
|
2964
|
+
managed cells differ, with the class of the difference."""
|
|
2965
|
+
if not NV:
|
|
2966
|
+
return []
|
|
2967
|
+
rows = [r for r in table if r.legal and r.mode_class
|
|
2968
|
+
and r.toolchain in NV]
|
|
2969
|
+
cells = {(r.program_id, r.toolchain, r.xnack): r for r in table}
|
|
2970
|
+
out = ["## Memory-mode differences, NVIDIA (separate versus managed)",
|
|
2971
|
+
"",
|
|
2972
|
+
"Legal programs whose NVHPC outcome differs between separate and "
|
|
2973
|
+
"managed memory, with the class of the difference "
|
|
2974
|
+
"(catalog/mode_differences.yaml nvhpc_entries): "
|
|
2975
|
+
+ "; ".join(f"**{k}**: {v}" for k, v in NV_MODE_CLASSES.items())
|
|
2976
|
+
+ "; **unclassified**: no entry yet.", ""]
|
|
2977
|
+
if not rows:
|
|
2978
|
+
return out + ["None.", ""]
|
|
2979
|
+
c = Counter((r.mode_class, r.toolchain) for r in rows)
|
|
2980
|
+
out += ["| class | " + " | ".join(TC_NAME[t] for t in NV) + " |",
|
|
2981
|
+
"|---|" + "---|" * len(NV)]
|
|
2982
|
+
for k in list(NV_MODE_CLASSES) + ["unclassified"]:
|
|
2983
|
+
if any(c[(k, t)] for t in NV):
|
|
2984
|
+
out.append(f"| {k} | " + " | ".join(str(c[(k, t)]) for t in NV)
|
|
2985
|
+
+ " |")
|
|
2986
|
+
out += ["", "| version | program | toolchain | separate | managed | "
|
|
2987
|
+
"class | finding / why |", "|---|---|---|---|---|---|---|"]
|
|
2988
|
+
for r in rows:
|
|
2989
|
+
e = nv_mode_entry(r.program_id, r.toolchain) or {}
|
|
2990
|
+
m = cells.get((r.program_id, r.toolchain, nv_modes(r.toolchain)[1]))
|
|
2991
|
+
why = (f"data/findings/{e['finding']}; " if e.get("finding")
|
|
2992
|
+
else "") + " ".join(str(e.get("why", "")).split())
|
|
2993
|
+
out.append(f"| {_vstr(r.version)} | {r.program_id} | "
|
|
2994
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | "
|
|
2995
|
+
f"{_md(r.label)} | {_md(m.label if m else '')} | "
|
|
2996
|
+
f"{r.mode_class} | {_md(_clip(why, 300))} |")
|
|
2997
|
+
return out + [""]
|
|
2998
|
+
|
|
2999
|
+
|
|
3000
|
+
def _portable_section(legal: list[Row]) -> list[str]:
|
|
3001
|
+
"""Per version, the headline cells by portable verdict and
|
|
3002
|
+
toolchain."""
|
|
3003
|
+
out = ["## Portable verdict (correct at HSA_XNACK=0 and 1"
|
|
3004
|
+
+ (" and on gfx90a)" if G90 else ")"), "",
|
|
3005
|
+
"The result a user can rely on for portable code (a discrete GPU "
|
|
3006
|
+
"has separate host and device memory) is **correct in both "
|
|
3007
|
+
"memory modes**: at HSA_XNACK=1 (unified memory) a missing or "
|
|
3008
|
+
"wrong map often still gives the right answer, so a mapping "
|
|
3009
|
+
"error shows only at HSA_XNACK=0. "
|
|
3010
|
+
+ ("**and on the discrete GPU**, Tioga's MI250X "
|
|
3011
|
+
"(gfx90a, HSA_XNACK=0, separate HBM): portable = correct on "
|
|
3012
|
+
"MI300A at HSA_XNACK=1, at HSA_XNACK=0 and on gfx90a. "
|
|
3013
|
+
if G90 else "")
|
|
3014
|
+
+ "Per toolchain, the legal "
|
|
3015
|
+
"programs' verdicts: "
|
|
3016
|
+
+ "; ".join(f"**{k}**: {v}" for k, v in PORTABLE.items())
|
|
3017
|
+
+ ".", ""]
|
|
3018
|
+
head = [r for r in legal if r.xnack == HEADLINE]
|
|
3019
|
+
for v in sorted({r.version for r in head}):
|
|
3020
|
+
rs = [r for r in head if r.version == v]
|
|
3021
|
+
c = Counter((r.portable, r.toolchain) for r in rs)
|
|
3022
|
+
out += [f"### {_spec(v)}", "",
|
|
3023
|
+
"| verdict | " + " | ".join(TC_NAME[t] for t in TOOLCHAINS)
|
|
3024
|
+
+ " |", "|---|" + "---|" * len(TOOLCHAINS)]
|
|
3025
|
+
for k in PORTABLE:
|
|
3026
|
+
if any(c[(k, t)] for t in TOOLCHAINS):
|
|
3027
|
+
out.append(f"| {k} | " + " | ".join(
|
|
3028
|
+
str(c[(k, t)]) for t in TOOLCHAINS) + " |")
|
|
3029
|
+
out.append("")
|
|
3030
|
+
return out
|
|
3031
|
+
|
|
3032
|
+
|
|
3033
|
+
def _mode_diff_section(table: list[Row]) -> list[str]:
|
|
3034
|
+
"""Every legal headline cell whose two modes differ, with the
|
|
3035
|
+
class of the difference (catalog/mode_differences.yaml)."""
|
|
3036
|
+
rows = [r for r in table if r.legal and r.mode_class
|
|
3037
|
+
and r.toolchain not in NV]
|
|
3038
|
+
copy = {(r.program_id, r.toolchain): r for r in table
|
|
3039
|
+
if r.xnack == COPY}
|
|
3040
|
+
c = Counter((r.mode_class, r.toolchain) for r in rows)
|
|
3041
|
+
out = ["## Memory-mode differences", "",
|
|
3042
|
+
"Legal programs (device and host+device) whose outcome differs "
|
|
3043
|
+
"between HSA_XNACK=1 and HSA_XNACK=0 on a toolchain, with the "
|
|
3044
|
+
"class of the difference (catalog/mode_differences.yaml): "
|
|
3045
|
+
+ "; ".join(f"**{k}**: {v}" for k, v in MODE_CLASSES.items())
|
|
3046
|
+
+ "; **unclassified**: no entry yet.", ""]
|
|
3047
|
+
if not rows:
|
|
3048
|
+
return out + ["None.", ""]
|
|
3049
|
+
out += ["| class | " + " | ".join(TC_NAME[t] for t in TOOLCHAINS)
|
|
3050
|
+
+ " |", "|---|" + "---|" * len(TOOLCHAINS)]
|
|
3051
|
+
for k in list(MODE_CLASSES) + ["unclassified"]:
|
|
3052
|
+
if any(c[(k, t)] for t in TOOLCHAINS):
|
|
3053
|
+
out.append(f"| {k} | " + " | ".join(str(c[(k, t)])
|
|
3054
|
+
for t in TOOLCHAINS) + " |")
|
|
3055
|
+
out += ["", "| version | program | toolchain | X=1 | X=0 | class | "
|
|
3056
|
+
"finding / why |", "|---|---|---|---|---|---|---|"]
|
|
3057
|
+
for r in rows:
|
|
3058
|
+
e = mode_entry(r.program_id, r.toolchain) or {}
|
|
3059
|
+
x0 = copy.get((r.program_id, r.toolchain))
|
|
3060
|
+
why = (f"data/findings/{e['finding']}; " if e.get("finding")
|
|
3061
|
+
else "") + " ".join(str(e.get("why", "")).split())
|
|
3062
|
+
out.append(f"| {_vstr(r.version)} | {r.program_id} | "
|
|
3063
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | "
|
|
3064
|
+
f"{_md(r.label)} | {_md(x0.label if x0 else '')} | "
|
|
3065
|
+
f"{r.mode_class} | {_md(_clip(why, 300))} |")
|
|
3066
|
+
return out + [""]
|
|
3067
|
+
|
|
3068
|
+
|
|
3069
|
+
def _arch_diff_section(table: list[Row]) -> list[str]:
|
|
3070
|
+
"""Every legal headline cell whose MI300A copy-mode cell and
|
|
3071
|
+
gfx90a cell differ, with the class of the difference."""
|
|
3072
|
+
if not G90:
|
|
3073
|
+
return []
|
|
3074
|
+
rows = [r for r in table if r.legal and r.arch_class]
|
|
3075
|
+
cells = {(r.program_id, r.toolchain, r.xnack): r for r in table}
|
|
3076
|
+
pairs = [r for r in table if r.legal and r.xnack == HEADLINE
|
|
3077
|
+
and (r.program_id, g90_of(r.toolchain), COPY) in cells
|
|
3078
|
+
and (r.program_id, r.toolchain, COPY) in cells]
|
|
3079
|
+
out = ["## gfx90a (MI250X) versus gfx942 (MI300A) copy mode", "",
|
|
3080
|
+
"Both run with separate host and device memory (HSA_XNACK=0); "
|
|
3081
|
+
"the MI250X is a discrete GPU (its own HBM), the MI300A an APU "
|
|
3082
|
+
"whose copy mode emulates one. Legal programs (device and "
|
|
3083
|
+
"host+device) whose gfx90a outcome differs from the MI300A "
|
|
3084
|
+
f"copy-mode outcome: {len(rows)} of {len(pairs)} compared "
|
|
3085
|
+
"cells, with the class of the difference "
|
|
3086
|
+
"(catalog/mode_differences.yaml, arch_entries): "
|
|
3087
|
+
+ "; ".join(f"**{k}**: {v}" for k, v in ARCH_CLASSES.items())
|
|
3088
|
+
+ "; **unclassified**: no entry yet.", ""]
|
|
3089
|
+
if not rows:
|
|
3090
|
+
return out + ["None.", ""]
|
|
3091
|
+
c = Counter((r.arch_class, r.toolchain) for r in rows)
|
|
3092
|
+
out += ["| class | " + " | ".join(TC_NAME[t] for t in TOOLCHAINS)
|
|
3093
|
+
+ " |", "|---|" + "---|" * len(TOOLCHAINS)]
|
|
3094
|
+
for k in list(ARCH_CLASSES) + ["unclassified"]:
|
|
3095
|
+
if any(c[(k, t)] for t in TOOLCHAINS):
|
|
3096
|
+
out.append(f"| {k} | " + " | ".join(str(c[(k, t)])
|
|
3097
|
+
for t in TOOLCHAINS) + " |")
|
|
3098
|
+
out += ["", "| version | program | toolchain | MI300A X=1 | MI300A X=0 "
|
|
3099
|
+
"| gfx90a | class | finding / why |",
|
|
3100
|
+
"|---|---|---|---|---|---|---|---|"]
|
|
3101
|
+
for r in rows:
|
|
3102
|
+
e = arch_entry(r.program_id, r.toolchain) or {}
|
|
3103
|
+
x0 = cells.get((r.program_id, r.toolchain, COPY))
|
|
3104
|
+
g = cells.get((r.program_id, g90_of(r.toolchain), COPY))
|
|
3105
|
+
why = (f"data/findings/{e['finding']}; " if e.get("finding")
|
|
3106
|
+
else "") + " ".join(str(e.get("why", "")).split())
|
|
3107
|
+
out.append(f"| {_vstr(r.version)} | {r.program_id} | "
|
|
3108
|
+
f"{TC_NAME.get(r.toolchain, r.toolchain)} | "
|
|
3109
|
+
f"{_md(r.label)} | {_md(x0.label if x0 else '')} | "
|
|
3110
|
+
f"{_md(g.label if g else '')} | {r.arch_class} | "
|
|
3111
|
+
f"{_md(_clip(why, 300))} |")
|
|
3112
|
+
return out + [""]
|
|
3113
|
+
|
|
3114
|
+
|
|
3115
|
+
def _presence_x1_section(legal: list[Row]) -> list[str]:
|
|
3116
|
+
"""Fix: the copy-mode programs whose presence checks were run
|
|
3117
|
+
at HSA_XNACK=1 too, and the cells where they fail there."""
|
|
3118
|
+
rs = [r for r in legal if r.xnack == HEADLINE and r.presence_x1]
|
|
3119
|
+
out = ["## Presence checks at HSA_XNACK=1", "",
|
|
3120
|
+
"A copy_mode_only program skips its checks under unified memory, "
|
|
3121
|
+
"so its HSA_XNACK=1 cell is no evidence. Presence "
|
|
3122
|
+
"(omp_target_is_present) is defined in both modes, so "
|
|
3123
|
+
"tools/campaigns/presence_x1.py ran each program with only its presence "
|
|
3124
|
+
"checks unguarded at HSA_XNACK=1 (tests/data/"
|
|
3125
|
+
"presence_x1_summary.json). A failure there makes the cell not "
|
|
3126
|
+
"portable (the portable verdict counts it as an HSA_XNACK=1 "
|
|
3127
|
+
"failure).", ""]
|
|
3128
|
+
if not rs:
|
|
3129
|
+
return out + ["None.", ""]
|
|
3130
|
+
c = Counter((r.toolchain, r.presence_x1) for r in rs)
|
|
3131
|
+
outs = sorted({r.presence_x1 for r in rs})
|
|
3132
|
+
out += ["| outcome | " + " | ".join(TC_NAME[t] for t in TOOLCHAINS)
|
|
3133
|
+
+ " |", "|---|" + "---|" * len(TOOLCHAINS)]
|
|
3134
|
+
out += [f"| {o} | " + " | ".join(str(c[(t, o)]) for t in TOOLCHAINS)
|
|
3135
|
+
+ " |" for o in outs]
|
|
3136
|
+
bad = [r for r in rs if r.presence_x1 == "wrong_answer"
|
|
3137
|
+
and r.evidence == "copy_mode_only"]
|
|
3138
|
+
out += ["", "Cells whose HSA_XNACK=1 'correct (copy-mode evidence)' "
|
|
3139
|
+
"hides a failing presence check: "
|
|
3140
|
+
+ (", ".join(f"{r.program_id} ({TC_NAME.get(r.toolchain, r.toolchain)})"
|
|
3141
|
+
for r in bad) or "none") + ".", ""]
|
|
3142
|
+
return out
|
|
3143
|
+
|
|
3144
|
+
|
|
3145
|
+
def _hostdev_section(rows: list[Row], model: str, host=None) -> list[str]:
|
|
3146
|
+
"""The host+device pattern programs, one row per program,
|
|
3147
|
+
apart from the headline per-atom tables."""
|
|
3148
|
+
out = ["## Host+device patterns (not headline results)", "",
|
|
3149
|
+
"Host constructs that feed a "
|
|
3150
|
+
+ ("compute" if model == "acc" else "target") + " region the way "
|
|
3151
|
+
"codes use them (an exception to the device-offload scope), one program per "
|
|
3152
|
+
"pattern (id ending in -hostdev). Each is judged on its device "
|
|
3153
|
+
"result (the check and ran_on_host of the device region); it is "
|
|
3154
|
+
"not the atom's oracle (no mutation audit) and never counts as "
|
|
3155
|
+
"the atom's support. The atom's headline is its device-only "
|
|
3156
|
+
"program in the tables above.", ""]
|
|
3157
|
+
if not rows:
|
|
3158
|
+
return out + ["None.", ""]
|
|
3159
|
+
out += [f"| version | atom | program | realistic use | {_head_modes()} |",
|
|
3160
|
+
"|---|---|---|---|" + "---|" * len(columns())]
|
|
3161
|
+
by: dict[str, list[Row]] = defaultdict(list)
|
|
3162
|
+
for r in rows:
|
|
3163
|
+
by[r.program_id].append(r)
|
|
3164
|
+
for pid in sorted(by, key=lambda p: (by[p][0].version, p)):
|
|
3165
|
+
rs = by[pid]
|
|
3166
|
+
r0 = rs[0]
|
|
3167
|
+
out.append(f"| {_vstr(r0.version)} | "
|
|
3168
|
+
f"`{r0.atom.removeprefix(model + '.')}` | {pid} | "
|
|
3169
|
+
f"{_md(r0.use)} | " + " | ".join(_mode_cells(rs, host))
|
|
3170
|
+
+ " |")
|
|
3171
|
+
return out + [""]
|
|
3172
|
+
|
|
3173
|
+
|
|
3174
|
+
def _out_of_scope_section(table: list[Row], model: str) -> list[str]:
|
|
3175
|
+
"""The atoms removed from the slice as host tests
|
|
3176
|
+
(catalog/<model>/out_of_scope.yaml), with the reason and the spec
|
|
3177
|
+
sentence."""
|
|
3178
|
+
from offload.engine.catalog import out_of_scope_atoms
|
|
3179
|
+
lang = table_lang(table) if table else "f90"
|
|
3180
|
+
out = ["## Out of scope (host execution)", "",
|
|
3181
|
+
"Atoms whose construct of interest runs only on the host, or "
|
|
3182
|
+
"whose effect never reaches a "
|
|
3183
|
+
+ ("compute" if model == "acc" else "target") + " region: not "
|
|
3184
|
+
"device offload, so they have no program, no cell and no "
|
|
3185
|
+
f"verdict here (catalog/{model}/out_of_scope.yaml; each sentence "
|
|
3186
|
+
"is checked against the spec text).", "",
|
|
3187
|
+
"| version | atom | reason | spec |", "|---|---|---|---|"]
|
|
3188
|
+
n = 0
|
|
3189
|
+
for v in sorted({r.version for r in table}):
|
|
3190
|
+
for atom, e in sorted(out_of_scope_atoms(v, lang).items()):
|
|
3191
|
+
sp = e["spec"].get(v) or {}
|
|
3192
|
+
out.append(f"| {_vstr(v)} | `{atom.removeprefix(model + '.')}` "
|
|
3193
|
+
f"| {_md(' '.join(e['reason'].split()))} | "
|
|
3194
|
+
f"{_md(sp.get('cite', ''))}: \"{_md(sp.get('quote', ''))}\" |")
|
|
3195
|
+
n += 1
|
|
3196
|
+
if not n:
|
|
3197
|
+
out.append("| - | none | - | - |")
|
|
3198
|
+
return out + [""]
|
|
3199
|
+
|
|
3200
|
+
|
|
3201
|
+
def _no_oracle_section(legal: list[Row]) -> list[str]:
|
|
3202
|
+
"""One row per (version, atom) whose program has no separating oracle
|
|
3203
|
+
, with the spec sentence that makes it so."""
|
|
3204
|
+
seen: dict[tuple, Row] = {}
|
|
3205
|
+
for r in legal:
|
|
3206
|
+
if r.evidence in NO_ORACLE_TEXT:
|
|
3207
|
+
seen.setdefault((r.version, r.atom), r)
|
|
3208
|
+
model = table_model(legal)
|
|
3209
|
+
out = ["## Atoms without a separating oracle", "",
|
|
3210
|
+
"No single run can tell these forms from their absence, and the "
|
|
3211
|
+
f"spec says why (catalog/{model}/no_oracle.yaml; every sentence is "
|
|
3212
|
+
"checked against the spec text). Their cells read as the "
|
|
3213
|
+
"category, never as support; a rejection or a wrong answer keeps "
|
|
3214
|
+
"its outcome.", "",
|
|
3215
|
+
"| version | atom | category | spec basis |", "|---|---|---|---|"]
|
|
3216
|
+
for (v, atom), r in sorted(seen.items()):
|
|
3217
|
+
out.append(f"| {_vstr(v)} | `{atom.removeprefix(model + '.')}` | "
|
|
3218
|
+
f"{NO_ORACLE_TEXT[r.evidence]} | {_md(r.basis)} |")
|
|
3219
|
+
if not seen:
|
|
3220
|
+
out.append("| - | none | - | - |")
|
|
3221
|
+
return out + [""]
|
|
3222
|
+
|
|
3223
|
+
|
|
3224
|
+
def read_baseline(path: Path) -> list[dict]:
|
|
3225
|
+
p = Path(path)
|
|
3226
|
+
if not p.is_file():
|
|
3227
|
+
return []
|
|
3228
|
+
return [json.loads(line) for line in p.read_text().splitlines()
|
|
3229
|
+
if line.strip()]
|
|
3230
|
+
|
|
3231
|
+
|
|
3232
|
+
def _is_g90_run(rr: dict) -> bool:
|
|
3233
|
+
"""A recorded run of gfx90a toolchains only."""
|
|
3234
|
+
tcs = rr.get("toolchains") or []
|
|
3235
|
+
return bool(tcs) and all(arch_of(t) == "gfx90a" for t in tcs)
|
|
3236
|
+
|
|
3237
|
+
|
|
3238
|
+
def _twin_machine(rr: dict) -> str | None:
|
|
3239
|
+
"""The machine of a recorded run of twins of one machine other than Tioga
|
|
3240
|
+
(MI300X, MI350X) only, else None."""
|
|
3241
|
+
reg = _reg()
|
|
3242
|
+
tcs = [reg.toolchains.get(t) for t in rr.get("toolchains") or []]
|
|
3243
|
+
ms = {t.machine for t in tcs if t and t.twin_of and reg.twin_tag(t.name) != "gfx90a"}
|
|
3244
|
+
return ms.pop() if tcs and len(ms) == 1 and all(
|
|
3245
|
+
t and t.twin_of and t.machine in ms for t in tcs) else None
|
|
3246
|
+
|
|
3247
|
+
|
|
3248
|
+
def _is_nv_run(rr: dict) -> bool:
|
|
3249
|
+
"""A recorded run of NVIDIA toolchains only: kept apart
|
|
3250
|
+
from the AMD modes' runs."""
|
|
3251
|
+
tcs = rr.get("toolchains") or []
|
|
3252
|
+
return bool(tcs) and all(t in NVIDIA_TOOLCHAINS for t in tcs)
|
|
3253
|
+
|
|
3254
|
+
|
|
3255
|
+
def read_meta(results: Path) -> dict:
|
|
3256
|
+
"""Run metadata per memory mode from <results>.xnack{0,1}.jsonl.meta.json
|
|
3257
|
+
and the ROCm versions recorded with CCE results."""
|
|
3258
|
+
meta: dict = {}
|
|
3259
|
+
results = Path(results)
|
|
3260
|
+
for x in MODES:
|
|
3261
|
+
p = results.with_name(f"{results.stem}.xnack{x}.jsonl.meta.json")
|
|
3262
|
+
if p.is_file():
|
|
3263
|
+
meta[x] = json.loads(p.read_text())
|
|
3264
|
+
stem = "baseline_c" if results.stem.endswith("_c") else "baseline"
|
|
3265
|
+
b = results.with_name(f"{stem}.xnack{x}.jsonl.meta.json")
|
|
3266
|
+
if b.is_file():
|
|
3267
|
+
meta[f"baseline{x}"] = json.loads(b.read_text())
|
|
3268
|
+
# reruns written straight into the merged matrix (run_matrix.py rerun
|
|
3269
|
+
# --out results/matrix.jsonl) are recorded in its own meta file; each
|
|
3270
|
+
# joins its mode's reruns once, in start order
|
|
3271
|
+
merged = results.with_name(f"{results.name}.meta.json")
|
|
3272
|
+
if merged.is_file():
|
|
3273
|
+
for rr in json.loads(merged.read_text()).get("reruns", []):
|
|
3274
|
+
x = rr.get("xnack")
|
|
3275
|
+
if _is_nv_run(rr): # Wingtip rows, not an AMD mode's
|
|
3276
|
+
meta.setdefault("nvhpc", {}).setdefault("reruns", []).append(
|
|
3277
|
+
rr)
|
|
3278
|
+
continue
|
|
3279
|
+
if _twin_machine(rr): # MI300X, MI350X rows, not a mode's
|
|
3280
|
+
meta.setdefault("twins", {}).setdefault(_twin_machine(rr), []).append(rr)
|
|
3281
|
+
continue
|
|
3282
|
+
if x not in MODES:
|
|
3283
|
+
continue
|
|
3284
|
+
if _is_g90_run(rr): # Tioga rows, not a mode's
|
|
3285
|
+
meta.setdefault("g90", {}).setdefault("reruns", []).append(
|
|
3286
|
+
rr)
|
|
3287
|
+
continue
|
|
3288
|
+
m = meta.setdefault(x, {})
|
|
3289
|
+
have = m.setdefault("reruns", [])
|
|
3290
|
+
if any(h.get("started") == rr.get("started") and
|
|
3291
|
+
h.get("host") == rr.get("host") for h in have):
|
|
3292
|
+
continue
|
|
3293
|
+
have.append(rr)
|
|
3294
|
+
have.sort(key=lambda r: r.get("started") or "")
|
|
3295
|
+
# Baseline rows replaced in the merged baseline file
|
|
3296
|
+
# (run_matrix.py replace --baseline) are recorded in its meta file
|
|
3297
|
+
stem = "baseline_c" if results.stem.endswith("_c") else "baseline"
|
|
3298
|
+
bmerged = results.with_name(f"{stem}.jsonl.meta.json")
|
|
3299
|
+
if bmerged.is_file():
|
|
3300
|
+
for rr in json.loads(bmerged.read_text()).get("reruns", []):
|
|
3301
|
+
x = rr.get("xnack")
|
|
3302
|
+
if _is_nv_run(rr):
|
|
3303
|
+
meta.setdefault("baseline_nvhpc", {}).setdefault(
|
|
3304
|
+
"reruns", []).append(rr)
|
|
3305
|
+
continue
|
|
3306
|
+
if x not in MODES:
|
|
3307
|
+
continue
|
|
3308
|
+
if _is_g90_run(rr):
|
|
3309
|
+
meta.setdefault("baseline_g90", {}).setdefault(
|
|
3310
|
+
"reruns", []).append(rr)
|
|
3311
|
+
continue
|
|
3312
|
+
have = meta.setdefault(f"baseline{x}", {}).setdefault(
|
|
3313
|
+
"reruns", [])
|
|
3314
|
+
if not any(h.get("started") == rr.get("started") and
|
|
3315
|
+
h.get("host") == rr.get("host") for h in have):
|
|
3316
|
+
have.append(rr)
|
|
3317
|
+
if results.is_file():
|
|
3318
|
+
meta["rocm"] = sorted({json.loads(line).get("rocm") or ""
|
|
3319
|
+
for line in results.read_text().splitlines()
|
|
3320
|
+
if line.strip()} - {""})
|
|
3321
|
+
return meta
|
|
3322
|
+
|
|
3323
|
+
|
|
3324
|
+
def write_report(results: Path = MATRIX, claims: Path = CLAIMS,
|
|
3325
|
+
audit: Path = AUDIT, baseline: Path = BASELINE,
|
|
3326
|
+
out_md: Path | None = None, out_csv: Path | None = None
|
|
3327
|
+
) -> tuple[list[Row], list[Mismatch]]:
|
|
3328
|
+
"""Build the table of `results` and write its Markdown and CSV (C
|
|
3329
|
+
results, results/matrix_c.jsonl with C_AUDIT and
|
|
3330
|
+
C_BASELINE)."""
|
|
3331
|
+
results = Path(results)
|
|
3332
|
+
# A changed canary of the campaign stops the report
|
|
3333
|
+
from offload.engine import canary
|
|
3334
|
+
canary.require_unchanged(
|
|
3335
|
+
canary.files_for(results), str(results.name),
|
|
3336
|
+
campaign_rows=[json.loads(x) for x in results.read_text()
|
|
3337
|
+
.splitlines() if x.strip()]
|
|
3338
|
+
if results.is_file() else None)
|
|
3339
|
+
out_md = out_md or results.with_name("support_table.md")
|
|
3340
|
+
out_csv = out_csv or results.with_name("support_table.csv")
|
|
3341
|
+
table = build_table(results, audit=audit)
|
|
3342
|
+
bad = sorted({(r.program_id, r.toolchain) for r in table
|
|
3343
|
+
if r.mode_class == "unclassified"})
|
|
3344
|
+
if bad:
|
|
3345
|
+
raise ValueError(f"unclassified memory-mode differences (add them "
|
|
3346
|
+
f"to {MODE_DIFF_FILE.name}; NVIDIA: nvhpc_entries)"
|
|
3347
|
+
f": {bad}")
|
|
3348
|
+
bad = sorted({f"{r.program_id} {r.toolchain}: {why}" for r in table
|
|
3349
|
+
if r.mode_class and r.mode_class != "unclassified" and
|
|
3350
|
+
(why := (nv_mode_class_problem if r.toolchain in NV else
|
|
3351
|
+
mode_class_problem)(r.mode_class, r.label,
|
|
3352
|
+
r.copy_label))})
|
|
3353
|
+
if bad:
|
|
3354
|
+
raise ValueError(f"memory-mode classes the cells contradict "
|
|
3355
|
+
f"(fix {MODE_DIFF_FILE.name}): {bad}")
|
|
3356
|
+
bad = sorted({(r.program_id, r.toolchain) for r in table
|
|
3357
|
+
if r.arch_class == "unclassified"})
|
|
3358
|
+
if bad:
|
|
3359
|
+
raise ValueError(f"unclassified gfx90a versus gfx942 differences "
|
|
3360
|
+
f"(add them to {MODE_DIFF_FILE.name} "
|
|
3361
|
+
f"arch_entries): {bad}")
|
|
3362
|
+
mism = compare_claims(table, claims)
|
|
3363
|
+
base = read_baseline(baseline)
|
|
3364
|
+
meta = read_meta(results)
|
|
3365
|
+
from offload.engine.ompvv import suite_verdicts
|
|
3366
|
+
suite = suite_verdicts(base, {r.atom for r in table})
|
|
3367
|
+
md = render_markdown(table, mism, claims=claims, baseline=base,
|
|
3368
|
+
meta=meta, suite=suite)
|
|
3369
|
+
comp = companion_section(results)
|
|
3370
|
+
if comp:
|
|
3371
|
+
md = md.rstrip("\n") + "\n\n" + "\n".join(comp)
|
|
3372
|
+
Path(out_md).write_text(md)
|
|
3373
|
+
Path(out_csv).write_text(render_csv(table, suite=suite, meta=meta))
|
|
3374
|
+
return table, mism
|