From dac0f7e83ebcaed451d5d0bcc3a4c7f949f0c26c Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Mon, 21 Oct 2024 16:46:45 -0700 Subject: [PATCH] [VPlan] Add general recipe matcher, replace handwritten ones (NFC) The new matcher is more flexible and can be used to build matchers for additional recipe types without unnecessary duplication. --- .../Transforms/Vectorize/VPlanPatternMatch.h | 117 ++++++++---------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h index ed0bb13d9425f6..1b05afd6b117a5 100644 --- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h +++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h @@ -120,7 +120,12 @@ template struct MatchRecipeAndOpcode { static bool match(const VPRecipeBase *R) { auto *DefR = dyn_cast(R); - return DefR && DefR->getOpcode() == Opcode; + // Check for recipes that do not have opcodes. + if constexpr (std::is_same::value || + std::is_same::value) + return DefR; + else + return DefR && DefR->getOpcode() == Opcode; } }; @@ -131,13 +136,34 @@ struct MatchRecipeAndOpcode { MatchRecipeAndOpcode::match(R); } }; +template +bool CheckTupleElements(const TupleTy &Ops, Fn P, std::index_sequence) { + return (P(std::get(Ops), Is) && ...); +} + +/// Helper to check if predicate \p P holds on all tuple elements in \p Ops +template +bool all_of_tuple_elements(const TupleTy &Ops, Fn P) { + return CheckTupleElements( + Ops, P, std::make_index_sequence::value>{}); +} } // namespace detail -template -struct UnaryRecipe_match { - Op0_t Op0; +template +struct Recipe_match { + Ops_t Ops; - UnaryRecipe_match(Op0_t Op0) : Op0(Op0) {} + Recipe_match() : Ops() { + static_assert(std::tuple_size::value == 0 && + "constructor can only be used with zero operands"); + } + Recipe_match(Ops_t Ops) : Ops(Ops) {} + template + Recipe_match(A_t A, B_t B) : Ops({A, B}) { + static_assert(std::tuple_size::value == 2 && + "constructor can only be used for binary matcher"); + } bool match(const VPValue *V) const { auto *DefR = V->getDefiningRecipe(); @@ -151,12 +177,25 @@ struct UnaryRecipe_match { bool match(const VPRecipeBase *R) const { if (!detail::MatchRecipeAndOpcode::match(R)) return false; - assert(R->getNumOperands() == 1 && - "recipe with matched opcode does not have 1 operands"); - return Op0.match(R->getOperand(0)); + assert(R->getNumOperands() == std::tuple_size::value && + "recipe with matched opcode the expected number of operands"); + + if (detail::all_of_tuple_elements(Ops, [R](auto Op, unsigned Idx) { + return Op.match(R->getOperand(Idx)); + })) + return true; + + return Commutative && + detail::all_of_tuple_elements(Ops, [R](auto Op, unsigned Idx) { + return Op.match(R->getOperand(R->getNumOperands() - Idx - 1)); + }); } }; +template +using UnaryRecipe_match = + Recipe_match, Opcode, false, RecipeTys...>; + template using UnaryVPInstruction_match = UnaryRecipe_match; @@ -168,32 +207,8 @@ using AllUnaryRecipe_match = template -struct BinaryRecipe_match { - Op0_t Op0; - Op1_t Op1; - - BinaryRecipe_match(Op0_t Op0, Op1_t Op1) : Op0(Op0), Op1(Op1) {} - - bool match(const VPValue *V) const { - auto *DefR = V->getDefiningRecipe(); - return DefR && match(DefR); - } - - bool match(const VPSingleDefRecipe *R) const { - return match(static_cast(R)); - } - - bool match(const VPRecipeBase *R) const { - if (!detail::MatchRecipeAndOpcode::match(R)) - return false; - assert(R->getNumOperands() == 2 && - "recipe with matched opcode does not have 2 operands"); - if (Op0.match(R->getOperand(0)) && Op1.match(R->getOperand(1))) - return true; - return Commutative && Op0.match(R->getOperand(1)) && - Op1.match(R->getOperand(0)); - } -}; +using BinaryRecipe_match = + Recipe_match, Opcode, Commutative, RecipeTys...>; template using BinaryVPInstruction_match = @@ -313,40 +328,16 @@ m_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) { return m_VPInstruction(Op0, Op1); } -struct VPCanonicalIVPHI_match { - bool match(const VPValue *V) const { - auto *DefR = V->getDefiningRecipe(); - return DefR && match(DefR); - } - - bool match(const VPRecipeBase *R) const { - return isa(R); - } -}; +using VPCanonicalIVPHI_match = + Recipe_match, 0, false, VPCanonicalIVPHIRecipe>; inline VPCanonicalIVPHI_match m_CanonicalIV() { return VPCanonicalIVPHI_match(); } -template struct VPScalarIVSteps_match { - Op0_t Op0; - Op1_t Op1; - - VPScalarIVSteps_match(Op0_t Op0, Op1_t Op1) : Op0(Op0), Op1(Op1) {} - - bool match(const VPValue *V) const { - auto *DefR = V->getDefiningRecipe(); - return DefR && match(DefR); - } - - bool match(const VPRecipeBase *R) const { - if (!isa(R)) - return false; - assert(R->getNumOperands() == 2 && - "VPScalarIVSteps must have exactly 2 operands"); - return Op0.match(R->getOperand(0)) && Op1.match(R->getOperand(1)); - } -}; +template +using VPScalarIVSteps_match = + Recipe_match, 0, false, VPScalarIVStepsRecipe>; template inline VPScalarIVSteps_match m_ScalarIVSteps(const Op0_t &Op0,