Skip to content

hipblaslt: replace the gfx1250v0 revision workaround with gfx1250-strict - #11777

Open
geotseng-amd wants to merge 10 commits into
ROCm:developfrom
geotseng-amd:users/geotseng/develop-gfx1250-strict
Open

geotseng-amd wants to merge 10 commits into
ROCm:developfrom
geotseng-amd:users/geotseng/develop-gfx1250-strict

Conversation

@geotseng-amd

@geotseng-amd geotseng-amd commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fan out the build when two architectures share an ISA

Follow-up to #10784, which
introduced gfx1250-strict as an ordinary architecture name. That PR left one
special case behind in CMake; this one removes it.

Motivation

gfx1250-strict is a silicon stepping of gfx1250. The two share the ISA tuple
(12, 5, 0) but are separate compiler targets producing different machine code
(ELF EF_AMDGPU_MACH 0x49 vs 0xEB), and a code object built for one is
rejected on the other.

Tensile keys its capability map by ISA, so a single TensileCreateLibrary
invocation cannot describe both. #10784 worked around that in the build system:
device-library/CMakeLists.txt peeled gfx1250-strict into a second
hipblaslt_create_device_library call with its own OUTPUT_DIR, and the
top-level lists carried an extra install rule driven by a global property. That
put an architecture name in the build system and would need extending by hand
for the next stepping.

This PR moves the split into TensileCreateLibrary, where the knowledge of
which names collide already lives. CMake goes back to a single call with
${GPU_TARGETS}.

The ISA-keyed machinery itself is deliberately left alone. Re-keying it by
architecture name would touch ~88 isaInfoMap[...] subscripts across 24 files
plus two rocisa C++ sites and the nanobind ABI, on every architecture's code
path. This PR separates the two architectures only where their outputs would
otherwise be written to the same place.

Technical Details

Fan-out

isaCollisionFreeGroups() partitions the requested names so no group holds two
that spell one ISA. A run handed more than one group re-executes itself once per
group as a subprocess -- separate processes rather than a loop, because a run
settles arch-dependent state process-wide.

A single group takes the old path untouched: no subprocess, and no change to
parallelism, scratch naming or cleanup. That covers every build naming no
stepping, which is every build that exists today apart from gfx1250's.

Jobs are split by architecture count rather than evenly. What forces a second
group is one stepping colliding, which leaves that group holding a single
architecture and the other holding the rest; an even split would run the large
group at half speed and idle half the machine once the small one finished.

Scratch directories

The groups run concurrently into one shared --output-path, and everything they
write outside library/ would collide there. Kernel basenames are derived from
the ISA -- getParameterValueAbbreviation renders (12, 5, 0) as "1250" for
both architectures -- so both runs name their .s and .o identically while the
machine code inside differs. The fixed-name artifacts (Kernels.cpp,
Kernels.h, the six static headers, code_object_tmp/Kernels.o) carry no
architecture at all, and the static headers #include each other by literal
name inside shipped C headers, so no naming convention can separate them.

Scratch therefore becomes build_tmp/<STEM>-<stepping> for a run that was asked
for a stepping, and stays build_tmp/<STEM> for every run that was not -- so
architecture sets that predate steppings keep the directory they had. Fan-out
children carry TENSILE_GROUP_BUILD so they reclaim only their own
subdirectory and leave a concurrent sibling's alone; a standalone run still
removes build_tmp whole.

Benchmark cache key (correctness fix)

The tuning cache key gains the compiler targets. Two steppings agree on every
other field the key covers, so without them the second run would load the
first's code objects and hand the wrong machine code to silicon that cannot run
it.

_loadLegacyCacheIfMatches is removed rather than kept as a fallback: that path
is keyed by directory alone, so it would bypass the new field and reintroduce
exactly the mismatch.

Architecture name matching

Tuning is selected by ArchitectureName, which a stepping spells as itself, so
gfx1250-strict logic is no longer absorbed into gfx1250.

archMatch() compares whole names for that reason -- a prefix comparison would
let gfx1250 claim a request for gfx1250-strict. It also strips a bracketed
predicate as well as a qualifier: TensileLogic filters with GPU_TARGETS
exactly as CMake passed it, having never split predicates off, so a whole-name
comparison alone would fail gfx950[cu=64] against its own logic and stop the
build. Both halves are load-bearing, and the test suite pins each direction
independently.

steppingArchOf() derives a stepping rather than taking it from a table: a name
is a stepping exactly when it does not survive a round trip through its ISA. A
future stepping needs no registration.

Removed knobs

