Skip to content

feat(hipdnn): report kernel ingestor runtime impact - #14227

Merged
GarbyGilbertAMD merged 16 commits into
developfrom
users/ggilbert/ALMIOPEN-2834/ingestor-impact-analysis-skill
Nov 6, 2026
Merged

GarbyGilbertAMD merged 16 commits into
developfrom
users/ggilbert/ALMIOPEN-2834/ingestor-impact-analysis-skill

Conversation

@GarbyGilbertAMD

@GarbyGilbertAMD GarbyGilbertAMD commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A kernel ingestor change can build cleanly and still break: the ingestor C++ compiles only with its flag ON, and a rocKE kernel's argument list, launch geometry and applicability rules are restated by hand in C++, test pins and descriptors. This adds the hipdnn-ingestor-impact skill, which reports those contracts and the kernel support predicates for any PR, branch, commit range or uncommitted change, and an IMPACT lane in hipdnn-deep-review that runs the same scripts and turns their results into findings.

JIRA ID : ALMIOPEN-2834

Risk Assessment

Risk 2 of 5. Review tooling only; no product code, build or CI file changes. impact_emit.py runs rocKE code from the analysed revisions, the same exposure as building them, in a child process with an allow-listed environment that drops credential variables (it keeps HOME and network access) and a timeout that ends its whole process tree.

ASIC Coverage

No ASIC impact: the scripts read git objects and run rocKE on the CPU only. Passing PR CI is sufficient.

Testing Summary

Testing Checklist

  • AI tooling tests - python -m pytest projects/hipdnn/tools/ai/tests - Status: Passed (551 passed, 10 skipped, one commit before this head; at this head test_ingestor_impact_emit.py passed in full and the changed test_ingestor_impact.py cases passed)
  • Skill validation - python projects/hipdnn/tools/ai/validate-skills.py - Status: Passed (17 skills)
  • Acceptance - impact_emit.py run and impact.py report --repo . --base <rev> --head <rev> [--target-tip <rev>] [--emit <json>] on the ranges above - Status: Passed
  • PR CI - GitHub PR checks - Status: Pending

Technical Changes

  • hipdnn-ingestor-impact/scripts/impact.py: reads git objects only and compares the merge base, the head and the head merged into the target tip. Each row is mismatch, changed, inert or outside, and the report ends in a Pack-time: line and Runtime impact: BREAKS | NEEDS-DEVICE <archs> | NONE | N/A. Launch-surface symbols resolve as launch_surface.py --check resolves them, a host-side surface function is compared with the code it reaches, and each static C++ argument list is compared with the builder it matches, the declared one first. Device arch lists come from the shipped descriptors' arch arrays, and plugin SDK headers in the loader's include closure are on the surface.
  • hipdnn-ingestor-impact/scripts/impact_emit.py: builds every shipped rocKE variant at both revisions on the CPU, multi-launch UKDs included, and compares arguments, IR digests and COMGR inputs, kernel by kernel for a KernelModule. It also puts each builder's support predicate, as the packer resolves it, to one-field variations of the shipped specs: an accepted spec the builder raises on, or a non-positive value one predicate of a module accepts and another refuses, is a pack-time finding, marked when the base already had it. A probe that never returns costs only its tables.
  • hipdnn-ingestor-impact/SKILL.md, reference.md: the workflow, report format and severities; the contracts, row meanings, emitter and predicate-table rules, known gaps, and a judge-written C++ differential table for header-only restatements.
  • hipdnn-deep-review: the IMPACT lane maps judged rows, predicate-table findings and C++ table disagreements to findings and names the device runs that would settle changed rows; the runbook re-runs it when the target tip moves, partition.py triggers it on the ingestor paths and plugin SDK headers, and the leak sweep carries its IDs.
  • The skill validator, docs/ai-rules.md and the root AGENTS.md register the new skill.

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #14227      +/-   ##
===========================================
- Coverage    77.66%   77.66%   -0.00%     
===========================================
  Files         2903     2903              
  Lines       490379   490379              
  Branches     74220    74220              
