tenstorrent/tt-emule

uplift

Diagnose and fix emule regressions caused by bumping the tt-metal pin (and its bundled tt-umd submodule).

Voir la source

Mode d’emploi

  1. Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
  2. Consultez le document original pour les outils, identifiants et environnements requis.
  3. Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/tenstorrent/tt-emule --skill uplift

Avant de commencer

  • L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
  • Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Document Skill original

Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.

Uplift: surviving a tt-metal / tt-umd pin bump

When the tt-metal pin moves forward (e.g. a companion change merges to main, or you re-pin to a newer commit), you import two moving parts at once: the tt-metal source and the tt-umd submodule it points at. Either can regress the emule build because emule substitutes its own runtime (SWEmuleChip, emulated_program_runner.cpp) and JIT kernel shims (include/jit_hw/) for the real hardware path. This skill is the methodology for turning a red post-bump regression into a proven root cause and a minimal, faithful fix.

The governing principle, learned the hard way: prove it by building and running, not by reading `git log`. Pickaxe/git show/blame routinely mislead (moved code, squash merges, shallow submodule object DBs that render a commit as a whole-tree add). A bisection that compiles and runs an oracle is the only authority.

When to invoke

  • A pin bump (or tt-metal-pin.txt change) makes a previously-green regression

fail.

  • Symptoms after a bump: device-open aborts (SIGABRT/SIGSEGV during

start_device), jit_compile_kernel: compiler failed (use of undeclared identifier ...), a deterministic hang, or new ATOL/PCC mismatches across many unrelated ops.

  • You suspect the bundled umd bump rather than tt-metal source, and need to

isolate which.

For a single missing compute shim (one <op>_tile), use /compute-llk-bringup. For data-corruption on a kernel that runs without crashing, use /memory-debug. This skill is for broad regressions that a pin bump introduced.

Step 0 — Read the failure correctly, don't trust the label

  • Inspect artifacts, not the check's green/red. A C++ regression job can show

"pass" while its result artifact shows aborts — known-failure allowlists and classify steps can absorb failures, and exit codes don't always propagate. Open the actual run log/artifact and read the per-test verdicts.

  • Classify the failure mode before chasing — they have different root-cause

paths:

  • device-open crash (fails in the device fixture / start_device, before

any op) → a shared runtime/reset/topology path, almost always umd or the emule chip.

  • JIT-compile error (undeclared identifier, header not found) → a jit_hw

shim drift; the kernel can't compile against emule's headers.

  • hang → often a JIT-infra issue (e.g. forking a compiler from a

many-threaded process), not the op.

  • data mismatch → op-correctness; usually /memory-debug territory.
  • Per-arch scope. Supported arches gate the decision; an out-of-scope arch is

typically allowlist-gated and expected-broken — don't let its churn block a bump, but don't silently regenerate its allowlist over a real new regression either.

Step 1 — Build a fast, deterministic oracle

Pick the smallest, fastest test that reproduces the failure with an unambiguous signal, and wrap it so it emits exactly GOOD / BAD / SKIP:

  • GOOD = clean pass.
  • BAD = the specific signature (e.g. a particular abort message + address, or

the exact compiler error). Match the signature, not just "non-zero exit", so an unrelated failure can't masquerade as the bug.

  • SKIP = build break or any other outcome → maps to git bisect 125.

Step 2 — Bisect: isolate metal-source vs the umd bump

The pin range moves both metal and the umd gitlink, so bisect in two stages.

  1. Metal bisect. git bisect start <new-pin> <old-pin> in tt-metal, then

git bisect run a script that, per commit: updates the umd submodule to that commit's recorded gitlink, builds only the oracle's target (not the whole tree), runs the oracle, and returns 0/1/125.

  • If the first-bad commit changes only the umd gitlink, the regression

rides with the umd bump — proceed to the umd bisect.

  • If it's a metal-source commit, the cause is in tt-metal; triage that commit.
  1. UMD bisect. Pin metal at the first-bad (umd-bump) commit, then

git bisect start <bad-umd> <good-umd> inside the submodule and bisect with the same oracle, varying only umd. Build breaks on old-umd-vs-newer-metal become SKIP.

