Skip to content

[DAG] computeKnownFPClass - Add handling for AssertNoFPClass#190070

Closed
joaovam wants to merge 173 commits intollvm:mainfrom
joaovam:computeKnownFPClass_AssertNoFPClass_handling
Closed

[DAG] computeKnownFPClass - Add handling for AssertNoFPClass#190070
joaovam wants to merge 173 commits intollvm:mainfrom
joaovam:computeKnownFPClass_AssertNoFPClass_handling

Conversation

@joaovam
Copy link
Copy Markdown
Contributor

@joaovam joaovam commented Apr 1, 2026

Resolves #189478

Adds code to handle AssertNoFPClass in computeKnownFPClass and adds IR test coverage for X86.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added backend:X86 llvm:SelectionDAG SelectionDAGISel as well labels Apr 1, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Apr 1, 2026

@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-llvm-selectiondag

@llvm/pr-subscribers-backend-x86

Author: Joao Victor Amorim Vieira (joaovam)

Changes

Resolves #189478

Adds code to handle AssertNoFPClass in computeKnownFPClass and adds IR test coverage for X86.


Patch is 111.81 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/190070.diff

2 Files Affected:

  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+575-492)
  • (modified) llvm/test/CodeGen/X86/known-fpclass.ll (+29)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 3716de880cce3..cb4a0d6d11e8a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -18,6 +18,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FloatingPointMode.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -92,8 +93,8 @@ static SDVTList makeVTList(const EVT *VTs, unsigned NumVTs) {
 }
 
 // Default null implementations of the callbacks.
-void SelectionDAG::DAGUpdateListener::NodeDeleted(SDNode*, SDNode*) {}
-void SelectionDAG::DAGUpdateListener::NodeUpdated(SDNode*) {}
+void SelectionDAG::DAGUpdateListener::NodeDeleted(SDNode *, SDNode *) {}
+void SelectionDAG::DAGUpdateListener::NodeUpdated(SDNode *) {}
 void SelectionDAG::DAGUpdateListener::NodeInserted(SDNode *) {}
 
 void SelectionDAG::DAGNodeDeletedListener::anchor() {}
@@ -101,13 +102,14 @@ void SelectionDAG::DAGNodeInsertedListener::anchor() {}
 
 #define DEBUG_TYPE "selectiondag"
 
-static cl::opt<bool> EnableMemCpyDAGOpt("enable-memcpy-dag-opt",
-       cl::Hidden, cl::init(true),
-       cl::desc("Gang up loads and stores generated by inlining of memcpy"));
+static cl::opt<bool> EnableMemCpyDAGOpt(
+    "enable-memcpy-dag-opt", cl::Hidden, cl::init(true),
+    cl::desc("Gang up loads and stores generated by inlining of memcpy"));
 
