diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp index 34f18ec7c121..f110a1481d75 100644 --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -85,19 +85,13 @@ class LatticeVal { /// 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 @@ public: 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 @@ public: /// 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 @@ public: 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 @@ public: } 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 @@ private: 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. @@ -996,8 +963,6 @@ void SCCPSolver::visitUnaryOperator(Instruction &I) { 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()); @@ -1032,8 +997,10 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) { } // 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. @@ -1054,7 +1021,6 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) { NonOverdefVal = &V1State; else if (!V2State.isOverdefined()) NonOverdefVal = &V2State; - if (NonOverdefVal) { if (NonOverdefVal->isUnknown()) return; @@ -1174,7 +1140,6 @@ void SCCPSolver::visitLoadInst(LoadInst &I) { 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); @@ -1449,11 +1414,11 @@ void SCCPSolver::Solve() { /// 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 DependsOnSkipped; for (BasicBlock &BB : F) { if (!BBExecutable.count(&BB)) continue; @@ -1468,14 +1433,15 @@ bool SCCPSolver::ResolvedUndefsIn(Function &F) { // 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(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,22 @@ bool SCCPSolver::ResolvedUndefsIn(Function &F) { // 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(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(&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 index 451693f1c90c..a16067fe278a 100644 --- 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 @@ target triple = "x86_64-unknown-linux-gnu" 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 @@ define internal i64 @fn1(i64 %p1) { ; 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 index eeb5b87e0a28..8b4f2590b9f8 100644 --- a/llvm/test/Transforms/IPConstantProp/PR26044.ll +++ b/llvm/test/Transforms/IPConstantProp/PR26044.ll @@ -11,7 +11,8 @@ define void @fn2(i32* %P) { ; 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 @@ define internal i32 @fn1(i32 %p1) { ; 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 index ede1a32c5f7a..4bd096e0a656 100644 --- 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 index 5277d9fa5c6e..7d75240a0d8e 100644 --- a/llvm/test/Transforms/SCCP/apint-bigint2.ll +++ b/llvm/test/Transforms/SCCP/apint-bigint2.ll @@ -18,7 +18,13 @@ define i101 @array() { } ; 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 @@ define i101 @large_aggregate() { 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 index c99ae5820b2d..4ba1f8db30ba 100644 --- 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 index 893331ea9867..d797c7a4d43c 100644 --- 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 index 426e3279661f..08de8dba4046 100644 --- a/llvm/test/Transforms/SCCP/ip-constant-ranges.ll +++ b/llvm/test/Transforms/SCCP/ip-constant-ranges.ll @@ -141,10 +141,12 @@ define double @test_struct({ double, double } %test) { ; 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 index b1660b545652..795a73f1d907 100644 --- a/llvm/test/Transforms/SCCP/ipsccp-basic.ll +++ b/llvm/test/Transforms/SCCP/ipsccp-basic.ll @@ -56,7 +56,9 @@ define void @test3a() { 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 @@ F: 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 @@ define i32 @test10a() nounwind { 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 @@ entry: %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 @@ define i64 @test11a() { %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 @@ 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) diff --git a/llvm/test/Transforms/SCCP/logical-nuke.ll b/llvm/test/Transforms/SCCP/logical-nuke.ll index 6ca16de4489b..5152e126e8fa 100644 --- 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/resolvedundefsin-tracked-fn.ll b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll new file mode 100644 index 000000000000..bfc465f9281e --- /dev/null +++ b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll @@ -0,0 +1,306 @@ +; 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 +} diff --git a/llvm/test/Transforms/SCCP/switch-multiple-undef.ll b/llvm/test/Transforms/SCCP/switch-multiple-undef.ll index 027c9c0c9ba7..df99e4eee7d2 100644 --- 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 @goo() 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 @@ third: 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 index 3fb2d97457d9..6e15d6b2bccd 100644 --- 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 index 7fdcd556dae6..e2a4268596f4 100644 --- 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 @@ define double @test1() { ; 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 @@ bb3.us: ; preds = %control.us 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.outer: ; preds = %bb3, %control.outer 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 @@ bb1: ; preds = %bb1.us-lcssa, %bb1. ; 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 }