[clang] [lldb] [llvm] [mlir] [ADT] Give DenseMapPair its own members instead of a std::pair base. NFC (PR #221853)

Fangrui Song via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 9 00:40:41 PDT 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/221853

>From 7add1b87468fc953e2545e0697913d9a78f93129 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 7 Sep 2026 15:29:35 -0700
Subject: [PATCH 01/10] [ADT] Give DenseMapPair its own members instead of a
 std::pair base. NFC

Some fast paths check `std::is_trivially_copyable_v<KeyT> &&
std::is_trivially_copyable_v<ValueT>` instead of the bucket type,
because std::pair has a non-trivial copy assignment (which costs trivial
copyability). Hold the members directly, making the bucket trivially
copyable.

instructions:u in a stage-2 clang build decreases by 0.35%, likely due
to saving std::pair instantiations (std::pair implementations have
expensive `enable_if`)
---
 clang/lib/Sema/SemaAttr.cpp                   |  2 +-
 llvm/include/llvm/ADT/DenseMap.h              | 54 +++++++++++++------
 llvm/lib/CodeGen/RegisterUsageInfo.cpp        |  2 +-
 llvm/lib/MC/StringTableBuilder.cpp            |  2 +-
 llvm/lib/MCA/HardwareUnits/LSUnit.cpp         |  2 +-
 .../lib/MCA/HardwareUnits/ResourceManager.cpp |  2 +-
 llvm/lib/Transforms/Scalar/GVNHoist.cpp       |  2 +-
 llvm/lib/Transforms/Scalar/GVNSink.cpp        |  5 +-
 .../Transforms/Vectorize/SLPVectorizer.cpp    |  4 +-
 mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp     |  5 +-
 10 files changed, 50 insertions(+), 30 deletions(-)

diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 67573c9f1c72a..35d14a4444595 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1074,7 +1074,7 @@ void Sema::ActOnPragmaAttributeAttribute(
     //    variable(is_parameter).
     //  - a sub-rule and a sibling that's negated. E.g.
     //    variable(is_thread_local) and variable(unless(is_parameter))
-    llvm::SmallDenseMap<int, std::pair<int, SourceRange>, 2>
+    llvm::SmallDenseMap<int, attr::ParsedSubjectMatchRuleSet::value_type, 2>
         RulesToFirstSpecifiedNegatedSubRule;
     for (const auto &Rule : Rules) {
       attr::SubjectMatchRule MatchRule = attr::SubjectMatchRule(Rule.first);
diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index 3073ab0ec9712..5ea7ce2aeff85 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -44,17 +44,39 @@
 namespace llvm {
 
 namespace detail {
+// A bucket holds a key and a value. Don't use std::pair, which has a
+// non-trivial copy assignment, which costs is_trivially_copyable.
+template <typename KeyT, typename ValueT> struct DenseMapPair {
+  using first_type = KeyT;
+  using second_type = ValueT;
+
+  KeyT first;
+  ValueT second;
+
+  DenseMapPair() : first(), second() {}
+  DenseMapPair(const KeyT &Key, const ValueT &Value)
+      : first(Key), second(Value) {}
+  DenseMapPair(KeyT &&Key, ValueT &&Value)
+      : first(std::move(Key)), second(std::move(Value)) {}
+  DenseMapPair(const std::pair<KeyT, ValueT> &P)
+      : first(P.first), second(P.second) {}
+  DenseMapPair(std::pair<KeyT, ValueT> &&P)
+      : first(std::move(P.first)), second(std::move(P.second)) {}
+
+  operator std::pair<KeyT, ValueT>() const { return {first, second}; }
+  operator std::pair<const KeyT, ValueT>() const { return {first, second}; }
+
+  friend bool operator==(const DenseMapPair &LHS, const DenseMapPair &RHS) {
+    return LHS.first == RHS.first && LHS.second == RHS.second;
+  }
+  friend bool operator!=(const DenseMapPair &LHS, const DenseMapPair &RHS) {
+    return !(LHS == RHS);
+  }
 
-// We extend a pair to allow users to override the bucket type with their own
-// implementation without requiring two members.
-template <typename KeyT, typename ValueT>
-struct DenseMapPair : std::pair<KeyT, ValueT> {
-  using std::pair<KeyT, ValueT>::pair;
-
-  KeyT &getFirst() { return std::pair<KeyT, ValueT>::first; }
-  const KeyT &getFirst() const { return std::pair<KeyT, ValueT>::first; }
-  ValueT &getSecond() { return std::pair<KeyT, ValueT>::second; }
-  const ValueT &getSecond() const { return std::pair<KeyT, ValueT>::second; }
+  KeyT &getFirst() { return first; }
+  const KeyT &getFirst() const { return first; }
+  ValueT &getSecond() { return second; }
+  const ValueT &getSecond() const { return second; }
 };
 
 } // end namespace detail
@@ -330,7 +352,7 @@ class DenseMapBase : public DebugEpochBase {
   /// Range insertion of pairs.
   template <typename InputIt> void insert(InputIt I, InputIt E) {
     for (; I != E; ++I)
-      insert(*I);
+      try_emplace(I->first, I->second);
   }
 
   /// Inserts range of 'std::pair<KeyT, ValueT>' values into the map.
@@ -462,10 +484,9 @@ class DenseMapBase : public DebugEpochBase {
   }
 
   void destroyAll() {
-    // No need to iterate through the buckets if both KeyT and ValueT are
-    // trivially destructible.
-    if constexpr (std::is_trivially_destructible_v<KeyT> &&
-                  std::is_trivially_destructible_v<ValueT>)
+    // No need to iterate through the buckets if the bucket is trivially
+    // destructible.
+    if constexpr (std::is_trivially_destructible_v<BucketT>)
       return;
 
     if (getNumBuckets() == 0) // Nothing to do.
@@ -555,8 +576,7 @@ class DenseMapBase : public DebugEpochBase {
     const UsedT *OtherU = other.getUsed();
     std::memcpy(U, OtherU,
                 llvm::densemap::detail::usedWords(NumBuckets) * sizeof(UsedT));
-    if constexpr (std::is_trivially_copyable_v<KeyT> &&
-                  std::is_trivially_copyable_v<ValueT>) {
+    if constexpr (std::is_trivially_copyable_v<BucketT>) {
       memcpy(reinterpret_cast<void *>(Buckets), OtherBuckets,
              NumBuckets * sizeof(BucketT));
     } else {
diff --git a/llvm/lib/CodeGen/RegisterUsageInfo.cpp b/llvm/lib/CodeGen/RegisterUsageInfo.cpp
index 38e4c30ceb634..d139bd430fb20 100644
--- a/llvm/lib/CodeGen/RegisterUsageInfo.cpp
+++ b/llvm/lib/CodeGen/RegisterUsageInfo.cpp
@@ -70,7 +70,7 @@ PhysicalRegisterUsageInfo::getRegUsageInfo(const Function &FP) {
 }
 
 void PhysicalRegisterUsageInfo::print(raw_ostream &OS, const Module *M) const {
-  using FuncPtrRegMaskPair = std::pair<const Function *, std::vector<uint32_t>>;
+  using FuncPtrRegMaskPair = decltype(RegMasks)::value_type;
 
   // Create a vector of pointer to RegMasks entries
   SmallVector<const FuncPtrRegMaskPair *, 64> FPRMPairVector(
diff --git a/llvm/lib/MC/StringTableBuilder.cpp b/llvm/lib/MC/StringTableBuilder.cpp
index eb1a62f1f6412..39d10ad128ed7 100644
--- a/llvm/lib/MC/StringTableBuilder.cpp
+++ b/llvm/lib/MC/StringTableBuilder.cpp
@@ -66,7 +66,7 @@ void StringTableBuilder::write(raw_ostream &OS) const {
   OS << Data;
 }
 
-using StringPair = std::pair<CachedHashStringRef, size_t>;
+using StringPair = DenseMap<CachedHashStringRef, size_t>::value_type;
 
 void StringTableBuilder::write(uint8_t *Buf) const {
   assert(isFinalized());
diff --git a/llvm/lib/MCA/HardwareUnits/LSUnit.cpp b/llvm/lib/MCA/HardwareUnits/LSUnit.cpp
index bf0b432524881..f88f82a7f5279 100644
--- a/llvm/lib/MCA/HardwareUnits/LSUnit.cpp
+++ b/llvm/lib/MCA/HardwareUnits/LSUnit.cpp
@@ -42,7 +42,7 @@ LSUnitBase::LSUnitBase(const MCSchedModel &SM, unsigned LQ, unsigned SQ,
 LSUnitBase::~LSUnitBase() = default;
 
 void LSUnit::cycleEvent() {
-  for (const std::pair<unsigned, std::unique_ptr<MemoryGroup>> &G : Groups)
+  for (const auto &G : Groups)
     G.second->cycleEvent();
 }
 
diff --git a/llvm/lib/MCA/HardwareUnits/ResourceManager.cpp b/llvm/lib/MCA/HardwareUnits/ResourceManager.cpp
index cdf3439e07d61..12d062ab6ff9f 100644
--- a/llvm/lib/MCA/HardwareUnits/ResourceManager.cpp
+++ b/llvm/lib/MCA/HardwareUnits/ResourceManager.cpp
@@ -473,7 +473,7 @@ void ResourceManager::fastIssueInstruction(
 }
 
 void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
-  for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
+  for (auto &BR : BusyResources) {
     if (BR.second)
       BR.second--;
     if (!BR.second) {
diff --git a/llvm/lib/Transforms/Scalar/GVNHoist.cpp b/llvm/lib/Transforms/Scalar/GVNHoist.cpp
index 37562a024a2b0..6bb6d6772fbfb 100644
--- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp
@@ -835,7 +835,7 @@ void GVNHoist::findHoistableCandidates(OutValuesType &CHIBBs,
 
   // CHIArgs now have the outgoing values, so check for anticipability and
   // accumulate hoistable candidates in HPL.
-  for (std::pair<BasicBlock *, SmallVector<CHIArg, 2>> &A : CHIBBs) {
+  for (auto &A : CHIBBs) {
     BasicBlock *BB = A.first;
     SmallVectorImpl<CHIArg> &CHIs = A.second;
     // Vector of PHIs contains PHIs for different instructions.
diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index 67196ef9715f1..eb7a9c68b5b61 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -606,7 +606,10 @@ GVNSink::analyzeInstructionForSinking(LockstepReverseIterator<false> &LRI,
       return std::nullopt;
     VNums[N]++;
   }
-  unsigned VNumToSink = llvm::max_element(VNums, llvm::less_second())->first;
+  unsigned VNumToSink =
+      llvm::max_element(VNums, [](const auto &L, const auto &R) {
+        return L.second < R.second;
+      })->first;
 
   if (VNums[VNumToSink] == 1)
     // Can't sink anything!
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 5de71f3bf87b5..d6edb1a2cee80 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -4480,9 +4480,7 @@ class slpvectorizer::BoUpSLP {
         } while (It != P.first->Scalars.end());
       }
       return all_of(PotentiallyReorderedEntriesCount,
-                    [&](const std::pair<const TreeEntry *, unsigned> &P) {
-                      return P.second == NumOps - 1;
-                    });
+                    [&](const auto &P) { return P.second == NumOps - 1; });
     }
 
     SmallVector<ScheduleCopyableData *>
diff --git a/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp b/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
index 370457c85e797..1fbadcc4eff72 100644
--- a/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
+++ b/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
@@ -760,9 +760,8 @@ ParallelToGpuLaunchLowering::matchAndRewrite(ParallelOp parallelOp,
 
   // Now that we succeeded creating the launch operation, also update the
   // bounds.
-  for (auto bound : launchBounds)
-    launchOp.setOperand(getLaunchOpArgumentNum(std::get<0>(bound)),
-                        std::get<1>(bound));
+  for (const auto &bound : launchBounds)
+    launchOp.setOperand(getLaunchOpArgumentNum(bound.first), bound.second);
 
   rewriter.eraseOp(parallelOp);
   return success();

>From 5639a88fcd38f6717a6c6f41ada3e0e77f4fab60 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 7 Sep 2026 19:23:06 -0700
Subject: [PATCH 02/10] explicit operator pair

---
 clang/lib/AST/VTableBuilder.cpp               |  2 +-
 clang/lib/Serialization/ASTWriter.cpp         |  2 +-
 llvm/include/llvm/ADT/DenseMap.h              | 18 +++++++++++----
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    |  2 +-
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |  2 +-
 llvm/lib/CodeGen/StackColoring.cpp            |  2 +-
 llvm/lib/IR/AsmWriter.cpp                     |  2 +-
 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp |  3 ++-
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp     |  3 ++-
 llvm/lib/Transforms/IPO/SampleProfile.cpp     |  3 +--
 llvm/lib/Transforms/Scalar/StructurizeCFG.cpp |  5 ++---
 llvm/lib/Transforms/Utils/Local.cpp           |  2 +-
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  2 +-
 llvm/lib/Transforms/Utils/SplitModule.cpp     |  2 +-
 .../Transforms/Vectorize/SLPVectorizer.cpp    |  4 +---
 llvm/unittests/ADT/DenseMapTest.cpp           | 22 +++++++++++++++++++
 mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp |  2 +-
 mlir/lib/IR/PDL/PDLPatternMatch.cpp           |  2 +-
 18 files changed, 55 insertions(+), 25 deletions(-)

diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index 3c05d4b22b03e..bef4c7d6869d6 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -3730,7 +3730,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation(
 
     const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
     for (const auto &Loc : Builder.vtable_locations()) {
-      auto Insert = NewMethodLocations.insert(Loc);
+      auto Insert = NewMethodLocations.try_emplace(Loc.first, Loc.second);
       if (!Insert.second) {
         const MethodVFTableLocation &NewLoc = Loc.second;
         MethodVFTableLocation &OldLoc = Insert.first->second;
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index de985b770cb01..d5a4ab3cfd2f2 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -3352,7 +3352,7 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
         if (!I.second.isPragma() &&
             I.second == Diag.getDiagnosticIDs()->getDefaultMapping(I.first))
           continue;
-        Mappings.push_back(I);
+        Mappings.emplace_back(I.first, I.second);
       }
 
       // Sort by diag::kind for deterministic output.
diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index 5ea7ce2aeff85..a03dbe1564cea 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -63,8 +63,13 @@ template <typename KeyT, typename ValueT> struct DenseMapPair {
   DenseMapPair(std::pair<KeyT, ValueT> &&P)
       : first(std::move(P.first)), second(std::move(P.second)) {}
 
-  operator std::pair<KeyT, ValueT>() const { return {first, second}; }
-  operator std::pair<const KeyT, ValueT>() const { return {first, second}; }
+  template <typename K, typename V,
+            std::enable_if_t<std::is_constructible_v<K, const KeyT &> &&
+                                 std::is_constructible_v<V, const ValueT &>,
+                             int> = 0>
+  explicit operator std::pair<K, V>() const {
+    return {first, second};
+  }
 
   friend bool operator==(const DenseMapPair &LHS, const DenseMapPair &RHS) {
     return LHS.first == RHS.first && LHS.second == RHS.second;
@@ -351,8 +356,13 @@ class DenseMapBase : public DebugEpochBase {
 
   /// Range insertion of pairs.
   template <typename InputIt> void insert(InputIt I, InputIt E) {
-    for (; I != E; ++I)
-      try_emplace(I->first, I->second);
+    for (; I != E; ++I) {
+      // Take the members rather than converting: a move iterator's operator*
+      // yields an rvalue, which forwarding carries through to each member.
+      auto &&KV = *I;
+      try_emplace(std::forward<decltype(KV)>(KV).first,
+                  std::forward<decltype(KV)>(KV).second);
+    }
   }
 
   /// Inserts range of 'std::pair<KeyT, ValueT>' values into the map.
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index f4ebfce24b016..0ed0817c46ec8 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -242,7 +242,7 @@ void BitcodeReaderMetadataList::tryToResolveCycles() {
 
   // Give up on finding a full definition for any forward decls that remain.
   for (const auto &Ref : OldTypeRefs.FwdDecls)
-    OldTypeRefs.Final.insert(Ref);
+    OldTypeRefs.Final.try_emplace(Ref.first, Ref.second);
   OldTypeRefs.FwdDecls.clear();
 
   // Upgrade from old type ref arrays.  In strange cases, this could add to
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 859e073b91cc5..0636b222e0441 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -580,7 +580,7 @@ class IndexBitcodeWriter : public BitcodeWriterBase {
     if (ModuleToSummariesForIndex) {
       for (auto &M : *ModuleToSummariesForIndex)
         for (auto &Summary : M.second) {
-          Callback(Summary, false);
+          Callback({Summary.first, Summary.second}, false);
           // Ensure aliasee is handled, e.g. for assigning a valueId,
           // even if we are not importing the aliasee directly (the
           // imported alias will contain a copy of aliasee).
diff --git a/llvm/lib/CodeGen/StackColoring.cpp b/llvm/lib/CodeGen/StackColoring.cpp
index aa42c0ee28532..0e4c1526d0d0b 100644
--- a/llvm/lib/CodeGen/StackColoring.cpp
+++ b/llvm/lib/CodeGen/StackColoring.cpp
@@ -927,7 +927,7 @@ void StackColoring::remapInstructions(DenseMap<int, int> &SlotRemap) {
   // Keep a list of allocas which has been affected by the remap.
   SmallPtrSet<const AllocaInst*, 32> MergedAllocas;
 
-  for (const std::pair<int, int> &SI : SlotRemap) {
+  for (const auto &SI : SlotRemap) {
     const AllocaInst *From = MFI->getObjectAllocation(SI.first);
     const AllocaInst *To = MFI->getObjectAllocation(SI.second);
     assert(To && From && "Invalid allocation object");
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 8e57bff1d36c9..e101c5524f5c3 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -5145,7 +5145,7 @@ void AssemblyWriter::writeAllAttributeGroups() {
   asVec.resize(Machine.as_size());
 
   for (auto &I : llvm::make_range(Machine.as_begin(), Machine.as_end()))
-    asVec[I.second] = I;
+    asVec[I.second] = {I.first, I.second};
 
   for (const auto &I : asVec)
     Out << "attributes #" << I.second << " = { "
diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index 51821dd7f23bb..9399b15eb3a75 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -732,7 +732,8 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
     return true; // No users, this is a dead argument.
 
   // Sort parts by offset.
-  append_range(ArgPartsVec, ArgParts);
+  for (const auto &Part : ArgParts)
+    ArgPartsVec.emplace_back(Part.first, Part.second);
   sort(ArgPartsVec, llvm::less_first());
 
   // Make sure the parts are non-overlapping.
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index a713ead683476..708522bb61344 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1157,7 +1157,8 @@ static bool inferInitializes(Argument &A, Function &F) {
     if (UPB != UsesPerBlock.end()) {
       // Sort uses in this block by instruction order.
       SmallVector<std::pair<Instruction *, ArgumentAccessInfo>, 2> Insts;
-      append_range(Insts, UPB->second.Insts);
+      for (const auto &Inst : UPB->second.Insts)
+        Insts.emplace_back(Inst.first, Inst.second);
       sort(Insts, [](std::pair<Instruction *, ArgumentAccessInfo> &LHS,
                      std::pair<Instruction *, ArgumentAccessInfo> &RHS) {
         return LHS.first->comesBefore(RHS.first);
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index cb11372183bc8..227a2a4ed9221 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -2205,8 +2205,7 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager &AM,
 
   // Account for cold calls not inlined....
   if (!FunctionSamples::ProfileIsCS)
-    for (const std::pair<Function *, NotInlinedProfileInfo> &pair :
-         notInlinedCallInfo)
+    for (const auto &pair : notInlinedCallInfo)
       updateProfileCallee(pair.first, pair.second.entryCount);
 
   if (RemoveProbeAfterProfileAnnotation &&
diff --git a/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp b/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp
index 9707eee005c56..9b9889b911eee 100644
--- a/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp
+++ b/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp
@@ -1135,9 +1135,8 @@ void StructurizeCFG::setPrevNode(BasicBlock *BB) {
 /// Does BB dominate all the predicates of Node?
 bool StructurizeCFG::dominatesPredicates(BasicBlock *BB, RegionNode *Node) {
   BBPredicates &Preds = Predicates[Node->getEntry()];
-  return llvm::all_of(Preds, [&](std::pair<BasicBlock *, PredInfo> Pred) {
-    return DT->dominates(BB, Pred.first);
-  });
+  return llvm::all_of(
+      Preds, [&](const auto &Pred) { return DT->dominates(BB, Pred.first); });
 }
 
 /// Can we predict that this node will always be called?
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index b88e506ae681a..f08f1e4868091 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -2852,7 +2852,7 @@ static bool markAliveBlocks(Function &F, SmallVectorImpl<bool> &Reachable,
         }
         if (DTU) {
           std::vector<DominatorTree::UpdateType> Updates;
-          for (const std::pair<BasicBlock *, int> &I : NumPerSuccessorCases)
+          for (const auto &I : NumPerSuccessorCases)
             if (I.second == 0)
               Updates.push_back({DominatorTree::Delete, BB, I.first});
           DTU->applyUpdates(Updates);
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index cc2c769b25ce9..ca96f2e70d810 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -1053,7 +1053,7 @@ bool SimplifyCFGOpt::simplifyEqualityComparisonWithOnlyPredecessor(
 
     if (DTU) {
       std::vector<DominatorTree::UpdateType> Updates;
-      for (const std::pair<BasicBlock *, int> &I : NumPerSuccessorCases)
+      for (const auto &I : NumPerSuccessorCases)
         if (I.second == 0)
           Updates.push_back({DominatorTree::Delete, PredDef, I.first});
       DTU->applyUpdates(Updates);
diff --git a/llvm/lib/Transforms/Utils/SplitModule.cpp b/llvm/lib/Transforms/Utils/SplitModule.cpp
index 8e70902c4dac2..a73b34fab462a 100644
--- a/llvm/lib/Transforms/Utils/SplitModule.cpp
+++ b/llvm/lib/Transforms/Utils/SplitModule.cpp
@@ -278,7 +278,7 @@ void llvm::SplitModule(
     for (unsigned I = 0; I < N; ++I) {
       if (auto It = ModuleFunctionCount.find(I);
           It != ModuleFunctionCount.end())
-        BalancingQueue.push(*It);
+        BalancingQueue.emplace(It->first, It->second);
       else
         BalancingQueue.push({I, 0});
     }
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index d6edb1a2cee80..323442be1c8a7 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -11632,9 +11632,7 @@ class InstructionsCompatibilityAnalysis {
           ++Counters[V];
         }
         if (Counters.size() == 2 &&
-            any_of(Counters, [&](const std::pair<const Value *, unsigned> &C) {
-              return C.second == 1;
-            }))
+            any_of(Counters, [&](const auto &C) { return C.second == 1; }))
           return true;
       }
       // First operand not a constant or splat? Last attempt - check for
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 1b3473fa0ec2d..65e9fdad5686a 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -16,10 +16,12 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <map>
+#include <memory>
 #include <optional>
 #include <set>
 #include <utility>
 #include <variant>
+#include <vector>
 
 using namespace llvm;
 
@@ -474,6 +476,14 @@ TEST(DenseMapCustomTest, EqualityComparison) {
   EXPECT_NE(M1, M3);
 }
 
+// Converting a bucket to a std::pair copies both members, so it must not happen
+// implicitly: `const std::pair<int, int> &P = *M.begin();` would bind to a
+// temporary rather than the bucket.
+static_assert(!std::is_convertible_v<detail::DenseMapPair<int, int>,
+                                     std::pair<int, int>>);
+static_assert(std::is_constructible_v<std::pair<int, int>,
+                                      detail::DenseMapPair<int, int>>);
+
 TEST(DenseMapCustomTest, InsertRange) {
   DenseMap<int, int> M;
 
@@ -483,6 +493,18 @@ TEST(DenseMapCustomTest, InsertRange) {
   EXPECT_EQ(M.size(), 2u);
   EXPECT_THAT(M, testing::UnorderedElementsAre(testing::Pair(0, 0),
                                                testing::Pair(1, 2)));
+
+  // A move iterator yields an rvalue from operator*, which the range insert
+  // must forward to the members for a move-only value to survive.
+  std::vector<std::pair<int, std::unique_ptr<int>>> MoveOnly;
+  MoveOnly.emplace_back(3, std::make_unique<int>(42));
+  DenseMap<int, std::unique_ptr<int>> MoveMap;
+  MoveMap.insert(std::make_move_iterator(MoveOnly.begin()),
+                 std::make_move_iterator(MoveOnly.end()));
+  auto It = MoveMap.find(3);
+  ASSERT_NE(It, MoveMap.end());
+  EXPECT_EQ(*It->second, 42);
+  EXPECT_EQ(MoveOnly[0].second, nullptr);
 }
 
 TEST(SmallDenseMapCustomTest, InsertRange) {
diff --git a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
index c38213850c6ea..9aebea1e1d72e 100644
--- a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
+++ b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
@@ -188,7 +188,7 @@ AffineMap mlir::makePermutationMap(
   for (auto *forInst : enclosingLoops) {
     auto it = loopToVectorDim.find(forInst);
     if (it != loopToVectorDim.end()) {
-      enclosingLoopToVectorDim.insert(*it);
+      enclosingLoopToVectorDim.try_emplace(it->first, it->second);
     }
   }
   return ::makePermutationMap(indices, enclosingLoopToVectorDim);
diff --git a/mlir/lib/IR/PDL/PDLPatternMatch.cpp b/mlir/lib/IR/PDL/PDLPatternMatch.cpp
index 62a71aa2c1daa..cc547d859adf4 100644
--- a/mlir/lib/IR/PDL/PDLPatternMatch.cpp
+++ b/mlir/lib/IR/PDL/PDLPatternMatch.cpp
@@ -83,7 +83,7 @@ void PDLPatternModule::mergeIn(PDLPatternModule &&other) {
   for (auto &it : other.configs)
     configs.emplace_back(std::move(it));
   for (auto &it : other.configMap)
-    configMap.insert(it);
+    configMap.try_emplace(it.first, it.second);
 
   // Steal the other state if we have no patterns.
   if (!pdlModule) {

>From e68ddbfc91a13efff72b6da74c61b606b381f6aa Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 7 Sep 2026 19:55:13 -0700
Subject: [PATCH 03/10] explicit operator pair

---
 llvm/include/llvm/ADT/DenseMap.h    |  7 ++-----
 llvm/unittests/ADT/DenseMapTest.cpp | 26 ++++++++++++++++++++++----
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index a03dbe1564cea..a2ed4c00204c1 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -63,11 +63,8 @@ template <typename KeyT, typename ValueT> struct DenseMapPair {
   DenseMapPair(std::pair<KeyT, ValueT> &&P)
       : first(std::move(P.first)), second(std::move(P.second)) {}
 
-  template <typename K, typename V,
-            std::enable_if_t<std::is_constructible_v<K, const KeyT &> &&
-                                 std::is_constructible_v<V, const ValueT &>,
-                             int> = 0>
-  explicit operator std::pair<K, V>() const {
+  explicit operator std::pair<KeyT, ValueT>() const { return {first, second}; }
+  explicit operator std::pair<const KeyT, ValueT>() const {
     return {first, second};
   }
 
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 65e9fdad5686a..996ce78196013 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -476,13 +476,15 @@ TEST(DenseMapCustomTest, EqualityComparison) {
   EXPECT_NE(M1, M3);
 }
 
+using IntBucket = detail::DenseMapPair<int, int>;
+
+static_assert(std::is_trivially_copyable_v<IntBucket>);
+static_assert(!std::is_trivially_default_constructible_v<IntBucket>);
+
 // Converting a bucket to a std::pair copies both members, so it must not happen
 // implicitly: `const std::pair<int, int> &P = *M.begin();` would bind to a
 // temporary rather than the bucket.
-static_assert(!std::is_convertible_v<detail::DenseMapPair<int, int>,
-                                     std::pair<int, int>>);
-static_assert(std::is_constructible_v<std::pair<int, int>,
-                                      detail::DenseMapPair<int, int>>);
+static_assert(!std::is_convertible_v<IntBucket, std::pair<int, int>>);
 
 TEST(DenseMapCustomTest, InsertRange) {
   DenseMap<int, int> M;
@@ -505,6 +507,16 @@ TEST(DenseMapCustomTest, InsertRange) {
   ASSERT_NE(It, MoveMap.end());
   EXPECT_EQ(*It->second, 42);
   EXPECT_EQ(MoveOnly[0].second, nullptr);
+
+  // Converting a bucket explicitly still reaches a vector's element type, and a
+  // std::map's, whose key is const.
+  DenseMap<int, int> Src({{1, 10}, {2, 20}});
+  SmallVector<std::pair<int, int>> Vec(Src.begin(), Src.end());
+  EXPECT_THAT(Vec, testing::UnorderedElementsAre(testing::Pair(1, 10),
+                                                 testing::Pair(2, 20)));
+  std::map<int, int> Sorted(Src.begin(), Src.end());
+  EXPECT_THAT(Sorted,
+              testing::ElementsAre(testing::Pair(1, 10), testing::Pair(2, 20)));
 }
 
 TEST(SmallDenseMapCustomTest, InsertRange) {
@@ -1217,4 +1229,10 @@ TEST(DenseMapCustomTest, MoveAssignInvalidatesIterators) {
 }
 #endif
 
+TEST(DenseMapCustomTest, BucketComparison) {
+  IntBucket A(1, 2), B(1, 2), C(1, 3);
+  EXPECT_EQ(A, B);
+  EXPECT_NE(A, C);
+}
+
 } // namespace

>From 3d888b01d06f3575229d7876cad0102d5886712a Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 7 Sep 2026 20:04:02 -0700
Subject: [PATCH 04/10] adopt structured bindings

---
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    | 4 ++--
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     | 6 +++---
 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp | 4 ++--
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp     | 4 ++--
 llvm/lib/Transforms/IPO/SampleProfile.cpp     | 4 ++--
 mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp     | 4 ++--
 mlir/lib/IR/PDL/PDLPatternMatch.cpp           | 3 +--
 7 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 0ed0817c46ec8..6f1fbd627edbc 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -241,8 +241,8 @@ void BitcodeReaderMetadataList::tryToResolveCycles() {
     return;
 
   // Give up on finding a full definition for any forward decls that remain.
-  for (const auto &Ref : OldTypeRefs.FwdDecls)
-    OldTypeRefs.Final.try_emplace(Ref.first, Ref.second);
+  for (const auto &[UUID, CT] : OldTypeRefs.FwdDecls)
+    OldTypeRefs.Final.try_emplace(UUID, CT);
   OldTypeRefs.FwdDecls.clear();
 
   // Upgrade from old type ref arrays.  In strange cases, this could add to
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 0636b222e0441..e33b6e0050318 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -579,12 +579,12 @@ class IndexBitcodeWriter : public BitcodeWriterBase {
   void forEachSummary(Functor Callback) {
     if (ModuleToSummariesForIndex) {
       for (auto &M : *ModuleToSummariesForIndex)
-        for (auto &Summary : M.second) {
-          Callback({Summary.first, Summary.second}, false);
+        for (auto &[GUID, GVS] : M.second) {
+          Callback({GUID, GVS}, false);
           // Ensure aliasee is handled, e.g. for assigning a valueId,
           // even if we are not importing the aliasee directly (the
           // imported alias will contain a copy of aliasee).
-          if (auto *AS = dyn_cast<AliasSummary>(Summary.getSecond()))
+          if (auto *AS = dyn_cast<AliasSummary>(GVS))
             Callback({AS->getAliaseeGUID(), &AS->getAliasee()}, true);
         }
     } else {
diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index 9399b15eb3a75..a63e8245dd4b5 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -732,8 +732,8 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
     return true; // No users, this is a dead argument.
 
   // Sort parts by offset.
-  for (const auto &Part : ArgParts)
-    ArgPartsVec.emplace_back(Part.first, Part.second);
+  for (const auto &[Offset, Part] : ArgParts)
+    ArgPartsVec.emplace_back(Offset, Part);
   sort(ArgPartsVec, llvm::less_first());
 
   // Make sure the parts are non-overlapping.
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 708522bb61344..af393e8657fc9 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1157,8 +1157,8 @@ static bool inferInitializes(Argument &A, Function &F) {
     if (UPB != UsesPerBlock.end()) {
       // Sort uses in this block by instruction order.
       SmallVector<std::pair<Instruction *, ArgumentAccessInfo>, 2> Insts;
-      for (const auto &Inst : UPB->second.Insts)
-        Insts.emplace_back(Inst.first, Inst.second);
+      for (const auto &[I, Info] : UPB->second.Insts)
+        Insts.emplace_back(I, Info);
       sort(Insts, [](std::pair<Instruction *, ArgumentAccessInfo> &LHS,
                      std::pair<Instruction *, ArgumentAccessInfo> &RHS) {
         return LHS.first->comesBefore(RHS.first);
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index 227a2a4ed9221..cd9549cd2f0cc 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -2205,8 +2205,8 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager &AM,
 
   // Account for cold calls not inlined....
   if (!FunctionSamples::ProfileIsCS)
-    for (const auto &pair : notInlinedCallInfo)
-      updateProfileCallee(pair.first, pair.second.entryCount);
+    for (const auto &[Fn, Info] : notInlinedCallInfo)
+      updateProfileCallee(Fn, Info.entryCount);
 
   if (RemoveProbeAfterProfileAnnotation &&
       FunctionSamples::ProfileIsProbeBased) {
diff --git a/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp b/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
index 1fbadcc4eff72..796083062ac72 100644
--- a/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
+++ b/mlir/lib/Conversion/SCFToGPU/SCFToGPU.cpp
@@ -760,8 +760,8 @@ ParallelToGpuLaunchLowering::matchAndRewrite(ParallelOp parallelOp,
 
   // Now that we succeeded creating the launch operation, also update the
   // bounds.
-  for (const auto &bound : launchBounds)
-    launchOp.setOperand(getLaunchOpArgumentNum(bound.first), bound.second);
+  for (const auto &[processor, bound] : launchBounds)
+    launchOp.setOperand(getLaunchOpArgumentNum(processor), bound);
 
   rewriter.eraseOp(parallelOp);
   return success();
diff --git a/mlir/lib/IR/PDL/PDLPatternMatch.cpp b/mlir/lib/IR/PDL/PDLPatternMatch.cpp
index cc547d859adf4..ceaced2610b6e 100644
--- a/mlir/lib/IR/PDL/PDLPatternMatch.cpp
+++ b/mlir/lib/IR/PDL/PDLPatternMatch.cpp
@@ -82,8 +82,7 @@ void PDLPatternModule::mergeIn(PDLPatternModule &&other) {
     registerRewriteFunction(it.first(), std::move(it.second));
   for (auto &it : other.configs)
     configs.emplace_back(std::move(it));
-  for (auto &it : other.configMap)
-    configMap.try_emplace(it.first, it.second);
+  configMap.insert_range(other.configMap);
 
   // Steal the other state if we have no patterns.
   if (!pdlModule) {

>From de1e10655a3df4626d22078fa52d5d20670060a3 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 7 Sep 2026 22:04:52 -0700
Subject: [PATCH 05/10] Use Vec.append(x.begin(), x.end()) instead of
 append_range

---
 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp |  3 +--
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp     |  3 +--
 llvm/unittests/ADT/DenseMapTest.cpp           | 12 ++++++------
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index a63e8245dd4b5..43eb005dfdbb3 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -732,8 +732,7 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
     return true; // No users, this is a dead argument.
 
   // Sort parts by offset.
-  for (const auto &[Offset, Part] : ArgParts)
-    ArgPartsVec.emplace_back(Offset, Part);
+  ArgPartsVec.append(ArgParts.begin(), ArgParts.end());
   sort(ArgPartsVec, llvm::less_first());
 
   // Make sure the parts are non-overlapping.
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index af393e8657fc9..e8925a6b0855e 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1157,8 +1157,7 @@ static bool inferInitializes(Argument &A, Function &F) {
     if (UPB != UsesPerBlock.end()) {
       // Sort uses in this block by instruction order.
       SmallVector<std::pair<Instruction *, ArgumentAccessInfo>, 2> Insts;
-      for (const auto &[I, Info] : UPB->second.Insts)
-        Insts.emplace_back(I, Info);
+      Insts.append(UPB->second.Insts.begin(), UPB->second.Insts.end());
       sort(Insts, [](std::pair<Instruction *, ArgumentAccessInfo> &LHS,
                      std::pair<Instruction *, ArgumentAccessInfo> &RHS) {
         return LHS.first->comesBefore(RHS.first);
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 996ce78196013..1dc0419ebd94a 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -1183,6 +1183,12 @@ TEST(DenseMapCustomTest, RemoveIfValueDtor) {
   EXPECT_EQ(0u, CtorTester::getNumConstructed());
 }
 
+TEST(DenseMapCustomTest, BucketComparison) {
+  IntBucket A(1, 2), B(1, 2), C(1, 3);
+  EXPECT_EQ(A, B);
+  EXPECT_NE(A, C);
+}
+
 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
 TEST(DenseMapCustomTest, EraseInvalidatesIterators) {
   DenseMap<int, int> M;
@@ -1229,10 +1235,4 @@ TEST(DenseMapCustomTest, MoveAssignInvalidatesIterators) {
 }
 #endif
 
-TEST(DenseMapCustomTest, BucketComparison) {
-  IntBucket A(1, 2), B(1, 2), C(1, 3);
-  EXPECT_EQ(A, B);
-  EXPECT_NE(A, C);
-}
-
 } // namespace

>From 1cf8c44d39178367a0ba62ab8f6b9f81f43a4a9c Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 8 Sep 2026 21:11:47 -0700
Subject: [PATCH 06/10] add an insert overload; drop mlir/ adaptation

---
 clang/lib/AST/VTableBuilder.cpp               |  2 +-
 llvm/include/llvm/ADT/DenseMap.h              | 29 ++++++++++++++-----
 llvm/unittests/ADT/DenseMapTest.cpp           |  7 +++++
 mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp |  2 +-
 4 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index bef4c7d6869d6..3c05d4b22b03e 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -3730,7 +3730,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation(
 
     const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
     for (const auto &Loc : Builder.vtable_locations()) {
-      auto Insert = NewMethodLocations.try_emplace(Loc.first, Loc.second);
+      auto Insert = NewMethodLocations.insert(Loc);
       if (!Insert.second) {
         const MethodVFTableLocation &NewLoc = Loc.second;
         MethodVFTableLocation &OldLoc = Insert.first->second;
diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index a2ed4c00204c1..664eac1d8027f 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -62,6 +62,12 @@ template <typename KeyT, typename ValueT> struct DenseMapPair {
       : first(P.first), second(P.second) {}
   DenseMapPair(std::pair<KeyT, ValueT> &&P)
       : first(std::move(P.first)), second(std::move(P.second)) {}
+  template <typename U1, typename U2>
+  DenseMapPair(const DenseMapPair<U1, U2> &P)
+      : first(P.first), second(P.second) {}
+  template <typename U1, typename U2>
+  DenseMapPair(DenseMapPair<U1, U2> &&P)
+      : first(std::move(P.first)), second(std::move(P.second)) {}
 
   explicit operator std::pair<KeyT, ValueT>() const { return {first, second}; }
   explicit operator std::pair<const KeyT, ValueT>() const {
@@ -316,6 +322,20 @@ class DenseMapBase : public DebugEpochBase {
     return try_emplace_impl(std::move(KV.first), std::move(KV.second));
   }
 
+  template <
+      typename B = BucketT,
+      typename = std::enable_if_t<!std::is_same_v<B, std::pair<KeyT, ValueT>>>>
+  std::pair<iterator, bool> insert(const BucketT &KV) {
+    return try_emplace_impl(KV.first, KV.second);
+  }
+
+  template <
+      typename B = BucketT,
+      typename = std::enable_if_t<!std::is_same_v<B, std::pair<KeyT, ValueT>>>>
+  std::pair<iterator, bool> insert(BucketT &&KV) {
+    return try_emplace_impl(std::move(KV.first), std::move(KV.second));
+  }
+
   // Inserts key,value pair into the map if the key isn't already in the map.
   // The value is constructed in-place if the key is not in the map, otherwise
   // it is not moved.
@@ -353,13 +373,8 @@ class DenseMapBase : public DebugEpochBase {
 
   /// Range insertion of pairs.
   template <typename InputIt> void insert(InputIt I, InputIt E) {
-    for (; I != E; ++I) {
-      // Take the members rather than converting: a move iterator's operator*
-      // yields an rvalue, which forwarding carries through to each member.
-      auto &&KV = *I;
-      try_emplace(std::forward<decltype(KV)>(KV).first,
-                  std::forward<decltype(KV)>(KV).second);
-    }
+    for (; I != E; ++I)
+      insert(*I);
   }
 
   /// Inserts range of 'std::pair<KeyT, ValueT>' values into the map.
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 1dc0419ebd94a..0fb270b76bdd6 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -517,6 +517,13 @@ TEST(DenseMapCustomTest, InsertRange) {
   std::map<int, int> Sorted(Src.begin(), Src.end());
   EXPECT_THAT(Sorted,
               testing::ElementsAre(testing::Pair(1, 10), testing::Pair(2, 20)));
+
+  // As polly inserts a DenseMap<BasicBlock *, BasicBlock *> into a
+  // DenseMap<AssertingVH<Value>, AssertingVH<Value>>, insert from a map whose
+  // key and value types only convert to this one's.
+  DenseMap<CtorTester, CtorTester, CtorTesterMapInfo> Convertible;
+  Convertible.insert_range(DenseMap<uint32_t, uint32_t>({{1, 10}}));
+  EXPECT_EQ(CtorTester(10), Convertible.lookup(CtorTester(1)));
 }
 
 TEST(SmallDenseMapCustomTest, InsertRange) {
diff --git a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
index 9aebea1e1d72e..c38213850c6ea 100644
--- a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
+++ b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
@@ -188,7 +188,7 @@ AffineMap mlir::makePermutationMap(
   for (auto *forInst : enclosingLoops) {
     auto it = loopToVectorDim.find(forInst);
     if (it != loopToVectorDim.end()) {
-      enclosingLoopToVectorDim.try_emplace(it->first, it->second);
+      enclosingLoopToVectorDim.insert(*it);
     }
   }
   return ::makePermutationMap(indices, enclosingLoopToVectorDim);

>From 6302db181e74b26a2b15d1d382073aaf68ba3300 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 8 Sep 2026 21:23:50 -0700
Subject: [PATCH 07/10] unittest

---
 llvm/unittests/ADT/DenseMapTest.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 0fb270b76bdd6..f6f96dba75747 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -508,6 +508,15 @@ TEST(DenseMapCustomTest, InsertRange) {
   EXPECT_EQ(*It->second, 42);
   EXPECT_EQ(MoveOnly[0].second, nullptr);
 
+  // A move iterator over a map yields bucket rvalues instead, which must reach
+  // insert(BucketT &&) rather than be copied.
+  DenseMap<int, std::unique_ptr<int>> MoveSrc;
+  MoveSrc.try_emplace(4, std::make_unique<int>(7));
+  MoveMap.insert(std::make_move_iterator(MoveSrc.begin()),
+                 std::make_move_iterator(MoveSrc.end()));
+  EXPECT_EQ(*MoveMap.find(4)->second, 7);
+  EXPECT_EQ(MoveSrc.find(4)->second, nullptr);
+
   // Converting a bucket explicitly still reaches a vector's element type, and a
   // std::map's, whose key is const.
   DenseMap<int, int> Src({{1, 10}, {2, 20}});

>From a8a3a46c7f47e28d160d9ead4252f509874a2e32 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 8 Sep 2026 22:14:19 -0700
Subject: [PATCH 08/10] apply Kazu changes

---
 llvm/lib/IR/AsmWriter.cpp           | 5 +++--
 llvm/unittests/ADT/DenseMapTest.cpp | 2 --
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index e101c5524f5c3..96918ec35fd7b 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -5144,8 +5144,9 @@ void AssemblyWriter::writeAllAttributeGroups() {
   std::vector<std::pair<AttributeSet, unsigned>> asVec;
   asVec.resize(Machine.as_size());
 
-  for (auto &I : llvm::make_range(Machine.as_begin(), Machine.as_end()))
-    asVec[I.second] = {I.first, I.second};
+  for (const auto &[AS, ID] :
+       llvm::make_range(Machine.as_begin(), Machine.as_end()))
+    asVec[ID] = {AS, ID};
 
   for (const auto &I : asVec)
     Out << "attributes #" << I.second << " = { "
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index f6f96dba75747..d3b5e97e2bf6b 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -16,12 +16,10 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <map>
-#include <memory>
 #include <optional>
 #include <set>
 #include <utility>
 #include <variant>
-#include <vector>
 
 using namespace llvm;
 

>From 9e42f6961b382e777de8fcc6e79788e09ccbfc9c Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 8 Sep 2026 23:21:30 -0700
Subject: [PATCH 09/10] remove explicit on operator std::pair to avoid fixing
 some call sites for clang-cl

---
 clang/lib/Serialization/ASTWriter.cpp                |  2 +-
 llvm/include/llvm/ADT/DenseMap.h                     |  6 ++----
 llvm/lib/IR/AsmWriter.cpp                            |  5 ++---
 llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp |  2 +-
 llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp       |  6 ++++--
 llvm/lib/Target/AMDGPU/SIInstrInfo.cpp               |  2 +-
 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp        |  2 +-
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp            |  2 +-
 llvm/lib/Transforms/Utils/SplitModule.cpp            |  2 +-
 llvm/unittests/ADT/DenseMapTest.cpp                  | 11 +++++------
 10 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index d5a4ab3cfd2f2..de985b770cb01 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -3352,7 +3352,7 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
         if (!I.second.isPragma() &&
             I.second == Diag.getDiagnosticIDs()->getDefaultMapping(I.first))
           continue;
-        Mappings.emplace_back(I.first, I.second);
+        Mappings.push_back(I);
       }
 
       // Sort by diag::kind for deterministic output.
diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index 664eac1d8027f..0126935ca3598 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -69,10 +69,8 @@ template <typename KeyT, typename ValueT> struct DenseMapPair {
   DenseMapPair(DenseMapPair<U1, U2> &&P)
       : first(std::move(P.first)), second(std::move(P.second)) {}
 
-  explicit operator std::pair<KeyT, ValueT>() const { return {first, second}; }
-  explicit operator std::pair<const KeyT, ValueT>() const {
-    return {first, second};
-  }
+  operator std::pair<KeyT, ValueT>() const { return {first, second}; }
+  operator std::pair<const KeyT, ValueT>() const { return {first, second}; }
 
   friend bool operator==(const DenseMapPair &LHS, const DenseMapPair &RHS) {
     return LHS.first == RHS.first && LHS.second == RHS.second;
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 96918ec35fd7b..8e57bff1d36c9 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -5144,9 +5144,8 @@ void AssemblyWriter::writeAllAttributeGroups() {
   std::vector<std::pair<AttributeSet, unsigned>> asVec;
   asVec.resize(Machine.as_size());
 
-  for (const auto &[AS, ID] :
-       llvm::make_range(Machine.as_begin(), Machine.as_end()))
-    asVec[ID] = {AS, ID};
+  for (auto &I : llvm::make_range(Machine.as_begin(), Machine.as_end()))
+    asVec[I.second] = I;
 
   for (const auto &I : asVec)
     Out << "attributes #" << I.second << " = { "
diff --git a/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp b/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp
index 30431cb54249c..2ed66dc09dbb6 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPURewriteOutArguments.cpp
@@ -378,7 +378,7 @@ bool AMDGPURewriteOutArguments::runOnFunction(Function &F) {
   // this function with a stub.
   NewFunc->splice(NewFunc->begin(), &F);
 
-  for (std::pair<ReturnInst *, ReplacementVec> &Replacement : Replacements) {
+  for (auto &Replacement : Replacements) {
     ReturnInst *RI = Replacement.first;
     IRBuilder<> B(RI);
     B.SetCurrentDebugLocation(RI->getDebugLoc());
diff --git a/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp b/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp
index 49514e2cb0b48..1be2dad7d0367 100644
--- a/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp
@@ -2233,8 +2233,10 @@ bool GCNHazardRecognizer::fixVALUPartialForwardingHazard(MachineInstr *MI) {
     int VALUs = 0;
 
     static unsigned getHashValue(const StateType &State) {
-      return hash_combine(State.ExecPos, State.VALUs,
-                          hash_combine_range(State.DefPos));
+      hash_code H = hash_combine(State.ExecPos, State.VALUs);
+      for (const auto &[Reg, Pos] : State.DefPos)
+        H = hash_combine(H, Reg, Pos);
+      return H;
     }
     static bool isEqual(const StateType &LHS, const StateType &RHS) {
       return LHS.DefPos == RHS.DefPos && LHS.ExecPos == RHS.ExecPos &&
diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
index 173c88f6c17fd..de91eca402c15 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
@@ -8139,7 +8139,7 @@ void SIInstrInfo::moveToVALU(SIInstrWorklist &Worklist,
            "Deferred MachineInstr are not supposed to re-populate worklist");
   }
 
-  for (std::pair<MachineInstr *, V2PhysSCopyInfo> &Entry : WaterFalls) {
+  for (auto &Entry : WaterFalls) {
     if (Entry.first->getOpcode() == AMDGPU::SI_CALL_ISEL)
       createWaterFallForSiCall(Entry.first, MDT, Entry.second.MOs,
                                Entry.second.SGPRs);
diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index 43eb005dfdbb3..51821dd7f23bb 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -732,7 +732,7 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
     return true; // No users, this is a dead argument.
 
   // Sort parts by offset.
-  ArgPartsVec.append(ArgParts.begin(), ArgParts.end());
+  append_range(ArgPartsVec, ArgParts);
   sort(ArgPartsVec, llvm::less_first());
 
   // Make sure the parts are non-overlapping.
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index e8925a6b0855e..a713ead683476 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1157,7 +1157,7 @@ static bool inferInitializes(Argument &A, Function &F) {
     if (UPB != UsesPerBlock.end()) {
       // Sort uses in this block by instruction order.
       SmallVector<std::pair<Instruction *, ArgumentAccessInfo>, 2> Insts;
-      Insts.append(UPB->second.Insts.begin(), UPB->second.Insts.end());
+      append_range(Insts, UPB->second.Insts);
       sort(Insts, [](std::pair<Instruction *, ArgumentAccessInfo> &LHS,
                      std::pair<Instruction *, ArgumentAccessInfo> &RHS) {
         return LHS.first->comesBefore(RHS.first);
diff --git a/llvm/lib/Transforms/Utils/SplitModule.cpp b/llvm/lib/Transforms/Utils/SplitModule.cpp
index a73b34fab462a..8e70902c4dac2 100644
--- a/llvm/lib/Transforms/Utils/SplitModule.cpp
+++ b/llvm/lib/Transforms/Utils/SplitModule.cpp
@@ -278,7 +278,7 @@ void llvm::SplitModule(
     for (unsigned I = 0; I < N; ++I) {
       if (auto It = ModuleFunctionCount.find(I);
           It != ModuleFunctionCount.end())
-        BalancingQueue.emplace(It->first, It->second);
+        BalancingQueue.push(*It);
       else
         BalancingQueue.push({I, 0});
     }
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index d3b5e97e2bf6b..9ff486cbc2cff 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -479,10 +479,9 @@ using IntBucket = detail::DenseMapPair<int, int>;
 static_assert(std::is_trivially_copyable_v<IntBucket>);
 static_assert(!std::is_trivially_default_constructible_v<IntBucket>);
 
-// Converting a bucket to a std::pair copies both members, so it must not happen
-// implicitly: `const std::pair<int, int> &P = *M.begin();` would bind to a
-// temporary rather than the bucket.
-static_assert(!std::is_convertible_v<IntBucket, std::pair<int, int>>);
+// A bucket converts to a std::pair, so code naming the pair type keeps working.
+static_assert(std::is_convertible_v<IntBucket, std::pair<int, int>>);
+static_assert(std::is_convertible_v<IntBucket, std::pair<const int, int>>);
 
 TEST(DenseMapCustomTest, InsertRange) {
   DenseMap<int, int> M;
@@ -515,8 +514,8 @@ TEST(DenseMapCustomTest, InsertRange) {
   EXPECT_EQ(*MoveMap.find(4)->second, 7);
   EXPECT_EQ(MoveSrc.find(4)->second, nullptr);
 
-  // Converting a bucket explicitly still reaches a vector's element type, and a
-  // std::map's, whose key is const.
+  // The conversion reaches a vector's element type, and a std::map's, whose key
+  // is const.
   DenseMap<int, int> Src({{1, 10}, {2, 20}});
   SmallVector<std::pair<int, int>> Vec(Src.begin(), Src.end());
   EXPECT_THAT(Vec, testing::UnorderedElementsAre(testing::Pair(1, 10),

>From 3a9f9cff5c93c5379d2e6dafa98a02bbddb6c84b Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 9 Sep 2026 00:40:23 -0700
Subject: [PATCH 10/10] fix googletest patch; fix lldbDataFormatters.py

---
 .../ExpressionParser/Clang/ClangASTImporter.cpp        |  5 ++---
 llvm/lib/MCA/InstrBuilder.cpp                          |  2 +-
 llvm/utils/lldbDataFormatters.py                       | 10 ----------
 .../include/gtest/internal/custom/gtest-printers.h     |  9 +++++----
 4 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
index 95664eec647ed..9d6dd2617a945 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
@@ -209,7 +209,7 @@ class DeclContextOverride {
   }
 
   ~DeclContextOverride() {
-    for (const std::pair<clang::Decl *, Backup> &backup : m_backups) {
+    for (const auto &backup : m_backups) {
       backup.first->setDeclContext(backup.second.decl_context);
       backup.first->setLexicalDeclContext(backup.second.lexical_decl_context);
     }
@@ -541,8 +541,7 @@ static bool ImportOffsetMap(clang::ASTContext *dest_ctx,
   // DenseMap with a pointer as the key type, this means we cannot simply
   // iterate over the map, as the order will be non-deterministic.  Instead we
   // have to sort by the offset and then insert in sorted order.
-  typedef llvm::DenseMap<const D *, O> MapType;
-  typedef typename MapType::value_type PairType;
+  typedef std::pair<const D *, O> PairType;
   std::vector<PairType> sorted_items;
   sorted_items.reserve(source_map.size());
   sorted_items.assign(source_map.begin(), source_map.end());
diff --git a/llvm/lib/MCA/InstrBuilder.cpp b/llvm/lib/MCA/InstrBuilder.cpp
index b7819daca5a66..c5169b908b7e8 100644
--- a/llvm/lib/MCA/InstrBuilder.cpp
+++ b/llvm/lib/MCA/InstrBuilder.cpp
@@ -185,7 +185,7 @@ static void initializeUsedResources(InstrDesc &ID,
   }
 
   // Identify extra buffers that are consumed through super resources.
-  for (const std::pair<uint64_t, unsigned> &SR : SuperResources) {
+  for (const auto &SR : SuperResources) {
     for (unsigned I = 1, E = NumProcResources; I < E; ++I) {
       if (SM.getResourceBufferSize(I) == -1)
         continue;
diff --git a/llvm/utils/lldbDataFormatters.py b/llvm/utils/lldbDataFormatters.py
index 1899f3acbe21b..d1765f87b52f4 100644
--- a/llvm/utils/lldbDataFormatters.py
+++ b/llvm/utils/lldbDataFormatters.py
@@ -465,16 +465,6 @@ def num_children(self) -> int:
     def get_child_at_index(self, child_index: int) -> lldb.SBValue:
         bucket_index = self.child_buckets[child_index]
         entry = self.valobj.GetValueForExpressionPath(f".Buckets[{bucket_index}]")
-
-        # By default, DenseMap instances use DenseMapPair to hold key-value
-        # entries. When the entry is a DenseMapPair, unwrap it to expose the
-        # children as simple std::pair values.
-        #
-        # This entry type is customizable (a template parameter). For other
-        # types, expose the entry type as is.
-        if entry.type.name.startswith("llvm::detail::DenseMapPair<"):
-            entry = entry.GetChildAtIndex(0)
-
         return entry.Clone(f"[{child_index}]")
 
     def update(self):
diff --git a/third-party/unittest/googletest/include/gtest/internal/custom/gtest-printers.h b/third-party/unittest/googletest/include/gtest/internal/custom/gtest-printers.h
index 06f85f89a5cb5..8a9ce020de20d 100644
--- a/third-party/unittest/googletest/include/gtest/internal/custom/gtest-printers.h
+++ b/third-party/unittest/googletest/include/gtest/internal/custom/gtest-printers.h
@@ -65,13 +65,14 @@ inline void PrintTo(const SmallVectorImpl<char> &S, std::ostream *OS) {
   *OS << ::testing::PrintToString(std::string(S.data(), S.size()));
 }
 
-// DenseMap's entries inherit from std::pair, and should act like pairs.
-// However gTest's provided `PrintTo(pair<K,V>)` template won't deduce K and V
-// because of the needed derived-to-base conversion.
+// gTest's provided `PrintTo(pair<K,V>)` template won't deduce K and V from a
+// DenseMap entry. Print the members rather than converting, which would copy
+// them and so require both to be copyable.
 namespace detail {
 template <typename K, typename V>
 inline void PrintTo(const DenseMapPair<K, V> &Pair, std::ostream *OS) {
-  *OS << ::testing::PrintToString(static_cast<const std::pair<K, V> &>(Pair));
+  *OS << "(" << ::testing::PrintToString(Pair.first) << ", "
+      << ::testing::PrintToString(Pair.second) << ")";
 }
 } // namespace detail
 



More information about the cfe-commits mailing list