llvm-project
llvm-project copied to clipboard
[DAG] SDPatternMatch - add m_BinOp/m_c_BinOp variants driven by TLI.isBinOp/isCommutativeBinOp
As noticed on #84759 - we should be able to allow folds to generally match any binary ops that are recognized by the TLI.isBinOp/isCommutativeBinOp callbacks, allowing us to match target binops in generic dag combines.
CC @mshockwave
Hi!
This issue may be a good introductory issue for people new to working on LLVM. If you would like to work on this issue, your first steps are:
- In the comments of the issue, request for it to be assigned to you.
- Fix the issue locally.
- Run the test suite locally. Remember that the subdirectories under
test/create fine-grained testing targets, so you can e.g. usemake check-clang-astto only run Clang's AST tests. - Create a Git commit.
- Run
git clang-format HEAD~1to format your changes. - Open a pull request to the upstream repository on GitHub. Detailed instructions can be found in GitHub's documentation.
If you have any further questions about this issue, don't hesitate to ask via a comment in the thread below.
@llvm/issue-subscribers-good-first-issue
Author: Simon Pilgrim (RKSimon)
CC @mshockwave
@RKSimon, I would like to work on this. I can provide a patch both for this issue and for reducing the matching code as mentioned here.
Needed a little help. Am I to add a variant of this code in SDPatternMatch.h but now use TLI.isBinOp/isCommutativeBinOp for the match logic? Because isBinOp works on binary opcodes and in the original thread,
I would favor adding pattern like PatternMatch::m_BinOp(LHS, RHS) was mentioned which implies no use of opcode. :
template <typename LHS_t, typename RHS_t, bool Commutable = false>
struct AnyBinaryOp_match {
LHS_t L;
RHS_t R;
// The evaluation order is always stable, regardless of Commutability.
// The LHS is always matched first.
AnyBinaryOp_match(const LHS_t &LHS, const RHS_t &RHS) : L(LHS), R(RHS) {}
template <typename OpTy> bool match(OpTy *V) {
if (auto *I = dyn_cast<BinaryOperator>(V))
return (L.match(I->getOperand(0)) && R.match(I->getOperand(1))) ||
(Commutable && L.match(I->getOperand(1)) &&
R.match(I->getOperand(0)));
return false;
}
};
template <typename LHS, typename RHS>
inline AnyBinaryOp_match<LHS, RHS> m_BinOp(const LHS &L, const RHS &R) {
return AnyBinaryOp_match<LHS, RHS>(L, R);
}
CC @mshockwave
We're going to need to extract the opcode, m_AnyBinOp(unsigned &Opcode, const LHS &L, const RHS &R) maybe?
but doesn't the m_BinOp variant of SDPatternMatch does exactly this ?
No, in this case we need to be told whatever the opcode is, not match for an explicit opcode
This is in SDPatternMatch.h :
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_BinOp(unsigned Opc, const LHS &L,
const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(Opc, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS,RHS,false> m_BinOp(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(TLI.isBinOp(), L, R);
}
So we need something like? :
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS,RHS,false> m_AnyBinOp(unsigned Opc,const LHS &L, const RHS &R) {
if (TLI.isBinOP(Opc)) {
return BinaryOpc_match<LHS, RHS, false>(TLI.isBinOp(), L, R);
}
}
gentle ping. I am aware of the weekly ping policy but it seems prudent so that we can progress on this.
@mshockwave Any suggestions please?
I think the general direction of your interface design makes sense; in addition to the existing m_BinOp/m_c_BinOp you're adding another two: m_AnyBinOp(LHS, RHS) for opcode-agnostic matching, and m_AnyBinOp(unsigned &Opc, LHS, RHS) for opcode-agnostic matching while extracting opcode at the same time.
Whether the new m_AnyBinOp and m_BinOp should share the underlying implementation is another thing, which I'm fine with either creating a new class for m_AnyBinOp or modifying the existing BinaryOpc_match. If you would like to take the latter option, you could create a new constructor for BinaryOpc_match that handles m_AnyBinOp's functionalities, namely, checking against TLI and extracting opcode. For instance BinaryOpc_match(unsigned *ExtractedOpcode, Pattern LHS, Patten RHS). Then, inside BinaryOpc_match::match, based on which constructor was used, we conditionally used the proper opcode checking strategy.
I have opened a draft PR. I am not sure how to handle the case where we tackle TLI with m_AnyBinOp(LHS, RHS), for now I have simply returned true. I would also like to have a sanity check on the tests.