Skip to content

feat(TensileLite): generate deterministic assembly - #9853

Merged
mgehre-amd merged 2 commits into
developfrom
users/mgehre/tensilelite-deterministic-labels
Aug 12, 2026
Merged

mgehre-amd merged 2 commits into
developfrom
users/mgehre/tensilelite-deterministic-labels

Conversation

@mgehre-amd

Copy link
Copy Markdown
Contributor

Problem

TensileLite's rocisa LabelManager generated unique branch/loop label names with a
random 16-character suffix (magicGenerator() using rand()), e.g.
NoBranch_T8JHFHKM7BO5OHXW. Because rand() is process-global shared state and
TensileCreateLibrary parallelizes kernel generation across multiprocessing
workers, the emitted label names differed run to run depending on how kernels were
distributed across workers. This is the only source of run-to-run nondeterminism in
the emitted assembly text, which defeats reproducible codegen and byte-for-byte
comparison of generated kernels.

Change

Replace the random suffix with a per-instance, monotonically increasing counter:

  • getUniqueNamePrefix(prefix) now emits prefix_<counter++>, keeping the
    collision-guard loop so a counter-derived name can never collide with a name that
    was added manually.
  • getUniqueName() delegates to getUniqueNamePrefix("label").
  • LabelManager gains a (data, counter) constructor and getCounter() so the
    counter is preserved through __getstate__ / __setstate__ / __deepcopy__ — a
    copied manager keeps producing unique names.

Each kernel receives a fresh LabelManager in KernelWriter._initKernel, so the
counter restarts at 0 for every kernel; labels only need to be unique within a single
kernel's assembly namespace.

Notes

  • getUniqueName()'s output format changes from a bare random token to label_<N>;
    it is only referenced by rocisa/test/test_label.py. All production call sites use
    getUniqueNamePrefix.

Testing

  • rocisa/test/test_label.py passes; verified deterministic output and that the
    counter survives pickle/deepcopy and restarts on a fresh manager.
  • Full TensileLite characterization suite
    (Tensile/Tests/unit/characterization) passes with the change, with no snapshot
    (.ambr) golden updates required
    :
    • _codegen (all assembly-emit tests and {basename, err} goldens): 254 passed
    • Solution characterizations (Class/Derivation/DerivationSweep/Arms/Breadth/Edges/Validity)
      and the remaining directories: all passed
  • No golden captures label text: the codegen goldens digest {basename, err} only,
    and codegen_harness.canonicalize_asm already normalizes label suffixes before any
    structural assertion, so counter-based labels flow through unchanged.

@therock-pr-bot

therock-pr-bot Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

❌ PR Check — Action Required