With the stepping named for itself throughout, HIPBLASLT_ASIC_REVISION and
--asic-revision have nothing left to select and are removed. Picking a
revision is now spelling it in GPU_TARGETS.

Packaging checks

validate_library_layout.py and check_dat_integrity.py accepted only
alphanumerics in an architecture token, so a stepping's files matched nothing.
An unmatched file leaves its subtree without masters, which the scanner reads as
a subtree to skip -- so the failure mode was a package passing silently rather
than an error. The token now admits hyphens.

Reproducibility

Logic files are merged in sorted order. Set iteration and readdir order were
leaking salted string hashing into the solution indices written into the master
library, so a build was not reproducible run-to-run. See "Deliberate behaviour
changes" below for the consequence.

Test Plan

Full procedure, including how to reproduce the environment, is in
docs/developer/gfx1250-strict-testing.md (added in this PR).

  1. TensileLite unit tests -- pytest -m unit over Tests/unit and
    Tests/extras.
  2. Architecture-name parsing regression -- the guard for existing
    architectures. Every name in architectureMap is expanded into every
    spelling CMake may emit (bare, :xnack+, :xnack-, [cu=64], [id=74a0])
    and checked in a matched pair: each spelling must match its own logic header,
    and must be claimed by no other architecture.
  3. Parser diff against the base commit -- gfxToIsa, baseArchName,
    steppingArchOf, isaToGfx, archNamesByIsa, splitArchsFromPredicates,
    isaCollisionFreeGroups, expandAllArchitectures and archMacroNames are
    dumped for every architecture x every spelling from both this branch and a
    worktree at the base commit, and the JSON diffed.
  4. Packaging script tests -- tools/scripts/tests/.
  5. pre-commit -- both the hipBLASLt config and the repo root config.
  6. stinkytofu -- ctest, run serially.
  7. Build -- ./install.sh -c -a "gfx1250;gfx1250-strict", then file counts
    per architecture, the ELF machine flag of both sets of code objects, and
    validate_library_layout.py on the install tree.
  8. hipblaslt-test *quick* against the locally built install tree, run
    twice on one host: once with the runtime reporting gfx1250-strict, once
    with HSA_DISABLE_GFX12_STRICT=1 so it reports gfx1250. This is the A/B
    that shows the two steppings select different libraries.

The guard in step 2 was validated by fault injection rather than assumed: making
archMatch compare prefixes fails 7 tests, all gfx1250-strict spellings;
dropping the predicate strip fails 48, across every predicated legacy target.
Neither direction alone would have caught the original regression.

Test Result

Check Result
TensileLite unit (-m unit) 7528 passed, 0 failed
Architecture-name parsing (test_arch_steppings.py) 758 passed
Parser diff vs base commit, 121 spellings 3 differences, all intended
Packaging script tests 14 passed
pre-commit (hipBLASLt config) 7531 passed, both hooks Passed
pre-commit (repo root config) Passed
stinkytofu ctest 1494 / 1494
Build, both architectures rc=0, 392 files each
ELF machine flag 0x49 (gfx1250), 0xEB (gfx1250-strict)
validate_library_layout.py OK
hipblaslt-test *quick*, gfx1250-strict 18043 passed, 0 failed (91 min)
hipblaslt-test *quick*, gfx1250 17899 passed, 0 failed

The 121-spelling parser diff shows the only behaviour changes are the intended
ones: archMatch matches 120/120 spellings against their own logic header, no
legacy architecture is claimed by a gfx1250* header, and the
splitArchsFromPredicates difference is ordering-only over the same 34-element
set.

The 144-test gap between the two *quick* runs is accounted for entirely by the
known_bug_matmul_small_bf16 quarantine: 144 such tests on gfx1250, 0 on
gfx1250-strict, and 18043 - 17899 = 144. Verified directly rather than
assumed, since a gap of any other size would mean something else changed.

Deliberate behaviour changes

These are intended and should be called out in review.

  1. Benchmark cache keys change for every architecture. The architecture is
    folded into the hash unconditionally, matching HelperKernelCache, so
    existing tuning output directories miss once and recompile. A conditional key
    would have kept the old value for non-stepping builds, but would also have
    left the same collision in place for any two architectures tuned into one
    output directory. Stale directories are inert, not invalid.
  2. Shipped .dat bytes change for every architecture. Making logic-file
    merge order deterministic changes the solution indices written into the
    master library. Worth knowing when diffing against a reference build.
  3. The linker response file moves from the current working directory to
    <destPath>.linker_args, so two concurrent runs cannot overwrite each
    other's.
  4. Kernels.cpp, Kernels.h and the six static headers no longer survive in
    the output root.
    They are written under the run's scratch directory
    instead, because their names carry no architecture and a concurrent sibling
    would otherwise overwrite them.
  5. _loadLegacyCacheIfMatches is removed (see above).

