/// constant - This LLVM Value has a specific constant value.
constant,
- /// forcedconstant - This LLVM Value was thought to be undef until
- /// ResolvedUndefsIn. This is treated just like 'constant', but if merged
- /// with another (different) constant, it goes to overdefined, instead of
- /// asserting.
- forcedconstant,
-
/// overdefined - This instruction is not known to be constant, and we know
/// it has a value.
overdefined
};
/// Val: This stores the current lattice value along with the Constant* for
- /// the constant if this is a 'constant' or 'forcedconstant' value.
+ /// the constant if this is a 'constant' value.
PointerIntPair<Constant *, 2, LatticeValueTy> Val;
LatticeValueTy getLatticeValue() const {
bool isUnknown() const { return getLatticeValue() == unknown; }
- bool isConstant() const {
- return getLatticeValue() == constant || getLatticeValue() == forcedconstant;
- }
+ bool isConstant() const { return getLatticeValue() == constant; }
bool isOverdefined() const { return getLatticeValue() == overdefined; }
/// markConstant - Return true if this is a change in status.
bool markConstant(Constant *V) {
- if (getLatticeValue() == constant) { // Constant but not forcedconstant.
+ if (getLatticeValue() == constant) { // Constant
assert(getConstant() == V && "Marking constant with different value");
return false;
}
- if (isUnknown()) {
- Val.setInt(constant);
- assert(V && "Marking constant with NULL");
- Val.setPointer(V);
- } else {
- assert(getLatticeValue() == forcedconstant &&
- "Cannot move from overdefined to constant!");
- // Stay at forcedconstant if the constant is the same.
- if (V == getConstant()) return false;
-
- // Otherwise, we go to overdefined. Assumptions made based on the
- // forced value are possibly wrong. Assuming this is another constant
- // could expose a contradiction.
- Val.setInt(overdefined);
- }
+ assert(isUnknown());
+ Val.setInt(constant);
+ assert(V && "Marking constant with NULL");
+ Val.setPointer(V);
return true;
}
return nullptr;
}
- void markForcedConstant(Constant *V) {
- assert(isUnknown() && "Can't force a defined value!");
- Val.setInt(forcedconstant);
- Val.setPointer(V);
- }
-
ValueLatticeElement toValueLattice() const {
if (isOverdefined())
return ValueLatticeElement::getOverdefined();
}
private:
- // pushToWorkList - Helper for markConstant/markForcedConstant/markOverdefined
+ // pushToWorkList - Helper for markConstant/markOverdefined
void pushToWorkList(LatticeVal &IV, Value *V) {
if (IV.isOverdefined())
return OverdefinedInstWorkList.push_back(V);
return markConstant(ValueState[V], V, C);
}
- void markForcedConstant(Value *V, Constant *C) {
- assert(!V->getType()->isStructTy() && "structs should use mergeInValue");
- LatticeVal &IV = ValueState[V];
- IV.markForcedConstant(C);
- LLVM_DEBUG(dbgs() << "markForcedConstant: " << *C << ": " << *V << '\n');
- pushToWorkList(IV, V);
- }
-
// markOverdefined - Make a value be marked as "overdefined". If the
// value is not already overdefined, add it to the overdefined instruction
// work list so that the users of the instruction are updated later.
LatticeVal V0State = getValueState(I.getOperand(0));
LatticeVal &IV = ValueState[&I];
- if (IV.isOverdefined()) return;
-
if (V0State.isConstant()) {
Constant *C = ConstantExpr::get(I.getOpcode(), V0State.getConstant());
}
// If something is undef, wait for it to resolve.
- if (!V1State.isOverdefined() && !V2State.isOverdefined())
+ if (!V1State.isOverdefined() && !V2State.isOverdefined()) {
+
return;
+ }
// Otherwise, one of our operands is overdefined. Try to produce something
// better than overdefined with some tricks.
NonOverdefVal = &V1State;
else if (!V2State.isOverdefined())
NonOverdefVal = &V2State;
-
if (NonOverdefVal) {
if (NonOverdefVal->isUnknown())
return;
if (PtrVal.isUnknown()) return; // The pointer is not resolved yet!
LatticeVal &IV = ValueState[&I];
- if (IV.isOverdefined()) return;
if (!PtrVal.isConstant() || I.isVolatile())
return (void)markOverdefined(IV, &I);
/// constraints on the condition of the branch, as that would impact other users
/// of the value.
///
-/// This scan also checks for values that use undefs, whose results are actually
-/// defined. For example, 'zext i8 undef to i32' should produce all zeros
-/// conservatively, as "(zext i8 X -> i32) & 0xFF00" must always return zero,
-/// even if X isn't defined.
+/// This scan also checks for values that use undefs. It conservatively marks
+/// them as overdefined.
bool SCCPSolver::ResolvedUndefsIn(Function &F) {
+ // Keep track of values that dependent on an yet unknown tracked function call. It only makes sense to resolve them once the call is resolved.
+ SmallPtrSet<Value *, 8> DependsOnSkipped;
for (BasicBlock &BB : F) {
if (!BBExecutable.count(&BB))
continue;
// Tracked calls must never be marked overdefined in ResolvedUndefsIn.
if (CallSite CS = CallSite(&I))
if (Function *F = CS.getCalledFunction())
- if (MRVFunctionsTracked.count(F))
+ if (MRVFunctionsTracked.count(F)) {
+ DependsOnSkipped.insert(&I);
continue;
+ }
// extractvalue and insertvalue don't need to be marked; they are
// tracked as precisely as their operands.
if (isa<ExtractValueInst>(I) || isa<InsertValueInst>(I))
continue;
-
// Send the results of everything else to overdefined. We could be
// more precise than this but it isn't worth bothering.
for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
// 2. It could be constant-foldable.
// Because of the way we solve return values, tracked calls must
// never be marked overdefined in ResolvedUndefsIn.
- if (CallSite CS = CallSite(&I)) {
+ if (CallSite CS = CallSite(&I))
if (Function *F = CS.getCalledFunction())
- if (TrackedRetVals.count(F))
+ if (TrackedRetVals.count(F)) {
+ DependsOnSkipped.insert(&I);
continue;
+ }
- // If the call is constant-foldable, we mark it overdefined because
- // we do not know what return values are valid.
- markOverdefined(&I);
- return true;
- }
-
- // extractvalue is safe; check here because the argument is a struct.
- if (isa<ExtractValueInst>(I))
+ // Skip instructions that depend on results of calls we skipped earlier. Otherwise we might mark I as overdefined to early when we would end up discovering a constant value for I, if the call later resolves to a constant.
+ if (any_of(I.operands(), [&DependsOnSkipped](Value *V) {
+ return DependsOnSkipped.find(V) != DependsOnSkipped.end(); })) {
+ DependsOnSkipped.insert(&I);
continue;
-
- // Compute the operand LatticeVals, for convenience below.
- // Anything taking a struct is conservatively assumed to require
- // overdefined markings.
- if (I.getOperand(0)->getType()->isStructTy()) {
- markOverdefined(&I);
- return true;
- }
- LatticeVal Op0LV = getValueState(I.getOperand(0));
- LatticeVal Op1LV;
- if (I.getNumOperands() == 2) {
- if (I.getOperand(1)->getType()->isStructTy()) {
- markOverdefined(&I);
- return true;
- }
-
- Op1LV = getValueState(I.getOperand(1));
}
- // If this is an instructions whose result is defined even if the input is
- // not fully defined, propagate the information.
- Type *ITy = I.getType();
- switch (I.getOpcode()) {
- case Instruction::Add:
- case Instruction::Sub:
- case Instruction::Trunc:
- case Instruction::FPTrunc:
- case Instruction::BitCast:
- break; // Any undef -> undef
- case Instruction::FSub:
- case Instruction::FAdd:
- case Instruction::FMul:
- case Instruction::FDiv:
- case Instruction::FRem:
- // Floating-point binary operation: be conservative.
- if (Op0LV.isUnknown() && Op1LV.isUnknown())
- markForcedConstant(&I, Constant::getNullValue(ITy));
- else
- markOverdefined(&I);
- return true;
- case Instruction::FNeg:
- break; // fneg undef -> undef
- case Instruction::ZExt:
- case Instruction::SExt:
- case Instruction::FPToUI:
- case Instruction::FPToSI:
- case Instruction::FPExt:
- case Instruction::PtrToInt:
- case Instruction::IntToPtr:
- case Instruction::SIToFP:
- case Instruction::UIToFP:
- // undef -> 0; some outputs are impossible
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- case Instruction::Mul:
- case Instruction::And:
- // Both operands undef -> undef
- if (Op0LV.isUnknown() && Op1LV.isUnknown())
- break;
- // undef * X -> 0. X could be zero.
- // undef & X -> 0. X could be zero.
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- case Instruction::Or:
- // Both operands undef -> undef
- if (Op0LV.isUnknown() && Op1LV.isUnknown())
- break;
- // undef | X -> -1. X could be -1.
- markForcedConstant(&I, Constant::getAllOnesValue(ITy));
- return true;
- case Instruction::Xor:
- // undef ^ undef -> 0; strictly speaking, this is not strictly
- // necessary, but we try to be nice to people who expect this
- // behavior in simple cases
- if (Op0LV.isUnknown() && Op1LV.isUnknown()) {
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- }
- // undef ^ X -> undef
- break;
- case Instruction::SDiv:
- case Instruction::UDiv:
- case Instruction::SRem:
- case Instruction::URem:
- // X / undef -> undef. No change.
- // X % undef -> undef. No change.
- if (Op1LV.isUnknown()) break;
-
- // X / 0 -> undef. No change.
- // X % 0 -> undef. No change.
- if (Op1LV.isConstant() && Op1LV.getConstant()->isZeroValue())
- break;
-
- // undef / X -> 0. X could be maxint.
- // undef % X -> 0. X could be 1.
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- case Instruction::AShr:
- // X >>a undef -> undef.
- if (Op1LV.isUnknown()) break;
-
- // Shifting by the bitwidth or more is undefined.
- if (Op1LV.isConstant()) {
- if (auto *ShiftAmt = Op1LV.getConstantInt())
- if (ShiftAmt->getLimitedValue() >=
- ShiftAmt->getType()->getScalarSizeInBits())
- break;
- }
- // undef >>a X -> 0
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- case Instruction::LShr:
- case Instruction::Shl:
- // X << undef -> undef.
- // X >> undef -> undef.
- if (Op1LV.isUnknown()) break;
-
- // Shifting by the bitwidth or more is undefined.
- if (Op1LV.isConstant()) {
- if (auto *ShiftAmt = Op1LV.getConstantInt())
- if (ShiftAmt->getLimitedValue() >=
- ShiftAmt->getType()->getScalarSizeInBits())
- break;
- }
-
- // undef << X -> 0
- // undef >> X -> 0
- markForcedConstant(&I, Constant::getNullValue(ITy));
- return true;
- case Instruction::Select:
- Op1LV = getValueState(I.getOperand(1));
- // undef ? X : Y -> X or Y. There could be commonality between X/Y.
- if (Op0LV.isUnknown()) {
- if (!Op1LV.isConstant()) // Pick the constant one if there is any.
- Op1LV = getValueState(I.getOperand(2));
- } else if (Op1LV.isUnknown()) {
- // c ? undef : undef -> undef. No change.
- Op1LV = getValueState(I.getOperand(2));
- if (Op1LV.isUnknown())
- break;
- // Otherwise, c ? undef : x -> x.
- } else {
- // Leave Op1LV as Operand(1)'s LatticeValue.
- }
-
- if (Op1LV.isConstant())
- markForcedConstant(&I, Op1LV.getConstant());
- else
- markOverdefined(&I);
- return true;
- case Instruction::Load:
- // A load here means one of two things: a load of undef from a global,
- // a load from an unknown pointer. Either way, having it return undef
- // is okay.
- break;
- case Instruction::ICmp:
- // X == undef -> undef. Other comparisons get more complicated.
- Op0LV = getValueState(I.getOperand(0));
- Op1LV = getValueState(I.getOperand(1));
-
- if ((Op0LV.isUnknown() || Op1LV.isUnknown()) &&
- cast<ICmpInst>(&I)->isEquality())
- break;
- markOverdefined(&I);
- return true;
- case Instruction::Call:
- case Instruction::Invoke:
- case Instruction::CallBr:
- llvm_unreachable("Call-like instructions should have be handled early");
- default:
- // If we don't know what should happen here, conservatively mark it
- // overdefined.
- markOverdefined(&I);
- return true;
- }
+ markOverdefined(&I);
+ return true;
}
// Check to see if we have a branch or switch on an undefined value. If so
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
; RUN: opt < %s -S -ipsccp | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define i64 @fn2() {
; CHECK-LABEL: define {{[^@]+}}@fn2()
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 undef)
+; CHECK-NEXT: [[CONV:%.*]] = sext i32 undef to i64
+; CHECK-NEXT: [[DIV:%.*]] = sdiv i64 8, [[CONV]]
+; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[DIV]])
; CHECK-NEXT: ret i64 [[CALL2]]
;
entry:
; CHECK-LABEL: define {{[^@]+}}@fn1
; CHECK-SAME: (i64 [[P1:%.*]])
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[COND:%.*]] = select i1 undef, i64 undef, i64 undef
+; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i64 [[P1]], 0
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i64 [[P1]], i64 [[P1]]
; CHECK-NEXT: ret i64 [[COND]]
;
entry:
; CHECK: for.cond1:
; CHECK-NEXT: br i1 false, label [[IF_END]], label [[IF_END]]
; CHECK: if.end:
-; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 undef)
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 4
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]])
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]]
; CHECK-NEXT: br label [[FOR_COND1:%.*]]
;
; CHECK-LABEL: define {{[^@]+}}@fn1
; CHECK-SAME: (i32 [[P1:%.*]])
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[COND:%.*]] = select i1 undef, i32 undef, i32 undef
+; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]]
; CHECK-NEXT: ret i32 [[COND]]
;
entry:
-; RUN: opt < %s -sccp -S | \
-; RUN: grep "ret i1 false"
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -sccp -S | FileCheck %s
define i1 @foo() {
- %X = and i1 false, undef ; <i1> [#uses=1]
- ret i1 %X
+; CHECK-LABEL: @foo(
+; CHECK-NEXT: [[X:%.*]] = and i1 false, undef
+; CHECK-NEXT: ret i1 [[X]]
+;
+ %X = and i1 false, undef ; <i1> [#uses=1]
+ ret i1 %X
}
}
; CHECK-LABEL: @large_aggregate
-; CHECK-NEXT: ret i101 undef
+; CHECK-NEXT: %B = load i101, i101* undef
+; CHECK-NEXT: %D = and i101 %B, 1
+; CHECK-NEXT: %DD = or i101 %D, 1
+; CHECK-NEXT: %G = getelementptr i101, i101* getelementptr inbounds ([6 x i101], [6 x i101]* @Y, i32 0, i32 5), i101 %DD
+; CHECK-NEXT: %L3 = load i101, i101* %G
+; CHECK-NEXT: ret i101 %L3
+;
define i101 @large_aggregate() {
%B = load i101, i101* undef
%D = and i101 %B, 1
ret i101 %L3
}
+; CHECK-LABEL: define i101 @large_aggregate_2() {
+; CHECK-NEXT: %D = and i101 undef, 1
+; CHECK-NEXT: %DD = or i101 %D, 1
+; CHECK-NEXT: %G = getelementptr i101, i101* getelementptr inbounds ([6 x i101], [6 x i101]* @Y, i32 0, i32 5), i101 %DD
+; CHECK-NEXT: %L3 = load i101, i101* %G
+; CHECK-NEXT: ret i101 %L3
+;
+define i101 @large_aggregate_2() {
+ %D = and i101 undef, 1
+ %DD = or i101 %D, 1
+ %F = getelementptr [6 x i101], [6 x i101]* @Y, i32 0, i32 5
+ %G = getelementptr i101, i101* %F, i101 %DD
+ %L3 = load i101, i101* %G
+ ret i101 %L3
+}
+
; CHECK-LABEL: @index_too_large
; CHECK-NEXT: store i101* getelementptr (i101, i101* getelementptr ([6 x i101], [6 x i101]* @Y, i32 0, i32 -1), i101 9224497936761618431), i101** undef
; CHECK-NEXT: ret void
-; RUN: opt < %s -ipsccp -S | not grep global
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -ipsccp -S | FileCheck %s
@G = internal global i66 undef
-
define void @foo() {
- %X = load i66, i66* @G
- store i66 %X, i66* @G
- ret void
+; CHECK-LABEL: @foo(
+; CHECK-NEXT: [[X:%.*]] = load i66, i66* @G
+; CHECK-NEXT: store i66 [[X]], i66* @G
+; CHECK-NEXT: ret void
+;
+ %X = load i66, i66* @G
+ store i66 %X, i66* @G
+ ret void
}
define i66 @bar() {
- %V = load i66, i66* @G
- %C = icmp eq i66 %V, 17
- br i1 %C, label %T, label %F
+; CHECK-LABEL: @bar(
+; CHECK-NEXT: [[V:%.*]] = load i66, i66* @G
+; CHECK-NEXT: [[C:%.*]] = icmp eq i66 [[V]], 17
+; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; CHECK: T:
+; CHECK-NEXT: store i66 17, i66* @G
+; CHECK-NEXT: ret i66 17
+; CHECK: F:
+; CHECK-NEXT: store i66 123, i66* @G
+; CHECK-NEXT: ret i66 0
+;
+ %V = load i66, i66* @G
+ %C = icmp eq i66 %V, 17
+ br i1 %C, label %T, label %F
T:
- store i66 17, i66* @G
- ret i66 %V
+ store i66 17, i66* @G
+ ret i66 %V
F:
- store i66 123, i66* @G
- ret i66 0
+ store i66 123, i66* @G
+ ret i66 0
}
-; RUN: opt < %s -sccp -S | not grep select
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -sccp -S | FileCheck %s
@A = constant i32 10
define i712 @test1() {
- %P = getelementptr i32, i32* @A, i32 0
- %B = ptrtoint i32* %P to i64
- %BB = and i64 %B, undef
- %C = icmp sge i64 %BB, 0
- %X = select i1 %C, i712 0, i712 1
- ret i712 %X
+; CHECK-LABEL: @test1(
+; CHECK-NEXT: [[BB:%.*]] = and i64 ptrtoint (i32* @A to i64), undef
+; CHECK-NEXT: [[C:%.*]] = icmp sge i64 [[BB]], 0
+; CHECK-NEXT: [[X:%.*]] = select i1 [[C]], i712 0, i712 1
+; CHECK-NEXT: ret i712 [[X]]
+;
+ %P = getelementptr i32, i32* @A, i32 0
+ %B = ptrtoint i32* %P to i64
+ %BB = and i64 %B, undef
+ %C = icmp sge i64 %BB, 0
+ %X = select i1 %C, i712 0, i712 1
+ ret i712 %X
}
define i712 @test2(i1 %C) {
- %X = select i1 %C, i712 0, i712 undef
- ret i712 %X
+; CHECK-LABEL: @test2(
+; CHECK-NEXT: ret i712 0
+;
+ %X = select i1 %C, i712 0, i712 undef
+ ret i712 %X
}
-
-
; Constant range for %x is [47, 302)
; CHECK-LABEL: @f5
; CHECK-NEXT: entry:
-; CHECK-NEXT: %cmp = icmp sgt i32 %x, undef
-; CHECK-NEXT: %res1 = select i1 %cmp, i32 1, i32 2
-; CHECK-NEXT: %res = add i32 %res1, 3
-; CHECK-NEXT: ret i32 %res
+; CHECK-NEXT: %cmp = icmp sgt i32 %x, undef
+; CHECK-NEXT: %cmp2 = icmp ne i32 undef, %x
+; CHECK-NEXT: %res1 = select i1 %cmp, i32 1, i32 2
+; CHECK-NEXT: %res2 = select i1 %cmp2, i32 3, i32 4
+; CHECK-NEXT: %res = add i32 %res1, %res2
+; CHECK-NEXT: ret i32 %res
define internal i32 @f5(i32 %x) {
entry:
%cmp = icmp sgt i32 %x, undef
ret void
}
; CHECK-LABEL: define void @test3a(
-; CHECK-NEXT: ret void
+; CHECK-NEXT: %X = load i32, i32* @G
+; CHECK-NEXT: store i32 %X, i32* @G
+; CHECK-NEXT: ret void
define i32 @test3b() {
ret i32 0
}
; CHECK-LABEL: define i32 @test3b(
-; CHECK-NOT: store
-; CHECK: ret i32 0
+; CHECK-NEXT: %V = load i32, i32* @G
+; CHECK-NEXT: %C = icmp eq i32 %V, 17
+; CHECK-NEXT: br i1 %C, label %T, label %F
+
+; CHECK-LABEL: T:
+; CHECK-NEXT: store i32 17, i32* @G
+; CHECK-NEXT: ret i32 17
+; CHECK-LABEL: F:
+; CHECK-NEXT: store i32 123, i32* @G
+; CHECK-NEXT: ret i32 0
;;======================== test4
entry:
%call = call i32 @test10b(i32 undef)
ret i32 %call
+
; CHECK-LABEL: define i32 @test10a(
-; CHECK: ret i32 0
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %call = call i32 @test10b(i32 undef)
+; CHECK-NEXT: ret i32 %call
}
define internal i32 @test10b(i32 %x) nounwind {
%r = and i32 %x, 1
ret i32 %r
; CHECK-LABEL: define internal i32 @test10b(
-; CHECK: ret i32 undef
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %r = and i32 undef, 1
+; CHECK-NEXT: ret i32 %r
}
;;======================== test11
%xor = xor i64 undef, undef
ret i64 %xor
; CHECK-LABEL: define i64 @test11a
-; CHECK: ret i64 0
+; CHECK-NEXT: %xor = xor i64 undef, undef
+; CHECK-NEXT: ret i64 %xor
}
define i64 @test11b() {
%call2 = call i64 @llvm.ctpop.i64(i64 %call1)
ret i64 %call2
; CHECK-LABEL: define i64 @test11b
-; CHECK: %[[call1:.*]] = call i64 @test11a()
-; CHECK-NOT: call i64 @llvm.ctpop.i64
-; CHECK-NEXT: ret i64 0
+; CHECK-NEXT: [[call1:%.*]] = call i64 @test11a()
+; CHECK-NEXT: [[call2:%.*]] = call i64 @llvm.ctpop.i64(i64 [[call1]])
+; CHECK-NEXT: ret i64 [[call2]]
}
declare i64 @llvm.ctpop.i64(i64)
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -sccp -S | FileCheck %s
; Test that SCCP has basic knowledge of when and/or/mul nuke overdefined values.
-; CHECK-LABEL: test
-; CHECK: ret i32 0
define i32 @test(i32 %X) {
+; CHECK-LABEL: @test(
+; CHECK-NEXT: ret i32 0
+;
%Y = and i32 %X, 0
ret i32 %Y
}
-; CHECK-LABEL: test2
-; CHECK: ret i32 -1
define i32 @test2(i32 %X) {
+; CHECK-LABEL: @test2(
+; CHECK-NEXT: ret i32 -1
+;
%Y = or i32 -1, %X
ret i32 %Y
}
-; CHECK-LABEL: test3
-; CHECK: ret i32 0
define i32 @test3(i32 %X) {
+; CHECK-LABEL: @test3(
+; CHECK-NEXT: [[Y:%.*]] = and i32 undef, [[X:%.*]]
+; CHECK-NEXT: ret i32 [[Y]]
+;
%Y = and i32 undef, %X
ret i32 %Y
}
-; CHECK-LABEL: test4
-; CHECK: ret i32 -1
define i32 @test4(i32 %X) {
+; CHECK-LABEL: @test4(
+; CHECK-NEXT: [[Y:%.*]] = or i32 [[X:%.*]], undef
+; CHECK-NEXT: ret i32 [[Y]]
+;
%Y = or i32 %X, undef
ret i32 %Y
}
; X * 0 = 0 even if X is overdefined.
-; CHECK-LABEL: test5
-; CHECK: ret i32 0
define i32 @test5(i32 %foo) {
+; CHECK-LABEL: @test5(
+; CHECK-NEXT: ret i32 0
+;
%patatino = mul i32 %foo, 0
ret i32 %patatino
}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -ipsccp -S %s | FileCheck %s
+
+%t1 = type opaque
+
+@e = common global i32 0, align 4
+
+; Test that we a skip unknown values depending on a unknown tracked call, until the call gets resolved. The @test1 and @test2 variants are very similar, they just check 2 different kinds of users (icmp and zext)
+
+define i32 @test1_m(i32 %h) {
+; CHECK-LABEL: define i32 @test1_m(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @test1_k(i8 [[CONV]], i32 0)
+; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64
+; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1*
+; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test1_g(%t1* [[TMP0]], i32 1)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %conv = trunc i32 %h to i8
+ %call = call i32 @test1_k(i8 %conv, i32 0)
+ %conv1 = sext i32 %h to i64
+ %0 = inttoptr i64 %conv1 to %t1*
+ %call2 = call i1 @test1_g(%t1* %0, i32 1)
+ ret i32 undef
+
+; uselistorder directives
+ uselistorder i32 %h, { 1, 0 }
+}
+
+declare void @use.1(i1)
+
+define internal i32 @test1_k(i8 %h, i32 %i) {
+; CHECK-LABEL: define {{.*}} @test1_k(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4
+; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to %t1*
+; CHECK-NEXT: [[CALL:%.*]] = call i1 @test1_g(%t1* [[TMP1]], i32 0)
+; CHECK-NEXT: call void @use.1(i1 false)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %0 = load i32, i32* @e, align 4
+ %conv = sext i32 %0 to i64
+ %1 = inttoptr i64 %conv to %t1*
+ %call = call i1 @test1_g(%t1* %1, i32 %i)
+ %frombool.1 = zext i1 %call to i8
+ %tobool.1 = trunc i8 %frombool.1 to i1
+ call void @use.1(i1 %tobool.1)
+ ret i32 undef
+}
+
+define internal i1 @test1_g(%t1* %h, i32 %i) #0 {
+; CHECK-LABEL: define {{.*}} @test1_g(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I:%.*]], 0
+; CHECK-NEXT: br i1 [[TOBOOL]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]]
+; CHECK: land.rhs:
+; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test1_j()
+; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0
+; CHECK-NEXT: br label [[LAND_END]]
+; CHECK: land.end:
+; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[TOBOOL1]], [[LAND_RHS]] ]
+; CHECK-NEXT: ret i1 undef
+;
+entry:
+ %tobool = icmp ne i32 %i, 0
+ br i1 %tobool, label %land.rhs, label %land.end
+
+land.rhs: ; preds = %entry
+ %call = call i32 (...) @test1_j()
+ %tobool1 = icmp ne i32 %call, 0
+ br label %land.end
+
+land.end: ; preds = %land.rhs, %entry
+ %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ]
+ ret i1 false
+}
+
+declare i32 @test1_j(...)
+
+define i32 @test2_m(i32 %h) #0 {
+; CHECK-LABEL: define {{.*}} @test2_m(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @test2_k(i8 [[CONV]], i32 0)
+; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64
+; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1*
+; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test2_g(%t1* [[TMP0]], i32 1)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %conv = trunc i32 %h to i8
+ %call = call i32 @test2_k(i8 %conv, i32 0)
+ %conv1 = sext i32 %h to i64
+ %0 = inttoptr i64 %conv1 to %t1*
+ %call2 = call i1 @test2_g(%t1* %0, i32 1)
+ ret i32 undef
+
+; uselistorder directives
+ uselistorder i32 %h, { 1, 0 }
+}
+
+define internal i32 @test2_k(i8 %h, i32 %i) {
+; CHECK-LABEL: define {{.*}} @test2_k(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4
+; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to %t1*
+; CHECK-NEXT: [[CALL:%.*]] = call i1 @test3_g(%t1* [[TMP1]], i32 0)
+; CHECK-NEXT: call void @use.1(i1 false)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %0 = load i32, i32* @e, align 4
+ %conv = sext i32 %0 to i64
+ %1 = inttoptr i64 %conv to %t1*
+ %call = call i1 @test3_g(%t1* %1, i32 %i)
+ %frombool = icmp slt i1 %call, 1
+ %add = add i1 %frombool, %frombool
+ call void @use.1(i1 %frombool)
+ ret i32 undef
+
+}
+
+define internal i1 @test2_g(%t1* %h, i32 %i) {
+; CHECK-LABEL: define {{.*}} @test2_g(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 true, label [[LAND_RHS:%.*]], label [[LAND_END:%.*]]
+; CHECK: land.rhs:
+; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test2_j()
+; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0
+; CHECK-NEXT: br label [[LAND_END]]
+; CHECK: land.end:
+; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[TOBOOL1]], [[LAND_RHS]] ]
+; CHECK-NEXT: ret i1 undef
+;
+entry:
+ %tobool = icmp ne i32 %i, 0
+ br i1 %tobool, label %land.rhs, label %land.end
+
+land.rhs: ; preds = %entry
+ %call = call i32 (...) @test2_j()
+ %tobool1 = icmp ne i32 %call, 0
+ br label %land.end
+
+land.end: ; preds = %land.rhs, %entry
+ %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ]
+ ret i1 false
+}
+
+declare i32 @test2_j(...)
+
+
+
+; Same as test_2*, but with a PHI node depending on a tracked call result.
+define i32 @test3_m(i32 %h) #0 {
+; CHECK-LABEL: define {{.*}} @test3_m(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @test3_k(i8 [[CONV]], i32 0)
+; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64
+; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1*
+; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test3_g(%t1* [[TMP0]], i32 1)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %conv = trunc i32 %h to i8
+ %call = call i32 @test3_k(i8 %conv, i32 0)
+ %conv1 = sext i32 %h to i64
+ %0 = inttoptr i64 %conv1 to %t1*
+ %call2 = call i1 @test3_g(%t1* %0, i32 1)
+ ret i32 undef
+
+; uselistorder directives
+ uselistorder i32 %h, { 1, 0 }
+}
+
+define internal i32 @test3_k(i8 %h, i32 %i) {
+; CHECK-LABEL: define {{.*}} @test3_k(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4
+; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to %t1*
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[PHI:%.*]] = phi i1 [ undef, [[ENTRY:%.*]] ], [ false, [[LOOP]] ]
+; CHECK-NEXT: [[CALL:%.*]] = call i1 @test3_g(%t1* [[TMP1]], i32 0)
+; CHECK-NEXT: call void @use.1(i1 false)
+; CHECK-NEXT: br i1 false, label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %0 = load i32, i32* @e, align 4
+ %conv = sext i32 %0 to i64
+ %1 = inttoptr i64 %conv to %t1*
+ br label %loop
+
+loop:
+ %phi = phi i1 [ undef, %entry], [ %call, %loop ]
+ %call = call i1 @test3_g(%t1* %1, i32 %i)
+ %frombool = icmp slt i1 %call, 1
+ %add = add i1 %frombool, %frombool
+ call void @use.1(i1 %frombool)
+ br i1 %call, label %loop, label %exit
+
+exit:
+ ret i32 undef
+}
+
+define internal i1 @test3_g(%t1* %h, i32 %i) {
+; CHECK-LABEL: define {{.*}} @test3_g(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I:%.*]], 0
+; CHECK-NEXT: br i1 [[TOBOOL]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]]
+; CHECK: land.rhs:
+; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test3_j()
+; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0
+; CHECK-NEXT: br label [[LAND_END]]
+; CHECK: land.end:
+; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[TOBOOL1]], [[LAND_RHS]] ]
+; CHECK-NEXT: ret i1 undef
+;
+entry:
+ %tobool = icmp ne i32 %i, 0
+ br i1 %tobool, label %land.rhs, label %land.end
+
+land.rhs: ; preds = %entry
+ %call = call i32 (...) @test3_j()
+ %tobool1 = icmp ne i32 %call, 0
+ br label %land.end
+
+land.end: ; preds = %land.rhs, %entry
+ %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ]
+ ret i1 false
+}
+
+declare i32 @test3_j(...)
+
+
+
+@contextsize = external dso_local local_unnamed_addr global i32, align 4
+@pcount = internal local_unnamed_addr global i32 0, align 4
+@maxposslen = external dso_local local_unnamed_addr global i32, align 4
+
+define void @test3() {
+; CHECK-LABEL: define {{.*}} @test3(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[IF_END16:%.*]]
+; CHECK: if.end16:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @contextsize, align 4
+; CHECK-NEXT: [[SUB18:%.*]] = sub i32 undef, [[TMP0]]
+; CHECK-NEXT: [[SUB19:%.*]] = sub i32 [[SUB18]], undef
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @maxposslen, align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], 8
+; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 undef, [[ADD]]
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* @pcount, align 4
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[DIV]], [[SUB19]]
+; CHECK-NEXT: [[CMP20:%.*]] = icmp sgt i32 [[TMP2]], [[MUL]]
+; CHECK-NEXT: br i1 [[CMP20]], label [[IF_THEN22:%.*]], label [[IF_END24:%.*]]
+; CHECK: if.then22:
+; CHECK-NEXT: store i32 [[MUL]], i32* @pcount, align 4
+; CHECK-NEXT: ret void
+; CHECK: if.end24:
+; CHECK-NEXT: [[CMP25474:%.*]] = icmp sgt i32 [[TMP2]], 0
+; CHECK-NEXT: br i1 [[CMP25474]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
+; CHECK: for.body:
+; CHECK-NEXT: [[DIV30:%.*]] = sdiv i32 0, [[SUB19]]
+; CHECK-NEXT: ret void
+; CHECK: for.end:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %if.end16
+
+if.end16: ; preds = %entry
+ %0 = load i32, i32* @contextsize, align 4
+ %sub18 = sub i32 undef, %0
+ %sub19 = sub i32 %sub18, undef
+ %1 = load i32, i32* @maxposslen, align 4
+ %add = add nsw i32 %1, 8
+ %div = sdiv i32 undef, %add
+ %2 = load i32, i32* @pcount, align 4
+ %mul = mul nsw i32 %div, %sub19
+ %cmp20 = icmp sgt i32 %2, %mul
+ br i1 %cmp20, label %if.then22, label %if.end24
+
+if.then22: ; preds = %if.end16
+ store i32 %mul, i32* @pcount, align 4
+ ret void
+
+if.end24: ; preds = %if.end16
+ %cmp25474 = icmp sgt i32 %2, 0
+ br i1 %cmp25474, label %for.body, label %for.end
+
+for.body: ; preds = %if.end24
+ %3 = trunc i64 0 to i32
+ %div30 = sdiv i32 %3, %sub19
+ ret void
+
+for.end: ; preds = %if.end24
+ ret void
+}
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -ipsccp < %s | FileCheck %s
declare void @foo()
declare void @patatino()
define void @test1(i32 %t) {
+; CHECK-LABEL: @test1(
+; CHECK-NEXT: [[CHOICE:%.*]] = icmp eq i32 undef, -1
+; CHECK-NEXT: switch i1 [[CHOICE]], label [[FIRST:%.*]] [
+; CHECK-NEXT: i1 false, label [[SECOND:%.*]]
+; CHECK-NEXT: i1 true, label [[THIRD:%.*]]
+; CHECK-NEXT: ]
+; CHECK: first:
+; CHECK-NEXT: call void @foo()
+; CHECK-NEXT: ret void
+; CHECK: second:
+; CHECK-NEXT: call void @goo()
+; CHECK-NEXT: ret void
+; CHECK: third:
+; CHECK-NEXT: call void @patatino()
+; CHECK-NEXT: ret void
+;
%choice = icmp eq i32 undef, -1
switch i1 %choice, label %first [i1 0, label %second
- i1 1, label %third]
+ i1 1, label %third]
first:
call void @foo()
ret void
call void @patatino()
ret void
}
-
-; CHECK: define void @test1(i32 %t) {
-; CHECK-NEXT: br label %second
-; CHECK: second:
-; CHECK-NEXT: call void @goo()
-; CHECK-NEXT: ret void
-; CHECK-NEXT: }
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -sccp -S | FileCheck %s
-; CHECK-LABEL: shift_undef_64
define void @shift_undef_64(i64* %p) {
+; CHECK-LABEL: @shift_undef_64(
+; CHECK-NEXT: [[R1:%.*]] = lshr i64 -1, 4294967296
+; CHECK-NEXT: store i64 [[R1]], i64* [[P:%.*]]
+; CHECK-NEXT: [[R2:%.*]] = ashr i64 -1, 4294967297
+; CHECK-NEXT: store i64 [[R2]], i64* [[P]]
+; CHECK-NEXT: [[R3:%.*]] = shl i64 -1, 4294967298
+; CHECK-NEXT: store i64 [[R3]], i64* [[P]]
+; CHECK-NEXT: ret void
+;
%r1 = lshr i64 -1, 4294967296 ; 2^32
- ; CHECK: store i64 undef
store i64 %r1, i64* %p
%r2 = ashr i64 -1, 4294967297 ; 2^32 + 1
- ; CHECK: store i64 undef
store i64 %r2, i64* %p
%r3 = shl i64 -1, 4294967298 ; 2^32 + 2
- ; CHECK: store i64 undef
store i64 %r3, i64* %p
ret void
}
-; CHECK-LABEL: shift_undef_65
define void @shift_undef_65(i65* %p) {
+; CHECK-LABEL: @shift_undef_65(
+; CHECK-NEXT: [[R1:%.*]] = lshr i65 2, -18446744073709551615
+; CHECK-NEXT: store i65 [[R1]], i65* [[P:%.*]]
+; CHECK-NEXT: [[R2:%.*]] = ashr i65 4, -18446744073709551615
+; CHECK-NEXT: store i65 [[R2]], i65* [[P]]
+; CHECK-NEXT: [[R3:%.*]] = shl i65 1, -18446744073709551615
+; CHECK-NEXT: store i65 [[R3]], i65* [[P]]
+; CHECK-NEXT: ret void
+;
%r1 = lshr i65 2, 18446744073709551617
- ; CHECK: store i65 undef
store i65 %r1, i65* %p
%r2 = ashr i65 4, 18446744073709551617
- ; CHECK: store i65 undef
store i65 %r2, i65* %p
%r3 = shl i65 1, 18446744073709551617
- ; CHECK: store i65 undef
store i65 %r3, i65* %p
ret void
}
-; CHECK-LABEL: shift_undef_256
define void @shift_undef_256(i256* %p) {
+; CHECK-LABEL: @shift_undef_256(
+; CHECK-NEXT: [[R1:%.*]] = lshr i256 2, 18446744073709551617
+; CHECK-NEXT: store i256 [[R1]], i256* [[P:%.*]]
+; CHECK-NEXT: [[R2:%.*]] = ashr i256 4, 18446744073709551618
+; CHECK-NEXT: store i256 [[R2]], i256* [[P]]
+; CHECK-NEXT: [[R3:%.*]] = shl i256 1, 18446744073709551619
+; CHECK-NEXT: store i256 [[R3]], i256* [[P]]
+; CHECK-NEXT: ret void
+;
%r1 = lshr i256 2, 18446744073709551617
- ; CHECK: store i256 undef
store i256 %r1, i256* %p
%r2 = ashr i256 4, 18446744073709551618
- ; CHECK: store i256 undef
store i256 %r2, i256* %p
%r3 = shl i256 1, 18446744073709551619
- ; CHECK: store i256 undef
store i256 %r3, i256* %p
ret void
}
-; CHECK-LABEL: shift_undef_511
define void @shift_undef_511(i511* %p) {
+; CHECK-LABEL: @shift_undef_511(
+; CHECK-NEXT: [[R1:%.*]] = lshr i511 -1, 1208925819614629174706276
+; CHECK-NEXT: store i511 [[R1]], i511* [[P:%.*]]
+; CHECK-NEXT: [[R2:%.*]] = ashr i511 -2, 1208925819614629174706200
+; CHECK-NEXT: store i511 [[R2]], i511* [[P]]
+; CHECK-NEXT: [[R3:%.*]] = shl i511 -3, 1208925819614629174706180
+; CHECK-NEXT: store i511 [[R3]], i511* [[P]]
+; CHECK-NEXT: ret void
+;
%r1 = lshr i511 -1, 1208925819614629174706276 ; 2^80 + 100
- ; CHECK: store i511 undef
store i511 %r1, i511* %p
%r2 = ashr i511 -2, 1208925819614629174706200
- ; CHECK: store i511 undef
store i511 %r2, i511* %p
%r3 = shl i511 -3, 1208925819614629174706180
- ; CHECK: store i511 undef
store i511 %r3, i511* %p
ret void
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -sccp -S < %s | FileCheck %s
; PR6940
define double @test1() {
+; CHECK-LABEL: @test1(
+; CHECK-NEXT: [[T:%.*]] = sitofp i32 undef to double
+; CHECK-NEXT: ret double [[T]]
+;
%t = sitofp i32 undef to double
ret double %t
-; CHECK-LABEL: @test1(
-; CHECK: ret double 0.0
}
; Check that lots of stuff doesn't get turned into undef.
define i32 @test2() nounwind readnone ssp {
; CHECK-LABEL: @test2(
+; CHECK-NEXT: init:
+; CHECK-NEXT: br label [[CONTROL_OUTER_OUTER:%.*]]
+; CHECK: control.outer.loopexit.us-lcssa:
+; CHECK-NEXT: br label [[CONTROL_OUTER_LOOPEXIT:%.*]]
+; CHECK: control.outer.loopexit:
+; CHECK-NEXT: br label [[CONTROL_OUTER_OUTER_BACKEDGE:%.*]]
+; CHECK: control.outer.outer:
+; CHECK-NEXT: [[SWITCHCOND_0_PH_PH:%.*]] = phi i32 [ 2, [[INIT:%.*]] ], [ 3, [[CONTROL_OUTER_OUTER_BACKEDGE]] ]
+; CHECK-NEXT: [[I_0_PH_PH:%.*]] = phi i32 [ undef, [[INIT]] ], [ [[I_0_PH_PH_BE:%.*]], [[CONTROL_OUTER_OUTER_BACKEDGE]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i32 [[I_0_PH_PH]], 0
+; CHECK-NEXT: br i1 [[TMP4]], label [[CONTROL_OUTER_OUTER_SPLIT_US:%.*]], label [[CONTROL_OUTER_OUTER_CONTROL_OUTER_OUTER_SPLIT_CRIT_EDGE:%.*]]
+; CHECK: control.outer.outer.control.outer.outer.split_crit_edge:
+; CHECK-NEXT: br label [[CONTROL_OUTER:%.*]]
+; CHECK: control.outer.outer.split.us:
+; CHECK-NEXT: br label [[CONTROL_OUTER_US:%.*]]
+; CHECK: control.outer.us:
+; CHECK-NEXT: [[A_0_PH_US:%.*]] = phi i32 [ [[SWITCHCOND_0_US:%.*]], [[BB3_US:%.*]] ], [ 4, [[CONTROL_OUTER_OUTER_SPLIT_US]] ]
+; CHECK-NEXT: [[SWITCHCOND_0_PH_US:%.*]] = phi i32 [ [[A_0_PH_US]], [[BB3_US]] ], [ [[SWITCHCOND_0_PH_PH]], [[CONTROL_OUTER_OUTER_SPLIT_US]] ]
+; CHECK-NEXT: br label [[CONTROL_US:%.*]]
+; CHECK: bb3.us:
+; CHECK-NEXT: br label [[CONTROL_OUTER_US]]
+; CHECK: bb0.us:
+; CHECK-NEXT: br label [[CONTROL_US]]
+; CHECK: control.us:
+; CHECK-NEXT: [[SWITCHCOND_0_US]] = phi i32 [ [[A_0_PH_US]], [[BB0_US:%.*]] ], [ [[SWITCHCOND_0_PH_US]], [[CONTROL_OUTER_US]] ]
+; CHECK-NEXT: switch i32 [[SWITCHCOND_0_US]], label [[CONTROL_OUTER_LOOPEXIT_US_LCSSA_US:%.*]] [
+; CHECK-NEXT: i32 0, label [[BB0_US]]
+; CHECK-NEXT: i32 1, label [[BB1_US_LCSSA_US:%.*]]
+; CHECK-NEXT: i32 3, label [[BB3_US]]
+; CHECK-NEXT: i32 4, label [[BB4_US_LCSSA_US:%.*]]
+; CHECK-NEXT: ]
+; CHECK: control.outer.loopexit.us-lcssa.us:
+; CHECK-NEXT: br label [[CONTROL_OUTER_LOOPEXIT]]
+; CHECK: bb1.us-lcssa.us:
+; CHECK-NEXT: br label [[BB1:%.*]]
+; CHECK: bb4.us-lcssa.us:
+; CHECK-NEXT: br label [[BB4:%.*]]
+; CHECK: control.outer:
+; CHECK-NEXT: [[A_0_PH:%.*]] = phi i32 [ [[NEXTID17:%.*]], [[BB3:%.*]] ], [ 4, [[CONTROL_OUTER_OUTER_CONTROL_OUTER_OUTER_SPLIT_CRIT_EDGE]] ]
+; CHECK-NEXT: [[SWITCHCOND_0_PH:%.*]] = phi i32 [ 0, [[BB3]] ], [ [[SWITCHCOND_0_PH_PH]], [[CONTROL_OUTER_OUTER_CONTROL_OUTER_OUTER_SPLIT_CRIT_EDGE]] ]
+; CHECK-NEXT: br label [[CONTROL:%.*]]
+; CHECK: control:
+; CHECK-NEXT: [[SWITCHCOND_0:%.*]] = phi i32 [ [[A_0_PH]], [[BB0:%.*]] ], [ [[SWITCHCOND_0_PH]], [[CONTROL_OUTER]] ]
+; CHECK-NEXT: switch i32 [[SWITCHCOND_0]], label [[CONTROL_OUTER_LOOPEXIT_US_LCSSA:%.*]] [
+; CHECK-NEXT: i32 0, label [[BB0]]
+; CHECK-NEXT: i32 1, label [[BB1_US_LCSSA:%.*]]
+; CHECK-NEXT: i32 3, label [[BB3]]
+; CHECK-NEXT: i32 4, label [[BB4_US_LCSSA:%.*]]
+; CHECK-NEXT: ]
+; CHECK: bb4.us-lcssa:
+; CHECK-NEXT: br label [[BB4]]
+; CHECK: bb4:
+; CHECK-NEXT: br label [[CONTROL_OUTER_OUTER_BACKEDGE]]
+; CHECK: control.outer.outer.backedge:
+; CHECK-NEXT: [[I_0_PH_PH_BE]] = phi i32 [ 1, [[BB4]] ], [ 0, [[CONTROL_OUTER_LOOPEXIT]] ]
+; CHECK-NEXT: br label [[CONTROL_OUTER_OUTER]]
+; CHECK: bb3:
+; CHECK-NEXT: [[NEXTID17]] = add i32 [[SWITCHCOND_0]], -2
+; CHECK-NEXT: br label [[CONTROL_OUTER]]
+; CHECK: bb0:
+; CHECK-NEXT: br label [[CONTROL]]
+; CHECK: bb1.us-lcssa:
+; CHECK-NEXT: br label [[BB1]]
+; CHECK: bb1:
+; CHECK-NEXT: ret i32 0
+;
init:
br label %control.outer.outer
bb0.us: ; preds = %control.us
br label %control.us
-; CHECK: control.us: ; preds = %bb0.us, %control.outer.us
-; CHECK-NEXT: %switchCond.0.us = phi i32
-; CHECK-NEXT: switch i32 %switchCond.0.us
control.us: ; preds = %bb0.us, %control.outer.us
%switchCond.0.us = phi i32 [ %A.0.ph.us, %bb0.us ], [ %switchCond.0.ph.us, %control.outer.us ] ; <i32> [#uses=2]
switch i32 %switchCond.0.us, label %control.outer.loopexit.us-lcssa.us [
- i32 0, label %bb0.us
- i32 1, label %bb1.us-lcssa.us
- i32 3, label %bb3.us
- i32 4, label %bb4.us-lcssa.us
+ i32 0, label %bb0.us
+ i32 1, label %bb1.us-lcssa.us
+ i32 3, label %bb3.us
+ i32 4, label %bb4.us-lcssa.us
]
control.outer.loopexit.us-lcssa.us: ; preds = %control.us
control: ; preds = %bb0, %control.outer
%switchCond.0 = phi i32 [ %A.0.ph, %bb0 ], [ %switchCond.0.ph, %control.outer ] ; <i32> [#uses=2]
switch i32 %switchCond.0, label %control.outer.loopexit.us-lcssa [
- i32 0, label %bb0
- i32 1, label %bb1.us-lcssa
- i32 3, label %bb3
- i32 4, label %bb4.us-lcssa
+ i32 0, label %bb0
+ i32 1, label %bb1.us-lcssa
+ i32 3, label %bb3
+ i32 4, label %bb4.us-lcssa
]
bb4.us-lcssa: ; preds = %control
; Make sure SCCP honors the xor "idiom"
; rdar://9956541
define i32 @test3() {
+; CHECK-LABEL: @test3(
+; CHECK-NEXT: [[T:%.*]] = xor i32 undef, undef
+; CHECK-NEXT: ret i32 [[T]]
+;
%t = xor i32 undef, undef
ret i32 %t
-; CHECK-LABEL: @test3(
-; CHECK: ret i32 0
}
; Be conservative with FP ops
define double @test4(double %x) {
+; CHECK-LABEL: @test4(
+; CHECK-NEXT: [[T:%.*]] = fadd double [[X:%.*]], undef
+; CHECK-NEXT: ret double [[T]]
+;
%t = fadd double %x, undef
ret double %t
-; CHECK-LABEL: @test4(
-; CHECK: fadd double %x, undef
}
; Make sure casts produce a possible value
define i32 @test5() {
+; CHECK-LABEL: @test5(
+; CHECK-NEXT: [[T:%.*]] = sext i8 undef to i32
+; CHECK-NEXT: ret i32 [[T]]
+;
%t = sext i8 undef to i32
ret i32 %t
-; CHECK-LABEL: @test5(
-; CHECK: ret i32 0
}
; Make sure ashr produces a possible value
define i32 @test6() {
+; CHECK-LABEL: @test6(
+; CHECK-NEXT: [[T:%.*]] = ashr i32 undef, 31
+; CHECK-NEXT: ret i32 [[T]]
+;
%t = ashr i32 undef, 31
ret i32 %t
-; CHECK-LABEL: @test6(
-; CHECK: ret i32 0
}
; Make sure lshr produces a possible value
define i32 @test7() {
+; CHECK-LABEL: @test7(
+; CHECK-NEXT: [[T:%.*]] = lshr i32 undef, 31
+; CHECK-NEXT: ret i32 [[T]]
+;
%t = lshr i32 undef, 31
ret i32 %t
-; CHECK-LABEL: @test7(
-; CHECK: ret i32 0
}
; icmp eq with undef simplifies to undef
define i1 @test8() {
+; CHECK-LABEL: @test8(
+; CHECK-NEXT: [[T:%.*]] = icmp eq i32 undef, -1
+; CHECK-NEXT: ret i1 [[T]]
+;
%t = icmp eq i32 undef, -1
ret i1 %t
-; CHECK-LABEL: @test8(
-; CHECK: ret i1 undef
}
; Make sure we don't conclude that relational comparisons simplify to undef
define i1 @test9() {
+; CHECK-LABEL: @test9(
+; CHECK-NEXT: [[T:%.*]] = icmp ugt i32 undef, -1
+; CHECK-NEXT: ret i1 [[T]]
+;
%t = icmp ugt i32 undef, -1
ret i1 %t
-; CHECK-LABEL: @test9(
-; CHECK: icmp ugt
}
; Make sure we handle extractvalue
-define i64 @test10() {
+define i64 @test10() {
+; CHECK-LABEL: @test10(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[E:%.*]] = extractvalue { i64, i64 } undef, 1
+; CHECK-NEXT: ret i64 [[E]]
+;
entry:
%e = extractvalue { i64, i64 } undef, 1
ret i64 %e
-; CHECK-LABEL: @test10(
-; CHECK: ret i64 undef
}
@GV = external global i32
define i32 @test11(i1 %tobool) {
+; CHECK-LABEL: @test11(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SHR4:%.*]] = ashr i32 undef, zext (i1 icmp eq (i32* bitcast (i32 (i1)* @test11 to i32*), i32* @GV) to i32)
+; CHECK-NEXT: ret i32 [[SHR4]]
+;
entry:
%shr4 = ashr i32 undef, zext (i1 icmp eq (i32* bitcast (i32 (i1)* @test11 to i32*), i32* @GV) to i32)
ret i32 %shr4
-; CHECK-LABEL: @test11(
-; CHECK: ret i32 0
}
; Test unary ops
define double @test12(double %x) {
+; CHECK-LABEL: @test12(
+; CHECK-NEXT: [[T:%.*]] = fneg double undef
+; CHECK-NEXT: ret double [[T]]
+;
%t = fneg double undef
ret double %t
-; CHECK-LABEL: @test12(
-; CHECK: double undef
}