[llvm] [NFC][SPIR-V] Use PatternMatch combinators in SPIRVEmitIntrinsics (PR #189554)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 01:28:52 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/189554

>From 4db17a57ebf4d795ec7ecd73e5328f86e2402d0d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 31 Mar 2026 09:46:01 +0200
Subject: [PATCH 1/2] [NFC][SPIR-V] Use PatternMatch combinators in
 SPIRVEmitIntrinsics

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 97 ++++++-------------
 1 file changed, 30 insertions(+), 67 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 9c2af100ad64e..b487d359253f6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -53,6 +53,7 @@
 // TODO: consider removing spv.track.constant in favor of spv.assign.type.
 
 using namespace llvm;
+using namespace llvm::PatternMatch;
 
 static cl::opt<bool>
     SpirvEmitOpNames("spirv-emit-op-names",
@@ -375,37 +376,28 @@ class SPIRVEmitIntrinsics
 };
 
 bool isConvergenceIntrinsic(const Instruction *I) {
-  const auto *II = dyn_cast<IntrinsicInst>(I);
-  if (!II)
-    return false;
-
-  return II->getIntrinsicID() == Intrinsic::experimental_convergence_entry ||
-         II->getIntrinsicID() == Intrinsic::experimental_convergence_loop ||
-         II->getIntrinsicID() == Intrinsic::experimental_convergence_anchor;
+  return match(
+      I, m_CombineOr(
+             m_Intrinsic<Intrinsic::experimental_convergence_entry>(),
+             m_CombineOr(
+                 m_Intrinsic<Intrinsic::experimental_convergence_loop>(),
+                 m_Intrinsic<Intrinsic::experimental_convergence_anchor>())));
 }
 
 bool expectIgnoredInIRTranslation(const Instruction *I) {
-  const auto *II = dyn_cast<IntrinsicInst>(I);
-  if (!II)
-    return false;
-  switch (II->getIntrinsicID()) {
-  case Intrinsic::invariant_start:
-  case Intrinsic::spv_resource_handlefrombinding:
-  case Intrinsic::spv_resource_getpointer:
-    return true;
-  default:
-    return false;
-  }
+  return match(
+      I,
+      m_CombineOr(
+          m_Intrinsic<Intrinsic::invariant_start>(),
+          m_CombineOr(m_Intrinsic<Intrinsic::spv_resource_handlefrombinding>(),
+                      m_Intrinsic<Intrinsic::spv_resource_getpointer>())));
 }
 
 // Returns the source pointer from `I` ignoring intermediate ptrcast.
 Value *getPointerRoot(Value *I) {
-  if (auto *II = dyn_cast<IntrinsicInst>(I)) {
-    if (II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
-      Value *V = II->getArgOperand(0);
-      return getPointerRoot(V);
-    }
-  }
+  Value *V;
+  if (match(I, m_Intrinsic<Intrinsic::spv_ptrcast>(m_Value(V))))
+    return getPointerRoot(V);
   return I;
 }
 
@@ -417,8 +409,7 @@ INITIALIZE_PASS(SPIRVEmitIntrinsics, "spirv-emit-intrinsics",
                 "SPIRV emit intrinsics", false, false)
 
 static inline bool isAssignTypeInstr(const Instruction *I) {
-  return isa<IntrinsicInst>(I) &&
-         cast<IntrinsicInst>(I)->getIntrinsicID() == Intrinsic::spv_assign_type;
+  return match(I, m_Intrinsic<Intrinsic::spv_assign_type>());
 }
 
 static bool isMemInstrToReplace(Instruction *I) {
@@ -450,14 +441,8 @@ static void setInsertPointAfterDef(IRBuilder<> &B, Instruction *I) {
 }
 
 static bool requireAssignType(Instruction *I) {
-  if (const auto *Intr = dyn_cast<IntrinsicInst>(I)) {
-    switch (Intr->getIntrinsicID()) {
-    case Intrinsic::invariant_start:
-    case Intrinsic::invariant_end:
-      return false;
-    }
-  }
-  return true;
+  return !match(I, m_CombineOr(m_Intrinsic<Intrinsic::invariant_start>(),
+                               m_Intrinsic<Intrinsic::invariant_end>()));
 }
 
 static inline void reportFatalOnTokenType(const Instruction *I) {
@@ -1640,21 +1625,9 @@ static void createSaturatedConversionDecoration(Instruction *I,
 }
 
 static void addSaturatedDecorationToIntrinsic(Instruction *I, IRBuilder<> &B) {
-  if (auto *CI = dyn_cast<CallInst>(I)) {
-    if (Function *Fu = CI->getCalledFunction()) {
-      if (Fu->isIntrinsic()) {
-        unsigned const int IntrinsicId = Fu->getIntrinsicID();
-        switch (IntrinsicId) {
-        case Intrinsic::fptosi_sat:
-        case Intrinsic::fptoui_sat:
-          createSaturatedConversionDecoration(I, B);
-          break;
-        default:
-          break;
-        }
-      }
-    }
-  }
+  if (match(I, m_CombineOr(m_Intrinsic<Intrinsic::fptosi_sat>(),
+                           m_Intrinsic<Intrinsic::fptoui_sat>())))
+    createSaturatedConversionDecoration(I, B);
 }
 
 Instruction *SPIRVEmitIntrinsics::visitCallInst(CallInst &Call) {
@@ -1745,12 +1718,7 @@ Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) {
 }
 
 static bool isFirstIndexZero(const GetElementPtrInst *GEP) {
-  if (GEP->getNumIndices() == 0)
-    return false;
-  if (const auto *CI = dyn_cast<ConstantInt>(GEP->getOperand(1))) {
-    return CI->getZExtValue() == 0;
-  }
-  return false;
+  return GEP->getNumIndices() > 0 && match(GEP->getOperand(1), m_Zero());
 }
 
 Instruction *SPIRVEmitIntrinsics::visitIntrinsicInst(IntrinsicInst &I) {
@@ -2496,19 +2464,15 @@ bool SPIRVEmitIntrinsics::shouldTryToAddMemAliasingDecoration(
   // Add aliasing decorations to internal load and store intrinsics
   // and atomic instructions, skipping atomic store as it won't have ID to
   // attach the decoration.
-  CallInst *CI = dyn_cast<CallInst>(Inst);
+  if (match(Inst, m_CombineOr(m_Intrinsic<Intrinsic::spv_load>(),
+                              m_Intrinsic<Intrinsic::spv_store>())))
+    return true;
+  auto *CI = dyn_cast<CallInst>(Inst);
   if (!CI)
     return false;
   if (Function *Fun = CI->getCalledFunction()) {
-    if (Fun->isIntrinsic()) {
-      switch (Fun->getIntrinsicID()) {
-      case Intrinsic::spv_load:
-      case Intrinsic::spv_store:
-        return true;
-      default:
-        return false;
-      }
-    }
+    if (Fun->isIntrinsic())
+      return false;
     std::string Name = getOclOrSpirvBuiltinDemangledName(Fun->getName());
     const std::string Prefix = "__spirv_Atomic";
     const bool IsAtomic = Name.find(Prefix) == 0;
@@ -3074,8 +3038,7 @@ SPIRVEmitIntrinsics::simplifyZeroLengthArrayGepInst(GetElementPtrInst *GEP) {
   Type *SrcTy = GEP->getSourceElementType();
   SmallVector<Value *, 8> Indices(GEP->indices());
   ArrayType *ArrTy = dyn_cast<ArrayType>(SrcTy);
-  if (ArrTy && ArrTy->getNumElements() == 0 &&
-      PatternMatch::match(Indices[0], PatternMatch::m_Zero())) {
+  if (ArrTy && ArrTy->getNumElements() == 0 && match(Indices[0], m_Zero())) {
     Indices.erase(Indices.begin());
     SrcTy = ArrTy->getElementType();
     return GetElementPtrInst::Create(SrcTy, GEP->getPointerOperand(), Indices,

>From 4fce2b80358185d84807e20ee94a39e32fcac2b1 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 13 Apr 2026 10:28:41 +0200
Subject: [PATCH 2/2] Address comments

---
 llvm/include/llvm/IR/PatternMatch.h           | 21 ++++++++++++
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 29 +++++++----------
 llvm/unittests/IR/PatternMatch.cpp            | 32 +++++++++++++++++++
 3 files changed, 64 insertions(+), 18 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 2160146245137..53dbfedb71d98 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -2874,6 +2874,18 @@ struct IntrinsicID_match {
   }
 };
 
+/// Match intrinsic calls with any of the given IDs.
+template <Intrinsic::ID... IntrIDs> struct IntrinsicIDs_match {
+  template <typename OpTy> bool match(OpTy *V) const {
+    if (const auto *CI = dyn_cast<CallInst>(V))
+      if (const auto *F = dyn_cast_or_null<Function>(CI->getCalledOperand())) {
+        Intrinsic::ID ID = F->getIntrinsicID();
+        return ((ID == IntrIDs) || ...);
+      }
+    return false;
+  }
+};
+
 /// Intrinsic matches are combinations of ID matchers, and argument
 /// matchers. Higher arity matcher are defined recursively in terms of and-ing
 /// them with lower arity matchers. Here's some convenient typedefs for up to
@@ -2920,6 +2932,15 @@ template <Intrinsic::ID IntrID> inline IntrinsicID_match m_Intrinsic() {
   return IntrinsicID_match(IntrID);
 }
 
+/// Match intrinsic calls with any of the given IDs like this:
+/// m_AnyIntrinsic<Intrinsic::fptosi_sat, Intrinsic::fptoui_sat>()
+/// This is more efficient than using nested m_CombineOr with m_Intrinsic
+/// because it performs the CallInst/Function cast only once.
+template <Intrinsic::ID... IntrIDs>
+inline IntrinsicIDs_match<IntrIDs...> m_AnyIntrinsic() {
+  return IntrinsicIDs_match<IntrIDs...>();
+}
+
 /// Matches MaskedLoad Intrinsic.
 template <typename Opnd0, typename Opnd1, typename Opnd2>
 inline typename m_Intrinsic_Ty<Opnd0, Opnd1, Opnd2>::Ty
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index b487d359253f6..7600b3a695a4e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -376,21 +376,15 @@ class SPIRVEmitIntrinsics
 };
 
 bool isConvergenceIntrinsic(const Instruction *I) {
-  return match(
-      I, m_CombineOr(
-             m_Intrinsic<Intrinsic::experimental_convergence_entry>(),
-             m_CombineOr(
-                 m_Intrinsic<Intrinsic::experimental_convergence_loop>(),
-                 m_Intrinsic<Intrinsic::experimental_convergence_anchor>())));
+  return match(I, m_AnyIntrinsic<Intrinsic::experimental_convergence_entry,
+                                 Intrinsic::experimental_convergence_loop,
+                                 Intrinsic::experimental_convergence_anchor>());
 }
 
 bool expectIgnoredInIRTranslation(const Instruction *I) {
-  return match(
-      I,
-      m_CombineOr(
-          m_Intrinsic<Intrinsic::invariant_start>(),
-          m_CombineOr(m_Intrinsic<Intrinsic::spv_resource_handlefrombinding>(),
-                      m_Intrinsic<Intrinsic::spv_resource_getpointer>())));
+  return match(I, m_AnyIntrinsic<Intrinsic::invariant_start,
+                                 Intrinsic::spv_resource_handlefrombinding,
+                                 Intrinsic::spv_resource_getpointer>());
 }
 
 // Returns the source pointer from `I` ignoring intermediate ptrcast.
@@ -441,8 +435,9 @@ static void setInsertPointAfterDef(IRBuilder<> &B, Instruction *I) {
 }
 
 static bool requireAssignType(Instruction *I) {
-  return !match(I, m_CombineOr(m_Intrinsic<Intrinsic::invariant_start>(),
-                               m_Intrinsic<Intrinsic::invariant_end>()));
+  return !match(
+      I,
+      m_AnyIntrinsic<Intrinsic::invariant_start, Intrinsic::invariant_end>());
 }
 
 static inline void reportFatalOnTokenType(const Instruction *I) {
@@ -1625,8 +1620,7 @@ static void createSaturatedConversionDecoration(Instruction *I,
 }
 
 static void addSaturatedDecorationToIntrinsic(Instruction *I, IRBuilder<> &B) {
-  if (match(I, m_CombineOr(m_Intrinsic<Intrinsic::fptosi_sat>(),
-                           m_Intrinsic<Intrinsic::fptoui_sat>())))
+  if (match(I, m_AnyIntrinsic<Intrinsic::fptosi_sat, Intrinsic::fptoui_sat>()))
     createSaturatedConversionDecoration(I, B);
 }
 
@@ -2464,8 +2458,7 @@ bool SPIRVEmitIntrinsics::shouldTryToAddMemAliasingDecoration(
   // Add aliasing decorations to internal load and store intrinsics
   // and atomic instructions, skipping atomic store as it won't have ID to
   // attach the decoration.
-  if (match(Inst, m_CombineOr(m_Intrinsic<Intrinsic::spv_load>(),
-                              m_Intrinsic<Intrinsic::spv_store>())))
+  if (match(Inst, m_AnyIntrinsic<Intrinsic::spv_load, Intrinsic::spv_store>()))
     return true;
   auto *CI = dyn_cast<CallInst>(Inst);
   if (!CI)
diff --git a/llvm/unittests/IR/PatternMatch.cpp b/llvm/unittests/IR/PatternMatch.cpp
index 8cbec2cba8392..3f81739ef3c7b 100644
--- a/llvm/unittests/IR/PatternMatch.cpp
+++ b/llvm/unittests/IR/PatternMatch.cpp
@@ -2105,6 +2105,38 @@ TEST_F(PatternMatchTest, IntrinsicMatcher) {
                             m_SpecificInt(10))));
 }
 
+TEST_F(PatternMatchTest, AnyIntrinsicMatcher) {
+  Value *Ops0[] = {IRB.getInt32(0)};
+  Value *Ops1[] = {IRB.getInt32(0)};
+  Module *M = BB->getParent()->getParent();
+
+  Function *BswapFn =
+      Intrinsic::getOrInsertDeclaration(M, Intrinsic::bswap, IRB.getInt32Ty());
+  Value *BswapCall = CallInst::Create(BswapFn, Ops0, "", BB);
+
+  Function *CtpopFn =
+      Intrinsic::getOrInsertDeclaration(M, Intrinsic::ctpop, IRB.getInt32Ty());
+  Value *CtpopCall = CallInst::Create(CtpopFn, Ops1, "", BB);
+
+  // Match any of the listed intrinsic IDs.
+  EXPECT_TRUE(
+      match(BswapCall, m_AnyIntrinsic<Intrinsic::bswap, Intrinsic::ctpop>()));
+  EXPECT_TRUE(
+      match(CtpopCall, m_AnyIntrinsic<Intrinsic::bswap, Intrinsic::ctpop>()));
+
+  // Should not match an unlisted intrinsic.
+  EXPECT_FALSE(match(
+      BswapCall, m_AnyIntrinsic<Intrinsic::ctpop, Intrinsic::bitreverse>()));
+
+  // Single ID should work like m_Intrinsic.
+  EXPECT_TRUE(match(BswapCall, m_AnyIntrinsic<Intrinsic::bswap>()));
+  EXPECT_FALSE(match(CtpopCall, m_AnyIntrinsic<Intrinsic::bswap>()));
+
+  // Non-intrinsic call should not match.
+  EXPECT_FALSE(match(IRB.getInt32(0),
+                     m_AnyIntrinsic<Intrinsic::bswap, Intrinsic::ctpop>()));
+}
+
 namespace {
 
 struct is_unsigned_zero_pred {



More information about the llvm-commits mailing list