Skip to content

[GlobalISel] Add and use a m_GAddLike pattern matcher. NFC #125435

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

Merged
merged 3 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 50 additions & 13 deletions llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ template <typename Pattern>
return P.match(MRI, &MI);
}

template <typename Pattern>
[[nodiscard]] bool mi_match(const MachineInstr &MI,
const MachineRegisterInfo &MRI, Pattern &&P) {
return P.match(MRI, &MI);
}

// TODO: Extend for N use.
template <typename SubPatternT> struct OneUse_match {
SubPatternT SubPat;
Expand Down Expand Up @@ -337,6 +343,19 @@ template <> struct bind_helper<MachineInstr *> {
}
};

template <> struct bind_helper<const MachineInstr *> {
static bool bind(const MachineRegisterInfo &MRI, const MachineInstr *&MI,
Register Reg) {
MI = MRI.getVRegDef(Reg);
return MI;
}
static bool bind(const MachineRegisterInfo &MRI, const MachineInstr *&MI,
const MachineInstr *Inst) {
MI = Inst;
return MI;
}
};

template <> struct bind_helper<LLT> {
static bool bind(const MachineRegisterInfo &MRI, LLT &Ty, Register Reg) {
Ty = MRI.getType(Reg);
Expand Down Expand Up @@ -368,6 +387,9 @@ template <typename Class> struct bind_ty {

inline bind_ty<Register> m_Reg(Register &R) { return R; }
inline bind_ty<MachineInstr *> m_MInstr(MachineInstr *&MI) { return MI; }
inline bind_ty<const MachineInstr *> m_MInstr(const MachineInstr *&MI) {
return MI;
}
inline bind_ty<LLT> m_Type(LLT &Ty) { return Ty; }
inline bind_ty<CmpInst::Predicate> m_Pred(CmpInst::Predicate &P) { return P; }
inline operand_type_match m_Pred() { return operand_type_match(); }
Expand Down Expand Up @@ -418,26 +440,28 @@ inline bind_ty<const ConstantFP *> m_GFCst(const ConstantFP *&C) { return C; }

// General helper for all the binary generic MI such as G_ADD/G_SUB etc
template <typename LHS_P, typename RHS_P, unsigned Opcode,
bool Commutable = false>
bool Commutable = false, unsigned Flags = MachineInstr::NoFlags>
struct BinaryOp_match {
LHS_P L;
RHS_P R;

BinaryOp_match(const LHS_P &LHS, const RHS_P &RHS) : L(LHS), R(RHS) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
const MachineInstr *TmpMI;
if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 3) {
return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
R.match(MRI, TmpMI->getOperand(2).getReg())) ||
// NOTE: When trying the alternative operand ordering
// with a commutative operation, it is imperative to always run
// the LHS sub-pattern (i.e. `L`) before the RHS sub-pattern
// (i.e. `R`). Otherwsie, m_DeferredReg/Type will not work as
// expected.
(Commutable && (L.match(MRI, TmpMI->getOperand(2).getReg()) &&
R.match(MRI, TmpMI->getOperand(1).getReg())));
if ((!L.match(MRI, TmpMI->getOperand(1).getReg()) ||
!R.match(MRI, TmpMI->getOperand(2).getReg())) &&
// NOTE: When trying the alternative operand ordering
// with a commutative operation, it is imperative to always run
// the LHS sub-pattern (i.e. `L`) before the RHS sub-pattern
// (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as
// expected.
(!Commutable || !L.match(MRI, TmpMI->getOperand(2).getReg()) ||
!R.match(MRI, TmpMI->getOperand(1).getReg())))
return false;
return (TmpMI->getFlags() & Flags) == Flags;
}
}
return false;
Expand All @@ -464,7 +488,7 @@ struct BinaryOpc_match {
// NOTE: When trying the alternative operand ordering
// with a commutative operation, it is imperative to always run
// the LHS sub-pattern (i.e. `L`) before the RHS sub-pattern
// (i.e. `R`). Otherwsie, m_DeferredReg/Type will not work as
// (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as
// expected.
(Commutable && (L.match(MRI, TmpMI->getOperand(2).getReg()) &&
R.match(MRI, TmpMI->getOperand(1).getReg())));
Expand Down Expand Up @@ -559,6 +583,19 @@ inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true> m_GOr(const LHS &L,
return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true>(L, R);
}

template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true,
MachineInstr::Disjoint>
m_GDisjointOr(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true,
MachineInstr::Disjoint>(L, R);
}

template <typename LHS, typename RHS>
inline auto m_GAddLike(const LHS &L, const RHS &R) {
return m_any_of(m_GAdd(L, R), m_GDisjointOr(L, R));
}

template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>
m_GShl(const LHS &L, const RHS &R) {
Expand Down Expand Up @@ -717,7 +754,7 @@ struct CompareOp_match {
// NOTE: When trying the alternative operand ordering
// with a commutative operation, it is imperative to always run
// the LHS sub-pattern (i.e. `L`) before the RHS sub-pattern
// (i.e. `R`). Otherwsie, m_DeferredReg/Type will not work as expected.
// (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as expected.
if (Commutable && L.match(MRI, RHS) && R.match(MRI, LHS) &&
P.match(MRI, CmpInst::getSwappedPredicate(TmpPred)))
return true;
Expand Down
4 changes: 1 addition & 3 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -1030,9 +1030,7 @@ def add_and_or_is_add : PatFrags<(ops node:$lhs, node:$rhs),
return CurDAG->isADDLike(SDValue(N,0));
}]> {
let GISelPredicateCode = [{
return MI.getOpcode() == TargetOpcode::G_ADD ||
(MI.getOpcode() == TargetOpcode::G_OR &&
MI.getFlag(MachineInstr::MIFlag::Disjoint));
return mi_match(MI, MRI, m_GAddLike(m_Reg(), m_Reg()));
}];
}

Expand Down
23 changes: 23 additions & 0 deletions llvm/unittests/CodeGen/GlobalISel/PatternMatchTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,29 @@ TEST_F(AArch64GISelMITest, DeferredMatching) {
m_GAdd(m_Reg(X), m_GSub(m_Reg(), m_DeferredReg(X)))));
}

TEST_F(AArch64GISelMITest, AddLike) {
setUp();
if (!TM)
GTEST_SKIP();
auto s64 = LLT::scalar(64);

auto Cst1 = B.buildConstant(s64, 42);
auto Cst2 = B.buildConstant(s64, 314);

auto Or1 = B.buildOr(s64, Cst1, Cst2, MachineInstr::Disjoint);
auto Or2 = B.buildOr(s64, Cst1, Cst2);
auto Add = B.buildAdd(s64, Cst1, Cst2);
auto Sub = B.buildSub(s64, Cst1, Cst2);

EXPECT_TRUE(mi_match(Or1.getReg(0), *MRI, m_GDisjointOr(m_Reg(), m_Reg())));
EXPECT_FALSE(mi_match(Or2.getReg(0), *MRI, m_GDisjointOr(m_Reg(), m_Reg())));

EXPECT_TRUE(mi_match(Add.getReg(0), *MRI, m_GAddLike(m_Reg(), m_Reg())));
EXPECT_FALSE(mi_match(Sub.getReg(0), *MRI, m_GAddLike(m_Reg(), m_Reg())));
EXPECT_TRUE(mi_match(Or1.getReg(0), *MRI, m_GAddLike(m_Reg(), m_Reg())));
EXPECT_FALSE(mi_match(Or2.getReg(0), *MRI, m_GAddLike(m_Reg(), m_Reg())));
}

} // namespace

int main(int argc, char **argv) {
Expand Down
Loading