Known, not addressed

  • isaInfoMap is annotated Dict[str, IsaInfo] across many signatures while
    production keys it by IsaVersion. The fakes in
    Ductile/test_benchmark_problems.py followed the annotation and used string
    keys; they are corrected here, but the annotation itself is left alone since
    fixing it is a wide, unrelated sweep.
  • Makefile:55 gates TRUE16_FEATURE on TARGET_ARCH, which is assigned
    nowhere in the repository, so make co never passes +real-true16 for any
    architecture, while Component.py:173 passes it for gfx1100, gfx1200,
    gfx1201, gfx1250 and gfx1250-strict. A Makefile rebuild therefore does not
    reproduce what Tensile assembled. On the 10.1 nightly the feature is already
    the default for these targets and the objects are byte-identical either way;
    on ROCm 7.13 three of four measured b8b8s kernels fail to assemble without it,
    loudly rather than as a silent miscompile. This predates the branch.
  • Auto-detection still reports the wrong stepping on an FFM host or a RHEL 8
    one. Validators.py:121 picks rocm_agent_enumerator when isRhel8() or inFFMEnv, and that tool's gfx\d+ capture truncates gfx1250-strict to
    gfx1250. _detectArchNames falls through to amdgpu-arch only when the
    enumerator answers with nothing, and a truncated name is not nothing, so the
    fallback never runs. Measured on A0 silicon: the enumerator says gfx1250,
    amdgpu-arch and the fallback path both say gfx1250-strict. A build that
    names its target -- --gpu-targets, -a, or the CMake default, which lists
    both -- is unaffected; only a bare Tensile <yaml> <out> is, and it then
    builds the shipping stepping and reports success. Ordinary Linux hosts select
    amdgpu-arch and are correct.

Not in this PR

The tuning logic move from Logic/asm_full/gfx1250v0/ to gfx1250-strict/ is
deliberately left out and will follow separately.

Submission Checklist

JIRA ID : AIHPBLAS-4727
JIRA ID : AIHPBLAS-4728
JIRA ID : AIHPBLAS-4729
JIRA ID : AIHPBLAS-4730
JIRA ID : AIHPSPAR-275

@therock-pr-bot

therock-pr-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Description ✅ Pass
Forbidden Files ✅ Pass
🧪 Unit Test ✅ Pass
🔎 pre-commit ✅ Pass
🚫 Draft PR 🔜 To Be Enabled
🚩 Feature Flag 🔜 To Be Enabled
📊 Code Coverage 🔜 To Be Enabled
🤖 therock-pr-bot ✅ Pass

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

therock-pr-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 39 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...blaslt/tensilelite/Tensile/Common/Architectures.py 86.67% 10 Missing and 4 partials ⚠️
...ipblaslt/tensilelite/Tensile/GenerateSummations.py 33.33% 6 Missing ⚠️
projects/hipblaslt/tensilelite/Tensile/GpuArch.py 92.77% 4 Missing and 2 partials ⚠️
...lt/tensilelite/Tensile/TensileCreateLibrary/Run.py 94.87% 6 Missing ⚠️
...ects/hipblaslt/tensilelite/Tensile/ClientWriter.py 25.00% 3 Missing ⚠️
projects/hipblaslt/tensilelite/Tensile/Tensile.py 83.33% 1 Missing and 1 partial ⚠️
...hipblaslt/tensilelite/Tensile/BenchmarkProblems.py 88.89% 1 Missing ⚠️
...aslt/tensilelite/Tensile/KernelWriterConversion.py 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #11777      +/-   ##
===========================================
+ Coverage    70.34%   70.35%   +0.01%     
===========================================
  Files         2812     2812              
  Lines       463054   463104      +50     
  Branches     68187    68187              
