[llvm] [InstCombine] Allow freezing multiple operands (PR #154336)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 25 02:07:30 PDT 2025
https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/154336
>From 82f3231dfbe43a0b2060e62b5feb61826eb5a50a Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Tue, 19 Aug 2025 14:37:40 +0200
Subject: [PATCH 1/3] [InstCombine] Allow freezing multiple operands
InstCombine tries to convert `freeze(inst(op))` to `inst(freeze(op))`.
Currently, this is limited to the case where a single operand needs
to be frozen, and all other operands are guaranteed non-poison.
This patch allows the transform even if multiple operands need to be
frozen. The existing limitation makes sure that we do not increase
the total number of freezes, but it also means that that we may
fail to eliminate freezes (via poison flag dropping) and may prevent
optimizations (as analysis generally can't look past freeze).
Overall, I believe that aggressively pushing freezes upwards is more
beneficial than harmful.
This is the middle-end version of #145939 in DAGCombine (which is
currently reverted for SDAG-specific reasons).
---
.../InstCombine/InstructionCombining.cpp | 32 +++----
.../Transforms/InstCombine/freeze-fp-ops.ll | 5 +-
llvm/test/Transforms/InstCombine/freeze.ll | 95 +++++++++----------
llvm/test/Transforms/InstCombine/icmp.ll | 7 +-
llvm/test/Transforms/InstCombine/nsw.ll | 7 +-
llvm/test/Transforms/InstCombine/select.ll | 18 ++--
.../sub-of-negatible-inseltpoison.ll | 10 +-
.../InstCombine/sub-of-negatible.ll | 10 +-
.../InstCombine/urem-via-cmp-select.ll | 7 +-
.../LoopVectorize/forked-pointers.ll | 15 +--
llvm/test/Transforms/PGOProfile/chr.ll | 28 +++---
.../X86/vector-reductions-logical.ll | 12 +--
12 files changed, 122 insertions(+), 124 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 5ee3bb1abe86e..17bbeefb8be7e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4961,14 +4961,11 @@ Instruction *InstCombinerImpl::visitLandingPadInst(LandingPadInst &LI) {
Value *
InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
// Try to push freeze through instructions that propagate but don't produce
- // poison as far as possible. If an operand of freeze follows three
- // conditions 1) one-use, 2) does not produce poison, and 3) has all but one
- // guaranteed-non-poison operands then push the freeze through to the one
- // operand that is not guaranteed non-poison. The actual transform is as
- // follows.
+ // poison as far as possible. If an operand of freeze is one-use and does
+ // not produce poison then push the freeze through to the operands that are
+ // not guaranteed non-poison. The actual transform is as follows.
// Op1 = ... ; Op1 can be posion
- // Op0 = Inst(Op1, NonPoisonOps...) ; Op0 has only one use and only have
- // ; single guaranteed-non-poison operands
+ // Op0 = Inst(Op1, NonPoisonOps...) ; Op0 has only one use
// ... = Freeze(Op0)
// =>
// Op1 = ...
@@ -4994,29 +4991,24 @@ InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
// If operand is guaranteed not to be poison, there is no need to add freeze
// to the operand. So we first find the operand that is not guaranteed to be
// poison.
- Value *MaybePoisonOperand = nullptr;
+ SmallSetVector<Value *, 4> MaybePoisonOperands;
for (Value *V : OrigOpInst->operands()) {
- if (isa<MetadataAsValue>(V) || isGuaranteedNotToBeUndefOrPoison(V) ||
- // Treat identical operands as a single operand.
- (MaybePoisonOperand && MaybePoisonOperand == V))
+ if (isa<MetadataAsValue>(V) || isGuaranteedNotToBeUndefOrPoison(V))
continue;
- if (!MaybePoisonOperand)
- MaybePoisonOperand = V;
- else
- return nullptr;
+ MaybePoisonOperands.insert(V);
}
OrigOpInst->dropPoisonGeneratingAnnotations();
// If all operands are guaranteed to be non-poison, we can drop freeze.
- if (!MaybePoisonOperand)
+ if (MaybePoisonOperands.empty())
return OrigOp;
Builder.SetInsertPoint(OrigOpInst);
- Value *FrozenMaybePoisonOperand = Builder.CreateFreeze(
- MaybePoisonOperand, MaybePoisonOperand->getName() + ".fr");
-
- OrigOpInst->replaceUsesOfWith(MaybePoisonOperand, FrozenMaybePoisonOperand);
+ for (Value *V : MaybePoisonOperands) {
+ Value *Frozen = Builder.CreateFreeze(V, V->getName() + ".fr");
+ OrigOpInst->replaceUsesOfWith(V, Frozen);
+ }
return OrigOp;
}
diff --git a/llvm/test/Transforms/InstCombine/freeze-fp-ops.ll b/llvm/test/Transforms/InstCombine/freeze-fp-ops.ll
index 15753ca3fac3d..08f81e6db9bb5 100644
--- a/llvm/test/Transforms/InstCombine/freeze-fp-ops.ll
+++ b/llvm/test/Transforms/InstCombine/freeze-fp-ops.ll
@@ -163,8 +163,9 @@ define float @freeze_sqrt(float %arg) {
define float @freeze_powi(float %arg0, i32 %arg1) {
; CHECK-LABEL: define float @freeze_powi(
; CHECK-SAME: float [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[OP:%.*]] = call float @llvm.powi.f32.i32(float [[ARG0]], i32 [[ARG1]])
-; CHECK-NEXT: [[FREEZE:%.*]] = freeze float [[OP]]
+; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze float [[ARG0]]
+; CHECK-NEXT: [[ARG1_FR:%.*]] = freeze i32 [[ARG1]]
+; CHECK-NEXT: [[FREEZE:%.*]] = call float @llvm.powi.f32.i32(float [[ARG0_FR]], i32 [[ARG1_FR]])
; CHECK-NEXT: ret float [[FREEZE]]
;
%op = call float @llvm.powi.f32.i32(float %arg0, i32 %arg1)
diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll
index ff234bb09336d..b29421a655fa8 100644
--- a/llvm/test/Transforms/InstCombine/freeze.ll
+++ b/llvm/test/Transforms/InstCombine/freeze.ll
@@ -106,11 +106,11 @@ define <3 x i4> @partial_undef_vec() {
; Move the freeze forward to prevent poison from spreading.
define i32 @early_freeze_test1(i32 %x, i32 %y) {
-; CHECK-LABEL: define i32 @early_freeze_test1(
-; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[V1:%.*]] = add i32 [[X]], [[Y]]
-; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1]]
-; CHECK-NEXT: [[V2:%.*]] = shl i32 [[V1_FR]], 1
+; CHECK-LABEL: @early_freeze_test1(
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y:%.*]]
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[X_FR]], [[Y_FR]]
+; CHECK-NEXT: [[V2:%.*]] = shl i32 [[V1]], 1
; CHECK-NEXT: [[V3:%.*]] = and i32 [[V2]], 2
; CHECK-NEXT: ret i32 [[V3]]
;
@@ -153,9 +153,8 @@ define i32 @early_freeze_test3(i32 %v1) {
}
define i32 @early_freeze_test4(i32 %v1) {
-; CHECK-LABEL: define i32 @early_freeze_test4(
-; CHECK-SAME: i32 [[V1:%.*]]) {
-; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1]]
+; CHECK-LABEL: @early_freeze_test4(
+; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1:%.*]]
; CHECK-NEXT: [[V2:%.*]] = mul i32 [[V1_FR]], [[V1_FR]]
; CHECK-NEXT: ret i32 [[V2]]
;
@@ -938,21 +937,20 @@ exit: ; preds = %loop
}
; The recurrence for the GEP offset can't produce poison so the freeze should
-; be pushed through to the ptr, but this is not currently supported.
+; be pushed through to the ptr.
define void @fold_phi_gep_phi_offset(ptr %init, ptr %end, i64 noundef %n) {
-; CHECK-LABEL: define void @fold_phi_gep_phi_offset(
-; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 noundef [[N:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[INIT]], %[[ENTRY]] ], [ [[I_NEXT_FR:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-LABEL: @fold_phi_gep_phi_offset(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = freeze ptr [[INIT:%.*]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[TMP0]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[N:%.*]], [[ENTRY]] ], [ [[OFF_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3
-; CHECK-NEXT: [[I_NEXT:%.*]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]]
-; CHECK-NEXT: [[I_NEXT_FR]] = freeze ptr [[I_NEXT]]
-; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT_FR]], [[END]]
-; CHECK-NEXT: br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]]
+; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT]], [[END:%.*]]
+; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
@@ -971,22 +969,21 @@ exit: ; preds = %loop
ret void
}
-; Offset is still guaranteed not to be poison, so the freeze could be moved
-; here if we strip inbounds from the GEP, but this is not currently supported.
+; Offset is still guaranteed not to be poison, so the freeze can be moved
+; here if we strip inbounds from the GEP.
define void @fold_phi_gep_inbounds_phi_offset(ptr %init, ptr %end, i64 noundef %n) {
-; CHECK-LABEL: define void @fold_phi_gep_inbounds_phi_offset(
-; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 noundef [[N:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[INIT]], %[[ENTRY]] ], [ [[I_NEXT_FR:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-LABEL: @fold_phi_gep_inbounds_phi_offset(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = freeze ptr [[INIT:%.*]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[TMP0]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[N:%.*]], [[ENTRY]] ], [ [[OFF_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3
-; CHECK-NEXT: [[I_NEXT:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 [[OFF_NEXT]]
-; CHECK-NEXT: [[I_NEXT_FR]] = freeze ptr [[I_NEXT]]
-; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT_FR]], [[END]]
-; CHECK-NEXT: br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]]
+; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT]], [[END:%.*]]
+; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
@@ -1005,21 +1002,21 @@ exit: ; preds = %loop
ret void
}
-; GEP can produce poison, check freeze isn't moved.
-define void @cant_fold_phi_gep_phi_offset(ptr %init, ptr %end, i64 %n) {
-; CHECK-LABEL: define void @cant_fold_phi_gep_phi_offset(
-; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[INIT]], %[[ENTRY]] ], [ [[I_NEXT_FR:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ]
+; Same as previous, but also requires freezing %n.
+define void @fold_phi_gep_phi_offset_multiple(ptr %init, ptr %end, i64 %n) {
+; CHECK-LABEL: @fold_phi_gep_phi_offset_multiple(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = freeze ptr [[INIT:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i64 [[N:%.*]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[TMP0]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[TMP1]], [[ENTRY]] ], [ [[OFF_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3
-; CHECK-NEXT: [[I_NEXT:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 [[OFF_NEXT]]
-; CHECK-NEXT: [[I_NEXT_FR]] = freeze ptr [[I_NEXT]]
-; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT_FR]], [[END]]
-; CHECK-NEXT: br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]]
+; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[I_NEXT]], [[END:%.*]]
+; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 0ee875aed3d0c..0faa7da482ef2 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -5838,10 +5838,9 @@ entry:
define i1 @icmp_freeze_sext(i16 %x, i16 %y) {
; CHECK-LABEL: define i1 @icmp_freeze_sext(
; CHECK-SAME: i16 [[X:%.*]], i16 [[Y:%.*]]) {
-; CHECK-NEXT: [[CMP1:%.*]] = icmp uge i16 [[X]], [[Y]]
-; CHECK-NEXT: [[CMP1_FR:%.*]] = freeze i1 [[CMP1]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[Y]], 0
-; CHECK-NEXT: [[CMP2:%.*]] = or i1 [[TMP1]], [[CMP1_FR]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i16 [[Y]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i16 [[X]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp uge i16 [[X_FR]], [[Y_FR]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp uge i16 %x, %y
diff --git a/llvm/test/Transforms/InstCombine/nsw.ll b/llvm/test/Transforms/InstCombine/nsw.ll
index b00f2e58add78..4615ac0ec1ffb 100644
--- a/llvm/test/Transforms/InstCombine/nsw.ll
+++ b/llvm/test/Transforms/InstCombine/nsw.ll
@@ -255,9 +255,10 @@ define i32 @sub_sub1_nsw_nsw(i32 %a, i32 %b, i32 %c) {
define i8 @neg_nsw_freeze(i8 %a1, i8 %a2) {
; CHECK-LABEL: @neg_nsw_freeze(
-; CHECK-NEXT: [[A_NEG:%.*]] = sub nsw i8 [[A2:%.*]], [[A1:%.*]]
-; CHECK-NEXT: [[FR_NEG:%.*]] = freeze i8 [[A_NEG]]
-; CHECK-NEXT: ret i8 [[FR_NEG]]
+; CHECK-NEXT: [[A1_FR:%.*]] = freeze i8 [[A1:%.*]]
+; CHECK-NEXT: [[A2_FR:%.*]] = freeze i8 [[A2:%.*]]
+; CHECK-NEXT: [[A_NEG:%.*]] = sub i8 [[A2_FR]], [[A1_FR]]
+; CHECK-NEXT: ret i8 [[A_NEG]]
;
%a = sub nsw i8 %a1, %a2
%fr = freeze i8 %a
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index ebfaa1e2198c0..db117aa28cad6 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -2847,7 +2847,8 @@ define void @cond_freeze_multipleuses(i8 %x, i8 %y) {
define i32 @select_freeze_icmp_eq(i32 %x, i32 %y) {
; CHECK-LABEL: define i32 @select_freeze_icmp_eq(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: ret i32 [[Y]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]]
+; CHECK-NEXT: ret i32 [[Y_FR]]
;
%c = icmp eq i32 %x, %y
%c.fr = freeze i1 %c
@@ -2858,7 +2859,8 @@ define i32 @select_freeze_icmp_eq(i32 %x, i32 %y) {
define i32 @select_freeze_icmp_ne(i32 %x, i32 %y) {
; CHECK-LABEL: define i32 @select_freeze_icmp_ne(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: ret i32 [[X]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]]
+; CHECK-NEXT: ret i32 [[X_FR]]
;
%c = icmp ne i32 %x, %y
%c.fr = freeze i1 %c
@@ -2869,9 +2871,9 @@ define i32 @select_freeze_icmp_ne(i32 %x, i32 %y) {
define i32 @select_freeze_icmp_else(i32 %x, i32 %y) {
; CHECK-LABEL: define i32 @select_freeze_icmp_else(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
-; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
-; CHECK-NEXT: [[V:%.*]] = select i1 [[C_FR]], i32 [[X]], i32 [[Y]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]]
+; CHECK-NEXT: [[V:%.*]] = call i32 @llvm.umin.i32(i32 [[X_FR]], i32 [[Y_FR]])
; CHECK-NEXT: ret i32 [[V]]
;
%c = icmp ult i32 %x, %y
@@ -2885,9 +2887,9 @@ declare void @use_i1_i32(i1, i32)
define void @select_freeze_icmp_multuses(i32 %x, i32 %y) {
; CHECK-LABEL: define void @select_freeze_icmp_multuses(
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[X]], [[Y]]
-; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
-; CHECK-NEXT: [[V:%.*]] = select i1 [[C_FR]], i32 [[X]], i32 [[Y]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]]
+; CHECK-NEXT: [[V:%.*]] = freeze i32 [[X]]
+; CHECK-NEXT: [[C_FR:%.*]] = icmp ne i32 [[V]], [[Y_FR]]
; CHECK-NEXT: call void @use_i1_i32(i1 [[C_FR]], i32 [[V]])
; CHECK-NEXT: ret void
;
diff --git a/llvm/test/Transforms/InstCombine/sub-of-negatible-inseltpoison.ll b/llvm/test/Transforms/InstCombine/sub-of-negatible-inseltpoison.ll
index 537497110cd8a..109669e91d22f 100644
--- a/llvm/test/Transforms/InstCombine/sub-of-negatible-inseltpoison.ll
+++ b/llvm/test/Transforms/InstCombine/sub-of-negatible-inseltpoison.ll
@@ -1446,8 +1446,9 @@ define i8 @dont_negate_ordinary_select(i8 %x, i8 %y, i8 %z, i1 %c) {
define i4 @negate_freeze(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: define i4 @negate_freeze(
; CHECK-SAME: i4 [[X:%.*]], i4 [[Y:%.*]], i4 [[Z:%.*]]) {
-; CHECK-NEXT: [[T0_NEG:%.*]] = sub i4 [[Y]], [[X]]
-; CHECK-NEXT: [[T1_NEG:%.*]] = freeze i4 [[T0_NEG]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i4 [[X]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i4 [[Y]]
+; CHECK-NEXT: [[T1_NEG:%.*]] = sub i4 [[Y_FR]], [[X_FR]]
; CHECK-NEXT: [[T2:%.*]] = add i4 [[T1_NEG]], [[Z]]
; CHECK-NEXT: ret i4 [[T2]]
;
@@ -1459,8 +1460,9 @@ define i4 @negate_freeze(i4 %x, i4 %y, i4 %z) {
define i4 @negate_freeze_extrause(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: define i4 @negate_freeze_extrause(
; CHECK-SAME: i4 [[X:%.*]], i4 [[Y:%.*]], i4 [[Z:%.*]]) {
-; CHECK-NEXT: [[T0:%.*]] = sub i4 [[X]], [[Y]]
-; CHECK-NEXT: [[T1:%.*]] = freeze i4 [[T0]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i4 [[X]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i4 [[Y]]
+; CHECK-NEXT: [[T1:%.*]] = sub i4 [[X_FR]], [[Y_FR]]
; CHECK-NEXT: call void @use4(i4 [[T1]])
; CHECK-NEXT: [[T2:%.*]] = sub i4 [[Z]], [[T1]]
; CHECK-NEXT: ret i4 [[T2]]
diff --git a/llvm/test/Transforms/InstCombine/sub-of-negatible.ll b/llvm/test/Transforms/InstCombine/sub-of-negatible.ll
index 3a91c14e8ba10..aad006de0e361 100644
--- a/llvm/test/Transforms/InstCombine/sub-of-negatible.ll
+++ b/llvm/test/Transforms/InstCombine/sub-of-negatible.ll
@@ -1543,8 +1543,9 @@ define <2 x i32> @negate_select_of_negation_poison(<2 x i1> %c, <2 x i32> %x) {
define i4 @negate_freeze(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: define i4 @negate_freeze(
; CHECK-SAME: i4 [[X:%.*]], i4 [[Y:%.*]], i4 [[Z:%.*]]) {
-; CHECK-NEXT: [[T0_NEG:%.*]] = sub i4 [[Y]], [[X]]
-; CHECK-NEXT: [[T1_NEG:%.*]] = freeze i4 [[T0_NEG]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i4 [[X]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i4 [[Y]]
+; CHECK-NEXT: [[T1_NEG:%.*]] = sub i4 [[Y_FR]], [[X_FR]]
; CHECK-NEXT: [[T2:%.*]] = add i4 [[T1_NEG]], [[Z]]
; CHECK-NEXT: ret i4 [[T2]]
;
@@ -1556,8 +1557,9 @@ define i4 @negate_freeze(i4 %x, i4 %y, i4 %z) {
define i4 @negate_freeze_extrause(i4 %x, i4 %y, i4 %z) {
; CHECK-LABEL: define i4 @negate_freeze_extrause(
; CHECK-SAME: i4 [[X:%.*]], i4 [[Y:%.*]], i4 [[Z:%.*]]) {
-; CHECK-NEXT: [[T0:%.*]] = sub i4 [[X]], [[Y]]
-; CHECK-NEXT: [[T1:%.*]] = freeze i4 [[T0]]
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i4 [[X]]
+; CHECK-NEXT: [[Y_FR:%.*]] = freeze i4 [[Y]]
+; CHECK-NEXT: [[T1:%.*]] = sub i4 [[X_FR]], [[Y_FR]]
; CHECK-NEXT: call void @use4(i4 [[T1]])
; CHECK-NEXT: [[T2:%.*]] = sub i4 [[Z]], [[T1]]
; CHECK-NEXT: ret i4 [[T2]]
diff --git a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
index 64809ddcae3f8..81e35daff6ec6 100644
--- a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
@@ -96,10 +96,11 @@ define i8 @urem_assume_with_unexpected_const(i8 %x, i8 %n) {
define i8 @urem_without_assume(i8 %arg, i8 %arg2) {
; CHECK-LABEL: define i8 @urem_without_assume(
; CHECK-SAME: i8 [[ARG:%.*]], i8 [[ARG2:%.*]]) {
-; CHECK-NEXT: [[X:%.*]] = urem i8 [[ARG]], [[ARG2]]
-; CHECK-NEXT: [[X_FR:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT: [[ARG2_FR:%.*]] = freeze i8 [[ARG2]]
+; CHECK-NEXT: [[ARG_FR:%.*]] = freeze i8 [[ARG]]
+; CHECK-NEXT: [[X_FR:%.*]] = urem i8 [[ARG_FR]], [[ARG2_FR]]
; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X_FR]], 1
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[ARG2]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[ARG2_FR]]
; CHECK-NEXT: [[OUT:%.*]] = select i1 [[TMP1]], i8 0, i8 [[ADD]]
; CHECK-NEXT: ret i8 [[OUT]]
;
diff --git a/llvm/test/Transforms/LoopVectorize/forked-pointers.ll b/llvm/test/Transforms/LoopVectorize/forked-pointers.ll
index 677163b51ec64..efd420c11ef06 100644
--- a/llvm/test/Transforms/LoopVectorize/forked-pointers.ll
+++ b/llvm/test/Transforms/LoopVectorize/forked-pointers.ll
@@ -17,21 +17,22 @@ target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
define dso_local void @forked_ptrs_different_base_same_offset(ptr nocapture readonly %Base1, ptr nocapture readonly %Base2, ptr nocapture %Dest, ptr nocapture readonly %Preds) {
; CHECK-LABEL: @forked_ptrs_different_base_same_offset(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[BASE1:%.*]] = freeze ptr [[BASE3:%.*]]
+; CHECK-NEXT: [[BASE2:%.*]] = freeze ptr [[BASE4:%.*]]
+; CHECK-NEXT: [[DEST:%.*]] = freeze ptr [[DEST2:%.*]]
; CHECK-NEXT: br i1 false, label [[SCALAR_PH:%.*]], label [[VECTOR_MEMCHECK:%.*]]
; CHECK: vector.memcheck:
-; CHECK-NEXT: [[DEST1:%.*]] = ptrtoint ptr [[DEST:%.*]] to i64
+; CHECK-NEXT: [[DEST1:%.*]] = ptrtoint ptr [[DEST]] to i64
; CHECK-NEXT: [[PREDS2:%.*]] = ptrtoint ptr [[PREDS:%.*]] to i64
-; CHECK-NEXT: [[BASE23:%.*]] = ptrtoint ptr [[BASE2:%.*]] to i64
-; CHECK-NEXT: [[BASE15:%.*]] = ptrtoint ptr [[BASE1:%.*]] to i64
+; CHECK-NEXT: [[BASE23:%.*]] = ptrtoint ptr [[BASE2]] to i64
+; CHECK-NEXT: [[BASE15:%.*]] = ptrtoint ptr [[BASE1]] to i64
; CHECK-NEXT: [[TMP0:%.*]] = sub i64 [[DEST1]], [[PREDS2]]
; CHECK-NEXT: [[DIFF_CHECK:%.*]] = icmp ult i64 [[TMP0]], 16
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 [[DEST1]], [[BASE23]]
-; CHECK-NEXT: [[DOTFR:%.*]] = freeze i64 [[TMP1]]
-; CHECK-NEXT: [[DIFF_CHECK4:%.*]] = icmp ult i64 [[DOTFR]], 16
+; CHECK-NEXT: [[DIFF_CHECK4:%.*]] = icmp ult i64 [[TMP1]], 16
; CHECK-NEXT: [[CONFLICT_RDX:%.*]] = or i1 [[DIFF_CHECK]], [[DIFF_CHECK4]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DEST1]], [[BASE15]]
-; CHECK-NEXT: [[DOTFR10:%.*]] = freeze i64 [[TMP2]]
-; CHECK-NEXT: [[DIFF_CHECK6:%.*]] = icmp ult i64 [[DOTFR10]], 16
+; CHECK-NEXT: [[DIFF_CHECK6:%.*]] = icmp ult i64 [[TMP2]], 16
; CHECK-NEXT: [[CONFLICT_RDX7:%.*]] = or i1 [[CONFLICT_RDX]], [[DIFF_CHECK6]]
; CHECK-NEXT: br i1 [[CONFLICT_RDX7]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]]
; CHECK: vector.ph:
diff --git a/llvm/test/Transforms/PGOProfile/chr.ll b/llvm/test/Transforms/PGOProfile/chr.ll
index 46f9a2bde7a23..f0a1574c5f209 100644
--- a/llvm/test/Transforms/PGOProfile/chr.ll
+++ b/llvm/test/Transforms/PGOProfile/chr.ll
@@ -1295,6 +1295,7 @@ define i32 @test_chr_14(ptr %i, ptr %j, i32 %sum0, i1 %pred, i32 %z) !prof !14 {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[Z_FR:%.*]] = freeze i32 [[Z:%.*]]
; CHECK-NEXT: [[I0:%.*]] = load i32, ptr [[I:%.*]], align 4
+; CHECK-NEXT: [[I0_FR:%.*]] = freeze i32 [[I0]]
; CHECK-NEXT: [[V1_NOT:%.*]] = icmp eq i32 [[Z_FR]], 1
; CHECK-NEXT: br i1 [[V1_NOT]], label [[BB1:%.*]], label [[ENTRY_SPLIT_NONCHR:%.*]], !prof [[PROF15]]
; CHECK: entry.split.nonchr:
@@ -1307,27 +1308,26 @@ define i32 @test_chr_14(ptr %i, ptr %j, i32 %sum0, i1 %pred, i32 %z) !prof !14 {
; CHECK-NEXT: br label [[BB1]]
; CHECK: bb1:
; CHECK-NEXT: [[J0:%.*]] = load i32, ptr [[J:%.*]], align 4
-; CHECK-NEXT: [[V6:%.*]] = and i32 [[I0]], 2
-; CHECK-NEXT: [[V4:%.*]] = icmp ne i32 [[V6]], [[J0]]
+; CHECK-NEXT: [[J0_FR:%.*]] = freeze i32 [[J0]]
+; CHECK-NEXT: [[V6:%.*]] = and i32 [[I0_FR]], 2
+; CHECK-NEXT: [[TMP0:%.*]] = icmp ne i32 [[V6]], [[J0_FR]]
; CHECK-NEXT: [[V8:%.*]] = add i32 [[SUM0:%.*]], 43
-; CHECK-NEXT: [[V5:%.*]] = icmp ne i32 [[I0]], [[J0]]
-; CHECK-NEXT: [[TMP0:%.*]] = freeze i1 [[V4]]
-; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[V5]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[I0_FR]], [[J0_FR]]
; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br i1 [[TMP2]], label [[BB1_SPLIT:%.*]], label [[BB1_SPLIT_NONCHR:%.*]], !prof [[PROF15]]
; CHECK: bb1.split:
; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: [[V9:%.*]] = and i32 [[I0]], 4
+; CHECK-NEXT: [[V9:%.*]] = and i32 [[I0_FR]], 4
; CHECK-NEXT: [[V10:%.*]] = icmp eq i32 [[V9]], 0
; CHECK-NEXT: br i1 [[V10]], label [[BB3:%.*]], label [[BB2:%.*]]
; CHECK: bb2:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb1.split.nonchr:
-; CHECK-NEXT: [[V5_NONCHR:%.*]] = icmp eq i32 [[I0]], [[J0]]
+; CHECK-NEXT: [[V5_NONCHR:%.*]] = icmp eq i32 [[I0_FR]], [[J0_FR]]
; CHECK-NEXT: [[SUM3_NONCHR:%.*]] = select i1 [[V5_NONCHR]], i32 [[SUM0]], i32 [[V8]], !prof [[PROF16]]
; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: [[V9_NONCHR:%.*]] = and i32 [[I0]], 4
+; CHECK-NEXT: [[V9_NONCHR:%.*]] = and i32 [[I0_FR]], 4
; CHECK-NEXT: [[V10_NONCHR:%.*]] = icmp eq i32 [[V9_NONCHR]], 0
; CHECK-NEXT: br i1 [[V10_NONCHR]], label [[BB3]], label [[BB2_NONCHR:%.*]]
; CHECK: bb2.nonchr:
@@ -1335,7 +1335,7 @@ define i32 @test_chr_14(ptr %i, ptr %j, i32 %sum0, i1 %pred, i32 %z) !prof !14 {
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[V8]], [[BB2]] ], [ [[V8]], [[BB1_SPLIT]] ], [ [[SUM3_NONCHR]], [[BB2_NONCHR]] ], [ [[SUM3_NONCHR]], [[BB1_SPLIT_NONCHR]] ]
-; CHECK-NEXT: [[V11:%.*]] = add i32 [[I0]], [[TMP3]]
+; CHECK-NEXT: [[V11:%.*]] = add i32 [[I0_FR]], [[TMP3]]
; CHECK-NEXT: ret i32 [[V11]]
;
entry:
@@ -1932,12 +1932,12 @@ define i32 @test_chr_21(i64 %i, i64 %k, i64 %j) "instcombine-no-verify-fixpoint"
; CHECK-LABEL: @test_chr_21(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[I_FR:%.*]] = freeze i64 [[I:%.*]]
-; CHECK-NEXT: [[CMP0:%.*]] = icmp ne i64 [[J:%.*]], [[K:%.*]]
-; CHECK-NEXT: [[TMP0:%.*]] = freeze i1 [[CMP0]]
+; CHECK-NEXT: [[J:%.*]] = freeze i64 [[J1:%.*]]
+; CHECK-NEXT: [[K:%.*]] = freeze i64 [[K1:%.*]]
+; CHECK-NEXT: [[CMP0:%.*]] = icmp ne i64 [[J]], [[K]]
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i64 [[J]], [[I_FR]]
; CHECK-NEXT: [[CMP_I:%.*]] = icmp ne i64 [[I_FR]], 86
-; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[CMP3]]
-; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[CMP0]], [[CMP3]]
; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[CMP_I]]
; CHECK-NEXT: br i1 [[TMP3]], label [[BB1:%.*]], label [[ENTRY_SPLIT_NONCHR:%.*]], !prof [[PROF15]]
; CHECK: bb1:
@@ -1963,7 +1963,7 @@ define i32 @test_chr_21(i64 %i, i64 %k, i64 %j) "instcombine-no-verify-fixpoint"
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[BB10:%.*]]
; CHECK: entry.split.nonchr:
-; CHECK-NEXT: br i1 [[TMP0]], label [[BB1_NONCHR:%.*]], label [[BB10]], !prof [[PROF18]]
+; CHECK-NEXT: br i1 [[CMP0]], label [[BB1_NONCHR:%.*]], label [[BB10]], !prof [[PROF18]]
; CHECK: bb1.nonchr:
; CHECK-NEXT: [[CMP2_NONCHR:%.*]] = icmp eq i64 [[I_FR]], 2
; CHECK-NEXT: br i1 [[CMP2_NONCHR]], label [[BB3_NONCHR:%.*]], label [[BB2_NONCHR:%.*]], !prof [[PROF16]]
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/vector-reductions-logical.ll b/llvm/test/Transforms/PhaseOrdering/X86/vector-reductions-logical.ll
index ee51e467c5b8e..f6e5b0b29700b 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/vector-reductions-logical.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/vector-reductions-logical.ll
@@ -86,11 +86,11 @@ return:
define float @test_merge_anyof_v4sf(<4 x float> %t) {
; CHECK-LABEL: @test_merge_anyof_v4sf(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <4 x float> [[T:%.*]], zeroinitializer
+; CHECK-NEXT: [[T:%.*]] = freeze <4 x float> [[T1:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = fcmp olt <4 x float> [[T]], zeroinitializer
; CHECK-NEXT: [[TMP1:%.*]] = fcmp ogt <4 x float> [[T]], splat (float 1.000000e+00)
; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <4 x i1> [[TMP1]], <4 x i1> [[TMP0]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
-; CHECK-NEXT: [[TMP4:%.*]] = freeze <8 x i1> [[TMP3]]
-; CHECK-NEXT: [[TMP5:%.*]] = bitcast <8 x i1> [[TMP4]] to i8
+; CHECK-NEXT: [[TMP5:%.*]] = bitcast <8 x i1> [[TMP3]] to i8
; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i8 [[TMP5]], 0
; CHECK-NEXT: [[SHIFT:%.*]] = shufflevector <4 x float> [[T]], <4 x float> poison, <4 x i32> <i32 1, i32 poison, i32 poison, i32 poison>
; CHECK-NEXT: [[TMP6:%.*]] = fadd <4 x float> [[T]], [[SHIFT]]
@@ -400,11 +400,11 @@ return:
define float @test_merge_anyof_v4si(<4 x i32> %t) {
; CHECK-LABEL: @test_merge_anyof_v4si(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp slt <4 x i32> [[T:%.*]], splat (i32 1)
+; CHECK-NEXT: [[T:%.*]] = freeze <4 x i32> [[T1:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = icmp slt <4 x i32> [[T]], splat (i32 1)
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt <4 x i32> [[T]], splat (i32 255)
; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <4 x i1> [[TMP1]], <4 x i1> [[TMP0]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
-; CHECK-NEXT: [[TMP4:%.*]] = freeze <8 x i1> [[TMP3]]
-; CHECK-NEXT: [[TMP5:%.*]] = bitcast <8 x i1> [[TMP4]] to i8
+; CHECK-NEXT: [[TMP5:%.*]] = bitcast <8 x i1> [[TMP3]] to i8
; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i8 [[TMP5]], 0
; CHECK-NEXT: [[SHIFT:%.*]] = shufflevector <4 x i32> [[T]], <4 x i32> poison, <4 x i32> <i32 1, i32 poison, i32 poison, i32 poison>
; CHECK-NEXT: [[TMP6:%.*]] = add nsw <4 x i32> [[T]], [[SHIFT]]
>From 33e66cc639708b6b75230efba90c130c0e279c4c Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 21 Aug 2025 14:45:15 +0200
Subject: [PATCH 2/3] Do single pass recursive propagation
---
.../InstCombine/InstructionCombining.cpp | 83 +++++++++++--------
1 file changed, 48 insertions(+), 35 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 17bbeefb8be7e..9273faf80290a 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4961,55 +4961,68 @@ Instruction *InstCombinerImpl::visitLandingPadInst(LandingPadInst &LI) {
Value *
InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
// Try to push freeze through instructions that propagate but don't produce
- // poison as far as possible. If an operand of freeze is one-use and does
- // not produce poison then push the freeze through to the operands that are
- // not guaranteed non-poison. The actual transform is as follows.
+ // poison as far as possible. If an operand of freeze does not produce poison
+ // then push the freeze through to the operands that are not guaranteed
+ // non-poison. The actual transform is as follows.
// Op1 = ... ; Op1 can be posion
- // Op0 = Inst(Op1, NonPoisonOps...) ; Op0 has only one use
+ // Op0 = Inst(Op1, NonPoisonOps...)
// ... = Freeze(Op0)
// =>
// Op1 = ...
// Op1.fr = Freeze(Op1)
// ... = Inst(Op1.fr, NonPoisonOps...)
- auto *OrigOp = OrigFI.getOperand(0);
- auto *OrigOpInst = dyn_cast<Instruction>(OrigOp);
- // While we could change the other users of OrigOp to use freeze(OrigOp), that
- // potentially reduces their optimization potential, so let's only do this iff
- // the OrigOp is only used by the freeze.
- if (!OrigOpInst || !OrigOpInst->hasOneUse() || isa<PHINode>(OrigOp))
- return nullptr;
+ auto CanPushFreeze = [](Value *V) {
+ if (!isa<Instruction>(V) || isa<PHINode>(V))
+ 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
- // benefit from them.)
- if (canCreateUndefOrPoison(cast<Operator>(OrigOp),
- /*ConsiderFlagsAndMetadata*/ false))
- return nullptr;
+ // 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
+ // benefit from them.)
+ return !canCreateUndefOrPoison(cast<Operator>(V),
+ /*ConsiderFlagsAndMetadata*/ false);
+ };
+
+ // Pushing freezes up long instruction chains can be expensive. Instead,
+ // we directly push the freeze all the way to the leaves. However, we leave
+ // deduplication of freezes on the same value for freezeOtherUses().
+ Use *OrigUse = &OrigFI.getOperandUse(0);
+ SmallPtrSet<Instruction *, 8> Visited;
+ SmallVector<Use *, 8> Worklist;
+ Worklist.push_back(OrigUse);
+ while (!Worklist.empty()) {
+ auto *U = Worklist.pop_back_val();
+ Value *V = U->get();
+ if (!CanPushFreeze(V)) {
+ // If we can't push through the original instruction, abort the transform.
+ if (U == OrigUse)
+ return nullptr;
- // If operand is guaranteed not to be poison, there is no need to add freeze
- // to the operand. So we first find the operand that is not guaranteed to be
- // poison.
- SmallSetVector<Value *, 4> MaybePoisonOperands;
- for (Value *V : OrigOpInst->operands()) {
- if (isa<MetadataAsValue>(V) || isGuaranteedNotToBeUndefOrPoison(V))
+ auto *UserI = cast<Instruction>(U->getUser());
+ Builder.SetInsertPoint(UserI);
+ Value *Frozen = Builder.CreateFreeze(V, V->getName() + ".fr");
+ U->set(Frozen);
continue;
- MaybePoisonOperands.insert(V);
- }
+ }
- OrigOpInst->dropPoisonGeneratingAnnotations();
+ auto *I = cast<Instruction>(V);
+ if (!Visited.insert(I).second)
+ continue;
- // If all operands are guaranteed to be non-poison, we can drop freeze.
- if (MaybePoisonOperands.empty())
- return OrigOp;
+ // reverse() to emit freezes in a more natural order.
+ for (Use &Op : reverse(I->operands())) {
+ Value *OpV = Op.get();
+ if (isa<MetadataAsValue>(OpV) || isGuaranteedNotToBeUndefOrPoison(OpV))
+ continue;
+ Worklist.push_back(&Op);
+ }
- Builder.SetInsertPoint(OrigOpInst);
- for (Value *V : MaybePoisonOperands) {
- Value *Frozen = Builder.CreateFreeze(V, V->getName() + ".fr");
- OrigOpInst->replaceUsesOfWith(V, Frozen);
+ I->dropPoisonGeneratingAnnotations();
+ this->Worklist.add(I);
}
- return OrigOp;
+
+ return OrigUse->get();
}
Instruction *InstCombinerImpl::foldFreezeIntoRecurrence(FreezeInst &FI,
>From 197ff0ae2187c57d68ab72e954ce2a75afd9b709 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 25 Aug 2025 11:07:13 +0200
Subject: [PATCH 3/3] Fix typo
---
llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 9273faf80290a..1c512ec1e21bb 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4964,7 +4964,7 @@ InstCombinerImpl::pushFreezeToPreventPoisonFromPropagating(FreezeInst &OrigFI) {
// poison as far as possible. If an operand of freeze does not produce poison
// then push the freeze through to the operands that are not guaranteed
// non-poison. The actual transform is as follows.
- // Op1 = ... ; Op1 can be posion
+ // Op1 = ... ; Op1 can be poison
// Op0 = Inst(Op1, NonPoisonOps...)
// ... = Freeze(Op0)
// =>
More information about the llvm-commits
mailing list