Repository navigation
cuTile: audit generated source locations (optional follow-ups to #1379) #1475
Description
Follow-up to #1379.
Merge status (2026-10-22)
#1602 is merged into main as 9718b4386. It implements const-generic array and generated reduction-region origins, and preserves all distinct source/caller origins for host-relocated and deduplicated checks without changing the shared public API. Exact source-location regressions, assembler checks, live CUDA-GDB tests, and all 25 applicable CI checks passed.
The combined injected-assertion item below remains open because actual backend trap/coredump attribution is still tracked in #1379. #1602 also protects the existing in-place/hoisted device-check and user/loop-assumption provenance. Wrapper assumptions remain with #1480; short-circuit/folded expressions remain with #1528; generated-constant fallback remains with #1385. The debugger resolver remains NVIDIA/cuda-gdb#29 / #1476.
Release integration is being validated in #1613 against release/v0.5.x; #1606 tracks the other v0.5.0 debugging candidates. This audit remains open for the unfinished items.
The proposed frontend fix passes the consuming statement's span into constant compilation, so generated constants point to the statement that needs them instead of the module header. Source inspection found other places where synthesized syntax or type spans can supply misleading locations.
The tasks below are optional, independently scoped follow-ups. They are not prerequisites for landing the targeted constant-span fix. These are source-level audit findings, not individually reproduced debugger failures.
Optional follow-up tasks
- Short-circuit boolean constants.
a && bgenerates a decidingfalseconstant;a || bgeneratestrue. Pass the logical expression's span throughcompile_bool_constantand the shared constant builder. See compile_binary_op.rs:293. - Constant-folded expressions. When an expression such as
2 + 3becomes a constant5, retain the original expression's span. This path callscompile_constant_from_exact_boundsdirectly, bypassingcompile_constant. See compile_binary_op.rs:674. - Injected assertions and their supporting operations. Retain the checked access's source location and inline chain on the assertion, comparisons, and generated constants. The assert builder already uses the access span; validate provenance through placement, folding, and backend trap lowering. Cover in-place checks, checks hoisted before loops, and checks relocated to the host launcher. Host
LaunchCheckcurrently carries only a predicate and cause string, so retaining source origins through relocation and deduplication needs explicit treatment. See placement, assert emission, and LaunchCheck. - Const-generic arrays. Arrays are reconstructed from formatted strings and passed to
compile_expression, so their generated literals bypasscompile_constant. Give them an explicit source-location policy; bindings materialized at function entry may need the current function/parameter definition rather than a consuming statement. Cover entry bindings, inlined functions, and inlined methods. - Generated reduction bodies.
reduce_sum,reduce_prod,reduce_min, andreduce_maxsynthesize bodies such as{ curr + prev }withsyn::parse_quote!. Attribute their generated operations to the reduction call, including operations inside the reduction region. See compile_intrinsic.rs:1674. - Generated kernel entry wrapper and the inlined user kernel. The generator resets every span, including its final call into the user kernel, to
Span::call_site(). Attribute generated wrapper setup and its call into the user kernel to the original user function's signature. Preserve the user kernel body's own statement locations and inline scope. Validate both the wrapper frame and the inlined user frame. Decide separately whether generated setup should be a user stepping point; a source origin is not necessarily a breakpoint location. See kernel_entry_generator.rs:648. - Generated assumes. Wrapper assumptions for nonnegative dimensions/strides and divisibility/alignment are synthesized from strings and inherit the wrapper's reset spans. Attribute wrapper-generated assumptions to the original user function signature, consistent with the wrapper policy. Assumptions generated within user code should retain the originating operation's span. Loop-derived assumptions already receive the loop span; preserve that behavior. User-written assumptions should retain their own call span. An assumption need not produce a separate machine instruction, but its location can propagate onto existing instructions during optimization. Test both IR/bytecode provenance and the emitted line tables. See wrapper generation, assume emission, and loop assumptions.
Validation
For each task taken on, check the affected operations' exact source locations and preserve the helper scope and inline call chain through ir_location. Include nested and cross-file helpers where relevant. Existing debug_info.rs tests provide a starting point for IR and bytecode regressions.
The separate tileiras assertion-lowering problem described in #1379 still needs validation at the actual SIGTRAP and GPU coredump. Correcting frontend locations or passing a source-breakpoint test alone does not establish that the missing trap frames are fixed.
Optimization audit (2026-10-10)
Full device-debug mode already disables in-kernel preheader hoisting, while proof discharge and host-launch relocation remain enabled. Both existing placement regressions passed with cargo test --locked -p cutile --test check_placement_ratchet device_debug_ on commit bafffe45662a5cdc064a1c6674c6c02d466a58e0. Those tests pin placement counts, not source provenance: add location/scope checks and cover optimized line-info mode as well. See compiler policy and tests.
The inline caller-line breakpoint problem is tracked separately in #1476, including a proposed resolver fix; an inside-helper breakpoint is only the temporary workaround.
Live validation of the agreed debugging behavior (2026-10-10)
Tested on the same repository revision (bafffe45662a5cdc064a1c6674c6c02d466a58e0), RTX PRO 2000 Blackwell laptop GPU, driver 610.43.02, tileiras/cuda-gdb 13.3. This is validation of current behavior, not an implemented fix.
| Area | Observed result |
|---|---|
| Entry wrapper | Does not meet the agreed policy. An address breakpoint at the actual kernel entry stops on module line 9, although the function signature is line 13. The instruction is LDC R1,c[0x0][0x37c]. Source stepping then goes from module line 9 to store line 18, back to module line 9, and then to line 18 again. Some module-header locations also come from synthesized constants inside the user kernel, so wrapper correction alone will not remove all such stops. |
| Assumes in full debug | In the tested kernels, no separate executable stops. An explicit assumption at line 15 is present in Tile IR/bytecode but absent from the cubin line table; its breakpoint resolves to line 16. Removing that explicit assumption produces identical SASS (88 instructions in full debug, 40 in optimized line-info mode). |
| Generated-assume location propagation | In a diagnostic copy of each IR module, only assumption locations were assigned distinct sentinel lines. All SASS instruction streams remained identical. Full debug emitted no sentinel line locations. In the optimized loop, however, the loop-derived assumption's sentinel appeared on existing SASS instructions. Thus absence of a separate assumption instruction does not establish that its span cannot affect source attribution. |
| Injected assertion before lowering | The bounds-check Assert carries the checked load's line 32 and inline chain read_tile:32 -> checked_loop:39 -> wrapper:9. Full debug keeps the check inside the loop: (discharged=0, hoisted=0, in_place=1). Optimized line-info mode moves it before the loop: (0,1,0), retaining that IR source chain. Neither variant has host-launch checks in this reproduction. |
| Assertion failure after lowering | Does not meet the agreed policy. The diagnostic names the correct load at line 32, but live SIGTRAP and a GPU coredump captured at that stop both show line 42 (the kernel's closing brace). The helper stack survives in this reproduction; it does not reproduce every symptom of #1379. The disassembly itself maps generated BPT.TRAP instructions to line 42. |
| Source stepping through the loop | Still misleading even with full debug and zero frontend hoists: stepping visits the module header and lines outside the displayed helper's source body. Disabling frontend hoisting is not sufficient to establish correct stepping or backend inline ranges. |
The failing-check backtrace, reproduced both live and when reopening an on-demand GPU coredump:
src/bin/location_audit.rs:32: ... partition access out of bounds ...
Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
#0 read_tile@location_audit:30:7 ... at src/bin/location_audit.rs:42
#1 checked_loop@location_audit:37:7 ... at src/bin/location_audit.rs:39
#2 checked_loop_entry ... at src/bin/location_audit.rs:9
The source lines in that test are:
// 30
fn read_tile<const B: i32>(x: &Tensor<f32, { [-1] }>, idx: i32) -> Tile<f32, { [B] }> {
let p = x.partition(shape![B]);
let tile = p.load([idx]); // 32: compiler injects the bounds check here
tile
}
#[cutile::entry()]
fn checked_loop<const B: i32>(out: &mut Tensor<f32, { [B] }>, x: &Tensor<f32, { [-1] }>, n: i32) {
for j in 0i32..n {
let tile = read_tile::<B>(x, j); // 39
out.store(tile);
}
} // 42: incorrect trap locationUse B=128, input extent 512, output extent 128, one block, and DebugInfoLevel::Full. n=3 completes normally; n=5 fails the injected check. The on-demand dump was captured with generate-core-file --no-cpu --cuda-lightweight --cuda-core-file <path> after SIGTRAP, then reopened with target cudacore <path>.
Six existing debug_info tests also passed, including the real assembler/disassembler inline-DWARF test. Their current assertions do not cover these wrapper, stepping, or generated-trap failures. Add targeted regressions when implementing each fix. The observed wrong trap location belongs with the backend-lowering investigation in #1379, independently of frontend constant/wrapper spans and #1476's call-line breakpoint resolver.
Implementation status (2026-10-10)
- Dmitry's call-line breakpoint: NVIDIA/cuda-gdb#29 resolves missing call-line rows through the existing inline call-site metadata before falling forward to the next line. The exact pending
break 132now stops at the first validation comparison with all helper frames present in the live GPU reproduction. - Entry wrapper and wrapper-generated assumes: fix(cutile): attribute generated entry setup to the user signature #1480 replaces the generated wrapper's module-header span with the original function signature. The regression fails before the change; all nine debug-info tests pass after it. Live CUDA-GDB confirms the wrapper frame and first entry instruction use the signature while the nested cross-file helper chain is preserved. The full repository suite results will be recorded in that PR.
- Next priority: injected assertions. The frontend assertion already retains the checked access and inline chain in the tested in-place and hoisted cases. The observed live/core
BPT.TRAPline mismatch needs the separate backend-lowering work in [BUG]: cuTile: at acuda_tile_assert!trap, cuda-gdb reports the module's first line and drops the inlined helper frames #1379; neither PR claims to fix it. Host-relocated check provenance remains a separate frontend audit item. - Synthesized-constant spans, including the shared constant builder, remain outside these changes and with the ongoing [BUG]: cuTile: at a
cuda_tile_assert!trap, cuda-gdb reports the module's first line and drops the inlined helper frames #1379 / fix: correct cuTile source locations for generated constants #1385 work.
Activity
GiGiKoneti commentedon Oct 12, 2026
@elibol, I'd like to pick up the remaining frontend source location items from this audit:
compile_binary_op.rs)compile_binary_op.rs)_function.rs&compile_inline.rs)compile_intrinsic.rs)Saw you already landed #1480 for the entry wrapper setup. Soo , will open a targeted PR covering the items above along with regression tests in
debug_info.rs. Did I miss anything else ?elibol commentedon Oct 15, 2026
I don't think you've missed anything else. Let’s start with the short-circuit boolean constants and a regression test in debug_info.rs. That one’s yours; we can split the remaining items into separate PRs.
Please check for overlap with #1385, and verify the constant gets the logical expression’s location while preserving the inline chain. Feel free to open a draft early for feedback.
GiGiKoneti commentedon Oct 16, 2026
@elibol
I opened draft PR #1528 covering the short-circuit boolean constants and a regression test in
debug_info.rs!Verified that the expression location and inlined helper chains are preserved.
fix(cutile): retain source origins for generated code and host checks (…