1 //===- VPlanPatternMatch.h - Match on VPValues and recipes ------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file provides a simple and efficient mechanism for performing general 10 // tree-based pattern matches on the VPlan values and recipes, based on 11 // LLVM's IR pattern matchers. 12 // 13 // Currently it provides generic matchers for unary and binary VPInstructions, 14 // and specialized matchers like m_Not, m_ActiveLaneMask, m_BranchOnCond, 15 // m_BranchOnCount to match specific VPInstructions. 16 // TODO: Add missing matchers for additional opcodes and recipes as needed. 17 // 18 //===----------------------------------------------------------------------===// 19 20 #ifndef LLVM_TRANSFORM_VECTORIZE_VPLANPATTERNMATCH_H 21 #define LLVM_TRANSFORM_VECTORIZE_VPLANPATTERNMATCH_H 22 23 #include "VPlan.h" 24 25 namespace llvm { 26 namespace VPlanPatternMatch { 27 28 template <typename Val, typename Pattern> bool match(Val *V, const Pattern &P) { 29 return P.match(V); 30 } 31 32 template <typename Pattern> bool match(VPUser *U, const Pattern &P) { 33 auto *R = dyn_cast<VPRecipeBase>(U); 34 return R && match(R, P); 35 } 36 37 template <typename Class> struct class_match { 38 template <typename ITy> bool match(ITy *V) const { return isa<Class>(V); } 39 }; 40 41 /// Match an arbitrary VPValue and ignore it. 42 inline class_match<VPValue> m_VPValue() { return class_match<VPValue>(); } 43 44 template <typename Class> struct bind_ty { 45 Class *&VR; 46 47 bind_ty(Class *&V) : VR(V) {} 48 49 template <typename ITy> bool match(ITy *V) const { 50 if (auto *CV = dyn_cast<Class>(V)) { 51 VR = CV; 52 return true; 53 } 54 return false; 55 } 56 }; 57 58 /// Match a specified VPValue. 59 struct specificval_ty { 60 const VPValue *Val; 61 62 specificval_ty(const VPValue *V) : Val(V) {} 63 64 bool match(VPValue *VPV) const { return VPV == Val; } 65 }; 66 67 inline specificval_ty m_Specific(const VPValue *VPV) { return VPV; } 68 69 /// Stores a reference to the VPValue *, not the VPValue * itself, 70 /// thus can be used in commutative matchers. 71 struct deferredval_ty { 72 VPValue *const &Val; 73 74 deferredval_ty(VPValue *const &V) : Val(V) {} 75 76 bool match(VPValue *const V) const { return V == Val; } 77 }; 78 79 /// Like m_Specific(), but works if the specific value to match is determined 80 /// as part of the same match() expression. For example: 81 /// m_Mul(m_VPValue(X), m_Specific(X)) is incorrect, because m_Specific() will 82 /// bind X before the pattern match starts. 83 /// m_Mul(m_VPValue(X), m_Deferred(X)) is correct, and will check against 84 /// whichever value m_VPValue(X) populated. 85 inline deferredval_ty m_Deferred(VPValue *const &V) { return V; } 86 87 /// Match an integer constant or vector of constants if Pred::isValue returns 88 /// true for the APInt. \p BitWidth optionally specifies the bitwidth the 89 /// matched constant must have. If it is 0, the matched constant can have any 90 /// bitwidth. 91 template <typename Pred, unsigned BitWidth = 0> struct int_pred_ty { 92 Pred P; 93 94 int_pred_ty(Pred P) : P(std::move(P)) {} 95 int_pred_ty() : P() {} 96 97 bool match(VPValue *VPV) const { 98 if (!VPV->isLiveIn()) 99 return false; 100 Value *V = VPV->getLiveInIRValue(); 101 if (!V) 102 return false; 103 const auto *CI = dyn_cast<ConstantInt>(V); 104 if (!CI && V->getType()->isVectorTy()) 105 if (const auto *C = dyn_cast<Constant>(V)) 106 CI = dyn_cast_or_null<ConstantInt>( 107 C->getSplatValue(/*AllowPoison=*/false)); 108 if (!CI) 109 return false; 110 111 if (BitWidth != 0 && CI->getBitWidth() != BitWidth) 112 return false; 113 return P.isValue(CI->getValue()); 114 } 115 }; 116 117 /// Match a specified integer value or vector of all elements of that 118 /// value. \p BitWidth optionally specifies the bitwidth the matched constant 119 /// must have. If it is 0, the matched constant can have any bitwidth. 120 struct is_specific_int { 121 APInt Val; 122 123 is_specific_int(APInt Val) : Val(std::move(Val)) {} 124 125 bool isValue(const APInt &C) const { return APInt::isSameValue(Val, C); } 126 }; 127 128 template <unsigned Bitwidth = 0> 129 using specific_intval = int_pred_ty<is_specific_int, Bitwidth>; 130 131 inline specific_intval<0> m_SpecificInt(uint64_t V) { 132 return specific_intval<0>(is_specific_int(APInt(64, V))); 133 } 134 135 inline specific_intval<1> m_False() { 136 return specific_intval<1>(is_specific_int(APInt(64, 0))); 137 } 138 139 inline specific_intval<1> m_True() { 140 return specific_intval<1>(is_specific_int(APInt(64, 1))); 141 } 142 143 struct is_all_ones { 144 bool isValue(const APInt &C) const { return C.isAllOnes(); } 145 }; 146 147 /// Match an integer or vector with all bits set. 148 /// For vectors, this includes constants with undefined elements. 149 inline int_pred_ty<is_all_ones> m_AllOnes() { 150 return int_pred_ty<is_all_ones>(); 151 } 152 153 /// Matching combinators 154 template <typename LTy, typename RTy> struct match_combine_or { 155 LTy L; 156 RTy R; 157 158 match_combine_or(const LTy &Left, const RTy &Right) : L(Left), R(Right) {} 159 160 template <typename ITy> bool match(ITy *V) const { 161 if (L.match(V)) 162 return true; 163 if (R.match(V)) 164 return true; 165 return false; 166 } 167 }; 168 169 template <typename LTy, typename RTy> struct match_combine_and { 170 LTy L; 171 RTy R; 172 173 match_combine_and(const LTy &Left, const RTy &Right) : L(Left), R(Right) {} 174 175 template <typename ITy> bool match(ITy *V) const { 176 return L.match(V) && R.match(V); 177 } 178 }; 179 180 /// Combine two pattern matchers matching L || R 181 template <typename LTy, typename RTy> 182 inline match_combine_or<LTy, RTy> m_CombineOr(const LTy &L, const RTy &R) { 183 return match_combine_or<LTy, RTy>(L, R); 184 } 185 186 /// Combine two pattern matchers matching L && R 187 template <typename LTy, typename RTy> 188 inline match_combine_and<LTy, RTy> m_CombineAnd(const LTy &L, const RTy &R) { 189 return match_combine_and<LTy, RTy>(L, R); 190 } 191 192 /// Match a VPValue, capturing it if we match. 193 inline bind_ty<VPValue> m_VPValue(VPValue *&V) { return V; } 194 195 /// Match a VPInstruction, capturing if we match. 196 inline bind_ty<VPInstruction> m_VPInstruction(VPInstruction *&V) { return V; } 197 198 template <typename Ops_t, unsigned Opcode, bool Commutative, 199 typename... RecipeTys> 200 struct Recipe_match { 201 Ops_t Ops; 202 203 Recipe_match() : Ops() { 204 static_assert(std::tuple_size<Ops_t>::value == 0 && 205 "constructor can only be used with zero operands"); 206 } 207 Recipe_match(Ops_t Ops) : Ops(Ops) {} 208 template <typename A_t, typename B_t> 209 Recipe_match(A_t A, B_t B) : Ops({A, B}) { 210 static_assert(std::tuple_size<Ops_t>::value == 2 && 211 "constructor can only be used for binary matcher"); 212 } 213 214 bool match(const VPValue *V) const { 215 auto *DefR = V->getDefiningRecipe(); 216 return DefR && match(DefR); 217 } 218 219 bool match(const VPSingleDefRecipe *R) const { 220 return match(static_cast<const VPRecipeBase *>(R)); 221 } 222 223 bool match(const VPRecipeBase *R) const { 224 if (std::tuple_size<Ops_t>::value == 0) { 225 assert(Opcode == VPInstruction::BuildVector && 226 "can only match BuildVector with empty ops"); 227 auto *VPI = dyn_cast<VPInstruction>(R); 228 return VPI && VPI->getOpcode() == VPInstruction::BuildVector; 229 } 230 231 if ((!matchRecipeAndOpcode<RecipeTys>(R) && ...)) 232 return false; 233 234 assert(R->getNumOperands() == std::tuple_size<Ops_t>::value && 235 "recipe with matched opcode does not have the expected number of " 236 "operands"); 237 238 auto IdxSeq = std::make_index_sequence<std::tuple_size<Ops_t>::value>(); 239 if (all_of_tuple_elements(IdxSeq, [R](auto Op, unsigned Idx) { 240 return Op.match(R->getOperand(Idx)); 241 })) 242 return true; 243 244 return Commutative && 245 all_of_tuple_elements(IdxSeq, [R](auto Op, unsigned Idx) { 246 return Op.match(R->getOperand(R->getNumOperands() - Idx - 1)); 247 }); 248 } 249 250 private: 251 template <typename RecipeTy> 252 static bool matchRecipeAndOpcode(const VPRecipeBase *R) { 253 auto *DefR = dyn_cast<RecipeTy>(R); 254 // Check for recipes that do not have opcodes. 255 if constexpr (std::is_same<RecipeTy, VPScalarIVStepsRecipe>::value || 256 std::is_same<RecipeTy, VPCanonicalIVPHIRecipe>::value || 257 std::is_same<RecipeTy, VPWidenSelectRecipe>::value || 258 std::is_same<RecipeTy, VPDerivedIVRecipe>::value || 259 std::is_same<RecipeTy, VPWidenGEPRecipe>::value) 260 return DefR; 261 else 262 return DefR && DefR->getOpcode() == Opcode; 263 } 264 265 /// Helper to check if predicate \p P holds on all tuple elements in Ops using 266 /// the provided index sequence. 267 template <typename Fn, std::size_t... Is> 268 bool all_of_tuple_elements(std::index_sequence<Is...>, Fn P) const { 269 return (P(std::get<Is>(Ops), Is) && ...); 270 } 271 }; 272 273 template <unsigned Opcode, typename... RecipeTys> 274 using ZeroOpRecipe_match = 275 Recipe_match<std::tuple<>, Opcode, false, RecipeTys...>; 276 277 template <typename Op0_t, unsigned Opcode, typename... RecipeTys> 278 using UnaryRecipe_match = 279 Recipe_match<std::tuple<Op0_t>, Opcode, false, RecipeTys...>; 280 281 template <typename Op0_t, unsigned Opcode> 282 using UnaryVPInstruction_match = 283 UnaryRecipe_match<Op0_t, Opcode, VPInstruction>; 284 285 template <unsigned Opcode> 286 using ZeroOpVPInstruction_match = ZeroOpRecipe_match<Opcode, VPInstruction>; 287 288 template <typename Op0_t, unsigned Opcode> 289 using AllUnaryRecipe_match = 290 UnaryRecipe_match<Op0_t, Opcode, VPWidenRecipe, VPReplicateRecipe, 291 VPWidenCastRecipe, VPInstruction>; 292 293 template <typename Op0_t, typename Op1_t, unsigned Opcode, bool Commutative, 294 typename... RecipeTys> 295 using BinaryRecipe_match = 296 Recipe_match<std::tuple<Op0_t, Op1_t>, Opcode, Commutative, RecipeTys...>; 297 298 template <typename Op0_t, typename Op1_t, unsigned Opcode> 299 using BinaryVPInstruction_match = 300 BinaryRecipe_match<Op0_t, Op1_t, Opcode, /*Commutative*/ false, 301 VPInstruction>; 302 303 template <typename Op0_t, typename Op1_t, typename Op2_t, unsigned Opcode, 304 bool Commutative, typename... RecipeTys> 305 using TernaryRecipe_match = Recipe_match<std::tuple<Op0_t, Op1_t, Op2_t>, 306 Opcode, Commutative, RecipeTys...>; 307 308 template <typename Op0_t, typename Op1_t, typename Op2_t, unsigned Opcode> 309 using TernaryVPInstruction_match = 310 TernaryRecipe_match<Op0_t, Op1_t, Op2_t, Opcode, /*Commutative*/ false, 311 VPInstruction>; 312 313 template <typename Op0_t, typename Op1_t, unsigned Opcode, 314 bool Commutative = false> 315 using AllBinaryRecipe_match = 316 BinaryRecipe_match<Op0_t, Op1_t, Opcode, Commutative, VPWidenRecipe, 317 VPReplicateRecipe, VPWidenCastRecipe, VPInstruction>; 318 319 /// BuildVector is matches only its opcode, w/o matching its operands as the 320 /// number of operands is not fixed. 321 inline ZeroOpVPInstruction_match<VPInstruction::BuildVector> m_BuildVector() { 322 return ZeroOpVPInstruction_match<VPInstruction::BuildVector>(); 323 } 324 325 template <unsigned Opcode, typename Op0_t> 326 inline UnaryVPInstruction_match<Op0_t, Opcode> 327 m_VPInstruction(const Op0_t &Op0) { 328 return UnaryVPInstruction_match<Op0_t, Opcode>(Op0); 329 } 330 331 template <unsigned Opcode, typename Op0_t, typename Op1_t> 332 inline BinaryVPInstruction_match<Op0_t, Op1_t, Opcode> 333 m_VPInstruction(const Op0_t &Op0, const Op1_t &Op1) { 334 return BinaryVPInstruction_match<Op0_t, Op1_t, Opcode>(Op0, Op1); 335 } 336 337 template <unsigned Opcode, typename Op0_t, typename Op1_t, typename Op2_t> 338 inline TernaryVPInstruction_match<Op0_t, Op1_t, Op2_t, Opcode> 339 m_VPInstruction(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) { 340 return TernaryVPInstruction_match<Op0_t, Op1_t, Op2_t, Opcode>( 341 {Op0, Op1, Op2}); 342 } 343 344 template <typename Op0_t, typename Op1_t, typename Op2_t, typename Op3_t, 345 unsigned Opcode, bool Commutative, typename... RecipeTys> 346 using Recipe4Op_match = Recipe_match<std::tuple<Op0_t, Op1_t, Op2_t, Op3_t>, 347 Opcode, Commutative, RecipeTys...>; 348 349 template <typename Op0_t, typename Op1_t, typename Op2_t, typename Op3_t, 350 unsigned Opcode> 351 using VPInstruction4Op_match = 352 Recipe4Op_match<Op0_t, Op1_t, Op2_t, Op3_t, Opcode, /*Commutative*/ false, 353 VPInstruction>; 354 355 template <unsigned Opcode, typename Op0_t, typename Op1_t, typename Op2_t, 356 typename Op3_t> 357 inline VPInstruction4Op_match<Op0_t, Op1_t, Op2_t, Op3_t, Opcode> 358 m_VPInstruction(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2, 359 const Op3_t &Op3) { 360 return VPInstruction4Op_match<Op0_t, Op1_t, Op2_t, Op3_t, Opcode>( 361 {Op0, Op1, Op2, Op3}); 362 } 363 template <typename Op0_t> 364 inline UnaryVPInstruction_match<Op0_t, Instruction::Freeze> 365 m_Freeze(const Op0_t &Op0) { 366 return m_VPInstruction<Instruction::Freeze>(Op0); 367 } 368 369 template <typename Op0_t> 370 inline UnaryVPInstruction_match<Op0_t, VPInstruction::BranchOnCond> 371 m_BranchOnCond(const Op0_t &Op0) { 372 return m_VPInstruction<VPInstruction::BranchOnCond>(Op0); 373 } 374 375 template <typename Op0_t> 376 inline UnaryVPInstruction_match<Op0_t, VPInstruction::Broadcast> 377 m_Broadcast(const Op0_t &Op0) { 378 return m_VPInstruction<VPInstruction::Broadcast>(Op0); 379 } 380 381 template <typename Op0_t, typename Op1_t> 382 inline BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::ActiveLaneMask> 383 m_ActiveLaneMask(const Op0_t &Op0, const Op1_t &Op1) { 384 return m_VPInstruction<VPInstruction::ActiveLaneMask>(Op0, Op1); 385 } 386 387 template <typename Op0_t, typename Op1_t> 388 inline BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::BranchOnCount> 389 m_BranchOnCount(const Op0_t &Op0, const Op1_t &Op1) { 390 return m_VPInstruction<VPInstruction::BranchOnCount>(Op0, Op1); 391 } 392 393 template <unsigned Opcode, typename Op0_t> 394 inline AllUnaryRecipe_match<Op0_t, Opcode> m_Unary(const Op0_t &Op0) { 395 return AllUnaryRecipe_match<Op0_t, Opcode>(Op0); 396 } 397 398 template <typename Op0_t> 399 inline AllUnaryRecipe_match<Op0_t, Instruction::Trunc> 400 m_Trunc(const Op0_t &Op0) { 401 return m_Unary<Instruction::Trunc, Op0_t>(Op0); 402 } 403 404 template <typename Op0_t> 405 inline AllUnaryRecipe_match<Op0_t, Instruction::ZExt> m_ZExt(const Op0_t &Op0) { 406 return m_Unary<Instruction::ZExt, Op0_t>(Op0); 407 } 408 409 template <typename Op0_t> 410 inline AllUnaryRecipe_match<Op0_t, Instruction::SExt> m_SExt(const Op0_t &Op0) { 411 return m_Unary<Instruction::SExt, Op0_t>(Op0); 412 } 413 414 template <typename Op0_t> 415 inline match_combine_or<AllUnaryRecipe_match<Op0_t, Instruction::ZExt>, 416 AllUnaryRecipe_match<Op0_t, Instruction::SExt>> 417 m_ZExtOrSExt(const Op0_t &Op0) { 418 return m_CombineOr(m_ZExt(Op0), m_SExt(Op0)); 419 } 420 421 template <unsigned Opcode, typename Op0_t, typename Op1_t, 422 bool Commutative = false> 423 inline AllBinaryRecipe_match<Op0_t, Op1_t, Opcode, Commutative> 424 m_Binary(const Op0_t &Op0, const Op1_t &Op1) { 425 return AllBinaryRecipe_match<Op0_t, Op1_t, Opcode, Commutative>(Op0, Op1); 426 } 427 428 template <unsigned Opcode, typename Op0_t, typename Op1_t> 429 inline AllBinaryRecipe_match<Op0_t, Op1_t, Opcode, true> 430 m_c_Binary(const Op0_t &Op0, const Op1_t &Op1) { 431 return AllBinaryRecipe_match<Op0_t, Op1_t, Opcode, true>(Op0, Op1); 432 } 433 434 template <typename Op0_t, typename Op1_t> 435 inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Mul> 436 m_Mul(const Op0_t &Op0, const Op1_t &Op1) { 437 return m_Binary<Instruction::Mul, Op0_t, Op1_t>(Op0, Op1); 438 } 439 440 template <typename Op0_t, typename Op1_t> 441 inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Mul, 442 /* Commutative =*/true> 443 m_c_Mul(const Op0_t &Op0, const Op1_t &Op1) { 444 return m_Binary<Instruction::Mul, Op0_t, Op1_t, true>(Op0, Op1); 445 } 446 447 /// Match a binary OR operation. Note that while conceptually the operands can 448 /// be matched commutatively, \p Commutative defaults to false in line with the 449 /// IR-based pattern matching infrastructure. Use m_c_BinaryOr for a commutative 450 /// version of the matcher. 451 template <typename Op0_t, typename Op1_t, bool Commutative = false> 452 inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Or, Commutative> 453 m_BinaryOr(const Op0_t &Op0, const Op1_t &Op1) { 454 return m_Binary<Instruction::Or, Op0_t, Op1_t, Commutative>(Op0, Op1); 455 } 456 457 template <typename Op0_t, typename Op1_t> 458 inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Or, 459 /*Commutative*/ true> 460 m_c_BinaryOr(const Op0_t &Op0, const Op1_t &Op1) { 461 return m_BinaryOr<Op0_t, Op1_t, /*Commutative*/ true>(Op0, Op1); 462 } 463 464 template <typename Op0_t, typename Op1_t> 465 using GEPLikeRecipe_match = 466 BinaryRecipe_match<Op0_t, Op1_t, Instruction::GetElementPtr, false, 467 VPWidenRecipe, VPReplicateRecipe, VPWidenGEPRecipe, 468 VPInstruction>; 469 470 template <typename Op0_t, typename Op1_t> 471 inline GEPLikeRecipe_match<Op0_t, Op1_t> m_GetElementPtr(const Op0_t &Op0, 472 const Op1_t &Op1) { 473 return GEPLikeRecipe_match<Op0_t, Op1_t>(Op0, Op1); 474 } 475 476 template <typename Op0_t, typename Op1_t, typename Op2_t, unsigned Opcode> 477 using AllTernaryRecipe_match = 478 Recipe_match<std::tuple<Op0_t, Op1_t, Op2_t>, Opcode, false, 479 VPReplicateRecipe, VPInstruction, VPWidenSelectRecipe>; 480 481 template <typename Op0_t, typename Op1_t, typename Op2_t> 482 inline AllTernaryRecipe_match<Op0_t, Op1_t, Op2_t, Instruction::Select> 483 m_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) { 484 return AllTernaryRecipe_match<Op0_t, Op1_t, Op2_t, Instruction::Select>( 485 {Op0, Op1, Op2}); 486 } 487 488 template <typename Op0_t> 489 inline match_combine_or<UnaryVPInstruction_match<Op0_t, VPInstruction::Not>, 490 AllBinaryRecipe_match<int_pred_ty<is_all_ones>, Op0_t, 491 Instruction::Xor, true>> 492 m_Not(const Op0_t &Op0) { 493 return m_CombineOr(m_VPInstruction<VPInstruction::Not>(Op0), 494 m_c_Binary<Instruction::Xor>(m_AllOnes(), Op0)); 495 } 496 497 template <typename Op0_t, typename Op1_t> 498 inline match_combine_or< 499 BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::LogicalAnd>, 500 AllTernaryRecipe_match<Op0_t, Op1_t, specific_intval<1>, 501 Instruction::Select>> 502 m_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) { 503 return m_CombineOr( 504 m_VPInstruction<VPInstruction::LogicalAnd, Op0_t, Op1_t>(Op0, Op1), 505 m_Select(Op0, Op1, m_False())); 506 } 507 508 template <typename Op0_t, typename Op1_t> 509 inline AllTernaryRecipe_match<Op0_t, specific_intval<1>, Op1_t, 510 Instruction::Select> 511 m_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) { 512 return m_Select(Op0, m_True(), Op1); 513 } 514 515 template <typename Op0_t, typename Op1_t, typename Op2_t> 516 using VPScalarIVSteps_match = 517 TernaryRecipe_match<Op0_t, Op1_t, Op2_t, 0, false, VPScalarIVStepsRecipe>; 518 519 template <typename Op0_t, typename Op1_t, typename Op2_t> 520 inline VPScalarIVSteps_match<Op0_t, Op1_t, Op2_t> 521 m_ScalarIVSteps(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) { 522 return VPScalarIVSteps_match<Op0_t, Op1_t, Op2_t>({Op0, Op1, Op2}); 523 } 524 525 template <typename Op0_t, typename Op1_t, typename Op2_t> 526 using VPDerivedIV_match = 527 Recipe_match<std::tuple<Op0_t, Op1_t, Op2_t>, 0, false, VPDerivedIVRecipe>; 528 529 template <typename Op0_t, typename Op1_t, typename Op2_t> 530 inline VPDerivedIV_match<Op0_t, Op1_t, Op2_t> 531 m_DerivedIV(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) { 532 return VPDerivedIV_match<Op0_t, Op1_t, Op2_t>({Op0, Op1, Op2}); 533 } 534 535 /// Match a call argument at a given argument index. 536 template <typename Opnd_t> struct Argument_match { 537 /// Call argument index to match. 538 unsigned OpI; 539 Opnd_t Val; 540 541 Argument_match(unsigned OpIdx, const Opnd_t &V) : OpI(OpIdx), Val(V) {} 542 543 template <typename OpTy> bool match(OpTy *V) const { 544 if (const auto *R = dyn_cast<VPWidenIntrinsicRecipe>(V)) 545 return Val.match(R->getOperand(OpI)); 546 if (const auto *R = dyn_cast<VPWidenCallRecipe>(V)) 547 return Val.match(R->getOperand(OpI)); 548 if (const auto *R = dyn_cast<VPReplicateRecipe>(V)) 549 if (isa<CallInst>(R->getUnderlyingInstr())) 550 return Val.match(R->getOperand(OpI + 1)); 551 return false; 552 } 553 }; 554 555 /// Match a call argument. 556 template <unsigned OpI, typename Opnd_t> 557 inline Argument_match<Opnd_t> m_Argument(const Opnd_t &Op) { 558 return Argument_match<Opnd_t>(OpI, Op); 559 } 560 561 /// Intrinsic matchers. 562 struct IntrinsicID_match { 563 unsigned ID; 564 565 IntrinsicID_match(Intrinsic::ID IntrID) : ID(IntrID) {} 566 567 template <typename OpTy> bool match(OpTy *V) const { 568 if (const auto *R = dyn_cast<VPWidenIntrinsicRecipe>(V)) 569 return R->getVectorIntrinsicID() == ID; 570 if (const auto *R = dyn_cast<VPWidenCallRecipe>(V)) 571 return R->getCalledScalarFunction()->getIntrinsicID() == ID; 572 if (const auto *R = dyn_cast<VPReplicateRecipe>(V)) 573 if (const auto *CI = dyn_cast<CallInst>(R->getUnderlyingInstr())) 574 if (const auto *F = CI->getCalledFunction()) 575 return F->getIntrinsicID() == ID; 576 return false; 577 } 578 }; 579 580 /// Intrinsic matches are combinations of ID matchers, and argument 581 /// matchers. Higher arity matcher are defined recursively in terms of and-ing 582 /// them with lower arity matchers. Here's some convenient typedefs for up to 583 /// several arguments, and more can be added as needed 584 template <typename T0 = void, typename T1 = void, typename T2 = void, 585 typename T3 = void> 586 struct m_Intrinsic_Ty; 587 template <typename T0> struct m_Intrinsic_Ty<T0> { 588 using Ty = match_combine_and<IntrinsicID_match, Argument_match<T0>>; 589 }; 590 template <typename T0, typename T1> struct m_Intrinsic_Ty<T0, T1> { 591 using Ty = 592 match_combine_and<typename m_Intrinsic_Ty<T0>::Ty, Argument_match<T1>>; 593 }; 594 template <typename T0, typename T1, typename T2> 595 struct m_Intrinsic_Ty<T0, T1, T2> { 596 using Ty = match_combine_and<typename m_Intrinsic_Ty<T0, T1>::Ty, 597 Argument_match<T2>>; 598 }; 599 template <typename T0, typename T1, typename T2, typename T3> 600 struct m_Intrinsic_Ty { 601 using Ty = match_combine_and<typename m_Intrinsic_Ty<T0, T1, T2>::Ty, 602 Argument_match<T3>>; 603 }; 604 605 /// Match intrinsic calls like this: 606 /// m_Intrinsic<Intrinsic::fabs>(m_VPValue(X), ...) 607 template <Intrinsic::ID IntrID> inline IntrinsicID_match m_Intrinsic() { 608 return IntrinsicID_match(IntrID); 609 } 610 611 template <Intrinsic::ID IntrID, typename T0> 612 inline typename m_Intrinsic_Ty<T0>::Ty m_Intrinsic(const T0 &Op0) { 613 return m_CombineAnd(m_Intrinsic<IntrID>(), m_Argument<0>(Op0)); 614 } 615 616 template <Intrinsic::ID IntrID, typename T0, typename T1> 617 inline typename m_Intrinsic_Ty<T0, T1>::Ty m_Intrinsic(const T0 &Op0, 618 const T1 &Op1) { 619 return m_CombineAnd(m_Intrinsic<IntrID>(Op0), m_Argument<1>(Op1)); 620 } 621 622 template <Intrinsic::ID IntrID, typename T0, typename T1, typename T2> 623 inline typename m_Intrinsic_Ty<T0, T1, T2>::Ty 624 m_Intrinsic(const T0 &Op0, const T1 &Op1, const T2 &Op2) { 625 return m_CombineAnd(m_Intrinsic<IntrID>(Op0, Op1), m_Argument<2>(Op2)); 626 } 627 628 template <Intrinsic::ID IntrID, typename T0, typename T1, typename T2, 629 typename T3> 630 inline typename m_Intrinsic_Ty<T0, T1, T2, T3>::Ty 631 m_Intrinsic(const T0 &Op0, const T1 &Op1, const T2 &Op2, const T3 &Op3) { 632 return m_CombineAnd(m_Intrinsic<IntrID>(Op0, Op1, Op2), m_Argument<3>(Op3)); 633 } 634 635 } // namespace VPlanPatternMatch 636 } // namespace llvm 637 638 #endif 639