Domain-guard triage: debug remarks at analysis-fact consumers#2655
Open
kimjune01 wants to merge 2 commits into
Open
Domain-guard triage: debug remarks at analysis-fact consumers#2655kimjune01 wants to merge 2 commits into
kimjune01 wants to merge 2 commits into
Conversation
Add DOMAIN_GUARD_TRUST, an LLVM_DEBUG one-liner emitted at each rewrite that trusts a domain-narrowing analysis fact (non_negative / no_nan). Instruments the five consuming sites on main: ReduceMaxMinMulPositiveScalar, the two CompareSimplify non_negative folds, the CompareSimplify no_nan fold, and AbsPositiveSimplify. The predicate and trusted value are already on the IR via setGuaranteedInIR; the remark adds the only missing bit -- the consuming rewrite's name -- plus a grep anchor ([domain-guard]) and a time-ordered trace, so a later domain-narrowing NaN is one grep from the rewrite that produced it. Zero always-on cost: LLVM_DEBUG compiles out under NDEBUG; no lit test uses -debug-only, so FileCheck is unaffected. LogSimplify's log(a*a)/log(a*b) folds consume no fact on main (that is EnzymeAD#2570); they become remark sites once their guard lands -- the shared macro makes that a one-liner. No local build available (heavy LLVM/MLIR/XLA); this is source-reasoned, CI is the verifier. See scope/domain-guard-triage/SCOPING.md (gitignored).
The abs (abspositive.mlir test5/test6) and reduce (reduce_max_min_mul_positive_ scalar.mlir reduce_max_mul_no_guarantee) consumers already have negative/ unknown-sign edge coverage. The CompareOpCanon non_negative/no_nan folds did not. Add compare_domain_guard.mlir: an unknown-sign, possibly-NaN operand must not let compare(x, const) collapse to a boolean constant. Guard-only by design -- it asserts a stablehlo.compare survives, not an exact folded form -- so it is robust to unrelated operand/direction canonicalizations and forbids only the fold-to-constant that the EnzymeAD#2648/EnzymeAD#2651 failure shape would produce. Passes on current main (the guard already holds there); it is a regression tripwire, not a fix. No local build (heavy LLVM/MLIR/XLA); CHECK lines reasoned, not run. CI verifies.
kimjune01
force-pushed
the
scope/domain-guard-triage
branch
from
July 23, 2026 02:26
9f78c7b to
bb3cf38
Compare
kimjune01
marked this pull request as ready for review
July 23, 2026 02:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A lightweight, reactive convention to make the next domain-narrowing wrong-code bug in
NonNegativeResultAnalysisand friends fast to triage. This is the cheap slice of #2654: not the provenance graph, and not a runtime sanitizer.The bug class
Several algebraic rewrites are licensed by a lightweight analysis fact (
non_negative/no_nan/finite) that can be wrong. When the fact is wrong the rewrite silently narrows a domain, turning a finite value into NaN or dropping a load-bearingabs. Recent instances (#2648 / #2649, #2651 / #2652) were all found by hand. This convention shortens the next such find.What it adds
A debug remark at each rewrite that trusts a domain-narrowing fact.
DOMAIN_GUARD_TRUST(rewrite, predicate, value)is oneLLVM_DEBUGline, wired at the consuming sites inEnzymeHLOOpt.cpp:ReduceMaxMinMulPositiveScalar, the twoCompareOpCanonnon_negativefolds, itsno_nanfold, andAbsPositiveSimplify.The predicate and the value are already on the IR (
setGuaranteedInIRwrites e.g.enzymexla.non_negative = GUARANTEEDand it survives to the pass output). The remark adds the only thing that is not on the IR: the name of the consuming rewrite, plus a time-ordered trace. A later domain-narrowing NaN is then one grep ([domain-guard]) from the rewrite that produced it.LLVM_DEBUGcompiles out underNDEBUGand is a singleDebugFlagbranch otherwise. No lit test uses-debug-only, so FileCheck is unaffected. Enable with-debug-only=enzymehloopt.LogSimplify'slog(a*a)/log(a*b)folds consume no fact today (that is log(a*a) → 2*log(a) returns NaN for negative inputs #2570); they become remark sites the moment a guard lands there, and the shared macro makes that a one-liner.A guard regression test (
compare_domain_guard.mlir): an unknown-sign, possibly-NaN operand must not letcompare(x, const)collapse to a boolean constant. Guard-only by design (it asserts astablehlo.comparesurvives, not an exact folded form), so it is robust to unrelated canonicalizations and forbids only the fold-to-constant that the domain-narrowing failure shape would produce.Scope
Deliberately just the reactive slice. The archetype fixes live in their own narrow PRs (#2649, #2652). The full decision-provenance system is #2654. A separate soundness gap surfaced while reviewing this (integer overflow, #2656) is filed for the maintainers to weigh, not fixed here.
No local build available (heavy LLVM/MLIR/XLA), so lit
CHECKlines are reasoned against the pass logic, not run. CI is the verifier.