===========================================
+ Hits        325706   325806     +100     
+ Misses      113761   113715      -46     
+ Partials     23587    23583       -4     
Flag Coverage Δ *Carryforward flag
TensileLite-CPP 46.40% <ø> (ø)
TensileLite-Unit 76.19% <88.89%> (+0.09%) ⬆️
hipBLAS 90.62% <ø> (ø) Carriedforward from 3ca9ff7
hipBLASLt 35.22% <ø> (-0.02%) ⬇️ Carriedforward from 3ca9ff7
hipCUB 82.68% <ø> (ø) Carriedforward from 3ca9ff7
hipDNN 87.01% <ø> (ø) Carriedforward from 3ca9ff7
hipFFT 44.37% <ø> (ø) Carriedforward from 3ca9ff7
hipRAND 76.12% <ø> (ø) Carriedforward from 3ca9ff7
hipSOLVER 68.96% <ø> (ø) Carriedforward from 3ca9ff7
hipSPARSE 86.99% <ø> (ø) Carriedforward from 3ca9ff7
rocBLAS 48.31% <ø> (ø) Carriedforward from 3ca9ff7
rocFFT 48.44% <ø> (ø) Carriedforward from 3ca9ff7
rocRAND 57.42% <ø> (ø) Carriedforward from 3ca9ff7
rocSOLVER 76.83% <ø> (ø) Carriedforward from 3ca9ff7
rocSPARSE 74.61% <ø> (ø) Carriedforward from 3ca9ff7
rocThrust 91.60% <ø> (ø) Carriedforward from 3ca9ff7

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...aslt/library/include/hipblaslt/hipblaslt_bfloat6.h 0.00% <ø> (ø)
...pblaslt/library/include/hipblaslt/hipblaslt_e5m3.h 0.00% <ø> (ø)
...laslt/library/include/hipblaslt/hipblaslt_float4.h 0.00% <ø> (ø)
...laslt/library/include/hipblaslt/hipblaslt_float6.h 0.00% <ø> (ø)
...c/amd_detail/rocblaslt/src/rocblaslt_auxiliary.cpp 68.27% <ø> (-0.13%) ⬇️
...rary/src/amd_detail/rocblaslt/src/tensile_host.cpp 41.90% <ø> (+0.06%) ⬆️
...pblaslt/tensilelite/Tensile/Common/Capabilities.py 100.00% <ø> (ø)
.../hipblaslt/tensilelite/Tensile/CustomYamlLoader.py 96.40% <100.00%> (ø)
projects/hipblaslt/tensilelite/Tensile/Hardware.py 96.71% <ø> (ø)
...ects/hipblaslt/tensilelite/Tensile/KernelWriter.py 73.90% <ø> (ø)
... and 12 more

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@geotseng-amd
geotseng-amd requested a review from a team as a code owner September 14, 2026 11:09

@hcman2 hcman2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK if test passed

Comment thread shared/stinkytofu/src/hardware/ArchHelper.cpp

@pmoutsias-amd pmoutsias-amd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the edit in the readme and it is a straight removal. LGTM.

@bnemanich bnemanich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mxdatagenerator change looks fine. Will need someone else to look over the rest of PR.

@geotseng-amd
geotseng-amd force-pushed the users/geotseng/develop-gfx1250-strict branch 4 times, most recently from 35a9809 to 3dd8f22 Compare September 17, 2026 04:13
geotseng-amd and others added 10 commits September 18, 2026 15:50
gfx1250 ships as two silicon steppings. They share ISA (12,5,0) but are
separate compiler targets whose code objects are rejected on each other,
and ROCr now reports the stepping directly in the agent name. Treat the
A0 stepping as an ordinary architecture named gfx1250-strict -- the name
the compiler and the runtime both use -- instead of deriving it from an
ASIC-revision probe.

Detection no longer probes asicRevision. Tensile/GpuArch.py (was
GpuRevisionTarget.py) asks amdgpu-arch, falling back to rocminfo, and
uses the name it gets back. It stays free of Tensile.Common so the
invoke build path does not pull in rocisa just to name a target, and it
avoids rocm_agent_enumerator, whose gfx\d+ capture group truncates the
suffix and would silently build B0 kernels for A0 silicon. This removes
gpu_revision_probe.cpp, rocblaslt_arch_revision.hpp, the --asic-revision
option, and the arch-revision mapping in the runtime.

A stepping is now derived rather than declared: a name is a stepping
exactly when it does not survive a round trip through its ISA, so
steppingArchOf() answers gfx1250 for gfx1250-strict and None for every
ordinary arch. That retires ARCH_COMPILER_TARGET, gfxToCompilerTarget()
and REVISION_SUBTREES; a future stepping needs no registration. What
remains declared is ARCH_CAP_OVERRIDES, because the assembler accepts
the same instructions under both steppings and cannot tell them apart
by probing.