===========================================
- Hits        380847   380846       -1     
+ Misses       84579    84578       -1     
- Partials     24953    24955       +2     
Flag Coverage Δ *Carryforward flag
TensileLite-CPP 51.04% <ø> (ø) Carriedforward from cd19d0c
TensileLite-Unit 84.34% <ø> (ø) Carriedforward from cd19d0c
hipBLAS 90.62% <ø> (ø) Carriedforward from cd19d0c
hipBLASLt 68.77% <ø> (ø) Carriedforward from cd19d0c
hipCUB 82.68% <ø> (ø) Carriedforward from cd19d0c
hipDNN 87.25% <ø> (-<0.01%) ⬇️
hipFFT 42.47% <ø> (ø) Carriedforward from cd19d0c
hipRAND 76.12% <ø> (ø) Carriedforward from cd19d0c
hipSOLVER 69.67% <ø> (ø) Carriedforward from cd19d0c
hipSPARSE 87.03% <ø> (ø) Carriedforward from cd19d0c
rocBLAS 69.20% <ø> (ø) Carriedforward from cd19d0c
rocFFT 49.54% <ø> (ø) Carriedforward from cd19d0c
rocRAND 57.32% <ø> (ø) Carriedforward from cd19d0c
rocSOLVER 76.74% <ø> (ø) Carriedforward from cd19d0c
rocSPARSE 83.54% <ø> (ø) Carriedforward from cd19d0c
rocThrust 93.15% <ø> (ø) Carriedforward from cd19d0c

*This pull request uses carry forward flags. Click here to find out more.
see 2 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.

@GarbyGilbertAMD
GarbyGilbertAMD marked this pull request as ready for review October 23, 2026 19:53
@GarbyGilbertAMD
GarbyGilbertAMD requested a review from a team as a code owner October 23, 2026 19:53
Base automatically changed from users/bharriso/hipdnn-deep-review-skill to develop October 27, 2026 16:16
@therock-pr-bot

therock-pr-bot Bot commented Oct 27, 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 Oct 27, 2026 •

Copy link
Copy Markdown

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

A change to the kernel ingestor can build cleanly and still break at run time. The ingestor C++ compiles only with the ingestor flag ON, and the contracts between a rocKE kernel and the C++ that launches it are kept by hand: the kernel's argument list is restated in native packs, test pins and descriptor signatures, and launch geometry, applicability rules and the shipped variant set have Python and C++ halves. A copy that falls out of step is caught on a device, in a flags-ON lane, or not at all, and two pull requests that each pass alone can break the target branch once both merge.

The hipdnn-deep-review skill gains an IMPACT lane for these contracts. impact.py reads git objects only and reports, per contract, what changed on which side between the merge base and the head, and also in the head merged into the target branch's tip, so a copy that landed on the target after the merge base is compared too. It judges content rather than paths: comment, whitespace and type-preserving data edits are inert, removed or re-signed Python symbols are traced to their readers through imports and base classes, and a surface file it cannot understand is reported as changed, never as safe. impact_emit.py builds every shipped rocKE variant at both revisions on the CPU, regenerating DVC-held catalogs with the generator's registry enumerator, and compares each variant's kernel arguments and emitted LLVM IR, which separates a kernel edit that changes shipped code from one that does not. Each run ends in one line: BREAKS, NEEDS-DEVICE with the architectures, NONE or N/A.

The lane brief maps these facts to findings, names the device runs that would settle what static analysis cannot, and hands CI coverage, build wiring and public API questions to the lanes that own them. The partition script scopes the lane's packet to the ingestor paths, the leak sweep accepts its finding IDs, and the skill's documentation describes the lane and the standalone command.

