Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: llvm: backport [RegisterCoalescer] Avoid retrying high-cost joins for live intervals
To:
OpenBSD tech <tech@openbsd.org>, OpenBSD ports <ports@openbsd.org>, robert@openbsd.org
Date:
Wed, 02 Sep 2026 20:13:01 +0200

Download raw body.

Thread
On Wed, 02 Sep 2026 15:38:16 +0200,
Kirill A. Korinsky <kirill@korins.ky> wrote:
> 
> tech@,
> 
> an extreamly long build of libggml which hangs by timeout by naddy's build
> allows to to catch a regression in LLVM which was fixed in upstream.
> 
> libcxx update changed the libggml generated ThinLTO workload enough to
> expose it.
> 
> As fix here a backport of
> https://github.com/llvm/llvm-project/commit/fd9e690cdbeb02832cc36d455bffda9e7e813785
> which allows to build libggml in minutes again.
> 
> I think that we should see some imporvment on bulk time on sparc64 as well,
> maybe on other platforms.
> 
> So, here the diff for both base and ports llvm which I had tested on amd64
> against libggml build.
> 

... and I forgot to add tech@.

Index: src/gnu/llvm/llvm/lib/CodeGen/RegisterCoalescer.cpp
===================================================================
RCS file: /home/cvs/src/gnu/llvm/llvm/lib/CodeGen/RegisterCoalescer.cpp,v
diff -u -p -r1.1.1.6 RegisterCoalescer.cpp
--- src/gnu/llvm/llvm/lib/CodeGen/RegisterCoalescer.cpp	29 May 2026 11:00:41 -0000	1.1.1.6
+++ src/gnu/llvm/llvm/lib/CodeGen/RegisterCoalescer.cpp	2 Sep 2026 12:47:21 -0000
@@ -232,21 +232,26 @@ class RegisterCoalescer : private LiveRa
   void setUndefOnPrunedSubRegUses(LiveInterval &LI, Register Reg,
                                   LaneBitmask PrunedLanes);
 
+  /// Result of attempting to coalesce a copy.
+  /// - Joined: the copy was removed or otherwise fully handled.
+  /// - Deferred: retry after other coalescing may make progress.
+  /// - Rejected: do not retry, either because the copy is not a coalescing
+  ///   candidate or because the join was intentionally rejected.
+  enum class JoinResult { Joined, Deferred, Rejected };
+
   /// Attempt to join intervals corresponding to SrcReg/DstReg, which are the
-  /// src/dst of the copy instruction CopyMI.  This returns true if the copy
-  /// was successfully coalesced away. If it is not currently possible to
-  /// coalesce this interval, but it may be possible if other things get
-  /// coalesced, then it returns true by reference in 'Again'.
-  bool joinCopy(MachineInstr *CopyMI, bool &Again,
-                SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs);
-
-  /// Attempt to join these two intervals.  On failure, this
-  /// returns false.  The output "SrcInt" will not have been modified, so we
-  /// can use this information below to update aliases.
-  bool joinIntervals(CoalescerPair &CP);
+  /// src/dst of the copy instruction CopyMI.
+  JoinResult joinCopy(MachineInstr *CopyMI,
+                      SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs);
+
+  /// Attempt to join these two intervals.  On failure, the output "SrcInt"
+  /// will not have been modified, so we can use this information below to
+  /// update aliases. Returns Deferred when it may be possible to join later,
+  /// or Rejected when retrying should be avoided.
+  JoinResult joinIntervals(CoalescerPair &CP);
 
-  /// Attempt joining two virtual registers. Return true on success.
-  bool joinVirtRegs(CoalescerPair &CP);
+  /// Attempt joining two virtual registers.
+  JoinResult joinVirtRegs(CoalescerPair &CP);
 
   /// If a live interval has many valnos and is coalesced with other
   /// live intervals many times, we regard such live interval as having
@@ -2054,23 +2059,22 @@ void RegisterCoalescer::setUndefOnPruned
   LIS->shrinkToUses(&LI);
 }
 
