diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -569,11 +569,13 @@ /// Return true if this function can prove that V is never undef value /// or poison value. - // + /// + /// If AllowUndef is true, it checks whether the value is not poison only. + /// /// If CtxI and DT are specified this method performs flow-sensitive analysis /// and returns true if it is guaranteed to be never undef or poison /// immediately before the CtxI. - bool isGuaranteedNotToBeUndefOrPoison(const Value *V, + bool isGuaranteedNotToBeUndefOrPoison(const Value *V, bool AllowUndef = false, const Instruction *CtxI = nullptr, const DominatorTree *DT = nullptr); diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5362,7 +5362,7 @@ /// Given operands for a Freeze, see if we can fold the result. static Value *SimplifyFreezeInst(Value *Op0, const SimplifyQuery &Q) { // Use a utility function defined in ValueTracking. - if (llvm::isGuaranteedNotToBeUndefOrPoison(Op0, Q.CxtI, Q.DT)) + if (llvm::isGuaranteedNotToBeUndefOrPoison(Op0, false, Q.CxtI, Q.DT)) return Op0; // We have room for improvement. return nullptr; diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -1797,6 +1797,10 @@ } } break; + case Instruction::Freeze: + if (isGuaranteedNotToBeUndefOrPoison(I->getOperand(0), true, Q.CxtI, Q.DT)) + computeKnownBits(I->getOperand(0), Known, Depth + 1, Q); + break; } } @@ -2461,6 +2465,13 @@ if (AllNonZeroConstants) return true; } + // freeze + else if (const FreezeInst *FI = dyn_cast(V)) { + auto Op = FI->getOperand(0); + if (isKnownNonZero(Op, Depth, Q) && + isGuaranteedNotToBeUndefOrPoison(Op, true, Q.CxtI, Q.DT)) + return true; + } KnownBits Known(BitWidth); computeKnownBits(V, Known, Depth, Q); @@ -4525,7 +4536,7 @@ return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch); } -bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, +bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, bool AllowUndef, const Instruction *CtxI, const DominatorTree *DT) { // If the value is a freeze instruction, then it can never @@ -4536,9 +4547,14 @@ // nor poison if their arguments are not poison/undef. // TODO: Deal with other Constant subclasses. - if (isa(V) || isa(V)) + if (isa(V) || isa(V) || isa(V) || + isa(V)) return true; + if (auto BI = dyn_cast(V)) + return isGuaranteedNotToBeUndefOrPoison(BI->getOperand(0), AllowUndef, CtxI, + DT); + if (auto PN = dyn_cast(V)) { if (llvm::all_of(PN->incoming_values(), [](const Use &U) { return isa(U.get()); @@ -4547,14 +4563,15 @@ } if (auto II = dyn_cast(V)) { - if (llvm::all_of(II->operands(), [](const Value *V) { - return isGuaranteedNotToBeUndefOrPoison(V); + if (llvm::all_of(II->operands(), [&](const Value *V) { + return isGuaranteedNotToBeUndefOrPoison(V, AllowUndef, CtxI, DT); })) return true; } if (auto I = dyn_cast(V)) { - if (programUndefinedIfFullPoison(I) && I->getType()->isIntegerTy(1)) + if (AllowUndef && programUndefinedIfFullPoison(I) && + I->getType()->isIntegerTy(1)) // Note: once we have an agreement that poison is a value-wise concept, // we can remove the isIntegerTy(1) constraint. return true; @@ -4564,6 +4581,35 @@ if (!CtxI || !CtxI->getParent() || !DT) return false; + // Retrieves operands from I = '(cmp op1 op2) and/or (cmp op3 op4) and/or ..', + // and checks whether V is in [op1, op2, op3, op4, ..] + auto ComparesValue = [V](const Value *Expr) { + auto I = dyn_cast(Expr); + SmallVector Worklist; + SmallPtrSet Visited; + Worklist.push_back(I); + Visited.insert(I); + + while (!Worklist.empty()) { + const Instruction *NextI = Worklist.pop_back_val(); + if (auto II = dyn_cast(NextI)) { + if (II->getOperand(0) == V || II->getOperand(1) == V) + return true; + } else if (auto BI = dyn_cast(NextI)) { + if (BI->getOpcode() == Instruction::And || + BI->getOpcode() == Instruction::Or) { + auto Op1 = dyn_cast(BI->getOperand(0)); + auto Op2 = dyn_cast(BI->getOperand(0)); + if (Op1 && Visited.insert(Op1).second) + Worklist.push_back(Op1); + if (Op2 && Visited.insert(Op2).second) + Worklist.push_back(Op2); + } + } + } + return false; + }; + // If V is used as a branch condition before reaching CtxI, V cannot be // undef or poison. // br V, BB1, BB2 @@ -4573,11 +4619,21 @@ while (Dominator) { auto *TI = Dominator->getBlock()->getTerminator(); - if (auto BI = dyn_cast(TI)) { - if (BI->isConditional() && BI->getCondition() == V) + const Value *Cond = nullptr; + if (auto BI = dyn_cast(TI)) + Cond = BI->isConditional() ? BI->getCondition() : nullptr; + else if (auto SI = dyn_cast(TI)) + Cond = SI->getCondition(); + + if (Cond) { + if (Cond == V) return true; - } else if (auto SI = dyn_cast(TI)) { - if (SI->getCondition() == V) + else if (AllowUndef && ComparesValue(Cond)) + // br (V != 0), BB1, BB2 + // BB1: + // CtxI ; V cannot be poison here. + // ; Note that V can partially be undef, e.g. V = (undef | 1). + // ; In that case, 'V == 0' becomes simply false. return true; } diff --git a/llvm/test/Transforms/InstSimplify/freeze.ll b/llvm/test/Transforms/InstSimplify/freeze.ll --- a/llvm/test/Transforms/InstSimplify/freeze.ll +++ b/llvm/test/Transforms/InstSimplify/freeze.ll @@ -19,6 +19,32 @@ ret i32 %x } +define float @make_const2() { +; CHECK-LABEL: @make_const2( +; CHECK-NEXT: ret float 1.000000e+01 +; + %x = freeze float 10.0 + ret float %x +} + +@glb = constant i32 0 + +define i32* @make_const_glb() { +; CHECK-LABEL: @make_const_glb( +; CHECK-NEXT: ret i32* @glb +; + %k = freeze i32* @glb + ret i32* %k +} + +define i32()* @make_const_fn() { +; CHECK-LABEL: @make_const_fn( +; CHECK-NEXT: ret i32 ()* @make_const +; + %k = freeze i32()* @make_const + ret i32()* %k +} + define i1 @brcond(i1 %c, i1 %c2) { ; CHECK-LABEL: @brcond( ; CHECK-NEXT: br i1 [[C:%.*]], label [[A:%.*]], label [[B:%.*]] diff --git a/llvm/test/Transforms/InstSimplify/known-non-zero.ll b/llvm/test/Transforms/InstSimplify/known-non-zero.ll --- a/llvm/test/Transforms/InstSimplify/known-non-zero.ll +++ b/llvm/test/Transforms/InstSimplify/known-non-zero.ll @@ -145,3 +145,47 @@ %inc = add nuw nsw i32 %shift.0, 1 br label %for.cond } + +define i1 @freeze_nonzero(i8 %x, i8 %mask) { +; CHECK-LABEL: @freeze_nonzero( +; CHECK-NEXT: [[Y:%.*]] = or i8 [[X:%.*]], [[MASK:%.*]] +; CHECK-NEXT: [[C:%.*]] = icmp ne i8 [[Y]], 0 +; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: ret i1 false +; CHECK: B: +; CHECK-NEXT: ret i1 false +; + %y = or i8 %x, %mask + %c = icmp ne i8 %y, 0 + br i1 %c, label %A, label %B +A: + %fr = freeze i8 %y + %c2 = icmp eq i8 %fr, 0 + ret i1 %c2 +B: + ret i1 0 +} + +define i1 @freeze_nonzero2(i8 %x, i8 %mask, i1 %mycond) { +; CHECK-LABEL: @freeze_nonzero2( +; CHECK-NEXT: [[Y:%.*]] = or i8 [[X:%.*]], [[MASK:%.*]] +; CHECK-NEXT: [[C:%.*]] = icmp ne i8 [[Y]], 0 +; CHECK-NEXT: [[CC:%.*]] = and i1 [[C]], [[MYCOND:%.*]] +; CHECK-NEXT: br i1 [[CC]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: ret i1 false +; CHECK: B: +; CHECK-NEXT: ret i1 false +; + %y = or i8 %x, %mask + %c = icmp ne i8 %y, 0 + %cc = and i1 %c, %mycond + br i1 %cc, label %A, label %B +A: + %fr = freeze i8 %y + %c2 = icmp eq i8 %fr, 0 + ret i1 %c2 +B: + ret i1 0 +}