ALMIOPEN-2834
impact.py could report a change as safe when it was not. A removed Python function lost every reader that still imported it as soon as any other file defined a function of the same name, which per-architecture builders do by design. A code edit to the generic-target header produced no row and rolled up as no surface file. A builder whose argument changed type kept its declared C++ list reported as agreeing, because the pair was compared by name only. And an emitter output file was folded into any report, so a stale file from an earlier head, one family for an unrelated source, or a family that built no variant could turn shipped-module rows inert.

A removed symbol now keeps every reader bound to its old module. The generic-target header is a surface file in impact.py and a trigger in partition.py. Declared pairs compare each argument's size and kind as well as its name, resolving types held in module or function variables, and report when types could not be compared. impact_emit.py records the tree ids it archived, impact.py refuses an emit file made for other trees or with a malformed shape, and an all-equal emit folds only rows on modules a built family imports. A builder whose parameters change only in which are conditional, or a declared C++ list that shares too few names to compare, now gets a row instead of silence. A method removed from a base class and read through a subclass or an untyped receiver is reported as changed when the reading file's imports reach the class.

The emitter's child runs with an allow-listed environment that passes no credentials, in its own process group so a timeout ends the whole tree, and with the newest LLVM flavor both revisions know. Two variants with one name but different specs are recorded as a failure rather than collapsed. C++ classification follows comment line splices and raw strings with encoding prefixes. A skipped merged view stays changed whenever the change touches the surface, and CMake and requirements files are labelled as left to the BUILD and DEPS lanes in the rows and the rollup instead of reading as no impact. The lane brief documents the emitter failure rule and the remaining blind spots of the symbol check, and the runbook counts the emitter's two workers against the core budget.

ALMIOPEN-2834
The emitter compared shipped variants by their LLVM IR alone, so a change that reaches the code object without touching the IR folded every shipped-module row to inert. Dropping the COMGR optimisation level from -O3 to -O2 for every packed kernel read as no runtime impact. impact_emit.py now records each variant's COMGR inputs as well (the ISA name, the codegen options per kernel and the fresh-process compile flag), writes them as schema 3, and fails every variant when the packer pins a lowering backend other than Python. impact.py refuses older emit files, keeps a family with changed COMGR inputs from folding, and never folds rows on code that runs after lowering, rocke/helpers/compile.py and rocke/runtime/, because an equal IR digest says nothing about it.

A changed DVC pointer no longer folds either. The emitter regenerates only the variant list of a DVC-held catalog, which cannot prove the file's other fields equal.

Module reachability missed two paths. An import of a name from its package (from rocke.helpers import f) stopped at the package __init__, so a body edit of the module that defines f, which a shipped builder runs, read inert. The import now also reaches the defining module through the package's re-exports. And a module the packer reaches got only the architectures of the builders that happen to import it too, although the packer compiles every packed variant through it, so the device run it named missed the others. Such a module now ships on every architecture.

Three paths were reported as runtime impact that are not. A rocKE C++ engine source is inert while no provider build file outside rocKE names the engine's targets and the packer pins the Python backend, since no shipped kernel is then built by the engine. CI test-category lists are left to the BUILD lane. And a merge conflict with the target tip no longer discards the whole merged view: the conflicted tree is analysed, rows that read a conflicted file are dropped, and one row stands in for the conflicted files, so a contract race in files that merged cleanly is still found. The lane brief documents each rule and the remaining gaps.

ALMIOPEN-2834
The question the deep review's IMPACT lane answers, whether a change breaks the kernel ingestor at run time while the default build stays green, is also asked outside a review: on a single commit, a branch before it is opened as a pull request, uncommitted work, or before the ingestor flags are turned ON by default. Running a multi-agent review for that is out of proportion, and the scripts that collect the facts were reachable only from inside the deep-review skill.

