-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Relax] Allow composition of DFPattern replacements #16732
[Relax] Allow composition of DFPattern replacements #16732
Conversation
This PR is currently marked as a draft, because it depends on the refactor done in #16730. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes seem easy to understand given the PR this is built on and I think the new behavior is intuitive as well: If the first branch of the OR does not result in a rewrite, check the second. In principle, I think this should be noted in whatever top-level documentation there is for the pattern matching grammar.
src/relax/ir/dataflow_matcher.cc
Outdated
ICHECK(matches_top_level); | ||
|
||
// Special handling if the user-supplied pattern is a `OrPattern`. | ||
// While the `ExtractMatchedExpr` can handle match the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a typo. I assume it's supposed to be "handle matching," correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, this was a typo. It should be "handle matching", and I've updated the PR with the correction.
The `rewrite_call` function accepts a `DFPattern`, and a function to rewrite expressions matching that pattern. Often, the rewriting function will perform additional validation that cannot be expressed within the `DFPattern` itself. If this additional validation fails, the rewriter function will return the matched expression unmodified. Prior to this commit, an `OrPattern` that matches on the first branch, but whose rewriter function does not apply a modification, would prevent the second branch from being checked. This commit updates the `ExprPatternRewriter` to check both branches of a `OrPattern`, if the rewriter function of the first branch does not modify the result.
7e9054b
to
5e0a54f
Compare
The pre-requisite PR #16730 has landed, so this PR is now rebased on top of |
Hi @Lunderberg, it seems that this PR introduces new regression. The original issue came from CI in MLC-LLM https://ci.mlc.ai/blue/organizations/jenkins/mlc-llm/detail/PR-2068/1/pipeline/. And I spent some time adapting your test Running the test below def test_backtrack_if_rewriter_returns_no_op():
pat_match_no_rewrite = is_op("relax.add")(wildcard(), wildcard())
pat_arg = wildcard()
pat_zeros = is_op("relax.zeros")(wildcard())
pat_add = is_op("relax.add")(pat_arg, pat_zeros)
pat = pat_match_no_rewrite | pat_add
def rewriter(expr, matches):
print(f"matching {pat} to {matches[pat]}")
assert isinstance(matches[pat], rx.Call)
if pat_match_no_rewrite in matches:
return expr ## <<== return the expr
elif pat_add in matches:
return expr ## <<== also return the expr
else:
raise RuntimeError("Pattern matched, but neither branch matched")
@R.function(private=True)
def before():
with R.dataflow():
A = R.ones([64, 128], "int32")
B = R.zeros([64, 128], "int32")
C = R.add(A, B)
R.output(C)
return C
after = rewrite_call(pat, rewriter, before) will yield the following output:
As you can see in the error message, with this PR, the pattern matcher mapped an |
The particular |
Ooh, that's a weird one, and thank you for the test case. I can reproduce it on my side, and will look in more detail tomorrow. For now, the first thing I'm noticing is that the failure occurs on the third time that
|
This is an interesting one.
So the call to
Edit: Or even better, this should be handled when filling in the |
This resolves a bug that was introduced in apache#16732. If a rewriter function returned a no-op, and the pattern-match continued, then the `matches` provided to the rewriter function in subsequent calls would contain a variable to which the matched expression was bound, not the matched expression itself. (e.g. For a match of `C = R.add(A,B)`, passing `C` to the rewriter instead of `R.add(A,B)`.) This bug was caused by incorrect re-wrapping of `OrPattern` in `ExprPatternRewriter`. Prior to apache#16732, all pattern-match results were populated by `ExtractMatchExpr`, and contained the result after applying `TryGetValOfVar`. When re-wrapping the result of an `OrPattern`, apache#16732 populated the additional matches with the result before applying `TryGetValOfVar`. This commit fixes the bug by applying `TryGetValOfVar`.
@MasterJH5574 I have a fix implemented in #16828. Can you try it on your side and verify that it resolves the issue in the end-to-end flow? |
@Lunderberg Thanks for the swift fix! It works for e2e compilation now. |
This resolves a bug that was introduced in apache#16732. If a rewriter function returned a no-op, and the pattern-match continued, then the `matches` provided to the rewriter function in subsequent calls would contain a variable to which the matched expression was bound, not the matched expression itself. (e.g. For a match of `C = R.add(A,B)`, passing `C` to the rewriter instead of `R.add(A,B)`.) This bug was caused by incorrect re-wrapping of `OrPattern` in `ExprPatternRewriter`. Prior to apache#16732, all pattern-match results were populated by `ExtractMatchExpr`, and contained the result after applying `TryGetValOfVar`. When re-wrapping the result of an `OrPattern`, apache#16732 populated the additional matches with the result before applying `TryGetValOfVar`. This commit fixes the bug by applying `TryGetValOfVar`.
This resolves a bug that was introduced in apache#16732. If a rewriter function returned a no-op, and the pattern-match continued, then the `matches` provided to the rewriter function in subsequent calls would contain a variable to which the matched expression was bound, not the matched expression itself. (e.g. For a match of `C = R.add(A,B)`, passing `C` to the rewriter instead of `R.add(A,B)`.) This bug was caused by incorrect re-wrapping of `OrPattern` in `ExprPatternRewriter`. Prior to apache#16732, all pattern-match results were populated by `ExtractMatchExpr`, and contained the result after applying `TryGetValOfVar`. When re-wrapping the result of an `OrPattern`, apache#16732 populated the additional matches with the result before applying `TryGetValOfVar`. This commit fixes the bug by applying `TryGetValOfVar`.
* [Relax][Bugfix] Provide the full Expr to pattern-match rewriter This resolves a bug that was introduced in #16732. If a rewriter function returned a no-op, and the pattern-match continued, then the `matches` provided to the rewriter function in subsequent calls would contain a variable to which the matched expression was bound, not the matched expression itself. (e.g. For a match of `C = R.add(A,B)`, passing `C` to the rewriter instead of `R.add(A,B)`.) This bug was caused by incorrect re-wrapping of `OrPattern` in `ExprPatternRewriter`. Prior to #16732, all pattern-match results were populated by `ExtractMatchExpr`, and contained the result after applying `TryGetValOfVar`. When re-wrapping the result of an `OrPattern`, #16732 populated the additional matches with the result before applying `TryGetValOfVar`. This commit fixes the bug by applying `TryGetValOfVar`. * Update with PR link of bugfix
[Relax] Allow composition of DFPattern replacements The `rewrite_call` function accepts a `DFPattern`, and a function to rewrite expressions matching that pattern. Often, the rewriting function will perform additional validation that cannot be expressed within the `DFPattern` itself. If this additional validation fails, the rewriter function will return the matched expression unmodified. Prior to this commit, an `OrPattern` that matches on the first branch, but whose rewriter function does not apply a modification, would prevent the second branch from being checked. This commit updates the `ExprPatternRewriter` to check both branches of a `OrPattern`, if the rewriter function of the first branch does not modify the result.
…he#16828) * [Relax][Bugfix] Provide the full Expr to pattern-match rewriter This resolves a bug that was introduced in apache#16732. If a rewriter function returned a no-op, and the pattern-match continued, then the `matches` provided to the rewriter function in subsequent calls would contain a variable to which the matched expression was bound, not the matched expression itself. (e.g. For a match of `C = R.add(A,B)`, passing `C` to the rewriter instead of `R.add(A,B)`.) This bug was caused by incorrect re-wrapping of `OrPattern` in `ExprPatternRewriter`. Prior to apache#16732, all pattern-match results were populated by `ExtractMatchExpr`, and contained the result after applying `TryGetValOfVar`. When re-wrapping the result of an `OrPattern`, apache#16732 populated the additional matches with the result before applying `TryGetValOfVar`. This commit fixes the bug by applying `TryGetValOfVar`. * Update with PR link of bugfix
The
rewrite_call
function accepts aDFPattern
, and a function torewrite expressions matching that pattern. Often, the rewriting
function will perform additional validation that cannot be expressed
within the
DFPattern
itself. If this additional validation fails,the rewriter function will return the matched expression unmodified.
Prior to this commit, an
OrPattern
that matches on the first branch,but whose rewriter function does not apply a modification, would
prevent the second branch from being checked. This commit updates the
ExprPatternRewriter
to check both branches of aOrPattern
, if therewriter function of the first branch does not modify the result.