Check Status Details
📝 PR Description ❌ Fail Error: PR description must reference a JIRA ID, ISSUE ID, or a GitHub closing keyword.
Expected: include a JIRA ID / ISSUE ID line (separator : or -, or omitted; value may be a JIRA key, a number with/without #, or a link), OR a closing keyword + issue reference. Accepted examples:
• JIRA ID : TESTAUTO-6039
• JIRA ID - #330
• JIRA ID #330
• JIRA ID (on separate line)
ROCM-25757
• ISSUE ID : TESTUTO-3334
• ISSUE ID #3334
• ISSUE ID - TESTAUTO-3433
• ISSUE ID (on separate line)
AIRUNTIME-2352
• ISSUE ID : https://github.com/<org_name>/<repo_name>/issues/1234
• Closes #10
• Fixes octo-org/octo-repo#100
• Resolves: #123
• #123
• https://github.com/<org_name>/<repo_name>/issues/123
Current: no valid JIRA/ISSUE/closing-keyword reference found
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

⚠️ 1 policy check(s) failed. Please address the issues above before this PR can be Reviewed.

🚫 Please fix the failed policies

  • ❌ PR Description

The Not ready to Review label was added to this PR. Once all policies pass, the label is removed automatically.

📖 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 Jul 24, 2026 •

Copy link
Copy Markdown

🚫 Please fix the failed policies before requesting reviews.

The following policy checks failed:

  • ❌ PR Description

The Not ready to Review label has been added to this PR.
Once all policies pass, the label will be removed automatically.

@mgehre-amd
mgehre-amd force-pushed the users/mgehre/tensilelite-deterministic-labels branch 2 times, most recently from 12cc9bc to 41f071b Compare July 24, 2026 13:53
@codecov

codecov Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #9853      +/-   ##
===========================================
- Coverage    69.61%   68.11%   -1.51%     
===========================================
  Files         2741     2771      +30     
  Lines       451317   463211   +11894     
  Branches     66491    74315    +7824     
===========================================
+ Hits        314177   315472    +1295     
- Misses      116920   125355    +8435     
- Partials     20220    22384    +2164     
Flag Coverage Δ *Carryforward flag
TensileLite 34.31% <ø> (+0.13%) ⬆️ Carriedforward from 42e91b1
TensileLite-CPP 38.12% <100.00%> (-<0.01%) ⬇️
TensileLite-Unit 64.90% <100.00%> (+<0.01%) ⬆️
hipBLAS 90.62% <ø> (ø) Carriedforward from 42e91b1
hipBLASLt 34.73% <ø> (ø)
hipCUB 82.68% <ø> (-0.79%) ⬇️ Carriedforward from 42e91b1
hipDNN 86.35% <ø> (-0.03%) ⬇️ Carriedforward from 42e91b1
hipFFT 46.91% <ø> (-0.42%) ⬇️ Carriedforward from 42e91b1
hipRAND 76.12% <ø> (ø) Carriedforward from 42e91b1
hipSOLVER 69.18% <ø> (ø) Carriedforward from 42e91b1
hipSPARSE 86.27% <ø> (ø) Carriedforward from 42e91b1
rocBLAS 47.95% <ø> (ø) Carriedforward from 42e91b1
rocFFT 47.08% <ø> (-0.53%) ⬇️ Carriedforward from 42e91b1
rocRAND 57.01% <ø> (-0.02%) ⬇️ Carriedforward from 42e91b1
rocSOLVER 76.92% <ø> (ø) Carriedforward from 42e91b1
rocSPARSE 72.58% <ø> (-0.01%) ⬇️ Carriedforward from 42e91b1
rocThrust 91.36% <ø> (-0.24%) ⬇️ Carriedforward from 42e91b1

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

Files with missing lines Coverage Δ
.../hipblaslt/tensilelite/rocisa/rocisa/src/label.cpp 100.00% <100.00%> (ø)

... and 445 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.

TensileLite's rocisa LabelManager generated unique label names with a
random 16-char suffix (magicGenerator using rand()). Because rand() is
process-global and TensileCreateLibrary parallelizes kernel generation
across multiprocessing workers, the emitted label names differed run to
run, defeating reproducible codegen.

Replace the random suffix with a per-instance monotonically increasing
counter. The counter is carried through the pickle/deepcopy state so
copied managers keep producing unique names. Each kernel gets a fresh
LabelManager in _initKernel, so the counter restarts at 0 per kernel;
labels only need to be unique within a single kernel's assembly.

Changes:
- getUniqueName now delegates to getUniqueNamePrefix("label"); both use
  the counter and keep the collision-guard loop against manually added
  names.
- LabelManager gains a (data, counter) constructor and getCounter() so
  __getstate__/__setstate__/__deepcopy__ preserve the counter.
@mgehre-amd
mgehre-amd force-pushed the users/mgehre/tensilelite-deterministic-labels branch from 41f071b to 42e91b1 Compare July 27, 2026 07:23
@mgehre-amd
mgehre-amd enabled auto-merge (squash) July 31, 2026 11:28
@mgehre-amd
mgehre-amd merged commit 2d56036 into develop Aug 12, 2026
116 of 142 checks passed
@mgehre-amd
mgehre-amd deleted the users/mgehre/tensilelite-deterministic-labels branch August 12, 2026 09:44
geotseng-amd added a commit that referenced this pull request Oct 5, 2026
test(tensilelite): normalize StinkyTofu label hashes before
canonicalize_asm

InsertClusterBarrierPass names its skip labels with a random 16-char
[0-9a-zA-Z] hash. canonicalize_asm only renames suffixes that are all
[A-Z0-9], so about one hash in 6000 took that path in one emit while its
counterpart in the other emit was renamed by _stableLabels instead,
making test_gfx1250v0_emits_what_gfx1250_strict_emits fail roughly once
in 750 runs.

Run _stableLabels on the raw source inside _emit, ahead of
canonicalize_asm, so every hash is renamed by the same pass, and compare
the emitted sources directly.

## Motivation


`Tensile/Tests/unit/test_arch_steppings.py::test_gfx1250v0_emits_what_gfx1250_strict_emits`,
added in #11777, fails intermittently in unrelated PRs' CI (for example
`s_preliminary_hipblaslt_PR-12538`: 1 failed / 9822 passed). The emitted
gfx1250v0 and gfx1250-strict kernels are actually identical; the failure
comes from how the test normalizes random label names before comparing
them. This PR removes the flake without weakening the assertion.