Compiling for gfx1250-strict predefines __gfx1250_strict__ and not
__gfx1250__, so guards testing only the latter fell through to their
else branch and produced non-functional kernels with no build error.
Eight such guards across memory_gfx.h, the hipblaslt float4/bfloat6/
float6/e5m3 headers, DataTypes_E5M3.hpp, and mxDataGenerator now accept
both. The CMake arch regexes in extops and matrix-transform were
likewise truncating "-strict" while stripping ":xnack+"; they now keep
it.

Because Tensile keys its capability map by ISA version, a single
TensileCreateLibrary invocation cannot hold both steppings. Rather than
name an architecture in the build system, the split lives in
TensileCreateLibrary: isaCollisionFreeGroups() partitions the requested
names so no group holds two that spell one ISA, and a run handed more
than one group re-executes itself once per group as a subprocess --
separate processes rather than a loop because a run settles
arch-dependent state process-wide. A single group, which is every build
that names no stepping, takes the old path untouched: no subprocess, no
change to parallelism, scratch naming or cleanup. CMake keeps one call
with ${GPU_TARGETS}.

Two runs sharing an output directory must not share intermediates:
kernel basenames come from the ISA, so both would name their .s and .o
identically while the machine code inside differs. Scratch therefore
becomes build_tmp/<STEM>-<stepping> for a run that was asked for one,
and stays build_tmp/<STEM> for every run that was not. Fan-out children
carry TENSILE_GROUP_BUILD so they reclaim only their own subdirectory
and leave a concurrent sibling's alone, while a standalone run still
removes build_tmp whole. Jobs are split by architecture count, so a
fourteen-architecture group no longer waits on a one-architecture
sibling holding half the cores.

The tuning cache key gains the compiler targets for the same reason.
Two steppings agree on every other field the key covers, so without
them the second run would load the first's code objects and hand the
wrong machine code to silicon that cannot run it. The legacy cache
fallback goes with it: that path is keyed by directory alone, so it
would bypass the new field and reintroduce exactly the mismatch.

Tuning is selected by ArchitectureName, which a stepping spells as
itself, so gfx1250-strict logic is no longer absorbed into gfx1250.
archMatch() compares whole names for that reason, and strips a
bracketed predicate as well as a qualifier -- TensileLogic filters with
GPU_TARGETS exactly as CMake passed it, having never split predicates
off, so a prefix-free comparison alone would fail gfx950[cu=64] against
its own logic and stop the build.

The generator scripts detect a name rather than an ISA, since deriving
the name back from the ISA builds gfx1250 code for either stepping.
They also pass the capability map to assignGlobalParameters, which has
required it since before this change.

With the stepping named for itself throughout, the
HIPBLASLT_ASIC_REVISION environment variable and the --asic-revision
flag have nothing left to select and are removed. Picking a revision is
now spelling it in GPU_TARGETS, which needs no separate knob and
generalises to the next stepping.

The packaging checks learn that an architecture token may contain a
hyphen. Their regexes accepted only alphanumerics, so a stepping's
files matched nothing, and an unmatched file leaves its subtree without
masters -- which the scanner reads as a subtree to skip, passing the
package silently rather than reporting it.

Logic files are now merged in sorted order. Set iteration and readdir
order were leaking salted string hashing into the solution indices
written into the master library, so a build was not reproducible.
Fixing it changes the shipped .dat bytes for every architecture, which
is worth knowing when diffing against a reference build.

stinkytofu keeps Gfx1250v0 as its C++ identity, since "gfx1250-strict"
is not spellable as an identifier; ArchHelper aliases the toolchain
spelling onto it and a FileCheck test pins that both spellings reach the
same cost table.

The stepping tests land as test_arch_steppings.py: the cases are about
steppings in general rather than about one name.

Note: the tuning logic move from Logic/asm_full/gfx1250v0/ to
gfx1250-strict/ is deliberately not part of this commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rlay

SUPPORTED_ISA cannot name a stepping, since a stepping shares the ISA
it steps from, so "all" expanded to every architecture except
gfx1250-strict. A default build therefore left A0 silicon with no
library to load, and the one spelling that was supposed to mean
"everything supported" was the one spelling that missed it.
supportedSteppings() reads the names off architectureMap instead,
restricted to steppings whose base architecture is already covered --
being a stepping does not make an unsupported architecture supported.
The expansion now collides with itself by design, a stepping and its
base being unbuildable in one run; isaCollisionFreeGroups partitions it
as it already does for names given explicitly.

The gfx1250v0 overlay machinery goes with the revision it described.
That workaround kept v0 logic in a subtree of gfx1250's corpus tagged
by ScheduleName, which needed find_gfx1250v0_overlay_violations to
check the tagging, --require-gfx1250v0-overlay to opt a corpus in, and
a matching CMake keyword to reach it -- all of it there because the two
revisions could not be told apart by name. gfx1250-strict has its own
architecture directory, so there is no overlay left to validate and
nothing for the caller to opt into.

