-
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
[EXPR] Expression-template based pattern matching. #2589
Conversation
@sgrechanik-h @ZihengJiang @antinucleon @jroesch please help review the PR |
CHECK(!(px + (px + px)).Match(r)); | ||
CHECK(!(px + (py + py)).Match(r)); | ||
CHECK((px + (py + pz)).Match(r)); | ||
{ |
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.
Does it make sense to make changes such that (px+(px+py))
too matches 1+ (y+1)
, that is, to take into consideration the +
's commutativity? Or is that commutativity something that needs to be taken care of independently?
@derisavi brings up the question on whether do we need to support commutativity so that My feeling is that we should not. Mainly because pattern matching in traditional sense only matches the structure. Supporting commutativity in + will make the time complexity blow up if we have a deep nest of adds, which may also be not desirable. |
src/arithmetic/pattern_match.h
Outdated
* so the result code should be as efficient as manually write pattern match code | ||
* using as cast and checks. | ||
* | ||
* The code below gives shows how to use the pattern matcher. |
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.
remove "gives"
src/arithmetic/pattern_match.h
Outdated
* It helps to simplify pattern matching and rewrites. | ||
* All the patterns are generated via expression template during compile time, | ||
* so the result code should be as efficient as manually write pattern match code | ||
* using as cast and checks. |
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.
Some typos, I guess it should be "written", and "as" seems redundant.
* arith::PVar<Expr> x, y, z; | ||
* | ||
* // The following code tries to match the declared pattern. | ||
* // Match will fill the result of match into PVar if successful. |
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.
Destructively filling the PVar
s doesn't feel right. It will cause problems with multithreading and with some cases when we want to match several expressions against the same pattern. So I suggest that we think in advance about these problems and how to prevent them.
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 current mechanism is certainly not thread-safe. I will add a comments on that
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.
I think my main concern is the possibility of writing something like this:
PVar<Expr> x, y;
if ((x + y).Match(expr)) {
if((x * y).Match(another_expr)) {
...
}
// x was refilled but it's not entirely clear from the code
}
It is possible to forbid this by raising an error in InitMatch_
if the variable has been filled already. However I'm not sure if it's worth the trouble for an internal tool, so I won't insist.
src/arithmetic/pattern_match.h
Outdated
* // The following code tries to match the declared pattern. | ||
* // Match will fill the result of match into PVar if successful. | ||
* // Note that z occurs twice in the pattern, | ||
* // an equality check to ensure each occurance of z is equivalent |
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.
I guess there is a word missing, "equality checked is performed" or something like this
src/arithmetic/pattern_match.h
Outdated
namespace tvm { | ||
namespace arith { | ||
/*! | ||
* \brief Base cass of all the patterns. |
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.
typo cass -> class
src/arithmetic/pattern_match.h
Outdated
* \brief Pattern not expression. | ||
* \tparam TCond The pattern type of the condition. | ||
* \tparam TA The pattern type of the true operand. | ||
* \tparam TB The pattern type of the false operand. |
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.
Parameter descriptions are from the next class.
src/arithmetic/pattern_match.h
Outdated
}; | ||
|
||
template<typename TA> | ||
inline PNotExpr<TA> operator!(const TA& value) { |
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.
Shouldn't it be Pattern<TA>
in the type of the argument?
} | ||
private: | ||
const T value_; | ||
}; |
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.
May be add some helper function to construct const patterns?
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.
Since PConst() is usually as short, I will skip this for now.
src/arithmetic/pattern_match.h
Outdated
* \return The result pattern. | ||
* | ||
* \tparam DType The pattern type of type. | ||
* \tparam TA The pattern type of the true operand. |
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 "type of the true operand" seems to be from some other place.
} | ||
|
||
// internal namespace | ||
namespace detail { |
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.
Don't know what the code style guide says but I would indent the contents of detail
for readability.
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 google code style do not indent namespace
Thanks, @sgrechanik-h @derisavi for helpful reviews, please check again. |
* arith::PVar<Expr> x, y, z; | ||
* | ||
* // The following code tries to match the declared pattern. | ||
* // Match will fill the result of match into PVar if successful. |
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.
I think my main concern is the possibility of writing something like this:
PVar<Expr> x, y;
if ((x + y).Match(expr)) {
if((x * y).Match(another_expr)) {
...
}
// x was refilled but it's not entirely clear from the code
}
It is possible to forbid this by raising an error in InitMatch_
if the variable has been filled already. However I'm not sure if it's worth the trouble for an internal tool, so I won't insist.
* \endcode | ||
* | ||
* \note The pattern matcher is not threadsafe, | ||
* do not use the same PVar in multiple threads. |
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.
I guess saving a pattern into a variable may also be dangerous:
PVar<Expr> x, y, z;
auto pat = max(x + y, z);
pat.Match(expr); // x + y may be already destroyed here?
If it's indeed the case, it's worth mentioning here as well.
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.
We should confirm, my understanding is that it will be fine, because the temporary patterns will be destructed at the end of the current scope, so they are still valid here
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.
I particular, the above code will translate to
{
PVar<Expr> x, y, z;
auto t0 = x + y;
auto pat = max(t0, z);
pat.Match(expr);
} // t0 is alive until here
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.
Also good point on the anti-pattern on bringing up the anti-pattern. I have updated comments to make people be aware of the potential misuse.
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.
I tried this code (if I'm not mistaken it's close to how the pattern classes work):
#include <iostream>
struct A {
A() { std::cout << "A()\n"; }
~A() { std::cout << "~A()\n"; }
};
struct C {
C(const A& a) : a_(a) { std::cout << "C()\n"; }
~C() { std::cout << "~C()\n"; }
void match() const { std::cout << "match\n"; }
const A& a_;
};
int main() {
auto pat = C(A());
pat.match();
}
The output is (both gcc and clang):
A()
C()
~A()
match
~C()
So A is destroyed before the call to match.
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.
Interesting, I didn't realize that was the case. Perhaps we need to do a deep construction by copy instead of by reference...
I have updated the code so that intermediate expression nest by value, @sgrechanik-h please check again |
src/arithmetic/pattern_match.h
Outdated
@@ -593,7 +632,8 @@ class PCallExpr : | |||
} | |||
|
|||
private: | |||
const std::tuple<const TArgs&...> args_; | |||
typename detail::nested_tuple_type_helper< | |||
std::tuple<TArgs...> >::Nested args_; |
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.
Won't something like std::tuple<typename TArgs::Nested...>
work here? Otherwise lgtm.
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.
Thanks, this is something I don't know before :)
Thanks, @ZihengJiang @sgrechanik-h @derisavi , this is now merged |
Pattern matching and rewriting is a common task in compiler analysis. So far, we manually write code that detects the patterns and rewrite them. This PR introduces a new utility to use expression-template for writing pattern matching code in C++.
As an example, here are two versions of the code to rewrite
max(x + z, y + z) => max(x, y) + z
Manual Pattern Matching
Use Pattern Match Util
The template inlining will generate the same code as if we are doing manual pattern matching.
See pattern_match_test.cc for more examples.