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
10 changes: 10 additions & 0 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,16 @@ findValuesAffectedByCondition(Value *Cond, bool IsAssume,
LLVM_ABI Value *stripNullTest(Value *V);
LLVM_ABI const Value *stripNullTest(const Value *V);

/// Enumerates all possible values of V and inserts them into the set \p
/// Constants. If \p AllowUndefOrPoison is false, it fails when V may contain
/// undef/poison elements. Returns true if the result is complete. Otherwise,
/// the result is incomplete (more than MaxCount values).
/// NOTE: The constant values are not distinct.
LLVM_ABI bool
collectPossibleValues(const Value *V,
SmallPtrSetImpl<const Constant *> &Constants,
unsigned MaxCount, bool AllowUndefOrPoison = true);

} // end namespace llvm

#endif // LLVM_ANALYSIS_VALUETRACKING_H
52 changes: 52 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10405,3 +10405,55 @@ const Value *llvm::stripNullTest(const Value *V) {
Value *llvm::stripNullTest(Value *V) {
return const_cast<Value *>(stripNullTest(const_cast<const Value *>(V)));
}

bool llvm::collectPossibleValues(const Value *V,
SmallPtrSetImpl<const Constant *> &Constants,
unsigned MaxCount, bool AllowUndefOrPoison) {
SmallPtrSet<const Instruction *, 8> Visited;
SmallVector<const Instruction *, 8> Worklist;
auto Push = [&](const Value *V) -> bool {
if (auto *C = dyn_cast<Constant>(V)) {
if (!AllowUndefOrPoison && !isGuaranteedNotToBeUndefOrPoison(C))
return false;
// Check existence first to avoid unnecessary allocations.
if (Constants.contains(C))
return true;
if (Constants.size() == MaxCount)
return false;
Constants.insert(C);
return true;
}

if (auto *Inst = dyn_cast<Instruction>(V)) {
if (Visited.insert(Inst).second)
Worklist.push_back(Inst);
return true;
}
return false;
};
if (!Push(V))
return false;
while (!Worklist.empty()) {
const Instruction *CurInst = Worklist.pop_back_val();
switch (CurInst->getOpcode()) {
case Instruction::Select:
if (!Push(CurInst->getOperand(1)))
return false;
if (!Push(CurInst->getOperand(2)))
return false;
break;
case Instruction::PHI:
for (Value *IncomingValue : cast<PHINode>(CurInst)->incoming_values()) {
// Fast path for recurrence PHI.
if (IncomingValue == CurInst)
continue;
if (!Push(IncomingValue))
return false;
}
break;
default:
return false;
}
}
return true;
}
67 changes: 40 additions & 27 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6020,6 +6020,8 @@ static bool eliminateDeadSwitchCases(SwitchInst *SI, DomTreeUpdater *DTU,
const DataLayout &DL) {
Value *Cond = SI->getCondition();
KnownBits Known = computeKnownBits(Cond, DL, AC, SI);
SmallPtrSet<const Constant *, 4> KnownValues;
bool IsKnownValuesValid = collectPossibleValues(Cond, KnownValues, 4);

// We can also eliminate cases by determining that their values are outside of
// the limited range of the condition based on how many significant (non-sign)
Expand All @@ -6039,15 +6041,18 @@ static bool eliminateDeadSwitchCases(SwitchInst *SI, DomTreeUpdater *DTU,
UniqueSuccessors.push_back(Successor);
++It->second;
}
const APInt &CaseVal = Case.getCaseValue()->getValue();
ConstantInt *CaseC = Case.getCaseValue();
const APInt &CaseVal = CaseC->getValue();
if (Known.Zero.intersects(CaseVal) || !Known.One.isSubsetOf(CaseVal) ||
(CaseVal.getSignificantBits() > MaxSignificantBitsInCond)) {
DeadCases.push_back(Case.getCaseValue());
(CaseVal.getSignificantBits() > MaxSignificantBitsInCond) ||
(IsKnownValuesValid && !KnownValues.contains(CaseC))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not work properly.

For the example given by @henrickarlsson collectPossibleValues is picking up "i32 ptrtoint (ptr @g to i32)" as a known value., The contains check here will not match the ConstantInt's found in the switch cases with any not yet resolved symbols.
Maybe collectPossibleValues only should return true when finding ConstantInt values?

Copy link
Member Author

Choose a reason for hiding this comment

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

@henrickarlsson @bjope Thanks for the report. I will take a look.

Copy link
Member Author

Choose a reason for hiding this comment

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

See #168084

DeadCases.push_back(CaseC);
if (DTU)
--NumPerSuccessorCases[Successor];
LLVM_DEBUG(dbgs() << "SimplifyCFG: switch case " << CaseVal
<< " is dead.\n");
}
} else if (IsKnownValuesValid)
KnownValues.erase(CaseC);
}