-bool RegisterCoalescer::joinCopy(
-    MachineInstr *CopyMI, bool &Again,
+RegisterCoalescer::JoinResult RegisterCoalescer::joinCopy(
+    MachineInstr *CopyMI,
     SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs) {
-  Again = false;
   LLVM_DEBUG(dbgs() << LIS->getInstructionIndex(*CopyMI) << '\t' << *CopyMI);
 
   CoalescerPair CP(*TRI);
   if (!CP.setRegisters(CopyMI)) {
     LLVM_DEBUG(dbgs() << "\tNot coalescable.\n");
-    return false;
+    return JoinResult::Rejected;
   }
 
   if (CP.getNewRC()) {
     if (RegClassInfo.getNumAllocatableRegs(CP.getNewRC()) == 0) {
       LLVM_DEBUG(dbgs() << "\tNo " << TRI->getRegClassName(CP.getNewRC())
                         << "are available for allocation\n");
-      return false;
+      return JoinResult::Rejected;
     }
 
     auto SrcRC = MRI->getRegClass(CP.getSrcReg());
@@ -2084,7 +2088,7 @@ bool RegisterCoalescer::joinCopy(
     if (!TRI->shouldCoalesce(CopyMI, SrcRC, SrcIdx, DstRC, DstIdx,
                              CP.getNewRC(), *LIS)) {
       LLVM_DEBUG(dbgs() << "\tSubtarget bailed on coalescing.\n");
-      return false;
+      return JoinResult::Rejected;
     }
   }
 
@@ -2095,7 +2099,7 @@ bool RegisterCoalescer::joinCopy(
     LLVM_DEBUG(dbgs() << "\tCopy is dead.\n");
     DeadDefs.push_back(CopyMI);
     eliminateDeadDefs();
-    return true;
+    return JoinResult::Joined;
   }
 
   // Eliminate undefs.
@@ -2103,9 +2107,9 @@ bool RegisterCoalescer::joinCopy(
     // If this is an IMPLICIT_DEF, leave it alone, but don't try to coalesce.
     if (MachineInstr *UndefMI = eliminateUndefCopy(CopyMI)) {
       if (UndefMI->isImplicitDef())
-        return false;
+        return JoinResult::Rejected;
       deleteInstr(CopyMI);
-      return false; // Not coalescable.
+      return JoinResult::Rejected; // Not coalescable.
     }
   }
 
@@ -2153,7 +2157,7 @@ bool RegisterCoalescer::joinCopy(
       LLVM_DEBUG(dbgs() << "\tMerged values:          " << LI << '\n');
     }
     deleteInstr(CopyMI);
-    return true;
+    return JoinResult::Joined;
   }
 
   // Enforce policies.
@@ -2166,10 +2170,10 @@ bool RegisterCoalescer::joinCopy(
       // the copy instead if it is cheap.
       bool IsDefCopy = false;
       if (reMaterializeDef(CP, CopyMI, IsDefCopy))
-        return true;
+        return JoinResult::Joined;
       if (IsDefCopy)
-        Again = true; // May be possible to coalesce later.
-      return false;
+        return JoinResult::Deferred; // May be possible to coalesce later.
+      return JoinResult::Rejected;
     }
   } else {
     // When possible, let DstReg be the larger interval.
@@ -2194,17 +2198,18 @@ bool RegisterCoalescer::joinCopy(
   ShrinkMask = LaneBitmask::getNone();
   ShrinkMainRange = false;
 
-  // Okay, attempt to join these two intervals.  On failure, this returns false.
-  // Otherwise, if one of the intervals being joined is a physreg, this method
-  // always canonicalizes DstInt to be it.  The output "SrcInt" will not have
-  // been modified, so we can use this information below to update aliases.
-  if (!joinIntervals(CP)) {
+  // Okay, attempt to join these two intervals.  If one of the intervals being
+  // joined is a physreg and the join succeeds, this method always canonicalizes
+  // DstInt to be it.  The output "SrcInt" will not have been modified, so we
+  // can use this information below to update aliases.
+  JoinResult Result = joinIntervals(CP);
+  if (Result != JoinResult::Joined) {
     // Coalescing failed.
 
     // Try rematerializing the definition of the source if it is cheap.
     bool IsDefCopy = false;
     if (reMaterializeDef(CP, CopyMI, IsDefCopy))
-      return true;
+      return JoinResult::Joined;
 
     // If we can eliminate the copy without merging the live segments, do so
     // now.
@@ -2222,7 +2227,7 @@ bool RegisterCoalescer::joinCopy(
           LLVM_DEBUG(dbgs() << "\t\tshrunk:   " << DstLI << '\n');
         }
         LLVM_DEBUG(dbgs() << "\tTrivial!\n");
-        return true;
+        return JoinResult::Joined;
       }
     }
 
@@ -2230,12 +2235,16 @@ bool RegisterCoalescer::joinCopy(
     // its predecessor.
     if (!CP.isPartial() && !CP.isPhys())
       if (removePartialRedundancy(CP, *CopyMI))
-        return true;
+        return JoinResult::Joined;
 
     // Otherwise, we are unable to join the intervals.
     LLVM_DEBUG(dbgs() << "\tInterference!\n");
-    Again = true; // May be possible to coalesce later.
-    return false;
+    // A high-cost interval is already too expensive to retry.  Keeping the copy
+    // in WorkList would make every subsequent successful join rescan it again,
+    // which can dominate compile time.
+    if (Result == JoinResult::Deferred)
+      LLVM_DEBUG(dbgs() << "\tWill retry later.\n");
+    return Result;
   }
 
   // Coalescing to a virtual register that is of a sub-register class of the
@@ -2307,7 +2316,7 @@ bool RegisterCoalescer::joinCopy(
   });
 
   ++numJoins;
-  return true;
+  return JoinResult::Joined;
 }
 
 bool RegisterCoalescer::joinReservedPhysReg(CoalescerPair &CP) {
@@ -3698,7 +3707,8 @@ bool RegisterCoalescer::isHighCostLiveIn
   return true;
 }
 
-bool RegisterCoalescer::joinVirtRegs(CoalescerPair &CP) {
+RegisterCoalescer::JoinResult
+RegisterCoalescer::joinVirtRegs(CoalescerPair &CP) {
   SmallVector<VNInfo *, 16> NewVNInfo;
   LiveInterval &RHS = LIS->getInterval(CP.getSrcReg());
   LiveInterval &LHS = LIS->getInterval(CP.getDstReg());
@@ -3710,17 +3720,24 @@ bool RegisterCoalescer::joinVirtRegs(Coa
 
   LLVM_DEBUG(dbgs() << "\t\tRHS = " << RHS << "\n\t\tLHS = " << LHS << '\n');
 
-  if (isHighCostLiveInterval(LHS) || isHighCostLiveInterval(RHS))
-    return false;
+  if (isHighCostLiveInterval(LHS) || isHighCostLiveInterval(RHS)) {
+    LLVM_DEBUG(dbgs() << "\t\tHigh-cost live interval: RHS valnos="
+                      << RHS.valnos.size() << ", segments=" << RHS.size()
+                      << "; LHS valnos=" << LHS.valnos.size()
+                      << ", segments=" << LHS.size() << '\n');
+    return JoinResult::Rejected;
+  }
 
-  // First compute NewVNInfo and the simple value mappings.
-  // Detect impossible conflicts early.
+  // First compute NewVNInfo and the simple value mappings. Conflicts found
+  // here only reject this attempt; subsequent coalescing may still make the
+  // same copy joinable, so keep it deferred.
   if (!LHSVals.mapValues(RHSVals) || !RHSVals.mapValues(LHSVals))
-    return false;
+    return JoinResult::Deferred;
 
   // Some conflicts can only be resolved after all values have been mapped.
+  // As above, unresolved conflicts are retryable interference.
   if (!LHSVals.resolveConflicts(RHSVals) || !RHSVals.resolveConflicts(LHSVals))
-    return false;
+    return JoinResult::Deferred;
 
   // All clear, the live ranges can be merged.
   if (RHS.hasSubRanges() || LHS.hasSubRanges()) {
@@ -3875,11 +3892,14 @@ bool RegisterCoalescer::joinVirtRegs(Coa
     LIS->extendToIndices((LiveRange &)LHS, EndPoints);
   }
 
-  return true;
+  return JoinResult::Joined;
 }
 
-bool RegisterCoalescer::joinIntervals(CoalescerPair &CP) {
-  return CP.isPhys() ? joinReservedPhysReg(CP) : joinVirtRegs(CP);
+RegisterCoalescer::JoinResult
+RegisterCoalescer::joinIntervals(CoalescerPair &CP) {
+  if (CP.isPhys())
+    return joinReservedPhysReg(CP) ? JoinResult::Joined : JoinResult::Deferred;
+  return joinVirtRegs(CP);
 }
 
 void RegisterCoalescer::buildVRegToDbgValueMap(MachineFunction &MF) {
@@ -4095,10 +4115,9 @@ bool RegisterCoalescer::copyCoalesceWork
       MI = nullptr;
       continue;
     }
-    bool Again = false;
-    bool Success = joinCopy(MI, Again, CurrentErasedInstrs);
-    Progress |= Success;
-    if (Success || !Again)
+    JoinResult Result = joinCopy(MI, CurrentErasedInstrs);
+    Progress |= Result == JoinResult::Joined;
+    if (Result != JoinResult::Deferred)
       MI = nullptr;
   }
   // Clear instructions not recorded in `ErasedInstrs` but erased.
Index: ports/devel/llvm/22/Makefile
===================================================================
RCS file: /home/cvs/ports/devel/llvm/22/Makefile,v
diff -u -p -r1.25 Makefile
--- ports/devel/llvm/22/Makefile	21 Aug 2026 13:27:47 -0000	1.25
+++ ports/devel/llvm/22/Makefile	2 Sep 2026 13:29:36 -0000
@@ -2,7 +2,7 @@ LLVM_MAJOR =	22
 LLVM_VERSION =	${LLVM_MAJOR}.1.8
 LLVM_PKGSPEC =	>=22,<23
 
-REVISION =	6
+REVISION =	7
 
 SHARED_LIBS +=	LLVM		0.0 \
 		LTO		0.0 \
Index: ports/devel/llvm/22/patches/patch-llvm_lib_CodeGen_RegisterCoalescer_cpp
===================================================================
RCS file: ports/devel/llvm/22/patches/patch-llvm_lib_CodeGen_RegisterCoalescer_cpp
diff -N ports/devel/llvm/22/patches/patch-llvm_lib_CodeGen_RegisterCoalescer_cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ports/devel/llvm/22/patches/patch-llvm_lib_CodeGen_RegisterCoalescer_cpp	2 Sep 2026 13:32:16 -0000
@@ -0,0 +1,262 @@
+Backport: [RegisterCoalescer] Avoid retrying high-cost joins for live intervals
+https://github.com/llvm/llvm-project/commit/fd9e690cdbeb02832cc36d455bffda9e7e813785
+
+Index: llvm/lib/CodeGen/RegisterCoalescer.cpp
+--- llvm/lib/CodeGen/RegisterCoalescer.cpp.orig
++++ llvm/lib/CodeGen/RegisterCoalescer.cpp
+@@ -232,21 +232,26 @@ class RegisterCoalescer : private LiveRangeEdit::Deleg
+   void setUndefOnPrunedSubRegUses(LiveInterval &LI, Register Reg,
+                                   LaneBitmask PrunedLanes);
+ 
++  /// Result of attempting to coalesce a copy.
++  /// - Joined: the copy was removed or otherwise fully handled.
++  /// - Deferred: retry after other coalescing may make progress.
++  /// - Rejected: do not retry, either because the copy is not a coalescing
++  ///   candidate or because the join was intentionally rejected.
++  enum class JoinResult { Joined, Deferred, Rejected };
++
+   /// Attempt to join intervals corresponding to SrcReg/DstReg, which are the
+-  /// src/dst of the copy instruction CopyMI.  This returns true if the copy
+-  /// was successfully coalesced away. If it is not currently possible to
+-  /// coalesce this interval, but it may be possible if other things get
+-  /// coalesced, then it returns true by reference in 'Again'.
+-  bool joinCopy(MachineInstr *CopyMI, bool &Again,
+-                SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs);
++  /// src/dst of the copy instruction CopyMI.
++  JoinResult joinCopy(MachineInstr *CopyMI,
++                      SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs);
+ 
+-  /// Attempt to join these two intervals.  On failure, this
+-  /// returns false.  The output "SrcInt" will not have been modified, so we
+-  /// can use this information below to update aliases.
+-  bool joinIntervals(CoalescerPair &CP);
++  /// Attempt to join these two intervals.  On failure, the output "SrcInt"
++  /// will not have been modified, so we can use this information below to
++  /// update aliases. Returns Deferred when it may be possible to join later,
++  /// or Rejected when retrying should be avoided.
++  JoinResult joinIntervals(CoalescerPair &CP);
+ 
+-  /// Attempt joining two virtual registers. Return true on success.
+-  bool joinVirtRegs(CoalescerPair &CP);
++  /// Attempt joining two virtual registers.
++  JoinResult joinVirtRegs(CoalescerPair &CP);
+ 
+   /// If a live interval has many valnos and is coalesced with other
+   /// live intervals many times, we regard such live interval as having
+@@ -2054,23 +2059,22 @@ void RegisterCoalescer::setUndefOnPrunedSubRegUses(Liv
+   LIS->shrinkToUses(&LI);
+ }
+ 
+-bool RegisterCoalescer::joinCopy(
+-    MachineInstr *CopyMI, bool &Again,
++RegisterCoalescer::JoinResult RegisterCoalescer::joinCopy(
++    MachineInstr *CopyMI,
+     SmallPtrSetImpl<MachineInstr *> &CurrentErasedInstrs) {
+-  Again = false;
+   LLVM_DEBUG(dbgs() << LIS->getInstructionIndex(*CopyMI) << '\t' << *CopyMI);
+ 
+   CoalescerPair CP(*TRI);
+   if (!CP.setRegisters(CopyMI)) {
+     LLVM_DEBUG(dbgs() << "\tNot coalescable.\n");
+-    return false;
++    return JoinResult::Rejected;
+   }
+ 
+   if (CP.getNewRC()) {
+     if (RegClassInfo.getNumAllocatableRegs(CP.getNewRC()) == 0) {
+       LLVM_DEBUG(dbgs() << "\tNo " << TRI->getRegClassName(CP.getNewRC())
+                         << "are available for allocation\n");
+-      return false;
++      return JoinResult::Rejected;
+     }
+ 
+     auto SrcRC = MRI->getRegClass(CP.getSrcReg());
+@@ -2084,7 +2088,7 @@ bool RegisterCoalescer::joinCopy(
+     if (!TRI->shouldCoalesce(CopyMI, SrcRC, SrcIdx, DstRC, DstIdx,
+                              CP.getNewRC(), *LIS)) {
+       LLVM_DEBUG(dbgs() << "\tSubtarget bailed on coalescing.\n");
+-      return false;
++      return JoinResult::Rejected;
+     }
+   }
+ 
+@@ -2095,7 +2099,7 @@ bool RegisterCoalescer::joinCopy(
+     LLVM_DEBUG(dbgs() << "\tCopy is dead.\n");
+     DeadDefs.push_back(CopyMI);
+     eliminateDeadDefs();
+-    return true;
++    return JoinResult::Joined;
+   }
+ 
+   // Eliminate undefs.
+@@ -2103,9 +2107,9 @@ bool RegisterCoalescer::joinCopy(
+     // If this is an IMPLICIT_DEF, leave it alone, but don't try to coalesce.
+     if (MachineInstr *UndefMI = eliminateUndefCopy(CopyMI)) {
+       if (UndefMI->isImplicitDef())
+-        return false;
++        return JoinResult::Rejected;
+       deleteInstr(CopyMI);
+-      return false; // Not coalescable.
++      return JoinResult::Rejected; // Not coalescable.
+     }
+   }
+ 
+@@ -2153,7 +2157,7 @@ bool RegisterCoalescer::joinCopy(
+       LLVM_DEBUG(dbgs() << "\tMerged values:          " << LI << '\n');
+     }
+     deleteInstr(CopyMI);
+-    return true;
++    return JoinResult::Joined;
+   }
+ 
+   // Enforce policies.
+@@ -2166,10 +2170,10 @@ bool RegisterCoalescer::joinCopy(
+       // the copy instead if it is cheap.
+       bool IsDefCopy = false;
+       if (reMaterializeDef(CP, CopyMI, IsDefCopy))
+-        return true;
++        return JoinResult::Joined;
+       if (IsDefCopy)
+-        Again = true; // May be possible to coalesce later.
+-      return false;
++        return JoinResult::Deferred; // May be possible to coalesce later.
++      return JoinResult::Rejected;
+     }
+   } else {
+     // When possible, let DstReg be the larger interval.
+@@ -2194,17 +2198,18 @@ bool RegisterCoalescer::joinCopy(
+   ShrinkMask = LaneBitmask::getNone();
+   ShrinkMainRange = false;
+ 
+-  // Okay, attempt to join these two intervals.  On failure, this returns false.
+-  // Otherwise, if one of the intervals being joined is a physreg, this method
+-  // always canonicalizes DstInt to be it.  The output "SrcInt" will not have
+-  // been modified, so we can use this information below to update aliases.
+-  if (!joinIntervals(CP)) {
++  // Okay, attempt to join these two intervals.  If one of the intervals being
++  // joined is a physreg and the join succeeds, this method always canonicalizes
++  // DstInt to be it.  The output "SrcInt" will not have been modified, so we
++  // can use this information below to update aliases.
++  JoinResult Result = joinIntervals(CP);
++  if (Result != JoinResult::Joined) {
+     // Coalescing failed.
+ 
+     // Try rematerializing the definition of the source if it is cheap.
+     bool IsDefCopy = false;
+     if (reMaterializeDef(CP, CopyMI, IsDefCopy))
+-      return true;
++      return JoinResult::Joined;
+ 
+     // If we can eliminate the copy without merging the live segments, do so
+     // now.
+@@ -2222,7 +2227,7 @@ bool RegisterCoalescer::joinCopy(
+           LLVM_DEBUG(dbgs() << "\t\tshrunk:   " << DstLI << '\n');
+         }
+         LLVM_DEBUG(dbgs() << "\tTrivial!\n");
+-        return true;
++        return JoinResult::Joined;
+       }
+     }
+ 
+@@ -2230,12 +2235,16 @@ bool RegisterCoalescer::joinCopy(
+     // its predecessor.
+     if (!CP.isPartial() && !CP.isPhys())
+       if (removePartialRedundancy(CP, *CopyMI))
+-        return true;
++        return JoinResult::Joined;
+ 
+     // Otherwise, we are unable to join the intervals.
+     LLVM_DEBUG(dbgs() << "\tInterference!\n");
+-    Again = true; // May be possible to coalesce later.
+-    return false;
++    // A high-cost interval is already too expensive to retry.  Keeping the copy
++    // in WorkList would make every subsequent successful join rescan it again,
++    // which can dominate compile time.
++    if (Result == JoinResult::Deferred)
++      LLVM_DEBUG(dbgs() << "\tWill retry later.\n");
++    return Result;
+   }
+ 
+   // Coalescing to a virtual register that is of a sub-register class of the
+@@ -2307,7 +2316,7 @@ bool RegisterCoalescer::joinCopy(
+   });
+ 
+   ++numJoins;
+-  return true;
++  return JoinResult::Joined;
+ }
+ 
+ bool RegisterCoalescer::joinReservedPhysReg(CoalescerPair &CP) {
+@@ -3698,7 +3707,8 @@ bool RegisterCoalescer::isHighCostLiveInterval(LiveInt
+   return true;
+ }
+ 
+-bool RegisterCoalescer::joinVirtRegs(CoalescerPair &CP) {
++RegisterCoalescer::JoinResult
++RegisterCoalescer::joinVirtRegs(CoalescerPair &CP) {
+   SmallVector<VNInfo *, 16> NewVNInfo;
+   LiveInterval &RHS = LIS->getInterval(CP.getSrcReg());
+   LiveInterval &LHS = LIS->getInterval(CP.getDstReg());
+@@ -3710,17 +3720,24 @@ bool RegisterCoalescer::joinVirtRegs(CoalescerPair &CP
+ 
+   LLVM_DEBUG(dbgs() << "\t\tRHS = " << RHS << "\n\t\tLHS = " << LHS << '\n');
+ 
+-  if (isHighCostLiveInterval(LHS) || isHighCostLiveInterval(RHS))
+-    return false;
++  if (isHighCostLiveInterval(LHS) || isHighCostLiveInterval(RHS)) {
++    LLVM_DEBUG(dbgs() << "\t\tHigh-cost live interval: RHS valnos="
++                      << RHS.valnos.size() << ", segments=" << RHS.size()
++                      << "; LHS valnos=" << LHS.valnos.size()
++                      << ", segments=" << LHS.size() << '\n');
++    return JoinResult::Rejected;
++  }
+ 
+-  // First compute NewVNInfo and the simple value mappings.
+-  // Detect impossible conflicts early.
++  // First compute NewVNInfo and the simple value mappings. Conflicts found
++  // here only reject this attempt; subsequent coalescing may still make the
++  // same copy joinable, so keep it deferred.
+   if (!LHSVals.mapValues(RHSVals) || !RHSVals.mapValues(LHSVals))
+-    return false;
++    return JoinResult::Deferred;
+ 
+   // Some conflicts can only be resolved after all values have been mapped.
++  // As above, unresolved conflicts are retryable interference.
+   if (!LHSVals.resolveConflicts(RHSVals) || !RHSVals.resolveConflicts(LHSVals))
+-    return false;
++    return JoinResult::Deferred;
+ 
+   // All clear, the live ranges can be merged.
+   if (RHS.hasSubRanges() || LHS.hasSubRanges()) {
+@@ -3875,11 +3892,14 @@ bool RegisterCoalescer::joinVirtRegs(CoalescerPair &CP
+     LIS->extendToIndices((LiveRange &)LHS, EndPoints);
+   }
+ 
+-  return true;
++  return JoinResult::Joined;
+ }
+ 
+-bool RegisterCoalescer::joinIntervals(CoalescerPair &CP) {
+-  return CP.isPhys() ? joinReservedPhysReg(CP) : joinVirtRegs(CP);
++RegisterCoalescer::JoinResult
++RegisterCoalescer::joinIntervals(CoalescerPair &CP) {
++  if (CP.isPhys())
++    return joinReservedPhysReg(CP) ? JoinResult::Joined : JoinResult::Deferred;
++  return joinVirtRegs(CP);
+ }
+ 
+ void RegisterCoalescer::buildVRegToDbgValueMap(MachineFunction &MF) {
+@@ -4095,10 +4115,9 @@ bool RegisterCoalescer::copyCoalesceWorkList(
+       MI = nullptr;
+       continue;
+     }
+-    bool Again = false;
+-    bool Success = joinCopy(MI, Again, CurrentErasedInstrs);
+-    Progress |= Success;
+-    if (Success || !Again)
++    JoinResult Result = joinCopy(MI, CurrentErasedInstrs);
++    Progress |= Result == JoinResult::Joined;
++    if (Result != JoinResult::Deferred)
+       MI = nullptr;
+   }
+   // Clear instructions not recorded in `ErasedInstrs` but erased.


-- 
wbr, Kirill