diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -85,19 +85,13 @@ /// 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 Val; LatticeValueTy getLatticeValue() const { @@ -109,9 +103,7 @@ 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; } @@ -131,26 +123,15 @@ /// 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; } @@ -170,12 +151,6 @@ 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(); @@ -421,7 +396,7 @@ } 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); @@ -443,14 +418,6 @@ 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. @@ -1032,8 +999,10 @@ } // 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. @@ -1449,10 +1418,8 @@ /// 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) { for (BasicBlock &BB : F) { if (!BBExecutable.count(&BB)) @@ -1475,7 +1442,6 @@ // tracked as precisely as their operands. if (isa(I) || isa(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) { @@ -1495,195 +1461,13 @@ // 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)) 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(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(&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 diff --git a/llvm/test/Transforms/IPConstantProp/PR16052.ll b/llvm/test/Transforms/IPConstantProp/PR16052.ll --- a/llvm/test/Transforms/IPConstantProp/PR16052.ll +++ b/llvm/test/Transforms/IPConstantProp/PR16052.ll @@ -1,4 +1,4 @@ -; 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" @@ -7,7 +7,9 @@ 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: @@ -21,7 +23,8 @@ ; 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: diff --git a/llvm/test/Transforms/IPConstantProp/PR26044.ll b/llvm/test/Transforms/IPConstantProp/PR26044.ll --- a/llvm/test/Transforms/IPConstantProp/PR26044.ll +++ b/llvm/test/Transforms/IPConstantProp/PR26044.ll @@ -11,7 +11,8 @@ ; 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:%.*]] ; @@ -33,7 +34,8 @@ ; 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: diff --git a/llvm/test/Transforms/SCCP/2006-12-19-UndefBug.ll b/llvm/test/Transforms/SCCP/2006-12-19-UndefBug.ll --- a/llvm/test/Transforms/SCCP/2006-12-19-UndefBug.ll +++ b/llvm/test/Transforms/SCCP/2006-12-19-UndefBug.ll @@ -1,8 +1,12 @@ -; 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 ; [#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 ; [#uses=1] + ret i1 %X } diff --git a/llvm/test/Transforms/SCCP/apint-bigint2.ll b/llvm/test/Transforms/SCCP/apint-bigint2.ll --- a/llvm/test/Transforms/SCCP/apint-bigint2.ll +++ b/llvm/test/Transforms/SCCP/apint-bigint2.ll @@ -18,7 +18,13 @@ } ; 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 @@ -29,6 +35,22 @@ 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 diff --git a/llvm/test/Transforms/SCCP/apint-ipsccp3.ll b/llvm/test/Transforms/SCCP/apint-ipsccp3.ll --- a/llvm/test/Transforms/SCCP/apint-ipsccp3.ll +++ b/llvm/test/Transforms/SCCP/apint-ipsccp3.ll @@ -1,23 +1,39 @@ -; 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 } diff --git a/llvm/test/Transforms/SCCP/apint-select.ll b/llvm/test/Transforms/SCCP/apint-select.ll --- a/llvm/test/Transforms/SCCP/apint-select.ll +++ b/llvm/test/Transforms/SCCP/apint-select.ll @@ -1,21 +1,29 @@ -; 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 } - - diff --git a/llvm/test/Transforms/SCCP/ip-constant-ranges.ll b/llvm/test/Transforms/SCCP/ip-constant-ranges.ll --- a/llvm/test/Transforms/SCCP/ip-constant-ranges.ll +++ b/llvm/test/Transforms/SCCP/ip-constant-ranges.ll @@ -141,10 +141,12 @@ ; 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 diff --git a/llvm/test/Transforms/SCCP/ipsccp-basic.ll b/llvm/test/Transforms/SCCP/ipsccp-basic.ll --- a/llvm/test/Transforms/SCCP/ipsccp-basic.ll +++ b/llvm/test/Transforms/SCCP/ipsccp-basic.ll @@ -56,7 +56,9 @@ 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() { @@ -71,9 +73,17 @@ 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 @@ -226,8 +236,11 @@ 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 { @@ -235,7 +248,9 @@ %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 @@ -244,7 +259,8 @@ %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() { @@ -252,9 +268,9 @@ %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) diff --git a/llvm/test/Transforms/SCCP/logical-nuke.ll b/llvm/test/Transforms/SCCP/logical-nuke.ll --- a/llvm/test/Transforms/SCCP/logical-nuke.ll +++ b/llvm/test/Transforms/SCCP/logical-nuke.ll @@ -1,39 +1,47 @@ +; 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 } diff --git a/llvm/test/Transforms/SCCP/switch-multiple-undef.ll b/llvm/test/Transforms/SCCP/switch-multiple-undef.ll --- a/llvm/test/Transforms/SCCP/switch-multiple-undef.ll +++ b/llvm/test/Transforms/SCCP/switch-multiple-undef.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -ipsccp < %s | FileCheck %s declare void @foo() @@ -5,9 +6,25 @@ 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 @@ -18,10 +35,3 @@ 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: } diff --git a/llvm/test/Transforms/SCCP/ub-shift.ll b/llvm/test/Transforms/SCCP/ub-shift.ll --- a/llvm/test/Transforms/SCCP/ub-shift.ll +++ b/llvm/test/Transforms/SCCP/ub-shift.ll @@ -1,68 +1,89 @@ +; 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 diff --git a/llvm/test/Transforms/SCCP/undef-resolve.ll b/llvm/test/Transforms/SCCP/undef-resolve.ll --- a/llvm/test/Transforms/SCCP/undef-resolve.ll +++ b/llvm/test/Transforms/SCCP/undef-resolve.ll @@ -1,12 +1,15 @@ +; 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 } @@ -14,6 +17,72 @@ ; 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 @@ -46,16 +115,13 @@ 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 ] ; [#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 @@ -75,10 +141,10 @@ control: ; preds = %bb0, %control.outer %switchCond.0 = phi i32 [ %A.0.ph, %bb0 ], [ %switchCond.0.ph, %control.outer ] ; [#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 @@ -108,83 +174,105 @@ ; 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 }