The new hipdnn-ingestor-impact skill owns impact.py and impact_emit.py and answers the question on its own. It pins the revisions for each input form, runs the static report and, when rocKE or hkp_pack Python changed in code, the emitter, judges each row, and reports the rollup with the device runs that would settle what static analysis cannot. Its reference holds what both uses share: the contracts, the row statuses and rollup, the emitter's rules, how each row is judged, and the known gaps.

The deep review consumes the skill the same way it consumes the superbuild skills. The IMPACT lane calls the scripts from the sibling skill directory and keeps only what a review adds: where the evidence goes, when the lane stops, how a judged row becomes a finding, and its handoffs. impact.py no longer imports the review's helper library or reads its PIN.md; the lane passes the clone, base, head and target tip explicitly. The tests move with the scripts, the skill validator checks the new skill's scripts and command fields, and the skill catalogs list it.

ALMIOPEN-2834
@GarbyGilbertAMD
GarbyGilbertAMD force-pushed the users/ggilbert/ALMIOPEN-2834/ingestor-impact-analysis-skill branch from 86c2f6a to f019d5c Compare October 27, 2026 17:25
@GarbyGilbertAMD GarbyGilbertAMD changed the title feat(hipdnn): report kernel ingestor runtime impact in deep review feat(hipdnn): report kernel ingestor runtime impact Oct 27, 2026
@GarbyGilbertAMD
GarbyGilbertAMD marked this pull request as draft October 27, 2026 21:18
@GarbyGilbertAMD

GarbyGilbertAMD commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Temporarily back to draft so it can absorb some updates (eg the multi-launch). Seems to break against integrations using the latest and greatest.

… surfaces

New ingestor engines read as BREAKS when nothing was broken. A launch_surface entry whose python_source names a method, such as a launch_block defined on a tuning spec class, was reported as pointing at a missing function, because impact.py looked the symbol up at module level only while launch_surface.py --check accepts any function of that name. A declared kernargs pair whose function is a SignatureBuilder factory, which has no b.param list, was reported as a missing builder. Every static list of a multi-launch pack was compared with the one declared builder, so the reduce kernel's argument list disagreed with the segment builder. A copy of an argument list that lacks a parameter the builder newly adds only conditionally was counted as stale, although every variant that leaves the condition off still passes that list. And the emitter lowered a builder returning a KernelModule as a single kernel, so every variant of such a family failed and its rows could not be settled.

impact.py now resolves launch_surface symbols by name anywhere in the module and reports a function missing only when no function of that name exists. A declared kernargs pair compares each static C++ list with the b.param builder its names match, searched in the surface's module and the manifest's own source module with the declared function first, and names that builder in the row; a list that matches no builder is still compared with the closest one and reported as a mismatch, and a mirror that holds no KernelArgument list says so. A copy that still matches the new list without its conditional parameters is changed rather than mismatched.

impact_emit.py lowers a KernelModule kernel by kernel, as the packer does, and records each companion launch's argument list; its output is schema 4. impact.py folds it by comparing each static C++ list with the launch it mirrors, so a companion's changed arguments are checked like the primary's. The reference documents the pairing rules and the remaining gaps.

ALMIOPEN-2834
impact.py resolved a launch_surface python_source symbol through the def and class bodies of the module only, so a function defined under a module-level if, try or with passed launch_surface.py --check but read as missing, or went unseen when it changed. It now matches every function of that name anywhere in the module, as the generator's own check does, and compares all of them.

The reference now states when a C++ argument list that matches no builder is compared at all (it must share at least two names, and half of its own, with one), and quotes the rows a list built by helper calls produces, including that a dynamic list gives no pair row when only the builder changes.

New test cases cover a second function of the same name changing, an edit to the manifest's source builder alone (a body edit reads changed, an argument type change reads mismatch), and a pack that only the manifest's source builder can pair with an emitted companion launch, since the pack-name fallback cannot find it.

ALMIOPEN-2834
…IOPEN-2834/ingestor-impact-analysis-skill

