Skip to content

[InstCombine] Extend Phi-Icmp use to include or #67682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 24, 2023
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
44 changes: 31 additions & 13 deletions llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1441,18 +1441,37 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
PHIUser->user_back() == &PN) {
return replaceInstUsesWith(PN, PoisonValue::get(PN.getType()));
}
// When a PHI is used only to be compared with zero, it is safe to replace
// an incoming value proved as known nonzero with any non-zero constant.
// For example, in the code below, the incoming value %v can be replaced
// with any non-zero constant based on the fact that the PHI is only used to
// be compared with zero and %v is a known non-zero value:
// %v = select %cond, 1, 2
// %p = phi [%v, BB] ...
// icmp eq, %p, 0
auto *CmpInst = dyn_cast<ICmpInst>(PHIUser);
// FIXME: To be simple, handle only integer type for now.
if (CmpInst && isa<IntegerType>(PN.getType()) && CmpInst->isEquality() &&
match(CmpInst->getOperand(1), m_Zero())) {
}

// When a PHI is used only to be compared with zero, it is safe to replace
// an incoming value proved as known nonzero with any non-zero constant.
// For example, in the code below, the incoming value %v can be replaced
// with any non-zero constant based on the fact that the PHI is only used to
// be compared with zero and %v is a known non-zero value:
// %v = select %cond, 1, 2
// %p = phi [%v, BB] ...
// icmp eq, %p, 0
// FIXME: To be simple, handle only integer type for now.
// This handles a small number of uses to keep the complexity down, and an
// icmp(or(phi)) can equally be replaced with any non-zero constant as the
// "or" will only add bits.
if (!PN.hasNUsesOrMore(3)) {
bool AllUsesOfPhiEndsInCmp = all_of(PN.users(), [&PN](User *U) {
auto *CmpInst = dyn_cast<ICmpInst>(U);
if (!CmpInst) {
// This is always correct as OR only add bits and we are checking
// against 0.
if (U->hasOneUse() && match(U, m_c_Or(m_Specific(&PN), m_Value())))
CmpInst = dyn_cast<ICmpInst>(U->user_back());
}
if (!CmpInst || !isa<IntegerType>(PN.getType()) ||
!CmpInst->isEquality() || !match(CmpInst->getOperand(1), m_Zero())) {
return false;
}
return true;
});
// All uses of PHI results in a compare with zero.
if (AllUsesOfPhiEndsInCmp) {
ConstantInt *NonZeroConst = nullptr;
bool MadeChange = false;
for (unsigned I = 0, E = PN.getNumIncomingValues(); I != E; ++I) {
Expand All @@ -1461,7 +1480,6 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
if (isKnownNonZero(VA, DL, 0, &AC, CtxI, &DT)) {
if (!NonZeroConst)
NonZeroConst = getAnyNonZeroConstInt(PN);

if (NonZeroConst != VA) {
replaceOperand(PN, I, NonZeroConst);
MadeChange = true;
Expand Down
302 changes: 302 additions & 0 deletions llvm/test/Transforms/InstCombine/phi.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,308 @@ if.end: ; preds = %entry, %if.then
ret i1 %cmp1
}

define i1 @phi_knownnonzero_eq_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_eq_oricmp(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %phi, %val
%cmp1 = icmp eq i32 %orphi, 0
ret i1 %cmp1
}

define i1 @phi_knownnonzero_eq_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_eq_oricmp_commuted(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %val, %phi
%cmp1 = icmp eq i32 %orphi, 0
ret i1 %cmp1
}

define i1 @phi_knownnonzero_ne_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_ne_oricmp(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %phi, %val
%cmp1 = icmp ne i32 %orphi, 0
ret i1 %cmp1
}

define i1 @phi_knownnonzero_ne_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_ne_oricmp_commuted(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %val, %phi
%cmp1 = icmp ne i32 %orphi, 0
ret i1 %cmp1
}

define i1 @phi_knownnonzero_eq_multiuse_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_eq_multiuse_oricmp(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
; CHECK: next:
; CHECK-NEXT: [[BOOL2:%.*]] = icmp eq i32 [[PHI]], 0
; CHECK-NEXT: br label [[CLEANUP]]
; CHECK: cleanup:
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
; CHECK-NEXT: ret i1 [[FINAL]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %phi, %val
%cmp1 = icmp eq i32 %orphi, 0
br i1 %cmp1, label %next, label %cleanup

next:
%bool2 = icmp eq i32 %phi, 0
br label %cleanup

cleanup:
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
ret i1 %final
}

define i1 @phi_knownnonzero_ne_multiuse_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_ne_multiuse_oricmp_commuted(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
; CHECK: next:
; CHECK-NEXT: [[BOOL2:%.*]] = icmp ne i32 [[PHI]], 0
; CHECK-NEXT: br label [[CLEANUP]]
; CHECK: cleanup:
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
; CHECK-NEXT: ret i1 [[FINAL]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%orphi = or i32 %val, %phi
%cmp1 = icmp ne i32 %orphi, 0
br i1 %cmp1, label %next, label %cleanup

next:
%bool2 = icmp ne i32 %phi, 0
br label %cleanup

cleanup:
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
ret i1 %final
}

define i1 @phi_knownnonzero_eq_multiuse_andicmp(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_eq_multiuse_andicmp(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LOAD]], [[N]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 2
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[SEL]], [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ANDPHI:%.*]] = and i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ANDPHI]], 0
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
; CHECK: next:
; CHECK-NEXT: [[BOOL2:%.*]] = icmp eq i32 [[PHI]], 0
; CHECK-NEXT: br label [[CLEANUP]]
; CHECK: cleanup:
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
; CHECK-NEXT: ret i1 [[FINAL]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%andphi = and i32 %phi, %val
%cmp1 = icmp eq i32 %andphi, 0
br i1 %cmp1, label %next, label %cleanup

next:
%bool2 = icmp eq i32 %phi, 0
br label %cleanup

cleanup:
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
ret i1 %final
}

define i1 @phi_knownnonzero_ne_multiuse_andicmp(i32 %n, i32 %s, ptr %P, i32 %val) {
; CHECK-LABEL: @phi_knownnonzero_ne_multiuse_andicmp(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LOAD]], [[N]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 2
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[SEL]], [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ANDPHI:%.*]] = and i32 [[PHI]], [[VAL:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ANDPHI]], 0
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
; CHECK: next:
; CHECK-NEXT: [[BOOL2:%.*]] = icmp ne i32 [[PHI]], 0
; CHECK-NEXT: br label [[CLEANUP]]
; CHECK: cleanup:
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
; CHECK-NEXT: ret i1 [[FINAL]]
;
entry:
%tobool = icmp slt i32 %n, %s
br i1 %tobool, label %if.end, label %if.then

if.then:
%load = load i32, ptr %P
%cmp = icmp eq i32 %n, %load
%sel = select i1 %cmp, i32 1, i32 2
br label %if.end

if.end:
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
%andphi = and i32 %phi, %val
%cmp1 = icmp ne i32 %andphi, 0
br i1 %cmp1, label %next, label %cleanup

next:
%bool2 = icmp ne i32 %phi, 0
br label %cleanup

cleanup:
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
ret i1 %final
}

; This would crash trying to delete an instruction (conv)
; that still had uses because the user (the phi) was not
; updated to remove a use from an unreachable block (g.exit).
Expand Down