[llvm] [InstCombine] Don't sink freeze when the operand has other uses (PR #219110)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 21:52:03 PDT 2026
https://github.com/junfengd-nv created https://github.com/llvm/llvm-project/pull/219110
Fixes #219102
InstCombine can infinite-loop on a `freeze` whose operand has other uses.
`tryToSinkInstruction` sinks the freeze into a unique-successor user block,
then `visitFreeze` -> `freezeOtherUses` moves it back to the operand
definition. If those extra uses are never rewritten (the freeze still does
not dominate them), the two transforms ping-pong forever.
`opt -debug-only=instcombine` shows the same instruction repeatedly:
>From 27872eb0165a0515df811749559e5299d1f5b472 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Wed, 26 Aug 2026 21:00:17 -0700
Subject: [PATCH] Don't sink freeze when the operand has other uses
---
.../InstCombine/InstructionCombining.cpp | 7 +++
.../Transforms/InstCombine/freeze-sink.ll | 46 +++++++++++++++++++
2 files changed, 53 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/freeze-sink.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b877f79a000cf..189c75edaadd2 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -5575,6 +5575,13 @@ bool InstCombinerImpl::tryToSinkInstruction(Instruction *I,
if (isa<AllocaInst>(I))
return false;
+ // Don't sink a freeze if its operand has other uses. freezeOtherUses moves
+ // the freeze up to the operand definition so it can replace those uses;
+ // sinking it back down fights that and can infinite-loop when some uses
+ // (e.g. a phi of an invoke result on the normal edge) are never replaced.
+ if (isa<FreezeInst>(I) && !I->getOperand(0)->hasOneUse())
+ return false;
+
// Do not sink into catchswitch blocks.
if (isa<CatchSwitchInst>(DestBlock->getTerminator()))
return false;
diff --git a/llvm/test/Transforms/InstCombine/freeze-sink.ll b/llvm/test/Transforms/InstCombine/freeze-sink.ll
new file mode 100644
index 0000000000000..c8a867e915fac
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/freeze-sink.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare ptr @get()
+declare void @escape(ptr)
+declare i32 @__gxx_personality_v0(...)
+
+; The invoke result is used by a phi on the normal destination. freezeOtherUses
+; cannot replace that use (the freeze sits after the phis), but sinking would
+; still move the freeze into %use. Those two transforms ping-ponged forever.
+define void @dont_sink_freeze_of_invoke_used_by_phi(i1 %c, ptr %other) personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: define void @dont_sink_freeze_of_invoke_used_by_phi(
+; CHECK-SAME: i1 [[C:%.*]], ptr [[OTHER:%.*]]) personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[P:%.*]] = invoke ptr @get()
+; CHECK-NEXT: to label %[[JOIN:.*]] unwind label %[[LPAD:.*]]
+; CHECK: [[JOIN]]:
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[OTHER]], %[[JOIN]] ]
+; CHECK-NEXT: [[FR:%.*]] = freeze ptr [[P]]
+; CHECK-NEXT: br i1 [[C]], label %[[USE:.*]], label %[[JOIN]]
+; CHECK: [[USE]]:
+; CHECK-NEXT: call void @escape(ptr [[FR]])
+; CHECK-NEXT: call void @escape(ptr [[PHI]])
+; CHECK-NEXT: ret void
+; CHECK: [[LPAD]]:
+; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: ret void
+;
+entry:
+ %p = invoke ptr @get() to label %join unwind label %lpad
+
+join:
+ %phi = phi ptr [ %p, %entry ], [ %other, %join ]
+ %fr = freeze ptr %p
+ br i1 %c, label %use, label %join
+
+use:
+ call void @escape(ptr %fr)
+ call void @escape(ptr %phi)
+ ret void
+
+lpad:
+ %lp = landingpad { ptr, i32 } cleanup
+ ret void
+}
More information about the llvm-commits
mailing list