The .amdgcn_target directive has to be brought back in step with the
assembler. rocisa derives it from the ISA alone, which spells the base
name for a stepping, while the build assembles that stepping with
-mcpu=<stepping>. Assemblers through ROCm 10.1 accepted the mismatch;
10.2 rejects it -- "target id '...--gfx1250' specifies a processor that
is not valid for subarch 'amdgpu12.50s'" -- so the build broke on a
toolchain move rather than on a code change. Rewriting the directive
before assembling is a no-op for every ordinary architecture, where the
target already equals the name derived from the ISA.

The packaging check learns that a hyphen is not enough to make a
stepping. _stepping_base accepted any hyphenated subtree, which would
have exempted library/gfx942-xnack+/ from the very check that reports
it; the trailing sign separates a target feature from a stepping there,
as it already does for filenames.

The test corpus marks a stepping as itself. configMarks reads the
architecture out of a filename, and without the suffix
bf16_CLS_gfx1250-strict.yaml would mark itself gfx1250 and claim to be
a config for the architecture it exists to stay off of; pytest.ini
registers the stepping's marker alongside its xfail- and skip- forms.

Co-authored-by: Cursor <cursoragent@cursor.com>
…cripts

amdgpu-arch reports a configuration (gfx950:sramecc+:xnack-), not a CMake
target. tox -e py3 forwards that string into --gpu-targets, so configure
rejects the whole name. Strip colon-delimited features on both the
get-gpu-arch and explicit --gpu-targets paths, and keep hyphenated
steppings such as gfx1250-strict.

Also install SoftmaxGenerator.py and LayerNormGenerator.py into the
tensilelite test artifact tree. Installed unit tests spawn those scripts
next to AMaxGenerator.py, which was the only generator previously shipped.

Co-authored-by: Cursor <cursoragent@cursor.com>
gfx1250-strict is a separate architecture name, so a skip list naming only
gfx1250 does not cover it: every config an architecture cannot run has to
name the stepping too, or tox runs it on v0 silicon and fails there.

Add skip-gfx1250-strict to the 410 configs already skipping the
architectures they do not support, and add the gemm, gradient and sparse
configs the stepping is meant to run, tuned for what v0 supports -- no
TDM-multicast and no fp4 32x16 WMMA.

Co-authored-by: Cursor <cursoragent@cursor.com>
gfx1250-strict is a separate compiler target, so the architecture
allowlist has to name it or configure rejects a build asked for it, and
the logic tree has to carry its own tuning or such a build ships an
empty spmm library.

The logic is declared under the stepping's own name, not gfx1250's: the
two share ISA 12.5.0 but reject each other's code objects, so the
selection must not let one claim the other's files.

Co-authored-by: Cursor <cursoragent@cursor.com>
The stepping is an architecture of its own now, so its logic has to live
under that name in all three places the build reads: the directory it is
globbed from, the filename's codename prefix, and the ArchitectureName
its own header declares.

The declared name is what keys the master library, and the per-arch
writes are keyed by the requested name, so a header still saying gfx1250
would key a library no write ever addresses -- a build that reports
success having shipped an empty subtree. Declaring the stepping also
keeps a plain gfx1250 build from globbing this tuning, which was derived
under capabilities v1 does not share.

Tuning values are unchanged; only the names are.

Co-authored-by: Cursor <cursoragent@cursor.com>
…est data and under the coverage gate

Origami matches gcnArchName exactly against a table holding base
architectures only, so a part reporting gfx1250-strict matched nothing,
left HipAMDGPU::analyticalHardware null, and threw on the assert guarding
every query that consults the analytical model. That path is not opt-in:
skDynamicGrid defaults to k_split_aware, so any Stream-K solution reaches
the assert unless TENSILE_STREAMK_DYNAMIC_GRID is set. Hand Origami a copy
of the properties naming the base architecture instead. The name is
rebuilt through toProcessor/toString rather than trimmed as a string, so
the revisions folded here stay the ones Tensile recognises and an
unrecognised name still resolves to something Origami rejects. Both entry
points read gcnArchName out of the properties they are handed, so the same
copy reaches each; renaming only the support check would trade the null
for a throw out of get_default_num_xcds. Only the copy is renamed --
archName() and the code-object subtree still spell the revision, and
Origami consumes machine constants rather than code objects, so the base
entry's instruction timings approximate the revision's performance and
affect nothing else.