-static cl::opt<int> MaxLdStGlue("ldstmemcpy-glue-max",
-       cl::desc("Number limit for gluing ld/st of memcpy."),
-       cl::Hidden, cl::init(0));
+static cl::opt<int>
+    MaxLdStGlue("ldstmemcpy-glue-max",
+                cl::desc("Number limit for gluing ld/st of memcpy."),
+                cl::Hidden, cl::init(0));
 
 static cl::opt<unsigned>
     MaxSteps("has-predecessor-max-steps", cl::Hidden, cl::init(8192),
@@ -128,12 +130,11 @@ unsigned SelectionDAG::getHasPredecessorMaxSteps() { return MaxSteps; }
 /// it returns true for things that are clearly not equal, like -0.0 and 0.0.
 /// As such, this method can be used to do an exact bit-for-bit comparison of
 /// two floating point values.
-bool ConstantFPSDNode::isExactlyValue(const APFloat& V) const {
+bool ConstantFPSDNode::isExactlyValue(const APFloat &V) const {
   return getValueAPF().bitwiseIsEqual(V);
 }
 
-bool ConstantFPSDNode::isValueValidForType(EVT VT,
-                                           const APFloat& Val) {
+bool ConstantFPSDNode::isValueValidForType(EVT VT, const APFloat &Val) {
   assert(VT.isFloatingPoint() && "Can only convert between FP types");
 
   // convert modifies in place, so make a copy.
@@ -189,7 +190,8 @@ bool ISD::isConstantSplatVectorAllOnes(const SDNode *N, bool BuildVectorOnly) {
     return isConstantSplatVector(N, SplatVal) && SplatVal.isAllOnes();
   }
 
-  if (N->getOpcode() != ISD::BUILD_VECTOR) return false;
+  if (N->getOpcode() != ISD::BUILD_VECTOR)
+    return false;
 
   unsigned i = 0, e = N->getNumOperands();
 
@@ -198,7 +200,8 @@ bool ISD::isConstantSplatVectorAllOnes(const SDNode *N, bool BuildVectorOnly) {
     ++i;
 
   // Do not accept an all-undef vector.
-  if (i == e) return false;
+  if (i == e)
+    return false;
 
   // Do not accept build_vectors that aren't all constants or which have non-~0
   // elements. We have to be a bit careful here, as the type of the constant
@@ -235,7 +238,8 @@ bool ISD::isConstantSplatVectorAllZeros(const SDNode *N, bool BuildVectorOnly) {
     return isConstantSplatVector(N, SplatVal) && SplatVal.isZero();
   }
 
-  if (N->getOpcode() != ISD::BUILD_VECTOR) return false;
+  if (N->getOpcode() != ISD::BUILD_VECTOR)
+    return false;
 
   bool IsAllUndef = true;
   for (const SDValue &Op : N->op_values()) {
@@ -622,20 +626,20 @@ ISD::CondCode ISD::getSetCCSwappedOperands(ISD::CondCode Operation) {
   // operation.
   unsigned OldL = (Operation >> 2) & 1;
   unsigned OldG = (Operation >> 1) & 1;
-  return ISD::CondCode((Operation & ~6) |  // Keep the N, U, E bits
-                       (OldL << 1) |       // New G bit
-                       (OldG << 2));       // New L bit.
+  return ISD::CondCode((Operation & ~6) | // Keep the N, U, E bits
+                       (OldL << 1) |      // New G bit
+                       (OldG << 2));      // New L bit.
 }
 
 static ISD::CondCode getSetCCInverseImpl(ISD::CondCode Op, bool isIntegerLike) {
   unsigned Operation = Op;
   if (isIntegerLike)
-    Operation ^= 7;   // Flip L, G, E bits, but not U.
+    Operation ^= 7; // Flip L, G, E bits, but not U.
   else
-    Operation ^= 15;  // Flip all of the condition bits.
+    Operation ^= 15; // Flip all of the condition bits.
 
   if (Operation > ISD::SETTRUE2)
-    Operation &= ~8;  // Don't let N and U bits get set.
+    Operation &= ~8; // Don't let N and U bits get set.
 
   return ISD::CondCode(Operation);
 }
@@ -654,17 +658,21 @@ ISD::CondCode ISD::GlobalISel::getSetCCInverse(ISD::CondCode Op,
 /// does not depend on the sign of the input (setne and seteq).
 static int isSignedOp(ISD::CondCode Opcode) {
   switch (Opcode) {
-  default: llvm_unreachable("Illegal integer setcc operation!");
+  default:
+    llvm_unreachable("Illegal integer setcc operation!");
   case ISD::SETEQ:
-  case ISD::SETNE: return 0;
+  case ISD::SETNE:
+    return 0;
   case ISD::SETLT:
   case ISD::SETLE:
   case ISD::SETGT:
-  case ISD::SETGE: return 1;
+  case ISD::SETGE:
+    return 1;
   case ISD::SETULT:
   case ISD::SETULE:
   case ISD::SETUGT:
-  case ISD::SETUGE: return 2;
+  case ISD::SETUGE:
+    return 2;
   }
 }
 
@@ -675,15 +683,15 @@ ISD::CondCode ISD::getSetCCOrOperation(ISD::CondCode Op1, ISD::CondCode Op2,
     // Cannot fold a signed integer setcc with an unsigned integer setcc.
     return ISD::SETCC_INVALID;
 
-  unsigned Op = Op1 | Op2;  // Combine all of the condition bits.
+  unsigned Op = Op1 | Op2; // Combine all of the condition bits.
 
   // If the N and U bits get set, then the resultant comparison DOES suddenly
   // care about orderedness, and it is true when ordered.
   if (Op > ISD::SETTRUE2)
-    Op &= ~16;     // Clear the U bit if the N bit is set.
+    Op &= ~16; // Clear the U bit if the N bit is set.
 
   // Canonicalize illegal integer setcc's.
-  if (IsInteger && Op == ISD::SETUNE)  // e.g. SETUGT | SETULT
+  if (IsInteger && Op == ISD::SETUNE) // e.g. SETUGT | SETULT
     Op = ISD::SETNE;
 
   return ISD::CondCode(Op);
@@ -702,12 +710,21 @@ ISD::CondCode ISD::getSetCCAndOperation(ISD::CondCode Op1, ISD::CondCode Op2,
   // Canonicalize illegal integer setcc's.
   if (IsInteger) {
     switch (Result) {
-    default: break;
-    case ISD::SETUO : Result = ISD::SETFALSE; break;  // SETUGT & SETULT
-    case ISD::SETOEQ:                                 // SETEQ  & SETU[LG]E
-    case ISD::SETUEQ: Result = ISD::SETEQ   ; break;  // SETUGE & SETULE
-    case ISD::SETOLT: Result = ISD::SETULT  ; break;  // SETULT & SETNE
-    case ISD::SETOGT: Result = ISD::SETUGT  ; break;  // SETUGT & SETNE
+    default:
+      break;
+    case ISD::SETUO:
+      Result = ISD::SETFALSE;
+      break;          // SETUGT & SETULT
+    case ISD::SETOEQ: // SETEQ  & SETU[LG]E
+    case ISD::SETUEQ:
+      Result = ISD::SETEQ;
+      break; // SETUGE & SETULE
+    case ISD::SETOLT:
+      Result = ISD::SETULT;
+      break; // SETULT & SETNE
+    case ISD::SETOGT:
+      Result = ISD::SETUGT;
+      break; // SETUGT & SETNE
     }
   }
 
@@ -719,7 +736,7 @@ ISD::CondCode ISD::getSetCCAndOperation(ISD::CondCode Op1, ISD::CondCode Op2,
 //===----------------------------------------------------------------------===//
 
 /// AddNodeIDOpcode - Add the node opcode to the NodeID data.
-static void AddNodeIDOpcode(FoldingSetNodeID &ID, unsigned OpC)  {
+static void AddNodeIDOpcode(FoldingSetNodeID &ID, unsigned OpC) {
   ID.AddInteger(OpC);
 }
 
@@ -730,8 +747,7 @@ static void AddNodeIDValueTypes(FoldingSetNodeID &ID, SDVTList VTList) {
 }
 
 /// AddNodeIDOperands - Various routines for adding operands to the NodeID data.
-static void AddNodeIDOperands(FoldingSetNodeID &ID,
-                              ArrayRef<SDValue> Ops) {
+static void AddNodeIDOperands(FoldingSetNodeID &ID, ArrayRef<SDValue> Ops) {
   for (const auto &Op : Ops) {
     ID.AddPointer(Op.getNode());
     ID.AddInteger(Op.getResNo());
@@ -739,16 +755,15 @@ static void AddNodeIDOperands(FoldingSetNodeID &ID,
 }
 
 /// AddNodeIDOperands - Various routines for adding operands to the NodeID data.
-static void AddNodeIDOperands(FoldingSetNodeID &ID,
-                              ArrayRef<SDUse> Ops) {
+static void AddNodeIDOperands(FoldingSetNodeID &ID, ArrayRef<SDUse> Ops) {
   for (const auto &Op : Ops) {
     ID.AddPointer(Op.getNode());
     ID.AddInteger(Op.getResNo());
   }
 }
 
-static void AddNodeIDNode(FoldingSetNodeID &ID, unsigned OpC,
-                          SDVTList VTList, ArrayRef<SDValue> OpList) {
+static void AddNodeIDNode(FoldingSetNodeID &ID, unsigned OpC, SDVTList VTList,
+                          ArrayRef<SDValue> OpList) {
   AddNodeIDOpcode(ID, OpC);
   AddNodeIDValueTypes(ID, VTList);
   AddNodeIDOperands(ID, OpList);
@@ -761,7 +776,8 @@ static void AddNodeIDCustom(FoldingSetNodeID &ID, const SDNode *N) {
   case ISD::ExternalSymbol:
   case ISD::MCSymbol:
     llvm_unreachable("Should only be used on nodes with operands");
-  default: break;  // Normal nodes don't need extra info.
+  default:
+    break; // Normal nodes don't need extra info.
   case ISD::TargetConstant:
   case ISD::Constant: {
     const ConstantSDNode *C = cast<ConstantSDNode>(N);
@@ -1021,10 +1037,11 @@ static bool doNotCSE(SDNode *N) {
     return true; // Never CSE anything that produces a glue result.
 
   switch (N->getOpcode()) {
-  default: break;
+  default:
+    break;
   case ISD::HANDLENODE:
   case ISD::EH_LABEL:
-    return true;   // Never CSE these nodes.
+    return true; // Never CSE these nodes.
   }
 
   // Check that remaining values produced are not flags.
@@ -1042,7 +1059,7 @@ void SelectionDAG::RemoveDeadNodes() {
   // to the root node, preventing it from being deleted.
   HandleSDNode Dummy(getRoot());
 
-  SmallVector<SDNode*, 128> DeadNodes;
+  SmallVector<SDNode *, 128> DeadNodes;
 
   // Add all obviously-dead nodes to the DeadNodes worklist.
   for (SDNode &Node : allnodes())
@@ -1077,7 +1094,7 @@ void SelectionDAG::RemoveDeadNodes(SmallVectorImpl<SDNode *> &DeadNodes) {
 
     // Next, brutally remove the operand list.  This is safe to do, as there are
     // no cycles in the graph.
-    for (SDNode::op_iterator I = N->op_begin(), E = N->op_end(); I != E; ) {
+    for (SDNode::op_iterator I = N->op_begin(), E = N->op_end(); I != E;) {
       SDUse &Use = *I++;
       SDNode *Operand = Use.getNode();
       Use.set(SDValue());
@@ -1091,8 +1108,8 @@ void SelectionDAG::RemoveDeadNodes(SmallVectorImpl<SDNode *> &DeadNodes) {
   }
 }
 
-void SelectionDAG::RemoveDeadNode(SDNode *N){
-  SmallVector<SDNode*, 16> DeadNodes(1, N);
+void SelectionDAG::RemoveDeadNode(SDNode *N) {
+  SmallVector<SDNode *, 16> DeadNodes(1, N);
 
   // Create a dummy node that adds a reference to the root node, preventing
   // it from being deleted.  (This matters if the root is an operand of the
@@ -1137,7 +1154,7 @@ void SDDbgInfo::erase(const SDNode *Node) {
   DbgValMapType::iterator I = DbgValMap.find(Node);
   if (I == DbgValMap.end())
     return;
-  for (auto &Val: I->second)
+  for (auto &Val : I->second)
     Val->setIsInvalidated();
   DbgValMap.erase(I);
 }
@@ -1239,7 +1256,8 @@ void SelectionDAG::InsertNode(SDNode *N) {
 bool SelectionDAG::RemoveNodeFromCSEMaps(SDNode *N) {
   bool Erased = false;
   switch (N->getOpcode()) {
-  case ISD::HANDLENODE: return false;  // noop.
+  case ISD::HANDLENODE:
+    return false; // noop.
   case ISD::CONDCODE:
     assert(CondCodeNodes[cast<CondCodeSDNode>(N)->get()] &&
            "Cond code doesn't exist!");
@@ -1281,7 +1299,7 @@ bool SelectionDAG::RemoveNodeFromCSEMaps(SDNode *N) {
   // Verify that the node was actually in one of the CSE maps, unless it has a
   // glue result (which cannot be CSE'd) or is one of the special cases that are
   // not subject to CSE.
-  if (!Erased && N->getValueType(N->getNumValues()-1) != MVT::Glue &&
+  if (!Erased && N->getValueType(N->getNumValues() - 1) != MVT::Glue &&
       !N->isMachineOpcode() && !doNotCSE(N)) {
     N->dump(this);
     dbgs() << "\n";
@@ -1295,8 +1313,7 @@ bool SelectionDAG::RemoveNodeFromCSEMaps(SDNode *N) {
 /// maps and modified in place. Add it back to the CSE maps, unless an identical
 /// node already exists, in which case transfer all its users to the existing
 /// node. This transfer can potentially trigger recursive merging.
-void
-SelectionDAG::AddModifiedNodeToCSEMaps(SDNode *N) {
+void SelectionDAG::AddModifiedNodeToCSEMaps(SDNode *N) {
   // For node types that aren't CSE'd, just act as if no identical node
   // already exists.
   if (!doNotCSE(N)) {
@@ -1332,7 +1349,7 @@ SDNode *SelectionDAG::FindModifiedNodeSlot(SDNode *N, SDValue Op,
   if (doNotCSE(N))
     return nullptr;
 
-  SDValue Ops[] = { Op };
+  SDValue Ops[] = {Op};
   FoldingSetNodeID ID;
   AddNodeIDNode(ID, N->getOpcode(), N->getVTList(), Ops);
   AddNodeIDCustom(ID, N);
@@ -1346,13 +1363,12 @@ SDNode *SelectionDAG::FindModifiedNodeSlot(SDNode *N, SDValue Op,
 /// were replaced with those specified.  If this node is never memoized,
 /// return null, otherwise return a pointer to the slot it would take.  If a
 /// node already exists with these operands, the slot will be non-null.
-SDNode *SelectionDAG::FindModifiedNodeSlot(SDNode *N,
-                                           SDValue Op1, SDValue Op2,
+SDNode *SelectionDAG::FindModifiedNodeSlot(SDNode *N, SDValue Op1, SDValue Op2,
                                            void *&InsertPos) {
   if (doNotCSE(N))
     return nullptr;
 
-  SDValue Ops[] = { Op1, Op2 };
+  SDValue Ops[] = {Op1, Op2};
   FoldingSetNodeID ID;
   AddNodeIDNode(ID, N->getOpcode(), N->getVTList(), Ops);
   AddNodeIDCustom(ID, N);
@@ -1444,7 +1460,8 @@ SDNode *SelectionDAG::FindNodeOrInsertPos(const FoldingSetNodeID &ID,
   SDNode *N = CSEMap.FindNodeOrInsertPos(ID, InsertPos);
   if (N) {
     switch (N->getOpcode()) {
-    default: break;
+    default:
+      break;
     case ISD::Constant:
     case ISD::ConstantFP:
       llvm_unreachable("Querying for Constant and ConstantFP nodes requires "
@@ -1521,21 +1538,18 @@ SelectionDAG::getStrictFPExtendOrRound(SDValue Op, SDValue Chain,
 }
 
 SDValue SelectionDAG::getAnyExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT) {
-  return VT.bitsGT(Op.getValueType()) ?
-    getNode(ISD::ANY_EXTEND, DL, VT, Op) :
-    getNode(ISD::TRUNCATE, DL, VT, Op);
+  return VT.bitsGT(Op.getValueType()) ? getNode(ISD::ANY_EXTEND, DL, VT, Op)
+                                      : getNode(ISD::TRUNCATE, DL, VT, Op);
 }
 
 SDValue SelectionDAG::getSExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT) {
-  return VT.bitsGT(Op.getValueType()) ?
-    getNode(ISD::SIGN_EXTEND, DL, VT, Op) :
-    getNode(ISD::TRUNCATE, DL, VT, Op);
+  return VT.bitsGT(Op.getValueType()) ? getNode(ISD::SIGN_EXTEND, DL, VT, Op)
+                                      : getNode(ISD::TRUNCATE, DL, VT, Op);
 }
 
 SDValue SelectionDAG::getZExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT) {
-  return VT.bitsGT(Op.getValueType()) ?
-    getNode(ISD::ZERO_EXTEND, DL, VT, Op) :
-    getNode(ISD::TRUNCATE, DL, VT, Op);
+  return VT.bitsGT(Op.getValueType()) ? getNode(ISD::ZERO_EXTEND, DL, VT, Op)
+                                      : getNode(ISD::TRUNCATE, DL, VT, Op);
 }
 
 SDValue SelectionDAG::getBitcastedAnyExtOrTrunc(SDValue Op, const SDLoc &DL,
@@ -1554,7 +1568,7 @@ SDValue SelectionDAG::getBitcastedAnyExtOrTrunc(SDValue Op, const SDLoc &DL,
 }
 
 SDValue SelectionDAG::getBitcastedSExtOrTrunc(SDValue Op, const SDLoc &DL,
-                                               EVT VT) {
+                                              EVT VT) {
   assert(!VT.isVector());
   auto Type = Op.getValueType();
   SDValue DestOp;
@@ -1569,7 +1583,7 @@ SDValue SelectionDAG::getBitcastedSExtOrTrunc(SDValue Op, const SDLoc &DL,
 }
 
 SDValue SelectionDAG::getBitcastedZExtOrTrunc(SDValue Op, const SDLoc &DL,
-                                               EVT VT) {
+                                              EVT VT) {
   assert(!VT.isVector());
   auto Type = Op.getValueType();
   SDValue DestOp;
@@ -1747,8 +1761,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL,
     unsigned ViaEltSizeInBits = ViaEltVT.getSizeInBits();
 
     // For scalable vectors, try to use a SPLAT_VECTOR_PARTS node.
-    if (VT.isScalableVector() ||
-        TLI->isOperationLegal(ISD::SPLAT_VECTOR, VT)) {
+    if (VT.isScalableVector() || TLI->isOperationLegal(ISD::SPLAT_VECTOR, VT)) {
       assert(EltVT.getSizeInBits() % ViaEltSizeInBits == 0 &&
              "Can only handle an even split!");
       unsigned Parts = EltVT.getSizeInBits() / ViaEltSizeInBits;
@@ -1951,7 +1964,7 @@ SDValue SelectionDAG::getGlobalAddress(const GlobalValue *GV, const SDLoc &DL,
   auto *N = newSDNode<GlobalAddressSDNode>(
       Opc, DL.getIROrder(), DL.getDebugLoc(), GV, VTs, Offset, TargetFlags);
   CSEMap.InsertNode(N, IP);
-    InsertNode(N);
+  InsertNode(N);
   return SDValue(N, 0);
 }
 
@@ -2084,14 +2097,15 @@ SDValue SelectionDAG::getBasicBlock(MachineBasicBlock *MBB) {
 }
 
 SDValue SelectionDAG::getValueType(EVT VT) {
-  if (VT.isSimple() && (unsigned)VT.getSimpleVT().SimpleTy >=
-      ValueTypeNodes.size())
-    ValueTypeNodes.resize(VT.getSimpleVT().SimpleTy+1);
+  if (VT.isSimple() &&
+      (unsigned)VT.getSimpleVT().SimpleTy >= ValueTypeNodes.size())
+    ValueTypeNodes.resize(VT.getSimpleVT().SimpleTy + 1);
 
-  SDNode *&N = VT.isExtended() ?
-    ExtendedValueTypeNodes[VT] : ValueTypeNodes[VT.getSimpleVT().SimpleTy];
+  SDNode *&N = VT.isExtended() ? ExtendedValueTypeNodes[VT]
+                               : ValueTypeNodes[VT.getSimpleVT().SimpleTy];
 
-  if (N) return SDValue(N, 0);
+  if (N)
+    return SDValue(N, 0);
   N = newSDNode<VTSDNode>(VT);
   InsertNode(N);
   return SDValue(N, 0);
@@ -2099,7 +2113,8 @@ SDValue SelectionDAG::getValueType(EVT VT) {
 
 SDValue SelectionDAG::getExternalSymbol(const char *Sym, EVT VT) {
   SDNode *&N = ExternalSymbols[Sym];
-  if (N) return SDValue(N, 0);
+  if (N)
+    return SDValue(N, 0);
   N = newSDNode<ExternalSymbolSDNode>(false, Sym, 0, getVTList(VT));
   InsertNode(N);
   return SDValue(N, 0);
@@ -2123,7 +2138,8 @@ SDValue SelectionDAG::getTargetExternalSymbol(const char *Sym, EVT VT,
                                               unsigned TargetFlags) {
   SDNode *&N =
       TargetExternalSymbols[std::pair<std::string, unsigned>(Sym, TargetFlags)];
-  if (N) return SDValue(N, 0);
+  if (N)
+    return SDValue(N, 0);
   N = newSDNode<ExternalSymbolSDNode>(true, Sym, TargetFlags, getVTList(VT));
   InsertNode(N);
   return SDValue(N, 0);
@@ -2137,7 +2153,7 @@ SDValue SelectionDAG::getTargetExternalSymbol(RTLIB::LibcallImpl Libcall,
 
 SDValue SelectionDAG::getCondCode(ISD::CondCode Cond) {
   if ((unsigned)Cond >= CondCodeNodes.size())
-    CondCodeNodes.resize(Cond+1);
+    CondCodeNodes.resize(Cond + 1);
 
   if (!CondCodeNodes[Cond]) {
     auto *N = newSDNode<CondCodeSDNode>(Cond);
@@ -2236,9 +2252,9 @@ SDValue SelectionDAG::getVectorShuffle(EVT VT, const SDLoc &dl, SDValue N1,
   // Validate that all indices in Mask are within the range of the elements
   // input to the shuffle.
   int NElts = Mask.size();
-  assert(llvm::all_of(Mask,
-                      [&](int M) { return M < (NElts * 2) && M >= -1; }) &&
-         "Index out of range");
+  assert(
+      llvm::all_of(Mask, [&](int M) { return M < (NElts * 2) && M >= -1; }) &&
+      "Index out of range");
 
   // Copy the mask so we can do any needed cleanup.
   SmallVector<int, 8> MaskVec(Mask);
@@ -2247,7 +2263,8 @@ SDValue SelectionDAG::getVectorShuffle(EVT VT, const SDLoc &dl, SDValue N1,
   if (N1 == N2) {
     N2 = getUNDEF(VT);
     for (int i = 0; i != NElts; ++i)
-      if (MaskVec[i] >= NElts) MaskVec[i] -= NElts;
+      if (MaskVec[i] >= NElts)
+        MaskVec[i] -= NElts;
   }
 
   // Canonicalize shuffle undef, v -> v, undef.  Commute the shuffle mask.
@@ -2315,8 +2332,10 @@ SDValue SelectionDAG::getVectorShuffle(EVT VT, const SDLoc &dl, SDValue N1,
   // If Identity s...
[truncated]

@joaovam joaovam force-pushed the computeKnownFPClass_AssertNoFPClass_handling branch from 6b2f74e to 93e3964 Compare April 2, 2026 00:45
justinfargnoli and others added 23 commits April 1, 2026 21:51
…computeUnrollCount()` (llvm#185979)

We currently set `UP.Count` to `TripCount` and `MaxTripCount` prior to
full and upper bound unrolling, respectively. This was likely done to
ensure that calls to `UCE.getUnrolledLoopSize(UP)` use the appropriate
trip count. However, we can use `UCE.getUnrolledLoopSize(UP,
FullUnrollTripCount)` instead.

To prevent unintentional unrolling, we set `UP.Count = 0` when
early-exiting `computeUnrollCount()`. (Note: this does not occur
[here](https://github.com/llvm/llvm-project/blob/eb687fb10613573b5d60dfbbb146e15f07809d82/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L1190-L1198).
This seems like a bug.)

We only perform early exits when evaluating runtime unrolling. At that
point, [we know `TripCount` is
false](https://github.com/justinfargnoli/llvm-project/blob/3fb31e7b06f6f6d08c7310506a2f363d198a6790/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L1157-L1158),
and thus we could not have leaked `TripCount`. However, we [could've
leaked
`MaxTripCount`](https://github.com/llvm/llvm-project/blob/eb687fb10613573b5d60dfbbb146e15f07809d82/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L1102-L1110).

It seems like:
https://github.com/llvm/llvm-project/blob/eb687fb10613573b5d60dfbbb146e15f07809d82/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L1181-L1188

was supposed to handle this case. However:

- It uses `<` instead of `<=`. This breaks the existing convention
[[1]](https://github.com/llvm/llvm-project/blob/eb687fb10613573b5d60dfbbb146e15f07809d82/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L869)
[[2]](https://github.com/llvm/llvm-project/blob/eb687fb10613573b5d60dfbbb146e15f07809d82/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L1103)
for how `UP.MaxUpperBound` is treated.
- It's ignored when a target sets `UP.Force = true`.

Thus:
- When `UP.Force == false`, we leak `MaxTripCount` into runtime
unrolling when `MaxTripCount && (UP.UpperBound || MaxOrZero) &&
MaxTripCount == UP.MaxUpperBound`
- When `UP.Force == true`, we leak `MaxTripCount` into runtime unrolling
when `MaxTripCount && (UP.UpperBound || MaxOrZero) && MaxTripCount <=
UP.MaxUpperBound`.

This PR:
- Uses `UCE.getUnrolledLoopSize(UP, FullUnrollTripCount)`
- Stops setting `TripCount` and `MaxTripCount` prior to calling
`shouldFullUnroll()`
- Removes the `UP.Count = 0` safeguards
- Swaps `<` with `<=`, to address the `UP.Force == false` case
- Adds a test to document the behavior change (no longer leaking
`MaxTripCount`) in the `UP.Force == true` case.
This patch adds dependencies on OpenMP and OpenACC dialects to prevent
link errors when compiling with shared libraries.
Fix two bugs in DocNode's comparison operators and add a new
Document::copyNode() method:

1. operator== was implemented via operator<, which hits llvm_unreachable
for Array/Map nodes. Implement operator== directly with recursive value
comparison for all node types.

2. operator< compared KindAndDoc pointers, causing cross-document nodes
of the same type and value to silently produce wrong results. Compare by
kind then by value instead.

3. Add Document::copyNode() for deep copying nodes between Documents
with independent memory ownership.
…missing ; (llvm#188123)

Fix an assertion failure in `ParseDecltypeSpecifier` when parsing
malformed expressions e.g. `decltype(union { ... )`.

When a class/union definition is missing a semicolon, clang's error
recovery may synthetically set the current token to `tok::semi` without
actually inserting it into the preprocessor's backtrack cache, see
https://github.com/llvm/llvm-project/blob/9096c9cda3df4d09c9c6e4efa1a07a232a9ac8f8/clang/lib/Parse/ParseDeclCXX.cpp#L1920-L1927

If `ParseDecltypeSpecifier` later encounters this synthetic semicolon
during its own error recovery, its attempts to revert the cache and
re-lex the tokens will lead to reading unexpected tokens from the
historical stream, failing the strict
`Tok.is(tok::semi)` assertion.

This patch removes this assertion, acknowledging that during invalid
parses and error recovery, the preprocessor's cache state may not
strictly align with the parser's simulated state.


Fixes: llvm#188014
This is the correct order according to the function prototype.

This should be NFC, because for PCH, AllowCompatibleDifferences
is always false: it is only used in isAcceptableASTFile, which
calls readASTFileControlBlock, which explicitely passes false.

We explicitely pass in `nullptr` for Diag, so the incorrect error
message isn't printed.
Closes llvm#187699 .

Fix incorrect parsing of `ConstArrayAttr`. `parse` method incorrectly
used type of `elts` parameter as type of the whole array. This means
that when reading back text or bytecode clangir files attribute did not
have correct type and correct `trailingZerosNum`. Type was always of
inner `elts`, meaning smaller then actual type if any trailing zeros
were present and `trailingZerosNum` was always zero.
…lvm#189676)

Whether an arith operation can be truncated to a given bitwidth should
also depend on the sign semantics of the operation itself. Consider:
```
%input = /* upper bound > INT32_MAX, <= UINT32_MAX */ : index
%c0 = arith.constant 0 : index
%cmp = arith.cmpi sle, %input, %c0 : index
```

Previously, `checkTruncatability()` would correctly judge that only an
unsigned truncation could be legal, however the narrowing would still
proceed despite the fact that the `sle` predicate treated the MSB as the
sign.

Ensure that the sign is checked for signed comparison predicates and for
signed elementwise operations by enforcing a `CastKind::Signed`
restriction, whereby the narrowing patterns bail out on incompatible
input range/operation signedness.

**AI tooling usage disclaimer**
LIT tests were expanded from manual reproducer examples with LLM
assistance. Those additional test cases were verified to
regression-test, proofread and edited manually in accordance with the
"Human in the loop" policy. LLMs/generative tooling were not used for
implementation/documentation purposes.

---------

Signed-off-by: Artem Gindinson <gindinson@roofline.ai>
Co-authored-by: GPT 5.4 <codex@openai.com>
Like OpenBSD, asan does not support GNU/Hurd yet.
…nteger (llvm#186421)

In preparation for updating DIL to handle assignments, this adds a
member variable to the DIL Interpreter indicating whether or not
updating program variables is allowed. For invocations from the LLDB
command prompt (through "frame variable") we want to allow it, but from
other places we might not. Therefore we also add new StackFrame
ExpressionPathOption, eExpressionPathOptionsAllowVarUpdates, which we
add to calls from CommandObjectFrame, and which is checked in
GetValueForVariableExpressionPath. Finally, we also add a parameter,
can_update_vars, with a default value of true, to
ValueObject::SetValueFromInteger, as that will be the main function used
to by assignment in DIL.
)

In response to [[RFC] Qualifying syntax rule numbers with Fortran
standard version in
comments](https://discourse.llvm.org/t/rfc-qualifying-syntax-rule-numbers-with-fortran-standard-version-in-comments/90167)
this PR adds a section on guidelines for referencing the Fortran
Standard.
…lvm#187168)

This PR adds code to FoldMemRefAliasOps / --fold-memref-alias-ops to use
the new IndexedMemoryAccessOpInterface and
IndexedMemCopyOpInterface and implement those operations for relevant
operations in the memref dialect.

This is a reordering of the changes planned in llvm#177014 and llvm#177016 to
make them more testable.

There are no behavior changes expected for how memref.load and
memref.store behave within the alias ops folding pass, though support
for new operations, like memref.prefetch, has been added.

Some error messages have been updated because certain laws of
memref.load/memref.store have been moved to IndexedAccessOpInterface.

Assisted-by: Claude 4.6 (helped deal with some of the boilerplate in the
rewrite patterns and with extracting the patch)
…FC (llvm#188808)

Replace unsigned with plui or pli_b to better indicate their usage.

Templatize the render function and rename it addSExtImm instead of
addSImm*Unsigned.
…lvm#189760)

Mark this test as UNSUPPORTED for android since android's libc doesn't
seem to support aligned_alloc.
This patch implements a new simple region pass that can vectorize
store-load chains.
)

We already returned for UADDSAT/USUBSAT leaving SADDSAT/SSUBSAT as the
only opcodes that can get here.
…vm#189696)

This patch changes the `Platform::LocateXXX` to return a map from
`FileSpec` to `LoadScriptFromSymFile` enum.

This is needed for llvm#188722,
where I intend to set `LoadScriptFromSymFile` per-module.

By default the `Platform::LocateXXX` set the value to whatever the
target's current `target.load-script-from-symbol-file` is set to. In
llvm#188722 we'll allow overriding
this per-target setting on a per-module basis.

Drive-by:
* Added logging when we fail to load a script.
We have a sshl instruction on RV32 in the 0.21 spec. Unfortunately,
we don't have a SSLLI instruction, but we can put a constant shift
amount in a register.
This fixes b813b0b.

Co-authored-by: Google Bazel Bot <google-bazel-bot@google.com>
Eliminate `MappingRIdx` by making it an identity function. Currently,
`MappingRIdx` is used to map the index of an `llvm_any*` type in an
intrinsic type signature to its overload index. Eliminating this mapping
means that dependent types in LLVM intrinsic definitions (like
`LLVMMatchType` and its subclasses) should use the overload index to
reference the overload type that it depends on (and not the index within
the llvm_any* subset of overloaded types).

See
https://discourse.llvm.org/t/rfc-simplifying-intrinsics-type-signature-iit-info-generation-encoding-in-intrinsicemitter-cpp/90383
…nsics (llvm#185347)

In outer-loop VPlan, avoid emitting vector intrinsic calls for intrinsics
without a vector form. In VPRecipeBuilder, detect missing vector intrinsic
mapping and emit scalar handling instead of a vector call.

Also fix assertion when `llvm.pseudoprobe` in VPlan's native path is being
treated as a `WIDEN-INTRINSIC`.

Reproducer: https://godbolt.org/z/GsPYobvYs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DAG] computeKnownFPClass - add ISD::AssertNoFPClass handling