[llvm] [InstCombine] Push freeze through non-recurrence PHIs (PR #157678)
Cullen Rhodes via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 9 07:12:57 PDT 2025
https://github.com/c-rhodes created https://github.com/llvm/llvm-project/pull/157678
#157112 adds a test '@fold_phi_noundef_start_value' where we can't currently remove the freeze, even though it's possible. The two places in instcombine that deal with freezes are
'pushFreezeToPreventPoisonFromPropagating' and
'foldFreezeIntoRecurrence'. The former doesn't support PHIs at all and the latter is restricted to recurrences. It doesn't consider this test a recurrence as the BB of the frozen PHI node '%loop.latch' does not dominate either of the BBs ('%loop', '%if.else') of the incoming values.
Therefore, I've updated 'pushFreezeToPreventPoisonFromPropagating' to support pushing the freeze to the incoming PHI values, as long as the BB of the frozen PHI *does not* dominate the BB of the incoming value(s). This fixes the test case added in #157112 and the other test changes look sensible, although perhaps I'm missing some edge cases (?).
The 'match(U.get(), m_Undef())' check for undef PHI incoming value looks necessary to catch the case covered by '@two_undef' test in freeze-phi.ll. Without this check the undef becomes zero which doesnt seem right.
>From 5dd26ff857b8a6873361711fa94a8795c533ad44 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Mon, 8 Sep 2025 10:40:48 +0000
Subject: [PATCH] [InstCombine] Push freeze through non-recurrence PHIs
PR #157112 adds a test '@fold_phi_noundef_start_value' where we can't
currently remove the freeze, even though it's possible. The two places
in instcombine that deal with freezes are
'pushFreezeToPreventPoisonFromPropagating' and
'foldFreezeIntoRecurrence'. The former doesn't support PHIs at all and
the latter is restricted to recurrences. It doesn't consider this test a
recurrence as the BB of the frozen PHI node '%loop.latch' does not
dominate either of the BBs ('%loop', '%if.else') of the incoming values.
Therefore, I've updated 'pushFreezeToPreventPoisonFromPropagating' to
support pushing the freeze to the incoming PHI values, as long as the BB
of the frozen PHI *does not* dominate the BB of the incoming value(s).
This fixes the test case added in #157112 and the other test changes
look sensible, although perhaps I'm missing some edge cases (?).
The 'match(U.get(), m_Undef())' check for undef PHI incoming value looks
necessary to catch the case covered by '@two_undef' test in
freeze-phi.ll. Without this check the undef becomes zero which doesnt
seem right.
---
.../InstCombine/InstructionCombining.cpp | 17 +++++++++++---
.../InstCombine/freeze-landingpad.ll | 5 ++--
.../test/Transforms/InstCombine/freeze-phi.ll | 7 +++---
llvm/test/Transforms/InstCombine/freeze.ll | 23 +++++++++----------
4 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index a74f292524b4d..4c562c5500de1 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -5042,10 +5042,18 @@ InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
// Op1.fr = Freeze(Op1)
// ... = Inst(Op1.fr, NonPoisonOps...)
- auto CanPushFreeze = [](Value *V) {
- if (!isa<Instruction>(V) || isa<PHINode>(V))
+ auto CanPushFreeze = [this](Value *V) {
+ if (!isa<Instruction>(V))
return false;
+ if (auto *PN = dyn_cast<PHINode>(V)) {
+ if (llvm::any_of(PN->incoming_values(), [this, &PN](Use &U) {
+ return DT.dominates(PN->getParent(), PN->getIncomingBlock(U)) ||
+ match(U.get(), m_Undef());
+ }))
+ return false;
+ }
+
// We can't push the freeze through an instruction which can itself create
// poison. If the only source of new poison is flags, we can simply
// strip them (since we know the only use is the freeze and nothing can
@@ -5070,7 +5078,10 @@ InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
return nullptr;
auto *UserI = cast<Instruction>(U->getUser());
- Builder.SetInsertPoint(UserI);
+ if (auto *PN = dyn_cast<PHINode>(UserI))
+ Builder.SetInsertPoint(PN->getIncomingBlock(*U)->getTerminator());
+ else
+ Builder.SetInsertPoint(UserI);
Value *Frozen = Builder.CreateFreeze(V, V->getName() + ".fr");
U->set(Frozen);
continue;
diff --git a/llvm/test/Transforms/InstCombine/freeze-landingpad.ll b/llvm/test/Transforms/InstCombine/freeze-landingpad.ll
index 29167958c857f..259808c046490 100644
--- a/llvm/test/Transforms/InstCombine/freeze-landingpad.ll
+++ b/llvm/test/Transforms/InstCombine/freeze-landingpad.ll
@@ -14,14 +14,13 @@ define i32 @propagate_freeze_in_landingpad() personality ptr null {
; CHECK-NEXT: [[RES1:%.*]] = invoke i32 @foo()
; CHECK-NEXT: to label [[NORMAL_RETURN]] unwind label [[EXCEPTIONAL_RETURN]]
; CHECK: normal_return:
-; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[X]], 1
+; CHECK-NEXT: [[INC]] = add i32 [[X]], 1
; CHECK-NEXT: br label [[INVOKE_BB1]]
; CHECK: exceptional_return:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[X]], [[INVOKE_BB1]] ], [ 0, [[INVOKE_BB2]] ]
; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
-; CHECK-NEXT: [[FR:%.*]] = freeze i32 [[PHI]]
-; CHECK-NEXT: [[RES:%.*]] = shl i32 [[FR]], 1
+; CHECK-NEXT: [[RES:%.*]] = shl i32 [[PHI]], 1
; CHECK-NEXT: ret i32 [[RES]]
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/freeze-phi.ll b/llvm/test/Transforms/InstCombine/freeze-phi.ll
index cdc9a5efe5933..098e6c5f6cdbb 100644
--- a/llvm/test/Transforms/InstCombine/freeze-phi.ll
+++ b/llvm/test/Transforms/InstCombine/freeze-phi.ll
@@ -94,13 +94,14 @@ define i32 @two(i1 %cond, i32 %x, i32 %x2) {
; CHECK-LABEL: @two(
; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
; CHECK: A:
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X:%.*]]
; CHECK-NEXT: br label [[C:%.*]]
; CHECK: B:
+; CHECK-NEXT: [[X2_FR:%.*]] = freeze i32 [[X2:%.*]]
; CHECK-NEXT: br label [[C]]
; CHECK: C:
-; CHECK-NEXT: [[Y:%.*]] = phi i32 [ [[X:%.*]], [[A]] ], [ [[X2:%.*]], [[B]] ]
-; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]]
-; CHECK-NEXT: ret i32 [[Y_FR]]
+; CHECK-NEXT: [[Y:%.*]] = phi i32 [ [[X_FR]], [[A]] ], [ [[X2_FR]], [[B]] ]
+; CHECK-NEXT: ret i32 [[Y]]
;
br i1 %cond, label %A, label %B
A:
diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll
index af5cb0c75537b..91e89b3b5a332 100644
--- a/llvm/test/Transforms/InstCombine/freeze.ll
+++ b/llvm/test/Transforms/InstCombine/freeze.ll
@@ -269,15 +269,15 @@ define void @freeze_dominated_uses_catchswitch(i1 %c, i32 %x) personality ptr @_
; CHECK-NEXT: invoke void @use_i32(i32 0)
; CHECK-NEXT: to label %[[CLEANUP:.*]] unwind label %[[CATCH_DISPATCH:.*]]
; CHECK: [[IF_ELSE]]:
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT: invoke void @use_i32(i32 1)
; CHECK-NEXT: to label %[[CLEANUP]] unwind label %[[CATCH_DISPATCH]]
; CHECK: [[CATCH_DISPATCH]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[IF_THEN]] ], [ [[X]], %[[IF_ELSE]] ]
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[IF_THEN]] ], [ [[X_FR]], %[[IF_ELSE]] ]
; CHECK-NEXT: [[CS:%.*]] = catchswitch within none [label %[[CATCH:.*]], label %catch2] unwind to caller
; CHECK: [[CATCH]]:
; CHECK-NEXT: [[CP:%.*]] = catchpad within [[CS]] [ptr null, i32 64, ptr null]
-; CHECK-NEXT: [[PHI_FREEZE:%.*]] = freeze i32 [[PHI]]
-; CHECK-NEXT: call void @use_i32(i32 [[PHI_FREEZE]]) [ "funclet"(token [[CP]]) ]
+; CHECK-NEXT: call void @use_i32(i32 [[PHI]]) [ "funclet"(token [[CP]]) ]
; CHECK-NEXT: unreachable
; CHECK: [[CATCH2:.*:]]
; CHECK-NEXT: [[CP2:%.*]] = catchpad within [[CS]] [ptr null, i32 64, ptr null]
@@ -384,15 +384,16 @@ define i32 @freeze_phi_followed_by_phi(i1 %c, i32 %y, i32 %z) {
; CHECK-LABEL: define i32 @freeze_phi_followed_by_phi(
; CHECK-SAME: i1 [[C:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[Z_FR:%.*]] = freeze i32 [[Z]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]]
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[JOIN:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: br label %[[JOIN]]
; CHECK: [[JOIN]]:
-; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[Y]], %[[IF]] ], [ [[Z]], %[[ENTRY]] ]
-; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[Z]], %[[IF]] ], [ [[Y]], %[[ENTRY]] ]
-; CHECK-NEXT: [[FR:%.*]] = freeze i32 [[X]]
-; CHECK-NEXT: call void @use_i32(i32 [[FR]])
-; CHECK-NEXT: call void @use_i32(i32 [[FR]])
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[Y_FR]], %[[IF]] ], [ [[Z_FR]], %[[ENTRY]] ]
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[Z_FR]], %[[IF]] ], [ [[Y_FR]], %[[ENTRY]] ]
+; CHECK-NEXT: call void @use_i32(i32 [[X]])
+; CHECK-NEXT: call void @use_i32(i32 [[X]])
; CHECK-NEXT: ret i32 [[PHI]]
;
entry:
@@ -1141,8 +1142,7 @@ exit:
}
; We can remove this freeze as the incoming values to the PHI have the same
-; well-defined start value and the GEP can't produce poison, but this is
-; currently unsupported.
+; well-defined start value and the GEP can't produce poison.
define void @fold_phi_noundef_start_value(ptr noundef %init, i1 %cond.0, i1 %cond.1, i1 %cond.2) {
; CHECK-LABEL: define void @fold_phi_noundef_start_value(
; CHECK-SAME: ptr noundef [[INIT:%.*]], i1 [[COND_0:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
@@ -1156,8 +1156,7 @@ define void @fold_phi_noundef_start_value(ptr noundef %init, i1 %cond.0, i1 %con
; CHECK-NEXT: br label %[[LOOP_LATCH]]
; CHECK: [[LOOP_LATCH]]:
; CHECK-NEXT: [[IV_2:%.*]] = phi ptr [ [[IV_0]], %[[LOOP]] ], [ [[IV_1]], %[[IF_ELSE]] ]
-; CHECK-NEXT: [[IV_2_FR:%.*]] = freeze ptr [[IV_2]]
-; CHECK-NEXT: [[IV_2_FR_INT:%.*]] = ptrtoint ptr [[IV_2_FR]] to i64
+; CHECK-NEXT: [[IV_2_FR_INT:%.*]] = ptrtoint ptr [[IV_2]] to i64
; CHECK-NEXT: [[IV_0_INT:%.*]] = ptrtoint ptr [[IV_0]] to i64
; CHECK-NEXT: [[IDX:%.*]] = sub i64 [[IV_0_INT]], [[IV_2_FR_INT]]
; CHECK-NEXT: [[IV_0_NEXT]] = getelementptr i8, ptr [[IV_0]], i64 [[IDX]]
More information about the llvm-commits
mailing list