match_test_category compares known_bug_platforms token-for-token with
strcasecmp against the name hipblasLtGetArchName reports, which keeps the
revision suffix. gfx1250-strict therefore missed the gfx1250 token and ran
the TF32 Inf cases ROCM-1545 already quarantines on gfx1250 and gfx950;
the defect is in silicon the revision shares, so list the token too.

Three subtile bf16 configs were missed when the rest of the corpus got its
skip-gfx1250-strict mark. gfx1250-strict is its own target, not a spelling
of gfx1250, so a config written for gfx1250 has to opt out of it the same
way it opts out of every other arch it was not written for; left unmarked
they run on the strict stepping and build code objects the silicon
rejects.

The sibling-DeviceNames check reads ScheduleName, gfx arch and CU count out
of each logic file's own header, and treats a file it cannot read as an
unknown of each. Nothing exercised that: the corpus the unit tests run
against is readable, so the three except arms in _arch_variant_key and
_chip_id_dir_suffix's empty-arch guard were never entered, and the
coverage ratchet caught the module dropping to 93.33%. Point the key at a
path that does not exist and at a header with no ArchitectureName, and
assert what the fallback is for: files it cannot read group together
rather than each becoming its own singleton, and a corpus containing one
still completes instead of raising.

test_a_detected_name_yields_a_target_the_build_accepts read
cmake/tensilelite_supported_architectures.cmake through parents[4] to check
the probe's vocabulary against the list GPU_TARGETS is validated against.
That path resolves in a checkout but reaches outside the tensilelite tree,
which is installed on its own as a test artifact without that file, so all
seven parametrizations failed on FileNotFoundError while a checkout stayed
green. Remove the test rather than add an install rule whose only consumer
is a test; cmake_gpu_target keeps the two tests beside it, and what is lost
is the guarantee that the two lists cannot drift apart.

Verified on a part reporting gfx1250-strict under ROCm 10.2.0a20260914:
analyticalHardware goes from NULL to present, while the base stepping
behaves exactly as before.

Co-authored-by: Cursor <cursoragent@cursor.com>
…logic

The 64 Gridbased logic files shipped InternalSupportParams with
KernArgsVersion: 2. That key is bound to the generator version rather than
being a tuning result, so library logic is meant to follow
defaultInternalSupportParams instead of pinning a layout that goes stale;
reorderSolutionDictForDictMerge drops it on write for exactly that reason.
Every other tree already reflects this -- gfx950's 129 files and gfx1250's
64 carry no occurrence, and gfx1250-strict was the only one that did.

Drop the key here too. The .s metadata and benchmark solution files still
carry it, so nothing that needs the generator's layout loses it. The
remaining line now matches gfx1250's byte for byte, so a later
regeneration will not churn these 64 lines again.

Co-authored-by: Cursor <cursoragent@cursor.com>
_detectArchNames asks a device enumerator first so that a target.lst or an
HSA_OVERRIDE_GFX_VERSION pin still wins, and took that answer verbatim.
As of ROCm 10.2 amdgpu-arch -- the default enumerator -- is a trampoline
that execs offload-arch, which names an agent from the KFD node's
gfx_target_version alone and loads neither ROCr nor HIP. Both gfx1250
steppings publish 120500 there, since the revision lives in the node's
capability bits 25:22 that offload-arch never reads, so an A0 part came
back as a bare "gfx1250" and the detect_gpu_archs fallback never ran.
Configs that name no ISA take their target from here, so on strict
silicon they built and tuned as base. Nothing failed to say so: a base
code object still loads and still runs on A0, the strict hazard
workarounds were simply absent, and the only sign was the architecture in
the artifact path. Route the enumerator's answer through
restore_steppings.

ROCr does apply the revision rule and rocminfo reports through ROCr, so
it still answers gfx1250-strict. Letting rocminfo only ever lengthen a
name keeps each tool doing what it is good for: rocminfo needs read-write
/dev/kfd, so a caller outside the render group gets nothing from it and
has to keep enumerating with amdgpu-arch. A base rocminfo reports under
more than one spelling is left alone rather than guessed at, since a box
holding both steppings has no single right answer to substitute, and a
base the enumeration already spells with a stepping is left alone for the
same reason from the other direction -- that answer came from a tool that
can tell the two apart on this box, so the device rocminfo happens not to
be reporting would otherwise be renamed to its neighbour's stepping.