# Conflicts:
#	projects/hipdnn/tools/ai/tests/test_deep_review_partition.py
@GarbyGilbertAMD
GarbyGilbertAMD marked this pull request as ready for review October 28, 2026 21:44
…n impact

The ingestor impact skill compared kernel argument lists and launch-surface
declarations between rocKE and their C++ copies, but never what either side
accepts. A support predicate could admit a spec its builder asserts on, a
module's companion predicate could accept a head count its primary refuses,
and a hand-restated C++ function could return a wrapped value for an input
the Python refuses.

While it builds each shipped variant, the emitter now records the specs the
build passes to the module's other build functions. After the builds it puts
each builder's support predicate, as the packer resolves it, to a table of
one-field variations of the shipped specs. A spec the predicate accepts and
the builder raises on is a builder finding. A value outside the sign range of
every shipped spec that one predicate of a module accepts and another refuses
is a sign finding. A finding the base revision's table also has is marked as
pre-existing, and the emit schema becomes 5.

impact.py lists the tables in their own section and prints a Pack-time line
above the runtime verdict. The line counts the findings a change introduces
and the older ones it ships for the first time, and stays apart from the
runtime verdict: the tables never change a row or the rollup. The skill and
the deep-review IMPACT lane gain a judge-written C++ differential table for
header-only restatements, a C++ tables summary line beside the two verdicts,
and a severity scale for every judged finding.

ALMIOPEN-2834
The ingestor impact report could call a change NONE or N/A when it was
not. Multi-launch UKDs, which list their launches under `sources`, were
neither rooted in the shipped closure nor built by the emitter, so an edit
to such a kernel read as reached by no shipped variant. A launch-surface
grid or block helper was compared as its named def alone, so a change in
the code it hands off to folded to inert on equal emitted IR. A flatc
header counted as regenerated beside any change under the generator or
the flatbuffers SDK, tests included, and plugin SDK headers that the
descriptor loader compiles in were outside the surface.

The closure now roots on every launch, and the emitter groups a
multi-launch UKD's launches by source, builder, spec and target as the
packer does, listing any it cannot read as not inspected; the emit schema
becomes 6. A host-side surface function is compared together with every
def, class and assignment it reaches by name. A flatc header is
regenerated only beside its own schema or flag file, and SDK headers in
the include closure of the ingestor SDK headers, with the generic-target
table's CMake template, are on the surface.

Device arch lists come from the UKD and KDP arch arrays with generic
targets expanded, and a file outside the shipped closure widens the arch
its path names to every arch a KDP named for it ships. With a target tip,
the emit fold also applies to merged-view rows whose module content is
the head's. A member read through a same-named class imported from
another module is no longer a reader of the removed one. A global_buffer
argument declared with a non-pointer size disagrees with a pointer
parameter, a path-only python_source locator is accepted, a static list
related to the declared builder is compared with it before a smaller
builder it happens to fit, and flatc_flags.txt is read as build input.
The registry enumerator stays in the emitter child's process group, so a
timeout ends it too.

ALMIOPEN-2834
A support-predicate probe calls the real builder on specs the predicate
accepts, and a builder that never returns on such a spec held the emitter
child until its timeout, losing the argument and IR comparison of every
shipped variant. The probes now run in a worker thread that a family's
budget bounds: a family past it is abandoned, its tables and those after it
read as timed out, and the document is still written. The emitter builds
its output document in one function that the report's loader is tested
against, so the two schema numbers cannot drift apart.

A b.param call under a with block, a try body or an if True branch always
runs, so it is no longer marked conditional, and a copy missing it is a
mismatch again. A static KernelArgument list with a comment inside a
literal's braces is read with the comment blanked rather than taken for an
argument. A launch-surface module that stops parsing is reported as such
instead of as a missing function, and an emit family without both base and
head params or companions is refused rather than crashing the fold.

