[llvm] [GlobalISel] Add immediates and predicates to `LegalityQuery` (PR #198979)

Demetrius Kanios via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 23:50:51 PDT 2026


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/198979

>From 000167e24e21e9765e81a2bc3bca33a0cdbb1eee Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 21 May 2026 01:05:18 -0700
Subject: [PATCH] Add immediates and predicates to LegalityQuery

---
 .../GlobalISel/LegalizationArtifactCombiner.h |  7 +--
 .../llvm/CodeGen/GlobalISel/LegalizerInfo.h   | 30 ++++++++++++-
 .../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 31 +++++++++----
 .../CodeGen/GlobalISel/LegalityPredicates.cpp | 43 +++++++++++++++++++
 llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp | 42 ++++++++++++------
 .../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 29 ++++++-------
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 30 ++++++-------
 7 files changed, 149 insertions(+), 63 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
index d8d7ccc0bd7a7..4d94316490d94 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
@@ -205,11 +205,12 @@ class LegalizationArtifactCombiner {
     Register TruncSrc;
     if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc)))) {
       LLT DstTy = MRI.getType(DstReg);
-      if (isInstUnsupported({TargetOpcode::G_SEXT_INREG, {DstTy}}))
-        return false;
-      LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI);
       LLT SrcTy = MRI.getType(SrcReg);
       uint64_t SizeInBits = SrcTy.getScalarSizeInBits();
+      if (isInstUnsupported(
+              {TargetOpcode::G_SEXT_INREG, {DstTy}, {}, {(int64_t)SizeInBits}}))
+        return false;
+      LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI);
       if (DstTy != MRI.getType(TruncSrc))
         TruncSrc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc).getReg(0);
       // Elide G_SEXT_INREG if possible. This is similar to eliding G_AND in
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h
index 1d6a6cfb15a50..a10615c6756d5 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h
@@ -20,6 +20,7 @@
 #include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGen/TargetOpcodes.h"
 #include "llvm/CodeGenTypes/LowLevelType.h"
+#include "llvm/IR/InstrTypes.h"
 #include "llvm/MC/MCInstrDesc.h"
 #include "llvm/Support/AtomicOrdering.h"
 #include "llvm/Support/CommandLine.h"
@@ -132,9 +133,15 @@ struct LegalityQuery {
   /// memory type for each MMO.
   ArrayRef<MemDesc> MMODescrs;
 
+  ArrayRef<int64_t> Immediates;
+  ArrayRef<CmpInst::Predicate> Predicates;
+
   constexpr LegalityQuery(unsigned Opcode, ArrayRef<LLT> Types,
-                          ArrayRef<MemDesc> MMODescrs = {})
-      : Opcode(Opcode), Types(Types), MMODescrs(MMODescrs) {}
+                          ArrayRef<MemDesc> MMODescrs = {},
+                          ArrayRef<int64_t> Immediates = {},
+                          ArrayRef<CmpInst::Predicate> Predicates = {})
+      : Opcode(Opcode), Types(Types), MMODescrs(MMODescrs),
+        Immediates(Immediates), Predicates(Predicates) {}
 
   LLVM_ABI raw_ostream &print(raw_ostream &OS) const;
 };
@@ -370,6 +377,25 @@ LLVM_ABI LegalityPredicate numElementsNotPow2(unsigned TypeIdx);
 /// stronger.
 LLVM_ABI LegalityPredicate
 atomicOrderingAtLeastOrStrongerThan(unsigned MMOIdx, AtomicOrdering Ordering);
+
+/// True iff the immediate at the given index has the specified value.
+LLVM_ABI LegalityPredicate immIs(unsigned ImmIdx, int64_t Imm);
+/// True iff the immediate at the given index has one of the specified values.
+LLVM_ABI LegalityPredicate immInSet(unsigned ImmIdx,
+                                    std::initializer_list<int64_t> ImmsInit);
+/// True iff the immediate at the given index does not have the specified value.
+LLVM_ABI LegalityPredicate immIsNot(unsigned ImmIdx, int64_t Imm);
+
+/// True iff the predicate at the given index is the specfied predicate
+LLVM_ABI LegalityPredicate predicateIs(unsigned PredIdx,
+                                       CmpInst::Predicate Pred);
+/// True iff the predicate at the given index is one of the specified predicates
+LLVM_ABI LegalityPredicate predicateInSet(
+    unsigned PredIdx, std::initializer_list<CmpInst::Predicate> PredsInit);
+/// True iff the predicate at the given index is not the specified predicate
+LLVM_ABI LegalityPredicate predicateIsNot(unsigned PredIdx,
+                                          CmpInst::Predicate Pred);
+
 } // end namespace LegalityPredicates
 
 namespace LegalizeMutations {
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index a910a900e775f..e094e93bc3fab 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -3449,7 +3449,7 @@ bool CombinerHelper::matchAshrShlToSextInreg(
   if (ShlCst != AshrCst)
     return false;
   if (!isLegalOrBeforeLegalizer(
-          {TargetOpcode::G_SEXT_INREG, {MRI.getType(Src)}}))
+          {TargetOpcode::G_SEXT_INREG, {MRI.getType(Src)}, {}, {Src - ShlCst}}))
     return false;
   MatchInfo = std::make_tuple(Src, ShlCst);
   return true;
