Summary
Rules registered via @dialect.canonicalize and @dialect.post_inference are applied to the method's top node only (un-walked), so an ordinary single-node RewriteRule registered this way is silently a no-op. Requesting clarification on the intended contract — and on whether the two are meant to differ — plus a small ergonomic fix.
What the code does today
- Registration appends
rule() to rules.canonicalize / rules.inference (kirin/ir/dialect.py).
- Application is identical in both passes:
Canonicalize.unsafe_run: for rule in dialect.rules.canonicalize: rule.rewrite(mt.code) (kirin/passes/canonicalize.py)
PostInference.unsafe_run: for rule in dialect.rules.inference: rule.rewrite(mt.code) (kirin/passes/post_inference.py)
- Both are run to fixpoint by their callers:
Default.canonicalize.fixpoint(mt) (default.py:41) and TypeInfer.post_inference.fixpoint(mt) (typeinfer.py:44).
RewriteRule.rewrite(node) (kirin/rewrite/abc.py) dispatches only on the node passed — no recursion — and mt.code is the top-level func.Function.
Net: both mechanisms are un-walked and fixpointed, so a rule whose rewrite_Statement targets statements nested in the body never sees them. The in-tree ilist post-inference rules (HintLen, Unroll, …) appear to work only because they're also run inside a Walk(...) in the fold chains.
Questions
- Is there an intended behavioral difference between
canonicalize and post_inference rules? They're currently applied identically (un-walked, fixpointed by caller).
- Is the intended contract that registered rules self-walk, or that the framework should
Walk them? Today registering an ordinary RewriteRule is silently a no-op.
Proposed enhancement
Add a walk: bool option to both decorators that wraps the rule in Walk at registration:
@dialect.post_inference(walk=True)
class MyRule(RewriteRule): ...
(Requires making the decorators parameterizable; they currently take the rule type directly.)
kirin-toolchain 0.22.11
cc: @kaihsin @neelay893
Summary
Rules registered via
@dialect.canonicalizeand@dialect.post_inferenceare applied to the method's top node only (un-walked), so an ordinary single-nodeRewriteRuleregistered this way is silently a no-op. Requesting clarification on the intended contract — and on whether the two are meant to differ — plus a small ergonomic fix.What the code does today
rule()torules.canonicalize/rules.inference(kirin/ir/dialect.py).Canonicalize.unsafe_run:for rule in dialect.rules.canonicalize: rule.rewrite(mt.code)(kirin/passes/canonicalize.py)PostInference.unsafe_run:for rule in dialect.rules.inference: rule.rewrite(mt.code)(kirin/passes/post_inference.py)Default.canonicalize.fixpoint(mt)(default.py:41) andTypeInfer.post_inference.fixpoint(mt)(typeinfer.py:44).RewriteRule.rewrite(node)(kirin/rewrite/abc.py) dispatches only on the node passed — no recursion — andmt.codeis the top-levelfunc.Function.Net: both mechanisms are un-walked and fixpointed, so a rule whose
rewrite_Statementtargets statements nested in the body never sees them. The in-treeilistpost-inference rules (HintLen,Unroll, …) appear to work only because they're also run inside aWalk(...)in the fold chains.Questions
canonicalizeandpost_inferencerules? They're currently applied identically (un-walked, fixpointed by caller).Walkthem? Today registering an ordinaryRewriteRuleis silently a no-op.Proposed enhancement
Add a
walk: booloption to both decorators that wraps the rule inWalkat registration:(Requires making the decorators parameterizable; they currently take the rule type directly.)
kirin-toolchain 0.22.11
cc: @kaihsin @neelay893