The deep-review partition triggers the IMPACT lane on every plugin SDK
header and on the generic-target CMake template, which the report now
treats as surface. The lane's emitter block is self-contained, the rollup
format and the severity of a descriptor value the loader rejects agree with
the reference, and the reference names the probe cut, the probe timeout,
the emitter's environment variables and the git version the merged view
needs.

ALMIOPEN-2834
Brings in develop up to eca8c4d. The one conflict is the deep-review
skill's front matter: this branch rewrote its description to name the
IMPACT lane and develop added the prior argument to its argument hint, so
the merge keeps both.

ALMIOPEN-2834
…r impact

The rocKE LLVM flavor ladder moved from lower_llvm.py into
rocke/core/llvm_profile.py, which lower_llvm.py now imports it from, so the
emitter could no longer find a default flavor on any tree that has the new
module. It now reads the ladder from llvm_profile.py when the tree has it
and from lower_llvm.py otherwise.

The packer accepts hip and hsaco entries in a multi-launch UKD's sources
map. Such a UKD has nothing for the emitter to build, so it is skipped like
a single hip launch instead of being listed as not inspected, which would
have kept every emit file from folding. FlyDSL keeps one DVC-held catalog
per architecture in its own producer tree; only a catalog under the rocKE
tree now counts as shipping the rocKE kernels of its architecture.

ALMIOPEN-2834
The emitter proves that the base and the base plus the change emit the
same code. A merged-view row folded whenever its own file in the merge
equalled the head's, which says nothing about a target that changed a
module the change imports. Such a row now folds only when the target
changed no descriptor and no module of the shipped closure since the base.

A class a reader imports through its package's __init__ re-export now
resolves to the module that defines it, so a member removed from that
class while the reader still uses it is a mismatch again rather than a
read of some other class. A launch-surface helper's reach also follows
module aliases, so code it calls as m.f through import a.b as m or from a
import b as m is compared with it. The plugin SDK include closure follows
quoted includes beside the including header, and an emitter row for a
generic-target family names the member architectures it was built for.

ALMIOPEN-2834
A reader's class is resolved through the repository first, but when it
does not resolve to a repository class the old rules still decide: a
name imported from another module (an external package included), or a
def or class of that name defined in the file, is not the defining
module's class. Dropping those rules read a member call on such a name
as a removed member still in use.

The emitter lists a descriptor whose kernel source is not an object, or
whose kind, source, builder or target is not a string, under
not_inspected instead of failing its child process on an unhashable
family key.

New test cases cover the merged-view fold guard for a target that edits
only a descriptor, the generic-target member fallback on every emit row
kind, and a quoted SDK include resolved from the include root. The
deep-review runbook, the skill's trigger text and the emitter docstring
now match the code.

ALMIOPEN-2834
When a reader's class does not resolve through the repository, only an
import from outside the repository now proves it is another class. An
import from a repository package whose export the resolver cannot follow
(an alias such as `Space = waves.Space`, or a lazy export) may still be
the defining module's class, so its read of a removed member stays a
break instead of being dropped.

The emitter lists a descriptor whose `name` is not a string under
not_inspected, where it used to fail its child process on an unhashable
key. A single-launch descriptor without a `name` is named by its `id`,
as a multi-launch one already was. The launch string check lives in one
helper, the docstring lists every non-rocKE packer kind, and new tests
cover a missing or null `kernel_source` and the kpack and embedded
source kinds.

ALMIOPEN-2834

@BrianHarrisonAMD BrianHarrisonAMD 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.

LGTM

@GarbyGilbertAMD
GarbyGilbertAMD merged commit dd51ecd into develop Nov 6, 2026
37 of 38 checks passed
@GarbyGilbertAMD
GarbyGilbertAMD deleted the users/ggilbert/ALMIOPEN-2834/ingestor-impact-analysis-skill branch November 6, 2026 21:04
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.

2 participants