Three testenvs had been adding --gpu-targets to the Tensile options
whenever the detected name ended in -strict. That covered for the
detection defect above while causing one of its own, forcing the strict
stepping onto configs pinned to other ISAs; with detection fixed it is
both unnecessary and wrong, so all three are gone. Adding --gpu-targets to
make a strict test pass is a symptom of this bug rather than a fix for it.
The same three testenvs exported TENSILE_ARCHITECTURE from the detected
name, which would put an unnormalized string back into Tensile downstream
of the normalization below. Nothing else reads the variable, so those are
gone too.

HSA_DISABLE_GFX12_STRICT picks which stepping the runtime reports on a
revision-0 part, and tox does not inherit a variable that is not listed,
so it read as unset inside the testenv -- which on ROCm 10.2 means the
base one. That name drives both test selection through skip-<arch> and
the client build, so dropping it did not narrow the run, it ran the base
half of the corpus on strict silicon and reported green.

An installed ROCm ships libtensilelite-host.so.1 too, and $ROCM_PATH/lib
ahead of the build directory let that copy win. The two are not
interchangeable: CMakeLists.txt defines TENSILE_YAML or TENSILE_MSGPACK
and never both, so a msgpack-only host library has no reader for the YAML
libraries Tensile writes by default. Every hardware config then failed at
once, the client dying on "Failed to load solution library" before it ran
a single GEMM, so put the build directory first.

build-client only overrides the compilers when it is given a ROCm path and
has no detection fallback of its own, so CMake's search won and found
/opt/rocm's amdclang++ -- which rejects gfx1250-strict outright on a host
whose system ROCm predates the target. Pin $ROCM_PATH at each call site.
The quotes are escaped because tox parses the command with shlex and would
otherwise strip them before the inner shell ever sees them.

The test-side detection called rocm_agent_enumerator, which parses
rocminfo with a capture group ending at gfx\d+ and so truncates a suffix
in exactly the way described above. That list is what config_helpers.
configMarks keys its skip-<arch> marks off, and the two spellings of a
config carry mirrored marks -- a strict config says skip-gfx1250, a base
config says skip-gfx1250-strict. Handing that comparison a truncated name
therefore reverses the selection rather than narrowing it. Read through
Tensile.GpuArch instead, the same detection the build uses, with
TENSILE_ROCM_PATH applied around the call and taken back out again so the
test-specific override does not leak into anything the tests launch.

That detection answers with a configuration rather than an architecture,
so the answer needs normalizing before a mark name is built from it:
amdgpu-arch names a gfx90a agent gfx90a:sramecc+:xnack-, listing the
target features that agent happens to have, while a mark is written for
the architecture. Since configMarks builds its names by concatenation --
"skip-%s" % arch -- an unnormalized answer looks for
skip-gfx90a:sramecc+:xnack- and finds nothing any config file spells,
unskipping every config pinned off that architecture: 446 tests on
gfx90a, 380 on gfx942, 324 on gfx950, each announced beforehand by an
Unknown pytest.mark warning carrying the feature string that nobody reads
as an error. Route the answer through cmake_gpu_target, the same
normalization the build spells GPU_TARGETS with, so only the
colon-delimited features come off. The hyphenated stepping stays, for the
mirrored-mark reason above: truncating gfx1250-strict to gfx1250 does not
narrow that selection either, it reverses it. The enumerator this
replaces had been taking the features off as a side effect of taking the
stepping off, which is why both axes have to be spelled out here.

_rocmShimReporting shims the detection tools through a fake ROCM_PATH so a
test cannot fall through to PATH and answer from the machine running it.
rocminfo is now consulted to restore a stepping, so it needs shimming for
the same reason; left out, it reported this machine's stepping onto
whatever architecture the caller asked for. It is the one tool here asked
in its own format, read for indented Name: lines rather than bare ones.

Co-authored-by: Cursor <cursoragent@cursor.com>
The rebase onto develop picked up the true16 NoSDWA change, whose arch
test "^gfx1[12][0-9][0-9]$" does not match a stepping suffix. Merging the
two cleanly left gfx1250-strict extops assembled without +real-true16
while gfx1250 got it, with no error to show for it.

Widen it the way the wavefront test above it is already widened. A
stepping shares its ISA with the architecture it steps, so true16 comes
with it; only what A0 genuinely lacks is gated per-stepping.
@mengzcai
mengzcai force-pushed the users/geotseng/develop-gfx1250-strict branch from 3dd8f22 to 43cdd0e Compare September 18, 2026 08:51
Comment on lines +2 to +3
- gfx1250-strict
- gfx1250-strict

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are both of these gfx1250-strict?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants