Skip to content

[FEAT] Add a compiled mode to the sanitizer - #480

Open
mark14wu wants to merge 1 commit into
ir-mode-corefrom
ir-mode
Open

mark14wu wants to merge 1 commit into
ir-mode-corefrom
ir-mode

Conversation

@mark14wu

Copy link
Copy Markdown
Collaborator

Summary

Adds a compiled mode to the sanitizer: Sanitizer(compile=True) or tile-sanitizer --compile script.py. Each launch is checked statically on the host-compiled TTIR instead of being interpreted. The kernel is not launched, so no GPU is needed and output tensors are not written.

Stacked on #479 (base branch ir-mode-core); review and merge that one first.

Behaviour

  • Every autotune config of a launch is checked with Z3 against that launch's scalar arguments, grid and tensor layouts. A launch is ok only if every config is proven in bounds for those arguments.
  • Findings, each with a witness (program ids, lanes, loop iteration):
    • out-of-bounds, against the view's element footprint (gaps of strided views count, as in eager mode);
    • integer-overflow of fixed-width address, mask, branch and loop values;
    • division-by-zero.
  • Whatever cannot be modeled is unsupported with a typed refusal kind; a solver unknown / timeout never counts as a proof, and each query has its own timeout.
  • A kernel that does not compile for the target (e.g. num_ctas>1 on the default cuda:89) is unsupported and the program continues; a call that does not bind the kernel's signature raises exactly as it does untraced.
  • Each launch records an IRVerdict with per-config verdicts in Launch.records; tilelens.save() round-trips it.

Evaluation

Differential corpus of 223 kernels against a concrete oracle (Triton's interpreter checking every access): 0 false proofs and 0 false out-of-bounds findings on Triton 3.6 and 3.8. 25 cases (~11%) are unsupported, mostly data-dependent masks, early returns / scf.while, gathers and multiple loops.

Testing

Full relevant set, CPU only: Triton 3.6 1379 passed / 31 skipped; Triton 3.8 1395 passed / 23 skipped. Only the pre-existing failures listed in the base PR remain.

Known limitations

  • ok holds for the launch's own arguments; since kernels are not launched, a later launch that reads an earlier launch's outputs sees unwritten values.
  • One loop per kernel; scf.while, early returns, calls, gathers and data-dependent masks/branches are reported as unsupported.
  • The default target is cuda:89; kernels that need another target can pass target= or set TILELENS_IR_TARGET.

Sanitizer(compile=True), or tile-sanitizer --compile, checks a launch
statically on the host-compiled TTIR instead of interpreting it. The
kernel is not launched, so no GPU is needed and outputs are not written.

- Every autotune config is checked with Z3 against the launch's scalar
  arguments, grid and tensor layouts; a launch is ok only if every config
  is proven in bounds.
- Findings: out-of-bounds (against the view's element footprint, gaps
  of strided views included), integer-overflow on address, mask, branch
  and loop values, and division-by-zero, each with a witness.
- Anything that cannot be modeled is reported as unsupported with a
  typed refusal; a solver unknown or timeout never counts as a proof.
  A kernel that fails to compile for the target is unsupported and the
  program continues; a call that does not bind raises as untraced.
- Each launch records an IRVerdict with per-config verdicts in
  Launch.records.
@mark14wu
mark14wu added this pull request to stack #481 September 29, 2026 20:19
@mark14wu
mark14wu marked this pull request as ready for review September 29, 2026 20:20
@github-actions

Copy link
Copy Markdown

Performance Benchmark

Benchmark main (min) PR (min) Change Samples
gemm 0.105s 0.106s +1.1% 20 / 20
gemm_oob 0.117s 0.118s +0.6% 20 / 20
indirect_load 0.022s 0.022s +0.2% 20 / 20
nested_loop 0.235s 0.238s +1.3% 20 / 20
block_pointer_loop_advance 0.126s 0.127s +1.2% 20 / 20
liger_jsd 0.139s 0.140s +1.2% 20 / 20
flaggems_layernorm 0.393s 0.399s +1.4% 20 / 20
swiglu 0.172s 0.173s +0.6% 20 / 20
cross_entropy 0.982s 0.987s +0.6% 20 / 20
fused_linear_jsd 0.210s 0.213s +1.4% 20 / 20
Total 2.500s 2.523s +0.9% N/A

Iterations: 1 warmup + 20 measured
Samples are shown as main / PR; long pytest benchmarks may use fewer samples.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant