diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -3368,9 +3368,10 @@ struct PotentialValuesState : AbstractState { using SetTy = DenseSet; - PotentialValuesState() : IsValidState(true) {} + PotentialValuesState() : IsValidState(true), UndefIsContained(false) {} - PotentialValuesState(bool IsValid) : IsValidState(IsValid) {} + PotentialValuesState(bool IsValid) + : IsValidState(IsValid), UndefIsContained(false) {} /// See AbstractState::isValidState(...) bool isValidState() const override { return IsValidState.isValidState(); } @@ -3399,11 +3400,19 @@ return Set; } + /// Returns whether this state contains an undef value or not. + bool undefIsContained() const { + assert(isValidState() && "This flag shoud not be used when it is invalid!"); + return UndefIsContained; + } + bool operator==(const PotentialValuesState &RHS) const { if (isValidState() != RHS.isValidState()) return false; if (!isValidState() && !RHS.isValidState()) return true; + if (undefIsContained() != RHS.undefIsContained()) + return false; return Set == RHS.getAssumedSet(); } @@ -3431,6 +3440,9 @@ /// Union assumed set with assumed set of the passed state \p PVS. void unionAssumed(const PotentialValuesState &PVS) { unionWith(PVS); } + /// Union assumed set with an undef value. + void unionAssumedWithUndef() { unionWithUndef(); } + /// "Clamp" this state with \p PVS. PotentialValuesState operator^=(const PotentialValuesState &PVS) { IsValidState ^= PVS.IsValidState; @@ -3452,6 +3464,10 @@ indicatePessimisticFixpoint(); } + /// If this state contains both undef and not undef, we can reduce + /// undef to the not undef value. + void reduceUndefValue() { UndefIsContained = UndefIsContained & Set.empty(); } + /// Insert an element into this set. void insert(const MemberTy &C) { if (!isValidState()) @@ -3472,9 +3488,17 @@ } for (const MemberTy &C : R.Set) Set.insert(C); + UndefIsContained |= R.undefIsContained(); + reduceUndefValue(); checkAndInvalidate(); } + /// Take union with an undef value. + void unionWithUndef() { + UndefIsContained = true; + reduceUndefValue(); + } + /// Take intersection with R. void intersectWith(const PotentialValuesState &R) { /// If R is a full set, do nothing. @@ -3491,6 +3515,8 @@ IntersectSet.insert(C); } Set = IntersectSet; + UndefIsContained &= R.undefIsContained(); + reduceUndefValue(); } /// A helper state which indicate whether this state is valid or not. @@ -3498,6 +3524,9 @@ /// Container for potential values SetTy Set; + + /// Flag for undef value + bool UndefIsContained; }; using PotentialConstantIntValuesState = PotentialValuesState; @@ -3544,8 +3573,12 @@ if (getAssumedSet().size() == 1) return cast(ConstantInt::get(getAssociatedValue().getType(), *(getAssumedSet().begin()))); - if (getAssumedSet().size() == 0) + if (getAssumedSet().size() == 0) { + if (undefIsContained()) + return cast( + ConstantInt::get(getAssociatedValue().getType(), 0)); return llvm::None; + } return nullptr; } diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -2165,9 +2165,12 @@ OS << "set-state(< {"; if (!S.isValidState()) OS << "full-set"; - else + else { for (auto &it : S.getAssumedSet()) OS << it << ", "; + if (S.undefIsContained()) + OS << "undef "; + } OS << "} >)"; return OS; diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -7353,10 +7353,7 @@ } if (isa(&V)) { - // Collapse the undef state to 0. - unionAssumed( - APInt(/* numBits */ getAssociatedType()->getIntegerBitWidth(), - /* val */ 0)); + unionAssumedWithUndef(); indicateOptimisticFixpoint(); return; } @@ -7477,6 +7474,20 @@ } } + bool calculateBinaryOperatorAndTakeUnion(const BinaryOperator *BinOp, + const APInt &LHS, const APInt &RHS) { + bool SkipOperation = false; + bool Unsupported = false; + APInt Result = + calculateBinaryOperator(BinOp, LHS, RHS, SkipOperation, Unsupported); + if (Unsupported) + return false; + // If SkipOperation is true, we can ignore this operand pair (L, R). + if (!SkipOperation) + unionAssumed(Result); + return isValidState(); + } + ChangeStatus updateWithICmpInst(Attributor &A, ICmpInst *ICI) { auto AssumedBefore = getAssumed(); Value *LHS = ICI->getOperand(0); @@ -7495,16 +7506,40 @@ const DenseSet &LHSAAPVS = LHSAA.getAssumedSet(); const DenseSet &RHSAAPVS = RHSAA.getAssumedSet(); - // TODO: Handle undef correctly. + // TODO: make use of undef flag to limit potential values aggressively. bool MaybeTrue = false, MaybeFalse = false; - for (const APInt &L : LHSAAPVS) { + const APInt Zero(RHS->getType()->getIntegerBitWidth(), 0); + if (LHSAA.undefIsContained() && RHSAA.undefIsContained()) { + // The result of any comparison between undefs can be soundly replaced + // with undef. + unionAssumedWithUndef(); + } else if (LHSAA.undefIsContained()) { + bool MaybeTrue = false, MaybeFalse = false; for (const APInt &R : RHSAAPVS) { - bool CmpResult = calculateICmpInst(ICI, L, R); + bool CmpResult = calculateICmpInst(ICI, Zero, R); MaybeTrue |= CmpResult; MaybeFalse |= !CmpResult; if (MaybeTrue & MaybeFalse) return indicatePessimisticFixpoint(); } + } else if (RHSAA.undefIsContained()) { + for (const APInt &L : LHSAAPVS) { + bool CmpResult = calculateICmpInst(ICI, L, Zero); + MaybeTrue |= CmpResult; + MaybeFalse |= !CmpResult; + if (MaybeTrue & MaybeFalse) + return indicatePessimisticFixpoint(); + } + } else { + for (const APInt &L : LHSAAPVS) { + for (const APInt &R : RHSAAPVS) { + bool CmpResult = calculateICmpInst(ICI, L, R); + MaybeTrue |= CmpResult; + MaybeFalse |= !CmpResult; + if (MaybeTrue & MaybeFalse) + return indicatePessimisticFixpoint(); + } + } } if (MaybeTrue) unionAssumed(APInt(/* numBits */ 1, /* val */ 1)); @@ -7530,8 +7565,13 @@ if (!RHSAA.isValidState()) return indicatePessimisticFixpoint(); - unionAssumed(LHSAA); - unionAssumed(RHSAA); + if (LHSAA.undefIsContained() && RHSAA.undefIsContained()) + // select i1 *, undef , undef => undef + unionAssumedWithUndef(); + else { + unionAssumed(LHSAA); + unionAssumed(RHSAA); + } return AssumedBefore == getAssumed() ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; } @@ -7547,11 +7587,14 @@ if (!SrcAA.isValidState()) return indicatePessimisticFixpoint(); const DenseSet &SrcAAPVS = SrcAA.getAssumedSet(); - for (const APInt &S : SrcAAPVS) { - APInt T = calculateCastInst(CI, S, ResultBitWidth); - unionAssumed(T); + if (SrcAA.undefIsContained()) + unionAssumedWithUndef(); + else { + for (const APInt &S : SrcAAPVS) { + APInt T = calculateCastInst(CI, S, ResultBitWidth); + unionAssumed(T); + } } - // TODO: Handle undef correctly. return AssumedBefore == getAssumed() ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; } @@ -7573,19 +7616,28 @@ const DenseSet &LHSAAPVS = LHSAA.getAssumedSet(); const DenseSet &RHSAAPVS = RHSAA.getAssumedSet(); + const APInt Zero = APInt(LHS->getType()->getIntegerBitWidth(), 0); - // TODO: Handle undef correctly - for (const APInt &L : LHSAAPVS) { + // TODO: make use of undef flag to limit potential values aggressively. + if (LHSAA.undefIsContained() && RHSAA.undefIsContained()) { + if (!calculateBinaryOperatorAndTakeUnion(BinOp, Zero, Zero)) + return indicatePessimisticFixpoint(); + } else if (LHSAA.undefIsContained()) { for (const APInt &R : RHSAAPVS) { - bool SkipOperation = false; - bool Unsupported = false; - APInt Result = - calculateBinaryOperator(BinOp, L, R, SkipOperation, Unsupported); - if (Unsupported) + if (!calculateBinaryOperatorAndTakeUnion(BinOp, Zero, R)) + return indicatePessimisticFixpoint(); + } + } else if (RHSAA.undefIsContained()) { + for (const APInt &L : LHSAAPVS) { + if (!calculateBinaryOperatorAndTakeUnion(BinOp, L, Zero)) return indicatePessimisticFixpoint(); - // If SkipOperation is true, we can ignore this operand pair (L, R). - if (!SkipOperation) - unionAssumed(Result); + } + } else { + for (const APInt &L : LHSAAPVS) { + for (const APInt &R : RHSAAPVS) { + if (!calculateBinaryOperatorAndTakeUnion(BinOp, L, R)) + return indicatePessimisticFixpoint(); + } } } return AssumedBefore == getAssumed() ? ChangeStatus::UNCHANGED @@ -7600,7 +7652,10 @@ *this, IRPosition::value(*IncomingValue)); if (!PotentialValuesAA.isValidState()) return indicatePessimisticFixpoint(); - unionAssumed(PotentialValuesAA.getAssumed()); + if (PotentialValuesAA.undefIsContained()) + unionAssumedWithUndef(); + else + unionAssumed(PotentialValuesAA.getAssumed()); } return AssumedBefore == getAssumed() ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; @@ -7688,10 +7743,7 @@ } if (isa(&V)) { - // Collapse the undef state to 0. - unionAssumed( - APInt(/* numBits */ getAssociatedType()->getIntegerBitWidth(), - /* val */ 0)); + unionAssumedWithUndef(); indicateOptimisticFixpoint(); return; } diff --git a/llvm/test/Transforms/Attributor/potential.ll b/llvm/test/Transforms/Attributor/potential.ll --- a/llvm/test/Transforms/Attributor/potential.ll +++ b/llvm/test/Transforms/Attributor/potential.ll @@ -493,20 +493,6 @@ ; and returned value of @potential_test10 can be simplified to 0(false) define internal i32 @may_return_undef(i32 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn -; IS__TUNIT____-LABEL: define {{[^@]+}}@may_return_undef -; IS__TUNIT____-SAME: (i32 [[C:%.*]]) -; IS__TUNIT____-NEXT: switch i32 [[C]], label [[OTHERWISE:%.*]] [ -; IS__TUNIT____-NEXT: i32 1, label [[A:%.*]] -; IS__TUNIT____-NEXT: i32 -1, label [[B:%.*]] -; IS__TUNIT____-NEXT: ] -; IS__TUNIT____: a: -; IS__TUNIT____-NEXT: ret i32 1 -; IS__TUNIT____: b: -; IS__TUNIT____-NEXT: ret i32 -1 -; IS__TUNIT____: otherwise: -; IS__TUNIT____-NEXT: ret i32 undef -; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@may_return_undef ; IS__CGSCC____-SAME: (i32 [[C:%.*]]) @@ -532,19 +518,10 @@ } define i1 @potential_test10(i32 %c) { -; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn -; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test10 -; IS__TUNIT_OPM-SAME: (i32 [[C:%.*]]) -; IS__TUNIT_OPM-NEXT: [[RET:%.*]] = call i32 @may_return_undef(i32 [[C]]) [[ATTR0]], [[RNG2:!range !.*]] -; IS__TUNIT_OPM-NEXT: [[CMP:%.*]] = icmp eq i32 [[RET]], 0 -; IS__TUNIT_OPM-NEXT: ret i1 [[CMP]] -; -; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn -; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test10 -; IS__TUNIT_NPM-SAME: (i32 [[C:%.*]]) -; IS__TUNIT_NPM-NEXT: [[RET:%.*]] = call i32 @may_return_undef(i32 [[C]]) [[ATTR0]], [[RNG3:!range !.*]] -; IS__TUNIT_NPM-NEXT: [[CMP:%.*]] = icmp eq i32 [[RET]], 0 -; IS__TUNIT_NPM-NEXT: ret i1 [[CMP]] +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@potential_test10 +; IS__TUNIT____-SAME: (i32 [[C:%.*]]) +; IS__TUNIT____-NEXT: ret i1 false ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test10 @@ -558,15 +535,350 @@ ret i1 %cmp } +define i32 @optimize_undef_1(i1 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@optimize_undef_1 +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) +; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: ret i32 0 +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: ret i32 1 +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@optimize_undef_1 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: ret i32 0 +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: ret i32 1 +; + br i1 %c, label %t, label %f +t: + ret i32 0 +f: + %undef = add i32 undef, 1 + ret i32 %undef +} + +define i32 @optimize_undef_2(i1 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@optimize_undef_2 +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) +; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: ret i32 0 +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: ret i32 -1 +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@optimize_undef_2 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: ret i32 0 +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: ret i32 -1 +; + br i1 %c, label %t, label %f +t: + ret i32 0 +f: + %undef = sub i32 undef, 1 + ret i32 %undef +} + +define i32 @optimize_undef_3(i1 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@optimize_undef_3 +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) +; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: ret i32 0 +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: ret i32 1 +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@optimize_undef_3 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: ret i32 0 +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: ret i32 1 +; + br i1 %c, label %t, label %f +t: + ret i32 0 +f: + %undef = icmp eq i32 undef, 0 + %undef2 = zext i1 %undef to i32 + ret i32 %undef2 +} + + +; FIXME: returned value can be simplified to 0 +define i32 @potential_test11(i1 %c) { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test11 +; IS__TUNIT_OPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_OPM-NEXT: [[ZERO1:%.*]] = call i32 @optimize_undef_1(i1 [[C]]) [[ATTR0]], [[RNG2:!range !.*]] +; IS__TUNIT_OPM-NEXT: [[ZERO2:%.*]] = call i32 @optimize_undef_2(i1 [[C]]) [[ATTR0]], [[RNG3:!range !.*]] +; IS__TUNIT_OPM-NEXT: [[ACC1:%.*]] = add i32 [[ZERO1]], [[ZERO2]] +; IS__TUNIT_OPM-NEXT: [[ACC2:%.*]] = add i32 [[ACC1]], 0 +; IS__TUNIT_OPM-NEXT: ret i32 [[ACC2]] +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test11 +; IS__TUNIT_NPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_NPM-NEXT: [[ZERO1:%.*]] = call i32 @optimize_undef_1(i1 [[C]]) [[ATTR0]], [[RNG0]] +; IS__TUNIT_NPM-NEXT: [[ZERO2:%.*]] = call i32 @optimize_undef_2(i1 [[C]]) [[ATTR0]], [[RNG3:!range !.*]] +; IS__TUNIT_NPM-NEXT: [[ACC1:%.*]] = add i32 [[ZERO1]], [[ZERO2]] +; IS__TUNIT_NPM-NEXT: [[ACC2:%.*]] = add i32 [[ACC1]], 0 +; IS__TUNIT_NPM-NEXT: ret i32 [[ACC2]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test11 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: [[ZERO1:%.*]] = call i32 @optimize_undef_1(i1 [[C]]) +; IS__CGSCC____-NEXT: [[ZERO2:%.*]] = call i32 @optimize_undef_2(i1 [[C]]) +; IS__CGSCC____-NEXT: [[ZERO3:%.*]] = call i32 @optimize_undef_3(i1 [[C]]) +; IS__CGSCC____-NEXT: [[ACC1:%.*]] = add i32 [[ZERO1]], [[ZERO2]] +; IS__CGSCC____-NEXT: [[ACC2:%.*]] = add i32 [[ACC1]], [[ZERO3]] +; IS__CGSCC____-NEXT: ret i32 [[ACC2]] +; + %zero1 = call i32 @optimize_undef_1(i1 %c) + %zero2 = call i32 @optimize_undef_2(i1 %c) + %zero3 = call i32 @optimize_undef_3(i1 %c) + %acc1 = add i32 %zero1, %zero2 + %acc2 = add i32 %acc1, %zero3 + ret i32 %acc2 +} + +define i32 @optimize_poison_1(i1 %c) { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@optimize_poison_1 +; IS__TUNIT_OPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_OPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT_OPM: t: +; IS__TUNIT_OPM-NEXT: ret i32 0 +; IS__TUNIT_OPM: f: +; IS__TUNIT_OPM-NEXT: ret i32 -1 +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@optimize_poison_1 +; IS__TUNIT_NPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_NPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT_NPM: t: +; IS__TUNIT_NPM-NEXT: ret i32 0 +; IS__TUNIT_NPM: f: +; IS__TUNIT_NPM-NEXT: ret i32 undef +; +; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@optimize_poison_1 +; IS__CGSCC_OPM-SAME: (i1 [[C:%.*]]) +; IS__CGSCC_OPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC_OPM: t: +; IS__CGSCC_OPM-NEXT: ret i32 0 +; IS__CGSCC_OPM: f: +; IS__CGSCC_OPM-NEXT: ret i32 -1 +; +; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@optimize_poison_1 +; IS__CGSCC_NPM-SAME: (i1 [[C:%.*]]) +; IS__CGSCC_NPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC_NPM: t: +; IS__CGSCC_NPM-NEXT: ret i32 0 +; IS__CGSCC_NPM: f: +; IS__CGSCC_NPM-NEXT: ret i32 undef +; + br i1 %c, label %t, label %f +t: + ret i32 0 +f: + %poison = sub nuw i32 0, 1 + ret i32 %poison +} + +; FIXME: returned value can be simplified to 0 +define i32 @potential_test12(i1 %c) { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test12 +; IS__TUNIT_OPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_OPM-NEXT: [[ZERO:%.*]] = call i32 @optimize_poison_1(i1 [[C]]) [[ATTR0]], [[RNG3]] +; IS__TUNIT_OPM-NEXT: ret i32 [[ZERO]] +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test12 +; IS__TUNIT_NPM-SAME: (i1 [[C:%.*]]) +; IS__TUNIT_NPM-NEXT: ret i32 0 +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test12 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: [[ZERO:%.*]] = call i32 @optimize_poison_1(i1 [[C]]) +; IS__CGSCC____-NEXT: ret i32 [[ZERO]] +; + %zero = call i32 @optimize_poison_1(i1 %c) + ret i32 %zero +} + +; Test 13 +; Do not simplify %ret in the callee to `%c`. +; The potential value of %c is {0, 1} (undef is merged). +; However, we should not simplify `and i32 %c, 3` to `%c` + +define internal i32 @potential_test13_callee(i32 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@potential_test13_callee +; IS__TUNIT____-SAME: (i32 [[C:%.*]]) +; IS__TUNIT____-NEXT: [[RET:%.*]] = and i32 [[C]], 3 +; IS__TUNIT____-NEXT: ret i32 [[RET]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test13_callee +; IS__CGSCC____-SAME: (i32 [[C:%.*]]) +; IS__CGSCC____-NEXT: [[RET:%.*]] = and i32 [[C]], 3 +; IS__CGSCC____-NEXT: ret i32 [[RET]] +; + %ret = and i32 %c, 3 + ret i32 %ret +} + +define i32 @potential_test13_caller1() { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test13_caller1() +; IS__TUNIT_OPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 0) [[ATTR0]], [[RNG2]] +; IS__TUNIT_OPM-NEXT: ret i32 [[RET]] +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test13_caller1() +; IS__TUNIT_NPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 0) [[ATTR0]], [[RNG0]] +; IS__TUNIT_NPM-NEXT: ret i32 [[RET]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test13_caller1() +; IS__CGSCC____-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 0) +; IS__CGSCC____-NEXT: ret i32 [[RET]] +; + %ret = call i32 @potential_test13_callee(i32 0) + ret i32 %ret +} + +define i32 @potential_test13_caller2() { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test13_caller2() +; IS__TUNIT_OPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 1) [[ATTR0]], [[RNG2]] +; IS__TUNIT_OPM-NEXT: ret i32 [[RET]] +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test13_caller2() +; IS__TUNIT_NPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 1) [[ATTR0]], [[RNG0]] +; IS__TUNIT_NPM-NEXT: ret i32 [[RET]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test13_caller2() +; IS__CGSCC____-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 1) +; IS__CGSCC____-NEXT: ret i32 [[RET]] +; + %ret = call i32 @potential_test13_callee(i32 1) + ret i32 %ret +} + +define i32 @potential_test13_caller3() { +; IS__TUNIT_OPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@potential_test13_caller3() +; IS__TUNIT_OPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) [[ATTR0]], [[RNG2]] +; IS__TUNIT_OPM-NEXT: ret i32 [[RET]] +; +; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@potential_test13_caller3() +; IS__TUNIT_NPM-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) [[ATTR0]], [[RNG0]] +; IS__TUNIT_NPM-NEXT: ret i32 [[RET]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test13_caller3() +; IS__CGSCC____-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) +; IS__CGSCC____-NEXT: ret i32 [[RET]] +; + %ret = call i32 @potential_test13_callee(i32 undef) + ret i32 %ret +} + +define i1 @potential_test14(i1 %c0, i1 %c1, i1 %c2, i1 %c3) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@potential_test14 +; IS__TUNIT____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) +; IS__TUNIT____-NEXT: [[X0:%.*]] = select i1 [[C0]], i32 0, i32 1 +; IS__TUNIT____-NEXT: [[X1:%.*]] = select i1 [[C1]], i32 [[X0]], i32 undef +; IS__TUNIT____-NEXT: [[Y2:%.*]] = select i1 [[C2]], i32 0, i32 7 +; IS__TUNIT____-NEXT: [[Z3:%.*]] = select i1 [[C3]], i32 [[X1]], i32 [[Y2]] +; IS__TUNIT____-NEXT: [[RET:%.*]] = icmp slt i32 [[Z3]], 7 +; IS__TUNIT____-NEXT: ret i1 [[RET]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test14 +; IS__CGSCC____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) +; IS__CGSCC____-NEXT: [[X0:%.*]] = select i1 [[C0]], i32 0, i32 1 +; IS__CGSCC____-NEXT: [[X1:%.*]] = select i1 [[C1]], i32 [[X0]], i32 undef +; IS__CGSCC____-NEXT: [[Y2:%.*]] = select i1 [[C2]], i32 0, i32 7 +; IS__CGSCC____-NEXT: [[Z3:%.*]] = select i1 [[C3]], i32 [[X1]], i32 [[Y2]] +; IS__CGSCC____-NEXT: [[RET:%.*]] = icmp slt i32 [[Z3]], 7 +; IS__CGSCC____-NEXT: ret i1 [[RET]] +; + %x0 = select i1 %c0, i32 0, i32 1 + %x1 = select i1 %c1, i32 %x0, i32 undef + %y2 = select i1 %c2, i32 0, i32 7 + %z3 = select i1 %c3, i32 %x1, i32 %y2 + %ret = icmp slt i32 %z3, 7 + ret i1 %ret +} + +define i1 @potential_test15(i1 %c0, i1 %c1) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@potential_test15 +; IS__TUNIT____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) +; IS__TUNIT____-NEXT: ret i1 false +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test15 +; IS__CGSCC____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) +; IS__CGSCC____-NEXT: ret i1 false +; + %x0 = select i1 %c0, i32 0, i32 1 + %x1 = select i1 %c1, i32 %x0, i32 undef + %ret = icmp eq i32 %x1, 7 + ret i1 %ret +} + +define i1 @potential_test16(i1 %c0, i1 %c1) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@potential_test16 +; IS__TUNIT____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) +; IS__TUNIT____-NEXT: ret i1 false +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@potential_test16 +; IS__CGSCC____-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) +; IS__CGSCC____-NEXT: ret i1 false +; + %x0 = select i1 %c0, i32 0, i32 undef + %x1 = select i1 %c1, i32 %x0, i32 1 + %ret = icmp eq i32 %x1, 7 + ret i1 %ret +} + ; IS__TUNIT_NPM: !0 = !{i32 0, i32 2} ; IS__TUNIT_NPM: !1 = !{i32 1, i32 4} ; IS__TUNIT_NPM: !2 = !{i32 3, i32 5} -; IS__TUNIT_NPM: !3 = !{i32 -1, i32 2} +; IS__TUNIT_NPM: !3 = !{i32 -1, i32 1} ; IS__TUNIT_NPM-NOT: !4 ; IS__TUNIT_OPM: !0 = !{i32 1, i32 4} ; IS__TUNIT_OPM: !1 = !{i32 3, i32 5} -; IS__TUNIT_OPM: !2 = !{i32 -1, i32 2} -; IS__TUNIT_OPM-NOT: !3 +; IS__TUNIT_OPM: !2 = !{i32 0, i32 2} +; IS__TUNIT_OPM: !3 = !{i32 -1, i32 1} +; IS__TUNIT_OPM-NOT: !4 ; IS__CGSCC____-NOT: !0 diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -644,12 +644,10 @@ } ; Check we merge undef and a constant properly. -; FIXME fold the addition and return the constant. define i8 @caller0() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller0() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller0() @@ -662,8 +660,7 @@ define i8 @caller1() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller1() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller1() @@ -676,8 +673,7 @@ define i8 @caller2() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller2() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller2() @@ -690,8 +686,7 @@ define i8 @caller_middle() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller_middle() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller_middle() @@ -704,8 +699,7 @@ define i8 @caller3() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller3() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller3() @@ -718,8 +712,7 @@ define i8 @caller4() { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@caller4() -; IS__TUNIT____-NEXT: [[C:%.*]] = call i8 @callee() -; IS__TUNIT____-NEXT: ret i8 [[C]] +; IS__TUNIT____-NEXT: ret i8 49 ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@caller4() @@ -730,15 +723,9 @@ ret i8 %c } define internal i8 @callee(i8 %a) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn -; IS__TUNIT____-LABEL: define {{[^@]+}}@callee() -; IS__TUNIT____-NEXT: [[C:%.*]] = add i8 42, 7 -; IS__TUNIT____-NEXT: ret i8 [[C]] -; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@callee() -; IS__CGSCC____-NEXT: [[C:%.*]] = add i8 42, 7 -; IS__CGSCC____-NEXT: ret i8 [[C]] +; IS__CGSCC____-NEXT: ret i8 49 ; %c = add i8 %a, 7 ret i8 %c