## Technical Details

**Where the randomness comes from.** #9853 made rocisa's `LabelManager`
deterministic (`prefix_<counter>`), but StinkyTofu's
`InsertClusterBarrierPass::makeRandomHash()`
(`shared/stinkytofu/src/transforms/asm/InsertClusterBarrierPass.cpp`)
still suffixes `label_skipCBPreSignal_*` / `label_skipCBPreSignal_LCL_*`
with a 16-char hash drawn from `[0-9a-zA-Z]`, seeded from
`std::random_device`. This kernel (`MULTICAST_MI`, `ClusterDim=[2,1]`)
carries 4 such labels per emit.

**Why two normalization passes disagree.** The test normalized each emit
twice, with different rules:

| Pass | Matches | Renames to |
|---|---|---|
| `canonicalize_asm` (inside `_emit`) | `_[A-Z0-9]{16}\b` | `_LBL<n>` |
| `_stableLabels` (in the test, afterwards) |
`_[A-Za-z0-9]{16}(?![A-Za-z0-9])` | `_L<n>` |

A hash is all `[A-Z0-9]` with probability (36/62)^16 ≈ 1/6000. When that
happens in only one of the two emits, that label becomes `_LBL0` on one
side and `_L3` on the other, and every later `_L<n>` shifts by one, so
the comparison fails. With 8 hashes per test run, the failure rate is
about 1 in 750.

Fixing `_stableLabels` alone is not enough: one label is spelled
`label_InitCIterWmma_label_skipCBPreSignal_<hash>_0`, where `\b` does
not fire before `_`, so `canonicalize_asm` would rename an all-caps hash
in some occurrences and not others. Normalization has to happen on the
raw source.

**Change.**
- Move `_stableLabels` to module scope.
- Apply it to the raw source in `_emit`, before `canonicalize_asm`.
After that, `canonicalize_asm`'s label regex has nothing left to match,
and it only strips `matrix_[ab]_reuse`.
- `test_gfx1250v0_emits_what_gfx1250_strict_emits` now asserts `v0Src ==
strictSrc` directly.

`test_gfx1250_emits_multicast_gfx1250_strict_does_not`, the only other
`_emit` caller, is unaffected: its `!=` check holds because the
multicast setup genuinely differs.

The root fix is to make StinkyTofu's label naming deterministic (as
#9853 did for rocisa); that is left to the StinkyTofu owners.

## Test Plan

Run in a gfx1250 FFM container (ROCm 10.2 nightly), with rocisa built
from this branch's tree:

1. Full file: `pytest Tensile/Tests/unit/test_arch_steppings.py`.
2. Fault injection on real emitted kernels: take raw strict and v0
sources, replace each of v0's 4 StinkyTofu hashes in turn with an
all-`[A-Z0-9]` hash, and compare under the old and new normalization
order.
3. Determinism check: emit 12 strict/v0 pairs (24 emits) and compare
them after label normalization, against each other and across pairs, to
confirm the hash is the only run-to-run variation.

## Test Result

1. `828 passed`.
2. Old order: all 4 injections mismatch (reproducing the CI failure
signature `_L3` vs `_LBL0` with shifted numbering). New order: all 4
compare equal, including the hash that also appears with a `_0` suffix.
3. All 24 emits are identical after normalization; no other
nondeterminism found.

## Submission Checklist

- [x] Look over the contributing guidelines at
https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

3 participants