Bisection rules that keep it honest:

  • Hold the emule layer fixed (the jit_hw headers and any local emule source)

for the whole bisect — the variable under test is metal+umd, exactly what the bump changed.

  • Build the minimal target so each step is a fast incremental relink.
  • Automate with `git bisect run` so you don't hand-judge steps.
  • Restore all repos (git bisect reset, re-checkout the intended pin, resync the

submodule) when done.

Step 3 — Prove the boundary and the mechanism

  • Controlled before/after: build+run the oracle at culprit^ (GOOD) and

culprit (BAD) with everything else fixed. That's the proof.

  • Read the mechanism from a two-tree diff, not `git show`. `git show

<culprit> can render a whole-tree add in a shallow/grafted submodule; use git diff <culprit^> <culprit> -- <file>` between two checked-out trees.

  • Read the upstream PR the culprit came from. It is the authoritative spec for

any emule-side mirror — its diff tells you the exact old→new API or behavior mapping to replicate.

Step 4 — Design the fix (two recurring classes)

Most uplift regressions are one of these.

A. Behavioral regression in a shared runtime path

Upstream changed a path that emule used to neutralize. Example shape: a device bring-up step that used to dispatch through a per-chip virtual the SW chip overrode as a no-op now issues an unconditional register write that the emule memory model can't satisfy.

Fix principles:

  • Preempt at the right layer, using an existing mode/chip-type signal the code

already branches on. Gate strictly on the emulated case so silicon / simulation / other modes are untouched.

  • No magic constants when a canonical accessor exists; prefer "skip the

operation for this mode" over "match a hardcoded address".

B. API-surface drift in the JIT kernel shims (jit_hw)

Upstream renamed/refactored a kernel-facing API (an enum collapse, a template signature change, a moved header) and the emule shim still exposes the old surface, so kernels fail to JIT-compile with undeclared identifier.

Fix principles:

  • Mirror the upstream change exactly in the shim — same enum/struct/signature

surface — using the upstream PR diff as the spec.

  • Preserve emule's bodies (host memcpy / no-op / emulated sync). Only the

option/signature surface changes; the emulated semantics stay.

  • Migrate all dependents in the same surface (the header's call sites, sibling

headers that reference the renamed symbols) so nothing dangles. Leave genuinely independent/older surfaces (e.g. a separate experimental/ API with its own self-contained definitions) alone.

  • Keep .claude/references/structure.yaml in sync when symbols are added/removed.

Step 5 — Wire the fix through the repo chain

A fix can span tt-umd → tt-metal → tt-emule. Order and push mechanics matter:

  • Push dependencies before dependents. If the metal commit bumps the umd

gitlink to a fix commit, that umd commit must be on its remote first, or the submodule pointer dangles for CI.

  • Never push without explicit go-ahead; for tt-emule's own PR, bump

tt-metal-pin.txt to the (pushed) metal fix commit, using the full SHA.

  • Re-sync the vendored e2e demo on any pin bump. The end-to-end model lane

(docs/e2e-models.md) drives an emule-vendored copy of simple_text_demo.py at tt-metal/tests/emule/models/test_tt_transformers_text_demo.py, which tracks upstream only as of the pin SHA in its header. When you move tt-metal-pin.txt, re-run git show <new-pin>:models/tt_transformers/demo/simple_text_demo.py into the vendored file and re-apply its two # emule: markers. The demo is not in the C++ regression, so this drift is otherwise uncaught until the nightly e2e lane runs against the new pin.

  • jit_hw edits need no C++ rebuild. Shims are compiled at JIT time, so a

jit_hw change takes effect on the next kernel compile — clear the JIT cache and re-run; iteration is fast.

Step 6 — Verify recovery and prove no regression

  • Oracle before/after on the built fix: the BAD signature is gone.
  • Broad sweep: run one test per previously-failing file. The headline

invariant is that the crash-signature count goes to zero; PASS count rises with no new failures.

  • Triage every residual into exactly one bucket: (a) caused by the fix — must