@@ -5855,7 +5855,10 @@ bool CombinerHelper::matchUDivOrURemByConst(MachineInstr &MI) const {
     if (!isLegalOrBeforeLegalizer(
             {TargetOpcode::G_ICMP,
              {DstTy.isVector() ? DstTy.changeElementSize(1) : LLT::scalar(1),
-              DstTy}}))
+              DstTy},
+             {},
+             {},
+             {CmpInst::Predicate::ICMP_EQ}}))
       return false;
     if (Opcode == TargetOpcode::G_UREM &&
         !isLegalOrBeforeLegalizer({TargetOpcode::G_SUB, {DstTy, DstTy}}))
@@ -8058,11 +8061,9 @@ bool CombinerHelper::tryFoldLogicOfFCmps(GLogicalBinOp *Logic,
   LLT CmpTy = MRI.getType(Cmp1->getReg(0));
   LLT CmpOperandTy = MRI.getType(Cmp1->getLHSReg());
 
-  // We build one fcmp, want to fold the fcmps, replace the logic op,
+  // We want to fold the fcmps, replace the logic op,
   // and the fcmps must have the same shape.
-  if (!isLegalOrBeforeLegalizer(
-          {TargetOpcode::G_FCMP, {CmpTy, CmpOperandTy}}) ||
-      !MRI.hasOneNonDBGUse(Logic->getReg(0)) ||
+  if (!MRI.hasOneNonDBGUse(Logic->getReg(0)) ||
       !MRI.hasOneNonDBGUse(Cmp1->getReg(0)) ||
       !MRI.hasOneNonDBGUse(Cmp2->getReg(0)) ||
       MRI.getType(Cmp1->getLHSReg()) != MRI.getType(Cmp2->getLHSReg()))
@@ -8085,11 +8086,23 @@ bool CombinerHelper::tryFoldLogicOfFCmps(GLogicalBinOp *Logic,
     // We determine the new predicate.
     unsigned CmpCodeL = getFCmpCode(PredL);
     unsigned CmpCodeR = getFCmpCode(PredR);
-    unsigned NewPred = IsAnd ? CmpCodeL & CmpCodeR : CmpCodeL | CmpCodeR;
+
+    // The fcmp predicates fill the lower part of the enum.
+    FCmpInst::Predicate Pred = static_cast<FCmpInst::Predicate>(
+        IsAnd ? CmpCodeL & CmpCodeR : CmpCodeL | CmpCodeR);
+
+    // Make sure the fcmp will be legal, or otherwise replaced by an appropriate
+    // constant.
+    if (!isLegalOrBeforeLegalizer(
+            {TargetOpcode::G_FCMP, {CmpTy, CmpOperandTy}, {}, {Pred}}) &&
+        !(Pred == FCmpInst::FCMP_FALSE &&
+          isConstantLegalOrBeforeLegalizer(CmpTy)) &&
+        !(Pred == FCmpInst::FCMP_TRUE &&
+          isConstantLegalOrBeforeLegalizer(CmpTy)))
+      return false;
+
     unsigned Flags = Cmp1->getFlags() | Cmp2->getFlags();
     MatchInfo = [=](MachineIRBuilder &B) {
-      // The fcmp predicates fill the lower part of the enum.
-      FCmpInst::Predicate Pred = static_cast<FCmpInst::Predicate>(NewPred);
       if (Pred == FCmpInst::FCMP_FALSE &&
           isConstantLegalOrBeforeLegalizer(CmpTy)) {
         auto False = B.buildConstant(CmpTy, 0);
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalityPredicates.cpp b/llvm/lib/CodeGen/GlobalISel/LegalityPredicates.cpp
index 247489f23e83f..c60b6d5be412e 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalityPredicates.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalityPredicates.cpp
@@ -249,3 +249,46 @@ LegalityPredicate LegalityPredicates::atomicOrderingAtLeastOrStrongerThan(
     return isAtLeastOrStrongerThan(Query.MMODescrs[MMOIdx].Ordering, Ordering);
   };
 }
+
+LegalityPredicate LegalityPredicates::immIs(unsigned ImmIdx, int64_t Imm) {
+  return [=](const LegalityQuery &Query) {
+    return Query.Immediates[ImmIdx] == Imm;
+  };
+}
+
+LegalityPredicate
+LegalityPredicates::immInSet(unsigned ImmIdx,
+                             std::initializer_list<int64_t> ImmsInit) {
+  SmallVector<int64_t, 4> Imms = ImmsInit;
+  return [=](const LegalityQuery &Query) {
+    return llvm::is_contained(Imms, Query.Immediates[ImmIdx]);
+  };
+}
+
+LegalityPredicate LegalityPredicates::immIsNot(unsigned ImmIdx, int64_t Imm) {
+  return [=](const LegalityQuery &Query) {
+    return Query.Immediates[ImmIdx] != Imm;
+  };
+}
+
+LegalityPredicate LegalityPredicates::predicateIs(unsigned PredIdx,
+                                                  CmpInst::Predicate Pred) {
+  return [=](const LegalityQuery &Query) {
+    return Query.Predicates[PredIdx] == Pred;
+  };
+}
+
+LegalityPredicate LegalityPredicates::predicateInSet(
+    unsigned PredIdx, std::initializer_list<CmpInst::Predicate> PredsInit) {
+  SmallVector<CmpInst::Predicate, 4> Preds = PredsInit;
+  return [=](const LegalityQuery &Query) {
+    return llvm::is_contained(Preds, Query.Predicates[PredIdx]);
+  };
+}
+
+LegalityPredicate LegalityPredicates::predicateIsNot(unsigned PredIdx,
+                                                     CmpInst::Predicate Pred) {
+  return [=](const LegalityQuery &Query) {
+    return Query.Predicates[PredIdx] != Pred;
+  };
+}
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp
index c2d474fdde696..49e3df5b6ca94 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp
@@ -91,6 +91,14 @@ raw_ostream &LegalityQuery::print(raw_ostream &OS) const {
   for (const auto &MMODescr : MMODescrs) {
     OS << MMODescr.MemoryTy << ", ";
   }
+  OS << "}, Imms={";
+  for (const auto Imm : Immediates) {
+    OS << Imm << ", ";
+  }
+  OS << "}, Predicates={";
+  for (const auto Pred : Predicates) {
+    OS << Pred << ", ";
+  }
   OS << "}";
 
   return OS;
@@ -355,30 +363,36 @@ LegalizeActionStep
 LegalizerInfo::getAction(const MachineInstr &MI,
                          const MachineRegisterInfo &MRI) const {
   SmallVector<LLT, 8> Types;
+  SmallVector<int64_t, 8> Immediates;
+  SmallVector<CmpInst::Predicate, 8> Predicates;
   SmallBitVector SeenTypes(8);
   ArrayRef<MCOperandInfo> OpInfo = MI.getDesc().operands();
   // FIXME: probably we'll need to cache the results here somehow?
   for (unsigned i = 0; i < MI.getDesc().getNumOperands(); ++i) {
-    if (!OpInfo[i].isGenericType())
-      continue;
-
-    // We must only record actions once for each TypeIdx; otherwise we'd
-    // try to legalize operands multiple times down the line.
-    unsigned TypeIdx = OpInfo[i].getGenericTypeIndex();
-    if (SeenTypes[TypeIdx])
-      continue;
-
-    SeenTypes.set(TypeIdx);
-
-    LLT Ty = getTypeFromTypeIdx(MI, MRI, i, TypeIdx);
-    Types.push_back(Ty);
+    if (OpInfo[i].isGenericType()) {
+      // We must only record actions once for each TypeIdx; otherwise we'd
+      // try to legalize operands multiple times down the line.
+      unsigned TypeIdx = OpInfo[i].getGenericTypeIndex();
+      if (SeenTypes[TypeIdx])
+        continue;
+
+      SeenTypes.set(TypeIdx);
+
+      LLT Ty = getTypeFromTypeIdx(MI, MRI, i, TypeIdx);
+      Types.push_back(Ty);
+    } else if (OpInfo[i].isGenericImm()) {
+      Immediates.push_back(MI.getOperand(i).getImm());
+    } else if (MI.getOperand(i).isPredicate()) {
+      Predicates.push_back(
+          static_cast<CmpInst::Predicate>(MI.getOperand(i).getPredicate()));
+    }
   }
 
   SmallVector<LegalityQuery::MemDesc, 2> MemDescrs;
   for (const auto &MMO : MI.memoperands())
     MemDescrs.push_back({*MMO});
 
-  return getAction({MI.getOpcode(), Types, MemDescrs});
+  return getAction({MI.getOpcode(), Types, MemDescrs, Immediates, Predicates});
 }
 
 bool LegalizerInfo::isLegal(const MachineInstr &MI,
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 716fab95eb943..2ee21b609aa06 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -178,10 +178,18 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
 
   getActionDefinitionsBuilder(G_TRUNC).alwaysLegal();
 
-  getActionDefinitionsBuilder(G_SEXT_INREG)
-      .customFor({sXLen})
-      .clampScalar(0, sXLen, sXLen)
-      .lower();
+  {
+    LegalityPredicate ValidSextInRegWidth = all(sizeIs(0, 64), immIs(0, 32));
+
+    if (STI.hasStdExtZbb())
+      ValidSextInRegWidth =
+          LegalityPredicates::any(ValidSextInRegWidth, immInSet(0, {8, 16}));
+
+    getActionDefinitionsBuilder(G_SEXT_INREG)
+        .legalIf(all(typeIs(0, sXLen), ValidSextInRegWidth))
+        .clampScalar(0, sXLen, sXLen)
+        .lower();
+  }
 
   // Merge/Unmerge
   for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
@@ -1495,19 +1503,6 @@ bool RISCVLegalizerInfo::legalizeCustom(
     Helper.Observer.changedInstr(MI);
     return true;
   }
-  case TargetOpcode::G_SEXT_INREG: {
-    LLT DstTy = MRI.getType(MI.getOperand(0).getReg());
-    int64_t SizeInBits = MI.getOperand(2).getImm();
-    // Source size of 32 is sext.w.
-    if (DstTy.getSizeInBits() == 64 && SizeInBits == 32)
-      return true;
-
-    if (STI.hasStdExtZbb() && (SizeInBits == 8 || SizeInBits == 16))
-      return true;
-
-    return Helper.lower(MI, 0, /* Unused hint type */ LLT()) ==
-           LegalizerHelper::Legalized;
-  }
   case TargetOpcode::G_ASHR:
   case TargetOpcode::G_LSHR:
   case TargetOpcode::G_SHL: {
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index fb499d0db1733..76d92243662cf 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -20,6 +20,7 @@
 
 using namespace llvm;
 using namespace LegalizeActions;
+using namespace LegalityPredicates;
 
 WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
     const WebAssemblySubtarget &ST) {
@@ -76,10 +77,17 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .clampScalar(0, s32, s32)
       .clampScalar(1, s64, s64);
 
-  getActionDefinitionsBuilder(G_SEXT_INREG)
-      .customFor(ST.hasSignExt(), {i32, i64})
-      .clampScalar(0, s32, s64)
-      .lower();
+  {
+    LegalizeRuleSet &Builder = getActionDefinitionsBuilder(G_SEXT_INREG);
+
+    if (ST.hasSignExt())
+      Builder.legalIf(
+          all(typeInSet(0, {i32, i64}),
+              LegalityPredicates::any(immInSet(0, {8, 16}),
+                                      all(typeIs(0, i64), immIs(0, 32)))));
+
+    Builder.clampScalar(0, s32, s64).lower();
+  }
 
   getActionDefinitionsBuilder({G_FCONSTANT, G_FABS, G_FNEG, G_FCEIL, G_FFLOOR,
                                G_INTRINSIC_TRUNC, G_FNEARBYINT, G_FRINT,
@@ -110,20 +118,6 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
     LegalizerHelper &Helper, MachineInstr &MI,
     LostDebugLocObserver &LocObserver) const {
   switch (MI.getOpcode()) {
-  case TargetOpcode::G_SEXT_INREG: {
-    assert(MI.getOperand(2).isImm() && "Expected immediate");
-
-    // Mark only 8/16/32-bit SEXT_INREG as legal
-    auto [DstType, SrcType] = MI.getFirst2LLTs();
-    auto ExtFromWidth = MI.getOperand(2).getImm();
-
-    if (ExtFromWidth == 8 || ExtFromWidth == 16 ||
-        (DstType.getScalarSizeInBits() == 64 && ExtFromWidth == 32)) {
-      return true;
-    }
-
-    return Helper.lower(MI, 0, DstType) != LegalizerHelper::UnableToLegalize;
-  }
   default:
     break;
   }



More information about the llvm-commits mailing list