Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5169,6 +5169,7 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
// - or: pick -1
// - select's condition: if the true value is constant, choose it by making
// the condition true.
// - phi: pick the common constant across operands
// - default: pick 0
//
// Note that this transform is intentionally done here rather than
Expand All @@ -5179,9 +5180,32 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
// TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
// duplicating logic for binops at least.
auto getUndefReplacement = [&](Type *Ty) {
Value *BestValue = nullptr;
auto pickCommonConstantFromPHI = [](PHINode &PN) -> Value * {
// phi(freeze(undef), C, C). Choose C for freeze so the PHI can be
// removed.
Constant *BestValue = nullptr;
for (Value *V : PN.incoming_values()) {
if (match(V, m_Freeze(m_Undef())))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it still valid if the incoming value is another freeze undef? I have not found a counterexample yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is fine. After all, we're not actually replacing that other freeze undef here. (If we did, we'd have to be more careful about multi-use.)

continue;

Constant *C = dyn_cast<Constant>(V);
if (!C)
return nullptr;

if (!isGuaranteedNotToBeUndefOrPoison(C))
return nullptr;

if (BestValue && BestValue != C)
return nullptr;

BestValue = C;
}
return BestValue;
};

Value *NullValue = Constant::getNullValue(Ty);
for (const auto *U : I.users()) {
Value *BestValue = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is used inside the loop below, it was part of the original code.

Copy link
Contributor Author

@mikael-nilsson-arm mikael-nilsson-arm Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like I have added newline between NullValue and BestValue, will undo it.

for (auto *U : I.users()) {
Value *V = NullValue;
if (match(U, m_Or(m_Value(), m_Value())))
V = ConstantInt::getAllOnesValue(Ty);
Expand All @@ -5190,6 +5214,9 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
else if (match(U, m_c_Select(m_Specific(&I), m_Value(V)))) {
if (!isGuaranteedNotToBeUndefOrPoison(V, &AC, &I, &DT))
V = NullValue;
} else if (auto *PHI = dyn_cast<PHINode>(U)) {
if (Value *MaybeV = pickCommonConstantFromPHI(*PHI))
V = MaybeV;
}

if (!BestValue)
Expand Down
274 changes: 274 additions & 0 deletions llvm/test/Transforms/InstCombine/in-freeze-phi.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -passes=instcombine -S < %s | FileCheck %s

define i32 @phi_freeze_same_consts(i1 %c0, i1 %c1) {
; CHECK-LABEL: define i32 @phi_freeze_same_consts(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: ret i32 42
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze i32 undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %final
cB:
br label %final

final:
%phi = phi i32 [ %f, %bb_freeze ], [ 42, %cA ], [ 42, %cB ]
ret i32 %phi
}

define i32 @phi_freeze_mixed_consts(i1 %c0, i1 %c1) {
; CHECK-LABEL: define i32 @phi_freeze_mixed_consts(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[BB_FREEZE]] ], [ 42, %[[CA]] ], [ 7, %[[CB]] ]
; CHECK-NEXT: ret i32 [[PHI]]
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze i32 undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %final
cB:
br label %final

final:
%phi = phi i32 [ %f, %bb_freeze ], [ 42, %cA ], [ 7, %cB ]
ret i32 %phi
}

define i32 @phi_freeze_with_nonconst_incoming(i32 %x, i1 %c0, i1 %c1) {
; CHECK-LABEL: define i32 @phi_freeze_with_nonconst_incoming(
; CHECK-SAME: i32 [[X:%.*]], i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[BB_FREEZE]] ], [ [[X]], %[[CA]] ], [ 13, %[[CB]] ]
; CHECK-NEXT: ret i32 [[PHI]]
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze i32 undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %final
cB:
br label %final

final:
%phi = phi i32 [ %f, %bb_freeze ], [ %x, %cA ], [ 13, %cB ]
ret i32 %phi
}

define <4 x i8> @phi_freeze_vector(i1 %c0, i1 %c1) {
; CHECK-LABEL: define <4 x i8> @phi_freeze_vector(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: ret <4 x i8> splat (i8 9)
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze <4 x i8> undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB

cA:
br label %final

cB:
br label %final

final:
%phi = phi <4 x i8> [ %f, %bb_freeze ],
[<i8 9, i8 9, i8 9, i8 9>, %cA ],
[<i8 9, i8 9, i8 9, i8 9>, %cB ]
ret <4 x i8> %phi
}

define i32 @multi_use_one_folds_one_not_zero(i1 %c0, i1 %c1, i1 %c2) {
; CHECK-LABEL: define i32 @multi_use_one_folds_one_not_zero(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_OTHER3:.*]], label %[[CC1:.*]]
; CHECK: [[BB_OTHER3]]:
; CHECK-NEXT: br label %[[MID:.*]]
; CHECK: [[CC1]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[MID]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[MID]]
; CHECK: [[MID]]:
; CHECK-NEXT: [[PHI_FOLD:%.*]] = phi i32 [ 0, %[[BB_OTHER3]] ], [ 1, %[[CA]] ], [ 1, %[[CB]] ]
; CHECK-NEXT: br i1 [[C2]], label %[[BB_FREEZE2:.*]], label %[[CD:.*]]
; CHECK: [[BB_FREEZE2]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER2:.*:]]
; CHECK-NEXT: br i1 true, label %[[CA]], label %[[CB]]
; CHECK: [[CC:.*:]]
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CD]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: ret i32 [[PHI_FOLD]]
;
entry:
%f = freeze i32 undef
br i1 %c0, label %bb_freeze, label %bb_other
bb_freeze:
br label %mid
bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %mid
cB:
br label %mid
mid:
%phi_no_fold = phi i32 [ %f, %bb_freeze ], [ 1, %cA ], [ 1, %cB ]
br i1 %c2, label %bb_freeze2, label %cD
bb_freeze2:
br label %final
bb_other2:
br i1 %c1, label %cA, label %cB
cC:
br label %final
cD:
br label %final
final:
%phi_fold = phi i32 [ %f, %bb_freeze2 ], [ 0, %cC ], [ 0, %cD ]
%a = add i32 %phi_fold, %phi_no_fold
ret i32 %a
}

