Repository navigation
feat: [rocjitsu] Adding EXEC state tracking for liveness - #7470
Conversation
accauble
left a comment
There was a problem hiding this comment.
This is my first pass of the codegen. Haven't reviewed the analysis yet. Mostly nit-picky things and a couple questions I have that could improve the code if it is feasible.
ec0cf8e to
0126c3d
Compare
atgutier
left a comment
There was a problem hiding this comment.
This overall LGTM.
An agent review flagged a few things that would be good to address:
MINOR (recommended before merge)
1. exec_state.cpp:131-161 — loop-header-as-scope-entry seeding edge case (soundness-adjacent). A
block that is both the kernel-scope entry and the target of an in-scope back-edge (loop header at
offset 0) is classified non-entry and seeded optimistically Full instead of Unknown — the only path
by which the forward MUST analysis could compute Full where it should be Unknown (→ an unsound
kill). In practice the absolute first block of a kernel cannot have an in-scope back-edge, so it's
robustness, not an observed bug. Fix: treat a block as entry-for-seeding if it's the scope leader OR
has no in-scope predecessor.
2. RESULT_COPY/RESULT_OR derived by string-matching semantic_class/operation while
WRITES_EXEC/IGNORES_EXEC come from the AST. A future EXEC-writing copy/or opcode under a class not
in the hand-list silently loses the combinator flag (degrades safe, but latent drift). Fix: derive
the combinator from the same AST path so the two flag sets can't diverge.
NIT
- exec_state.h:7-13 file-level @details ends mid-sentence ("two-point lattice:") without enumerating
Full/Unknown.
- Internal enums (ExecWrite, Combinator) lack explicit : uint8_t underlying type while public
ExecState has it.
- binary_translator.cpp:567 benign uint8_t→uint32_t wave-size widening; an assert(wave_size == 32 ||
64) in the ExecMaskAnalysis ctor would document the invariant.
- s_cbranch_execz/execnz get IGNORES_EXEC (correct — branches aren't lane-masked) but no READS_EXEC.
These branches do test EXEC; whether that should mark EXEC live for liveness is a semantics call
worth deciding. If yes, it's a generator change (emit READS_EXEC for those opcodes).
Fixed the all the issues except comment #2. I think moving the RESULT_COPY and RESULT_R flags into the AST could be a follow-up PR. |
✅ All Checks Passed — Ready for Review
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🎉 All checks passed! This PR is ready for review. |
Pre-commit check failed⛔ pre-commit failed Please run locally:
This repo uses |
accauble
left a comment
There was a problem hiding this comment.
Still need to update a default wave size in exec_state.h.
|
@theSK2005 could you rebase please? |
dde6c35 to
f0d600a
Compare
There was a problem hiding this comment.
A couple of comments, the other one is above: #7470 (comment)
kuhar
left a comment
There was a problem hiding this comment.
This seems to be ~10% slower on rccl hotswap translation -- we need to bring the overhead down before this can land.
Does the rccl translation need the EXEC state tracking for correctness? |
|
I found one meaningful optimization: 20a238e
If we can, we should have a very quick check that answers this and decides whether to skip the analysis or not. |
…rom using EXEC state tracking
kuhar
left a comment
There was a problem hiding this comment.
Original anchor: emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/analysis/exec_state.h:77
Could we make before_ sparse and store only instructions whose incoming state is Full? before() already returns Unknown for missing entries, and liveness only distinguishes the Full case, so an unordered_set<const Instruction *> full_before_ preserves the query contract while avoiding a map node and state value for every instruction.
I prototyped this as local commit 20a238e3de (5 insertions, 4 deletions). On the exact 224,676,512-byte RCCL object, three CPU-32-pinned runs reduced median CPU time from 184.46 s to 171.64 s (-6.95%) and wall time from 184.53 s to 171.72 s (-6.94%). Median peak RSS was effectively flat: 15,977,508 KiB to 15,977,872 KiB (+0.002%). A matched perf stat run showed task-clock -7.51%, cycles -7.93%, instructions -1.88%, branches -1.95%, branch misses -2.96%, and about 71k fewer page faults. All runs produced the same 224,676,512-byte output with SHA-256 7c843d92dddbe69b1944a84eabd3d8d6a64304c6a84f1a03af638a16f7d15d9b; all 76 focused liveness, EXEC, and gfx1250 translation tests passed.
d511903 to
4cc3f37
Compare
Reconciles this branch's evolved EXEC/register-spilling work with develop's official EXEC-tracking merge (#7470) and picks up develop-only changes (#9793 GuestKfd, scoped_cfg_edges extraction, analysis-layer additions). Conflict resolution: - amdgpu_code_object.{h,cpp} and tests/code/amdgpu_code_object_test.cpp: kept this branch's version. Its kernel_descriptor_scan / KernelDescriptorInfo refactor supersedes #7470's AmdGpuCodeObject::kernel_wavefront_size and ::kernel_entry_text_offsets members, which are removed here. - patch/instrumentor.cpp: kept this branch's body (scan_kernel_descriptors + the kernel_wavefront_size free function), and added develop's analysis/exec_state.h include -- required because develop's LivenessAnalysis now holds a unique_ptr<ExecMaskAnalysis>, whose complete type is needed wherever a LivenessAnalysis is destroyed (instrumentor.cpp constructs one). - tests/patch/instrumentor_test.cpp: kept this branch's version; develop's added cases exercised the removed members (covered here by kernel_descriptor_scan_test.cpp instead). Adopted develop's analysis layer (liveness 4-arg exec signature, exec_state/def_use, scoped_cfg_edges.{h,cpp}, binary_translator refactor) which this branch had not modified. Builds clean.
Eleven conflicts, all from #7470, which moved the scalar operand resolvers out of the codegen template and into shared/scalar_operand_resolve.h. The generated per-arch operand.cpp files now emit only their M0 encoding value and call the shared header, so ten of the eleven are that substitution and take develop's side unchanged. The eleventh is the template those files came from. This branch had added TTMP routing to the emitted resolvers: encoding values 108..123 name the trap handler's private scratch file, which hardware banks separately from the wave's SGPR allocation and which the CP seeds with the dispatch identity rocm-dbgapi reads back out of the CWSR area. Sending them through read_sgpr()/write_sgpr() lets a shader that never traps clobber them, and makes the debugger read wave identity out of whatever SGPR happens to alias. develop's header sends them there. Take develop's refactor and carry the routing into the header instead, at all four sites (32- and 64-bit reads, 32- and 64-bit writes), keeping the file's own literal-selector idiom rather than importing this branch's kTtmpSelector* constants across the vm/ layering. Gfx1250B0ToA0Library.ReportsTranslatorDiagnosticsAndRequiredWork and HsaHotswapHookTest.RendersTranslatorDiagnosticsAndDumpsFailedSource, noted as pre-existing develop failures in the previous merge, are fixed upstream by 1e3a135 and now pass.
PR #7470 had a bad merge with PRs #8933 (intended to bring in register_exec_fn) and #9099 (intended to remove register_exec_fn) in _generator.py. Prior to PR #7470, develop didn't have this function and PR #7470 brought it back in when it shouldn't. Regeneration currently fails on develop, this fixes it. No changes to generated ISA. ## Test Plan Reran all rocjitsu tests ## Test Result All pass ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md.
Exposed flags to label instructions that ignore EXEC or write to EXEC so it is accessible by liveness. Then iterate EXEC state per instruction in reverse post order so that liveness VGPR/accVGPR kills can be done properly. Moved some resolve_scalar functions into a shared header to reduce code overlap.
Defer handling instructions that require EXEC to be AllOnes.
Addresses Issue #7025.