be zero; (b) the original bug — must be zero; (c) a separate pre-existing gap that was merely unmasked once earlier crashes stopped. A fix that doesn't touch op math can't cause a data mismatch — reason from that.

  • Don't conflate a sweep with the curated suite. A one-test-per-file sweep

runs the first collected test in each file, which is usually not what the curated regression script selects (it uses -k/::node subsets). Before treating a sweep failure as real, check whether the curated script even exercises that node:

  • Map the failing node to the script's exact run_pytest args and resolve

membership with --collect-only.

  • Validate your collection-parsing first. Verbose pytest config (-vvs)

makes --collect-only print a <Function ...> tree, not file::node lines — grep for the <Function ...> reprs, and confirm your grep matches a test you know is selected before trusting any zero counts. (Class-based tests are Class::method; a bare file::method nodeid will error.)

Step 7 — De-scope and track what isn't yours

  • A residual that is a separate pre-existing gap (e.g. a missing compute-LLK

shim, a known SIGFPE, an op-correctness mismatch) should be filed and tracked, not used to block the uplift, and not "fixed" by widening the uplift fix's scope.

  • If the uplift's root cause is upstream-intended (a legitimate refactor), the

emule side is what must adapt — the fix belongs in the emule layer (umd SW chip / jit_hw), not a revert.

  • Note any cross-arch implication (a shim/runtime fix for the supported arch often

also addresses the same root cause flagged for an out-of-scope arch) but don't chase the out-of-scope arch in the same change.

Anti-patterns (consolidated)

  1. Concluding from `git log -S` / `git show` / blame. They mislead on moved

code, squashes, and shallow submodules. Bisect (build+run) is authoritative.

  1. Trusting a green CI label. Read the artifact; allowlists hide aborts.
  2. A range-based guard that swallows real errors. Gate on the specific

case/mode, not "anything that would fault".

  1. Intercepting low-level I/O to dodge a high-level behavior change. Preempt

the operation at the layer that owns it.

  1. Hardcoding magic values when a canonical accessor or a mode flag exists.
  2. Pushing a dependent before its dependency (metal gitlink before the umd

commit it references).

  1. Reading a one-test-per-file sweep as the curated suite. Check

--collect-only membership, with a validated parser.

References

  • /compute-llk-bringup — for a single missing compute <op>_tile shim (the

common Class-B fix when the drift is one op).

  • /memory-debug — for data-corruption residuals (Class-C, op math).
  • docs/api-injection-points.md — where emule intercepts the pipeline

(helps choose the "right layer" in Step 4A).

  • references/emule-mapping.md — HW concept → emule strategy vocabulary.
  • BUILD_GUIDE.md — build/test setup, the targets to build for an oracle, and the

regression scripts.

Tâches connexes

du même dépôt

Autres Skills

Tous les Skills
tenstorrent
Communauté

index-based-ops

Bring up or debug index-based compute ops (TopK, Sort, Argmax) in tt-emule — ops that return values + indices, where ties make the index choice non-unique. Use when implementing one of these ops or triaging its value/index test failures.

installations
1
GitHub Stars
5
Mis à jour
16 oct.
tenstorrent
Communauté

memory-debug

Diagnose data-corruption failures in tt-emule operations (wrong values, partial zeros, off-by-N row/col writes). Use when a regression test reports ATOL 1e-3 on otherwise-functional kernel paths.

installations
1
GitHub Stars
5
Mis à jour
16 oct.
tenstorrent
Communauté

verify-mock

Verification checklist to walk before declaring a tt-emule mock complete. Use after implementing a stub (typically at the end of /implement-mock or /compute-llk-bringup) to catch the recurring failure modes — signature drift, no-op math, missing format dispatch, JIT-cache staleness, regression baseline drift.

installations
1
GitHub Stars
5
Mis à jour
16 oct.
tenstorrent
Communauté

workarounds

Registry of deliberate, non-ideal workarounds currently live in tt-emule (hacks we accepted to make something pass, NOT normal development practice). Read this before adding any new workaround, and when touching code that carries one — each entry records what the hack is, why it is not the right fix, the real root cause, and the path to removing it.

installations
1
GitHub Stars
5
Mis à jour
16 oct.