// If we can prove that the cases must cover all possible values, the
Expand All @@ -6058,33 +6063,41 @@ static bool eliminateDeadSwitchCases(SwitchInst *SI, DomTreeUpdater *DTU,
const unsigned NumUnknownBits =
Known.getBitWidth() - (Known.Zero | Known.One).popcount();
assert(NumUnknownBits <= Known.getBitWidth());
if (HasDefault && DeadCases.empty() &&
NumUnknownBits < 64 /* avoid overflow */) {
uint64_t AllNumCases = 1ULL << NumUnknownBits;
if (SI->getNumCases() == AllNumCases) {
if (HasDefault && DeadCases.empty()) {
if (IsKnownValuesValid && all_of(KnownValues, IsaPred<UndefValue>)) {
createUnreachableSwitchDefault(SI, DTU);
return true;
}
// When only one case value is missing, replace default with that case.
// Eliminating the default branch will provide more opportunities for
// optimization, such as lookup tables.
if (SI->getNumCases() == AllNumCases - 1) {
assert(NumUnknownBits > 1 && "Should be canonicalized to a branch");
IntegerType *CondTy = cast<IntegerType>(Cond->getType());
if (CondTy->getIntegerBitWidth() > 64 ||
!DL.fitsInLegalInteger(CondTy->getIntegerBitWidth()))
return false;

uint64_t MissingCaseVal = 0;
for (const auto &Case : SI->cases())
MissingCaseVal ^= Case.getCaseValue()->getValue().getLimitedValue();
auto *MissingCase =
cast<ConstantInt>(ConstantInt::get(Cond->getType(), MissingCaseVal));
SwitchInstProfUpdateWrapper SIW(*SI);
SIW.addCase(MissingCase, SI->getDefaultDest(), SIW.getSuccessorWeight(0));
createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false);
SIW.setSuccessorWeight(0, 0);
return true;
if (NumUnknownBits < 64 /* avoid overflow */) {
uint64_t AllNumCases = 1ULL << NumUnknownBits;
if (SI->getNumCases() == AllNumCases) {
createUnreachableSwitchDefault(SI, DTU);
return true;
}
// When only one case value is missing, replace default with that case.
// Eliminating the default branch will provide more opportunities for
// optimization, such as lookup tables.
if (SI->getNumCases() == AllNumCases - 1) {
assert(NumUnknownBits > 1 && "Should be canonicalized to a branch");
IntegerType *CondTy = cast<IntegerType>(Cond->getType());
if (CondTy->getIntegerBitWidth() > 64 ||
!DL.fitsInLegalInteger(CondTy->getIntegerBitWidth()))
return false;

uint64_t MissingCaseVal = 0;
for (const auto &Case : SI->cases())
MissingCaseVal ^= Case.getCaseValue()->getValue().getLimitedValue();
auto *MissingCase = cast<ConstantInt>(
ConstantInt::get(Cond->getType(), MissingCaseVal));
SwitchInstProfUpdateWrapper SIW(*SI);
SIW.addCase(MissingCase, SI->getDefaultDest(),
SIW.getSuccessorWeight(0));
createUnreachableSwitchDefault(SI, DTU,
/*RemoveOrigDefaultBlock*/ false);
SIW.setSuccessorWeight(0, 0);
return true;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/AArch64/arm64-ccmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -430,12 +430,12 @@ declare i32 @foo()

; Test case distilled from 126.gcc.
; The phi in sw.bb.i.i gets multiple operands for the %entry predecessor.
define void @build_modify_expr() nounwind ssp {
define void @build_modify_expr(i32 %cond) nounwind ssp {
; CHECK-LABEL: build_modify_expr:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: ret
entry:
switch i32 undef, label %sw.bb.i.i [
switch i32 %cond, label %sw.bb.i.i [
i32 69, label %if.end85
i32 70, label %if.end85
i32 71, label %if.end85
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/CodeGen/Hexagon/vect/zext-v4i1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048"
target triple = "hexagon"

define i32 @fred(ptr %a0) #0 {
define i32 @fred(ptr %a0, i32 %cond) #0 {
; CHECK-LABEL: fred:
; CHECK: // %bb.0: // %b0
; CHECK-NEXT: {
; CHECK-NEXT: if (p0) jump:nt .LBB0_2
; CHECK-NEXT: p0 = cmp.eq(r1,#5); if (!p0.new) jump:t .LBB0_2
; CHECK-NEXT: }
; CHECK-NEXT: // %bb.1: // %b2
; CHECK-NEXT: {
Expand Down Expand Up @@ -40,7 +40,7 @@ define i32 @fred(ptr %a0) #0 {
; CHECK-NEXT: jumpr r31
; CHECK-NEXT: }
b0:
switch i32 undef, label %b14 [
switch i32 %cond, label %b14 [
i32 5, label %b2
i32 3, label %b1
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,132 @@ bees:
unreachable
}

define void @pr165179(i1 %cond) {
; CHECK-LABEL: @pr165179(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
; CHECK-NEXT: br label [[SWITCHBB:%.*]]
; CHECK: if.else:
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
; CHECK-NEXT: br label [[SWITCHBB]]
; CHECK: exit:
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
; CHECK-NEXT: ret void
;
entry:
br i1 %cond, label %if.then, label %if.else

if.then:
tail call void @bees.a() nounwind
br label %switchbb

if.else:
tail call void @bees.b() nounwind
br label %switchbb

switchbb:
%cond1 = phi i32 [ 1, %if.else ], [ -1, %if.then ]
switch i32 %cond1, label %default [
i32 1, label %exit
i32 -1, label %exit
]

exit:
tail call void @bees.a() nounwind
ret void

default:
tail call void @bees.b() nounwind
ret void
}

define void @switch_remove_dead_case_phi(i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @switch_remove_dead_case_phi(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[SWITCHBB:%.*]], label [[IF_ELSE]]
; CHECK: if.else:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 3, [[ENTRY:%.*]] ], [ -1, [[IF_THEN]] ]
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
; CHECK-NEXT: br label [[SWITCHBB]]
; CHECK: switchbb:
; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[PHI]], [[IF_ELSE]] ], [ 5, [[IF_THEN]] ]
; CHECK-NEXT: [[COND3:%.*]] = icmp eq i32 [[COND]], -1
; CHECK-NEXT: br i1 [[COND3]], label [[EXIT:%.*]], label [[DEFAULT:%.*]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
; CHECK: default:
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
; CHECK-NEXT: br label [[COMMON_RET]]
;
entry:
br i1 %cond1, label %if.then, label %if.else

if.then:
tail call void @bees.a() nounwind
br i1 %cond2, label %switchbb, label %if.else

if.else:
%phi = phi i32 [ 3, %entry ], [ -1, %if.then ]
tail call void @bees.b() nounwind
br label %switchbb

switchbb:
%cond = phi i32 [ %phi, %if.else ], [ 5, %if.then ]
switch i32 %cond, label %default [
i32 1, label %exit
i32 -1, label %exit
]

exit:
tail call void @bees.a() nounwind
ret void

default:
tail call void @bees.b() nounwind
ret void
}

define void @switch_remove_dead_case_select(i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @switch_remove_dead_case_select(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[X:%.*]] = select i1 [[COND1:%.*]], i32 -1, i32 3
; CHECK-NEXT: [[Y:%.*]] = select i1 [[COND2:%.*]], i32 [[X]], i32 5
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[Y]], -1
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[DEFAULT:%.*]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
; CHECK: default:
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
; CHECK-NEXT: br label [[COMMON_RET]]
;
entry:
%x = select i1 %cond1, i32 -1, i32 3
%y = select i1 %cond2, i32 %x, i32 5
switch i32 %y, label %default [
i32 1, label %exit
i32 -1, label %exit
]

exit:
tail call void @bees.a() nounwind
ret void

default:
tail call void @bees.b() nounwind
ret void
}

declare void @llvm.trap() nounwind noreturn
declare void @bees.a() nounwind
declare void @bees.b() nounwind
Expand Down
1 change: 1 addition & 0 deletions llvm/test/Transforms/SimplifyCFG/switch_mask.ll
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ define i1 @pr88607() {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND:%.*]] = select i1 false, i32 4, i32 1
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 false, i32 2, i32 [[COND]]
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i32 [[SPEC_SELECT]], 1
; CHECK-NEXT: ret i1 false
;
entry:
Expand Down
7 changes: 3 additions & 4 deletions llvm/test/Transforms/SimplifyCFG/switch_undef.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
define void @f6() #0 {
; CHECK-LABEL: @f6(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: br label [[F1_EXIT_I:%.*]]
; CHECK: f1.exit.i:
; CHECK-NEXT: [[TOBOOL7_I:%.*]] = icmp ne i16 1, 0
; CHECK-NEXT: br label [[FOR_COND_I]]
; CHECK-NEXT: br label [[F1_EXIT_I]]
;

entry:
br label %for.cond.i

Expand Down