define i32 @phi_freeze_poison(i1 %c0, i1 %c1) {
; CHECK-LABEL: define i32 @phi_freeze_poison(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: ret i32 0
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze i32 undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %final
cB:
br label %final

final:
%phi = phi i32 [ %f, %bb_freeze ], [ poison, %cA ], [ poison, %cB ]
ret i32 %phi
}

define <2 x i32> @phi_freeze_poison_vec(i1 %c0, i1 %c1) {
; CHECK-LABEL: define <2 x i32> @phi_freeze_poison_vec(
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C0]], label %[[BB_FREEZE:.*]], label %[[BB_OTHER:.*]]
; CHECK: [[BB_FREEZE]]:
; CHECK-NEXT: br label %[[FINAL:.*]]
; CHECK: [[BB_OTHER]]:
; CHECK-NEXT: br i1 [[C1]], label %[[CA:.*]], label %[[CB:.*]]
; CHECK: [[CA]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[CB]]:
; CHECK-NEXT: br label %[[FINAL]]
; CHECK: [[FINAL]]:
; CHECK-NEXT: [[PHI:%.*]] = phi <2 x i32> [ zeroinitializer, %[[BB_FREEZE]] ], [ <i32 poison, i32 1>, %[[CA]] ], [ <i32 poison, i32 1>, %[[CB]] ]
; CHECK-NEXT: ret <2 x i32> [[PHI]]
;
entry:
br i1 %c0, label %bb_freeze, label %bb_other

bb_freeze:
%f = freeze <2 x i32> undef
br label %final

bb_other:
br i1 %c1, label %cA, label %cB
cA:
br label %final
cB:
br label %final

final:
%phi = phi <2 x i32> [ %f, %bb_freeze ], [ <i32 poison, i32 1>, %cA ], [ <i32 poison, i32 1>, %cB ]
ret <2 x i32> %phi
}