diff --git a/include/llvm/IR/Metadata.h b/include/llvm/IR/Metadata.h index 3bf6d38d311d..27a5d6f80041 100644 --- a/include/llvm/IR/Metadata.h +++ b/include/llvm/IR/Metadata.h @@ -693,6 +693,7 @@ class MDNode : public Metadata { static AAMDNodes getMostGenericAA(const AAMDNodes &A, const AAMDNodes &B); static MDNode *getMostGenericFPMath(MDNode *A, MDNode *B); static MDNode *getMostGenericRange(MDNode *A, MDNode *B); + static MDNode *getMostGenericAliasScope(MDNode *A, MDNode *B); }; /// \brief Uniquable metadata node. diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 30280033ee20..b9ea8489aeb2 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -287,6 +287,9 @@ void initializeStackMapLivenessPass(PassRegistry&); void initializeMachineCombinerPass(PassRegistry &); void initializeLoadCombinePass(PassRegistry&); void initializeRewriteSymbolsPass(PassRegistry&); + +// Specific to the rust-lang llvm branch: +void initializeNullCheckEliminationPass(PassRegistry&); } #endif diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index 2e8feab6d29d..1764652e37e7 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -167,6 +167,9 @@ namespace { (void) llvm::createSeparateConstOffsetFromGEPPass(); (void) llvm::createRewriteSymbolsPass(); + // Specific to the rust-lang llvm branch: + (void) llvm::createNullCheckEliminationPass(); + (void)new llvm::IntervalPartition(); (void)new llvm::ScalarEvolution(); ((llvm::Function*)nullptr)->viewCFGOnly(); diff --git a/include/llvm/Transforms/Scalar.h b/include/llvm/Transforms/Scalar.h index 5dcd89948759..12fd59b57919 100644 --- a/include/llvm/Transforms/Scalar.h +++ b/include/llvm/Transforms/Scalar.h @@ -405,6 +405,13 @@ createSeparateConstOffsetFromGEPPass(const TargetMachine *TM = nullptr, // BasicBlockPass *createLoadCombinePass(); +// Specific to the rust-lang llvm branch: +//===----------------------------------------------------------------------===// +// +// NullCheckElimination - Eliminate null checks. +// +FunctionPass *createNullCheckEliminationPass(); + } // End llvm namespace #endif diff --git a/lib/Analysis/TypeBasedAliasAnalysis.cpp b/lib/Analysis/TypeBasedAliasAnalysis.cpp index 085ce920139b..ff8955870cb5 100644 --- a/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ b/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -623,8 +623,8 @@ void Instruction::getAAMetadata(AAMDNodes &N, bool Merge) const { N.TBAA = getMetadata(LLVMContext::MD_tbaa); if (Merge) - N.Scope = - MDNode::intersect(N.Scope, getMetadata(LLVMContext::MD_alias_scope)); + N.Scope = MDNode::getMostGenericAliasScope( + N.Scope, getMetadata(LLVMContext::MD_alias_scope)); else N.Scope = getMetadata(LLVMContext::MD_alias_scope); diff --git a/lib/IR/Metadata.cpp b/lib/IR/Metadata.cpp index 2c6b332dc8ee..63e5730f954e 100644 --- a/lib/IR/Metadata.cpp +++ b/lib/IR/Metadata.cpp @@ -826,6 +826,28 @@ MDNode *MDNode::intersect(MDNode *A, MDNode *B) { return getOrSelfReference(A->getContext(), MDs); } +MDNode *MDNode::getMostGenericAliasScope(MDNode *A, MDNode *B) { + if (!A || !B) + return nullptr; + + SmallVector MDs(B->op_begin(), B->op_end()); + for (unsigned i = 0, ie = A->getNumOperands(); i != ie; ++i) { + Metadata *MD = A->getOperand(i); + bool insert = true; + for (unsigned j = 0, je = B->getNumOperands(); j != je; ++j) + if (MD == B->getOperand(j)) { + insert = false; + break; + } + if (insert) + MDs.push_back(MD); + } + + // FIXME: This preserves long-standing behaviour, but is it really the right + // behaviour? Or was that an unintended side-effect of node uniquing? + return getOrSelfReference(A->getContext(), MDs); +} + MDNode *MDNode::getMostGenericFPMath(MDNode *A, MDNode *B) { if (!A || !B) return nullptr; diff --git a/lib/Support/regcomp.c b/lib/Support/regcomp.c index 0b5b765f89e1..b79692966473 100644 --- a/lib/Support/regcomp.c +++ b/lib/Support/regcomp.c @@ -49,6 +49,14 @@ #include "regcclass.h" #include "regcname.h" +#include "llvm/Config/config.h" +#if HAVE_STDINT_H +#include +#else +/* Pessimistically bound memory use */ +#define SIZE_MAX UINT_MAX +#endif + /* * parse structure, passed up and down to avoid global variables and * other clumsinesses @@ -1069,6 +1077,8 @@ allocset(struct parse *p) p->ncsalloc += CHAR_BIT; nc = p->ncsalloc; + if (nc > SIZE_MAX / sizeof(cset)) + goto nomem; assert(nc % CHAR_BIT == 0); nbytes = nc / CHAR_BIT * css; @@ -1412,6 +1422,11 @@ enlarge(struct parse *p, sopno size) if (p->ssize >= size) return; + if ((unsigned long)size > SIZE_MAX / sizeof(sop)) { + SETERROR(REG_ESPACE); + return; + } + sp = (sop *)realloc(p->strip, size*sizeof(sop)); if (sp == NULL) { SETERROR(REG_ESPACE); @@ -1428,6 +1443,12 @@ static void stripsnug(struct parse *p, struct re_guts *g) { g->nstates = p->slen; + if ((unsigned long)p->slen > SIZE_MAX / sizeof(sop)) { + g->strip = p->strip; + SETERROR(REG_ESPACE); + return; + } + g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); if (g->strip == NULL) { SETERROR(REG_ESPACE); diff --git a/lib/Target/AArch64/AArch64ISelLowering.cpp b/lib/Target/AArch64/AArch64ISelLowering.cpp index 19f51ce9450c..399b5eeaf5f5 100644 --- a/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -6287,6 +6287,8 @@ static SDValue EmitVectorComparison(SDValue LHS, SDValue RHS, AArch64CC::CondCode CC, bool NoNans, EVT VT, SDLoc dl, SelectionDAG &DAG) { EVT SrcVT = LHS.getValueType(); + assert(VT.getSizeInBits() == SrcVT.getSizeInBits() && + "function only supposed to emit natural comparisons"); BuildVectorSDNode *BVN = dyn_cast(RHS.getNode()); APInt CnstBits(VT.getSizeInBits(), 0); @@ -6381,13 +6383,15 @@ SDValue AArch64TargetLowering::LowerVSETCC(SDValue Op, ISD::CondCode CC = cast(Op.getOperand(2))->get(); SDValue LHS = Op.getOperand(0); SDValue RHS = Op.getOperand(1); + EVT CmpVT = LHS.getValueType().changeVectorElementTypeToInteger(); SDLoc dl(Op); if (LHS.getValueType().getVectorElementType().isInteger()) { assert(LHS.getValueType() == RHS.getValueType()); AArch64CC::CondCode AArch64CC = changeIntCCToAArch64CC(CC); - return EmitVectorComparison(LHS, RHS, AArch64CC, false, Op.getValueType(), - dl, DAG); + SDValue Cmp = + EmitVectorComparison(LHS, RHS, AArch64CC, false, CmpVT, dl, DAG); + return DAG.getSExtOrTrunc(Cmp, dl, Op.getValueType()); } assert(LHS.getValueType().getVectorElementType() == MVT::f32 || @@ -6401,19 +6405,21 @@ SDValue AArch64TargetLowering::LowerVSETCC(SDValue Op, bool NoNaNs = getTargetMachine().Options.NoNaNsFPMath; SDValue Cmp = - EmitVectorComparison(LHS, RHS, CC1, NoNaNs, Op.getValueType(), dl, DAG); + EmitVectorComparison(LHS, RHS, CC1, NoNaNs, CmpVT, dl, DAG); if (!Cmp.getNode()) return SDValue(); if (CC2 != AArch64CC::AL) { SDValue Cmp2 = - EmitVectorComparison(LHS, RHS, CC2, NoNaNs, Op.getValueType(), dl, DAG); + EmitVectorComparison(LHS, RHS, CC2, NoNaNs, CmpVT, dl, DAG); if (!Cmp2.getNode()) return SDValue(); - Cmp = DAG.getNode(ISD::OR, dl, Cmp.getValueType(), Cmp, Cmp2); + Cmp = DAG.getNode(ISD::OR, dl, CmpVT, Cmp, Cmp2); } + Cmp = DAG.getSExtOrTrunc(Cmp, dl, Op.getValueType()); + if (ShouldInvert) return Cmp = DAG.getNOT(dl, Cmp, Cmp.getValueType()); diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp index 90a3d3438f8c..a1de5efb4507 100644 --- a/lib/Target/ARM/ARMISelLowering.cpp +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -4487,6 +4487,7 @@ static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) { SDValue Op0 = Op.getOperand(0); SDValue Op1 = Op.getOperand(1); SDValue CC = Op.getOperand(2); + EVT CmpVT = Op0.getValueType().changeVectorElementTypeToInteger(); EVT VT = Op.getValueType(); ISD::CondCode SetCCOpcode = cast(CC)->get(); SDLoc dl(Op); @@ -4516,8 +4517,8 @@ static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) { TmpOp0 = Op0; TmpOp1 = Op1; Opc = ISD::OR; - Op0 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp1, TmpOp0); - Op1 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp0, TmpOp1); + Op0 = DAG.getNode(ARMISD::VCGT, dl, CmpVT, TmpOp1, TmpOp0); + Op1 = DAG.getNode(ARMISD::VCGT, dl, CmpVT, TmpOp0, TmpOp1); break; case ISD::SETUO: Invert = true; // Fallthrough case ISD::SETO: @@ -4525,8 +4526,8 @@ static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) { TmpOp0 = Op0; TmpOp1 = Op1; Opc = ISD::OR; - Op0 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp1, TmpOp0); - Op1 = DAG.getNode(ARMISD::VCGE, dl, VT, TmpOp0, TmpOp1); + Op0 = DAG.getNode(ARMISD::VCGT, dl, CmpVT, TmpOp1, TmpOp0); + Op1 = DAG.getNode(ARMISD::VCGE, dl, CmpVT, TmpOp0, TmpOp1); break; } } else { @@ -4560,8 +4561,8 @@ static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) { if (AndOp.getNode() && AndOp.getOpcode() == ISD::AND) { Opc = ARMISD::VTST; - Op0 = DAG.getNode(ISD::BITCAST, dl, VT, AndOp.getOperand(0)); - Op1 = DAG.getNode(ISD::BITCAST, dl, VT, AndOp.getOperand(1)); + Op0 = DAG.getNode(ISD::BITCAST, dl, CmpVT, AndOp.getOperand(0)); + Op1 = DAG.getNode(ISD::BITCAST, dl, CmpVT, AndOp.getOperand(1)); Invert = !Invert; } } @@ -4587,22 +4588,24 @@ static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) { if (SingleOp.getNode()) { switch (Opc) { case ARMISD::VCEQ: - Result = DAG.getNode(ARMISD::VCEQZ, dl, VT, SingleOp); break; + Result = DAG.getNode(ARMISD::VCEQZ, dl, CmpVT, SingleOp); break; case ARMISD::VCGE: - Result = DAG.getNode(ARMISD::VCGEZ, dl, VT, SingleOp); break; + Result = DAG.getNode(ARMISD::VCGEZ, dl, CmpVT, SingleOp); break; case ARMISD::VCLEZ: - Result = DAG.getNode(ARMISD::VCLEZ, dl, VT, SingleOp); break; + Result = DAG.getNode(ARMISD::VCLEZ, dl, CmpVT, SingleOp); break; case ARMISD::VCGT: - Result = DAG.getNode(ARMISD::VCGTZ, dl, VT, SingleOp); break; + Result = DAG.getNode(ARMISD::VCGTZ, dl, CmpVT, SingleOp); break; case ARMISD::VCLTZ: - Result = DAG.getNode(ARMISD::VCLTZ, dl, VT, SingleOp); break; + Result = DAG.getNode(ARMISD::VCLTZ, dl, CmpVT, SingleOp); break; default: - Result = DAG.getNode(Opc, dl, VT, Op0, Op1); + Result = DAG.getNode(Opc, dl, CmpVT, Op0, Op1); } } else { - Result = DAG.getNode(Opc, dl, VT, Op0, Op1); + Result = DAG.getNode(Opc, dl, CmpVT, Op0, Op1); } + Result = DAG.getSExtOrTrunc(Result, dl, VT); + if (Invert) Result = DAG.getNOT(dl, Result, VT); diff --git a/lib/Transforms/IPO/PassManagerBuilder.cpp b/lib/Transforms/IPO/PassManagerBuilder.cpp index 0414caa61fca..906d56fa6dcc 100644 --- a/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -220,6 +220,8 @@ void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) { MPM.add(createEarlyCSEPass()); // Catch trivial redundancies MPM.add(createJumpThreadingPass()); // Thread jumps. MPM.add(createCorrelatedValuePropagationPass()); // Propagate conditionals + // Specific to the rust-lang llvm branch: + MPM.add(createNullCheckEliminationPass()); // Eliminate null checks MPM.add(createCFGSimplificationPass()); // Merge & remove BBs MPM.add(createInstructionCombiningPass()); // Combine silly seq's addExtensionsToPM(EP_Peephole, MPM); @@ -255,6 +257,8 @@ void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) { addExtensionsToPM(EP_Peephole, MPM); MPM.add(createJumpThreadingPass()); // Thread jumps MPM.add(createCorrelatedValuePropagationPass()); + // Specific to the rust-lang llvm branch: + MPM.add(createNullCheckEliminationPass()); // Eliminate null checks MPM.add(createDeadStoreEliminationPass()); // Delete dead stores addExtensionsToPM(EP_ScalarOptimizerLate, MPM); diff --git a/lib/Transforms/Scalar/CMakeLists.txt b/lib/Transforms/Scalar/CMakeLists.txt index b3ee11ed67cd..4218a5bbbbf8 100644 --- a/lib/Transforms/Scalar/CMakeLists.txt +++ b/lib/Transforms/Scalar/CMakeLists.txt @@ -24,6 +24,7 @@ add_llvm_library(LLVMScalarOpts LowerAtomic.cpp MemCpyOptimizer.cpp MergedLoadStoreMotion.cpp + NullCheckElimination.cpp PartiallyInlineLibCalls.cpp Reassociate.cpp Reg2Mem.cpp diff --git a/lib/Transforms/Scalar/NullCheckElimination.cpp b/lib/Transforms/Scalar/NullCheckElimination.cpp new file mode 100644 index 000000000000..f0fa3aed6ff9 --- /dev/null +++ b/lib/Transforms/Scalar/NullCheckElimination.cpp @@ -0,0 +1,404 @@ +//===-- NullCheckElimination.cpp - Null Check Elimination Pass ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Pass.h" +using namespace llvm; + +#define DEBUG_TYPE "null-check-elimination" + +namespace { + struct NullCheckElimination : public FunctionPass { + static char ID; + NullCheckElimination() : FunctionPass(ID) { + initializeNullCheckEliminationPass(*PassRegistry::getPassRegistry()); + } + bool runOnFunction(Function &F) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + } + + private: + static const unsigned kPhiLimit = 16; + typedef SmallPtrSet SmallPhiSet; + + enum CmpKind { + /// A null check of an unconditionally nonnull-or-poison value. + NullCheckDefiniteCmp, + + /// A null check of the phi representing a nontrivial inbounds recurrence, + /// which is not known to be unconditionally nonnull-or-poison. + NullCheckRecurrenceCmp, + + /// A comparison of the phi representing a nontrivial inbounds recurrence + /// with an inbounds GEP derived from the base of the recurrence, which + /// will typically represent a bound on the recurrence. + RecurrencePhiBoundCmp, + }; + + enum CmpPred { + CmpEq, + CmpNe, + }; + + struct CmpDesc { + CmpDesc(CmpKind k, CmpPred p, Use *u, Value *v) + : kind(k), pred(p), use(u), ptrValue(v) { } + + CmpKind kind; + CmpPred pred; + Use *use; + Value *ptrValue; + }; + + typedef SmallVector CmpDescVec; + + bool isNonNullOrPoisonPhi(SmallPhiSet *VisitedPhis, PHINode*); + Value *isNontrivialInBoundsRecurrence(PHINode*); + + bool classifyCmp(CmpDescVec*, Use*); + bool findRelevantCmps(CmpDescVec*, Use*); + + bool blockContainsLoadDerivedFrom(BasicBlock*, Value*); + + /// Tracks values that are unconditionally nonnull-or-poison by definition, + /// but not values that are known nonnull-or-poison in a given context by + /// their uses, e.g. in recurrences. + DenseSet NonNullOrPoisonValues; + + /// Tracks values that are bases of nontrivial inbounds recurrences. + DenseSet InBoundsRecurrenceBases; + + /// Maps phis that correspond to nontrivial inbounds recurrences to their + /// base values. + DenseMap InBoundsRecurrenceBaseMap; + }; +} + +char NullCheckElimination::ID = 0; +INITIALIZE_PASS_BEGIN(NullCheckElimination, + "null-check-elimination", + "Null Check Elimination", + false, false) +INITIALIZE_PASS_END(NullCheckElimination, + "null-check-elimination", + "Null Check Elimination", + false, false) + +FunctionPass *llvm::createNullCheckEliminationPass() { + return new NullCheckElimination(); +} + +static GetElementPtrInst *castToInBoundsGEP(Value *V) { + auto *GEP = dyn_cast(V); + if (!GEP || !GEP->isInBounds()) + return nullptr; + return GEP; +} + +static bool isZeroConstant(Value *V) { + auto *C = dyn_cast(V); + return C && C->isZeroValue(); +} + +bool NullCheckElimination::runOnFunction(Function &F) { + if (skipOptnoneFunction(F)) + return false; + + bool Changed = false; + + // Collect arguments with the `nonnull` attribute. + for (auto &Arg : F.args()) { + if (Arg.hasNonNullAttr()) + NonNullOrPoisonValues.insert(&Arg); + } + + // Collect instructions that definitely produce nonnull-or-poison values. At + // the moment, this is restricted to inbounds GEPs, and phis that are derived + // entirely from nonnull-or-poison-values (including other phis that are + // themselves derived from the same). + for (auto &BB : F) { + for (auto &I : BB) { + if (auto *GEP = castToInBoundsGEP(&I)) { + NonNullOrPoisonValues.insert(GEP); + } else if (auto *PN = dyn_cast(&I)) { + SmallPhiSet VisitedPHIs; + if (isNonNullOrPoisonPhi(&VisitedPHIs, PN)) + NonNullOrPoisonValues.insert(PN); + + if (auto *BaseV = isNontrivialInBoundsRecurrence(PN)) { + InBoundsRecurrenceBases.insert(BaseV); + InBoundsRecurrenceBaseMap[PN] = BaseV; + } + } + } + } + + for (auto &BB : F) { + // This could also be extended to handle SwitchInst, but using a SwitchInst + // for a null check seems unlikely. + auto *BI = dyn_cast(BB.getTerminator()); + if (!BI || BI->isUnconditional()) + continue; + + // The first operand of a conditional branch is the condition. + CmpDescVec Cmps; + if (!findRelevantCmps(&Cmps, &BI->getOperandUse(0))) + continue; + + for (auto &Cmp : Cmps) { + // We are only tracking comparisons of inbounds recurrence phis with their + // bounds so that we can eliminate null checks based on them, which are of + // kind NullCheckRecurrenceCmp. We can't use a lone RecurrencePhiBoundCmp + // to perform any optimizations. + if (Cmp.kind == RecurrencePhiBoundCmp) + continue; + + if (Cmp.kind == NullCheckRecurrenceCmp) { + // Look for a matching RecurrencePhiBoundCmp. If one exists, then we can + // be sure that this branch condition depends on the recurrence. Since + // both the bounds and the recurrence successor value are inbounds, and + // they are both derived from the base, the base being null would imply + // that the bounds and recurrence successor values are poison. + bool FoundMatchingCmp = false; + for (auto &OtherCmp : Cmps) { + if (OtherCmp.kind == RecurrencePhiBoundCmp && + OtherCmp.ptrValue == Cmp.ptrValue) { + FoundMatchingCmp = true; + break; + } + } + if (!FoundMatchingCmp) + continue; + } + + BasicBlock *NonNullBB; + if (Cmp.pred == CmpEq) { + // If the comparison instruction is checking for equality with null then + // the pointer is nonnull on the `false` branch. + NonNullBB = BI->getSuccessor(1); + } else { + // Otherwise, if the comparison instruction is checking for inequality + // with null, the pointer is nonnull on the `true` branch. + NonNullBB = BI->getSuccessor(0); + } + + // This is a crude approximation of control dependence: if the branch + // target has a single predecessor edge, then it must be control- + // dependent on the branch. + if (!NonNullBB->getSinglePredecessor()) + continue; + + // Due to the semantics of poison values in LLVM, we have to check that + // there is actually some externally visible side effect that is dependent + // on the poison value. Since poison values are otherwise treated as + // undef, and a load of undef is undefined behavior (which is externally + // visible), it suffices to look for a load of the nonnull-or-poison + // value. + // + // This could be extended to any block control-dependent on this branch of + // the null check, it's unclear if that will actually catch more cases in + // real code. + if (blockContainsLoadDerivedFrom(NonNullBB, Cmp.ptrValue)) { + Type *BoolTy = Type::getInt1Ty(F.getContext()); + Value *NewV = ConstantInt::get(BoolTy, Cmp.pred == CmpNe); + Cmp.use->set(NewV); + Changed = true; + } + } + } + + NonNullOrPoisonValues.clear(); + InBoundsRecurrenceBases.clear(); + InBoundsRecurrenceBaseMap.clear(); + + return Changed; +} + +/// Checks whether a phi is derived from known nonnnull-or-poison values, +/// including other phis that are derived from the same. May return `false` +/// conservatively in some cases, e.g. if exploring a large cycle of phis. +/// +/// This function may also insert any inbounds GEPs that it finds into +/// NonNullOrPoisonValues. +bool +NullCheckElimination::isNonNullOrPoisonPhi(SmallPhiSet *VisitedPhis, + PHINode *PN) { + // If we've already seen this phi, return `true`, even though it may not be + // nonnull, since some other operand in a cycle of phis may invalidate the + // optimistic assumption that the entire cycle is nonnull, including this phi. + if (!VisitedPhis->insert(PN).second) + return true; + + // Use a sensible limit to avoid iterating over long chains of phis that are + // unlikely to be nonnull. + if (VisitedPhis->size() >= kPhiLimit) + return false; + + unsigned numOperands = PN->getNumOperands(); + for (unsigned i = 0; i < numOperands; ++i) { + Value *SrcValue = PN->getOperand(i); + if (NonNullOrPoisonValues.count(SrcValue)) { + continue; + } else if (auto *GEP = castToInBoundsGEP(SrcValue)) { + NonNullOrPoisonValues.insert(GEP); + } else if (auto *SrcPN = dyn_cast(SrcValue)) { + if (!isNonNullOrPoisonPhi(VisitedPhis, SrcPN)) + return false; + } else { + return false; + } + } + + return true; +} + +/// Determines whether a phi corresponds to an inbounds recurrence where the +/// base is not a known nonnull-or-poison value. Returns the base value, or +/// null if the phi doesn't correspond to such a recurrence. +Value *NullCheckElimination::isNontrivialInBoundsRecurrence(PHINode *PN) { + if (PN->getNumOperands() != 2) + return nullptr; + + Value *BaseV; + GetElementPtrInst *SuccessorI; + if (auto *GEP = castToInBoundsGEP(PN->getOperand(0))) { + BaseV = PN->getOperand(1); + SuccessorI = GEP; + } else if (auto *GEP = castToInBoundsGEP(PN->getOperand(1))) { + BaseV = PN->getOperand(0); + SuccessorI = GEP; + } else { + return nullptr; + } + + if (NonNullOrPoisonValues.count(BaseV) || SuccessorI->getOperand(0) != PN) + return nullptr; + + return BaseV; +} + +/// Determines whether an ICmpInst is one of the forms that is relevant to +/// null check elimination, and then adds a CmpDesc to Cmps when applicable. +/// The ICmpInst is passed as a Use so this Use can be placed into the CmpDesc, +/// but the Use parameter must be a Use of an ICmpInst. +bool NullCheckElimination::classifyCmp(CmpDescVec *Cmps, Use *U) { + auto *CI = cast(U); + if (!CI->isEquality()) + return false; + + CmpPred Pred = (CI->getPredicate() == llvm::CmpInst::ICMP_EQ) ? CmpEq : CmpNe; + Value *Op0 = CI->getOperand(0); + Value *Op1 = CI->getOperand(1); + + if (NonNullOrPoisonValues.count(Op0)) { + if (isZeroConstant(Op1)) { + Cmps->push_back(CmpDesc(NullCheckDefiniteCmp, Pred, U, Op0)); + return true; + } + + auto it = InBoundsRecurrenceBaseMap.find(Op1); + if (it == InBoundsRecurrenceBaseMap.end()) + return false; + + auto *GEP = castToInBoundsGEP(Op0); + if (!GEP) + return false; + + auto *BaseV = it->second; + if (GEP->getOperand(0) != BaseV) + return false; + + Cmps->push_back(CmpDesc(RecurrencePhiBoundCmp, Pred, U, Op1)); + return true; + } + + // Since InstCombine or InstSimplify should have canonicalized a comparison + // with `null` to have the `null` in the second operand, we don't need to + // handle the case where Op0 is `null` like we did with Op1 above. + if (NonNullOrPoisonValues.count(Op1)) { + auto it = InBoundsRecurrenceBaseMap.find(Op0); + if (it == InBoundsRecurrenceBaseMap.end()) + return false; + + auto *GEP = castToInBoundsGEP(Op1); + if (!GEP) + return false; + + auto *BaseV = it->second; + if (GEP->getOperand(0) != BaseV) + return false; + + Cmps->push_back(CmpDesc(RecurrencePhiBoundCmp, Pred, U, Op0)); + return true; + } + + if (InBoundsRecurrenceBaseMap.count(Op0)) { + if (isZeroConstant(Op1)) { + Cmps->push_back(CmpDesc(NullCheckRecurrenceCmp, Pred, U, Op0)); + return true; + } + } + + return false; +} + +/// Classifies the comparisons that are relevant to null check elimination, +/// starting from a Use. The CmpDescs of the comparisons are collected in Cmps. +bool NullCheckElimination::findRelevantCmps(CmpDescVec *Cmps, Use *U) { + auto *I = dyn_cast(U->get()); + if (!I) + return false; + + if (isa(I)) + return classifyCmp(Cmps, U); + + unsigned Opcode = I->getOpcode(); + if (Opcode == Instruction::Or || Opcode == Instruction::And) { + bool FoundCmps = findRelevantCmps(Cmps, &I->getOperandUse(0)); + FoundCmps |= findRelevantCmps(Cmps, &I->getOperandUse(1)); + return FoundCmps; + } + + return false; +} + +/// Determines whether `BB` contains a load from `PtrV`, or any inbounds GEP +/// derived from `PtrV`. +bool +NullCheckElimination::blockContainsLoadDerivedFrom(BasicBlock *BB, + Value *PtrV) { + for (auto &I : *BB) { + auto *LI = dyn_cast(&I); + if (!LI) + continue; + + Value *V = LI->getPointerOperand(); + while (1) { + if (V == PtrV) + return true; + + auto *GEP = castToInBoundsGEP(V); + if (!GEP) + break; + + V = GEP->getOperand(0); + } + } + + return false; +} + diff --git a/lib/Transforms/Scalar/Scalar.cpp b/lib/Transforms/Scalar/Scalar.cpp index a16e9e29a1f1..8dcd6720597d 100644 --- a/lib/Transforms/Scalar/Scalar.cpp +++ b/lib/Transforms/Scalar/Scalar.cpp @@ -69,6 +69,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) { initializeTailCallElimPass(Registry); initializeSeparateConstOffsetFromGEPPass(Registry); initializeLoadCombinePass(Registry); + initializeNullCheckEliminationPass(Registry); } void LLVMInitializeScalarOpts(LLVMPassRegistryRef R) { diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp index 08a4b3f3b737..2a84d7e0c484 100644 --- a/lib/Transforms/Utils/Local.cpp +++ b/lib/Transforms/Utils/Local.cpp @@ -1328,6 +1328,8 @@ void llvm::combineMetadata(Instruction *K, const Instruction *J, ArrayRefsetMetadata(Kind, MDNode::getMostGenericTBAA(JMD, KMD)); break; case LLVMContext::MD_alias_scope: + K->setMetadata(Kind, MDNode::getMostGenericAliasScope(JMD, KMD)); + break; case LLVMContext::MD_noalias: K->setMetadata(Kind, MDNode::intersect(JMD, KMD)); break; diff --git a/lib/Transforms/Vectorize/SLPVectorizer.cpp b/lib/Transforms/Vectorize/SLPVectorizer.cpp index 4834782ecc14..2213031ecdb9 100644 --- a/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ b/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -208,6 +208,8 @@ static Instruction *propagateMetadata(Instruction *I, ArrayRef VL) { MD = MDNode::getMostGenericTBAA(MD, IMD); break; case LLVMContext::MD_alias_scope: + MD = MDNode::getMostGenericAliasScope(MD, IMD); + break; case LLVMContext::MD_noalias: MD = MDNode::intersect(MD, IMD); break; diff --git a/test/CodeGen/AArch64/setcc-type-mismatch.ll b/test/CodeGen/AArch64/setcc-type-mismatch.ll new file mode 100644 index 000000000000..86817fa4fa40 --- /dev/null +++ b/test/CodeGen/AArch64/setcc-type-mismatch.ll @@ -0,0 +1,11 @@ +; RUN: llc -mtriple=aarch64-linux-gnu %s -o - | FileCheck %s + +define void @test_mismatched_setcc(<4 x i22> %l, <4 x i22> %r, <4 x i1>* %addr) { +; CHECK-LABEL: test_mismatched_setcc: +; CHECK: cmeq [[CMP128:v[0-9]+]].4s, {{v[0-9]+}}.4s, {{v[0-9]+}}.4s +; CHECK: xtn {{v[0-9]+}}.4h, [[CMP128]].4s + + %tst = icmp eq <4 x i22> %l, %r + store <4 x i1> %tst, <4 x i1>* %addr + ret void +} diff --git a/test/CodeGen/ARM/setcc-type-mismatch.ll b/test/CodeGen/ARM/setcc-type-mismatch.ll new file mode 100644 index 000000000000..2cfdba12db54 --- /dev/null +++ b/test/CodeGen/ARM/setcc-type-mismatch.ll @@ -0,0 +1,11 @@ +; RUN: llc -mtriple=armv7-linux-gnueabihf %s -o - | FileCheck %s + +define void @test_mismatched_setcc(<4 x i22> %l, <4 x i22> %r, <4 x i1>* %addr) { +; CHECK-LABEL: test_mismatched_setcc: +; CHECK: vceq.i32 [[CMP128:q[0-9]+]], {{q[0-9]+}}, {{q[0-9]+}} +; CHECK: vmovn.i32 {{d[0-9]+}}, [[CMP128]] + + %tst = icmp eq <4 x i22> %l, %r + store <4 x i1> %tst, <4 x i1>* %addr + ret void +} diff --git a/test/Transforms/NullCheckElimination/basic.ll b/test/Transforms/NullCheckElimination/basic.ll new file mode 100644 index 000000000000..8ea5851f627a --- /dev/null +++ b/test/Transforms/NullCheckElimination/basic.ll @@ -0,0 +1,285 @@ +; RUN: opt < %s -null-check-elimination -instsimplify -S | FileCheck %s + +define i64 @test_arg_simple(i64* nonnull %p) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_arg_simple +; CHECK-NOT: , null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_arg_simple_ne(i64* nonnull %p) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp ne i64* %p0, null + br i1 %b0, label %match_else, label %return + +; CHECK-LABEL: @test_arg_simple +; CHECK-NOT: , null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_arg_simple_fail(i64* %p) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_arg_simple_fail +; CHECK: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_arg_simple_fail_control_dep(i64* nonnull %p) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + br i1 undef, label %loop_body2, label %match_else + +loop_body2: + %b0 = icmp eq i64* %p0, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_arg_simple_fail_control_dep +; CHECK: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_simple(i64* %p) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_simple +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_simple_ne(i64* %p) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp ne i64* %p1, null + br i1 %b0, label %match_else, label %return + +; CHECK-LABEL: @test_inbounds_simple +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_simple_fail(i64* %p) { +entry: + %p0 = getelementptr i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_simple_fail +; CHECK: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_simple_fail_control_dep(i64* %p) { +entry: + %p0 = getelementptr i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + br i1 undef, label %loop_body2, label %match_else + +loop_body2: + %b0 = icmp eq i64* %p1, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_simple_fail_control_dep +; CHECK: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_or(i64* %p, i64* %q) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, %q + %b1 = icmp eq i64* %p1, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_or +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_and(i64* %p, i64* %q) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, %q + %b1 = icmp eq i64* %p1, null + %b2 = and i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_and +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_derived_load(i64* %p) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_derived_load +; CHECK-NOT: null + +match_else: + %p2 = getelementptr inbounds i64* %p1, i64 1 + %i0 = load i64* %p2 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_derived_load_fail(i64* %p) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %b0 = icmp eq i64* %p1, null + br i1 %b0, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_derived_load_fail +; CHECK: icmp eq i64* %p1, null + +match_else: + %p2 = getelementptr inbounds i64* %p1, i64 1 + %p3 = getelementptr i64* %p1, i64 1 + %i0 = load i64* %p3 + %b1 = icmp ugt i64 %i0, 10 + br i1 %b1, label %return, label %loop_body + +return: + ret i64 0 +} + diff --git a/test/Transforms/NullCheckElimination/complex.ll b/test/Transforms/NullCheckElimination/complex.ll new file mode 100644 index 000000000000..3059d02ad8dc --- /dev/null +++ b/test/Transforms/NullCheckElimination/complex.ll @@ -0,0 +1,316 @@ +; RUN: opt < %s -null-check-elimination -instsimplify -S | FileCheck %s + +define i64 @test_arg_multiple(i64* nonnull %p, i64* nonnull %q) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %q0 = phi i64* [ %q, %entry ], [ %q1, %match_else ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_arg_multiple +; CHECK-NOT: , null + +match_else: + %i0 = load i64* %p0 + %i1 = load i64* %q0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %q1 = getelementptr inbounds i64* %q0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_arg_multiple_fail(i64* nonnull %p, i64* %q) { +entry: + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %q0 = phi i64* [ %q, %entry ], [ %q1, %match_else ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_arg_multiple_fail +; CHECK-NOT: icmp eq i64* %p0, null +; CHECK: icmp eq i64* %q0, null + +match_else: + %i0 = load i64* %p0 + %i1 = load i64* %q0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %q1 = getelementptr inbounds i64* %q0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_multiple(i64* %p, i64* %q) { +entry: + %p0 = getelementptr inbounds i64* %p, i64 0 + %q0 = getelementptr inbounds i64* %q, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %q1 = phi i64* [ %q0, %entry ], [ %q2, %match_else ] + %b0 = icmp eq i64* %p1, null + %b1 = icmp eq i64* %q1, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_multiple +; CHECK-NOT: , null + +match_else: + %i0 = load i64* %p1 + %i1 = load i64* %q1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %q2 = getelementptr inbounds i64* %q1, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_multiple_fail(i64* %p, i64* %q) { +entry: + %p0 = getelementptr i64* %p, i64 0 + %q0 = getelementptr inbounds i64* %q, i64 0 + br label %loop_body + +loop_body: + %p1 = phi i64* [ %p0, %entry ], [ %p2, %match_else ] + %q1 = phi i64* [ %q0, %entry ], [ %q2, %match_else ] + %b0 = icmp eq i64* %p1, null + %b1 = icmp eq i64* %q1, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_multiple_fail +; CHECK: icmp eq i64* %p1, null +; CHECK-NOT: icmp eq i64* %q1, null + +match_else: + %i0 = load i64* %p1 + %i1 = load i64* %q1 + %p2 = getelementptr inbounds i64* %p1, i64 1 + %q2 = getelementptr inbounds i64* %q1, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, %q + %b1 = icmp eq i64* %p0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or_fail1(i64* %p, i64 %len) { +entry: + %q = getelementptr i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, %q + %b1 = icmp eq i64* %p0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or_fail1 +; CHECK: icmp eq i64* %p0, null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or_fail2(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, %q + %b1 = icmp eq i64* %p0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or_fail2 +; CHECK: icmp eq i64* %p0, null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_and(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p, %entry ], [ %p1, %match_else ] + %b0 = icmp eq i64* %p0, %q + %b1 = icmp eq i64* %p0, null + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_and +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or_rev(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p1, %match_else ], [ %p, %entry ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q, %p0 + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or_rev +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or_rev_fail1(i64* %p, i64 %len) { +entry: + %q = getelementptr i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p1, %match_else ], [ %p, %entry ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q, %p0 + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or_rev_fail1 +; CHECK: icmp eq i64* %p0, null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_or_rev_fail2(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p1, %match_else ], [ %p, %entry ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q, %p0 + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_or_rev_fail2 +; CHECK: icmp eq i64* %p0, null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + +define i64 @test_inbounds_recurrence_and_rev(i64* %p, i64 %len) { +entry: + %q = getelementptr inbounds i64* %p, i64 %len + br label %loop_body + +loop_body: + %p0 = phi i64* [ %p1, %match_else ], [ %p, %entry ] + %b0 = icmp eq i64* %p0, null + %b1 = icmp eq i64* %q, %p0 + %b2 = or i1 %b0, %b1 + br i1 %b2, label %return, label %match_else + +; CHECK-LABEL: @test_inbounds_recurrence_and_rev +; CHECK-NOT: null + +match_else: + %i0 = load i64* %p0 + %p1 = getelementptr inbounds i64* %p0, i64 1 + %b3 = icmp ugt i64 %i0, 10 + br i1 %b3, label %return, label %loop_body + +return: + ret i64 0 +} + diff --git a/test/Transforms/Util/combine-alias-scope-metadata.ll b/test/Transforms/Util/combine-alias-scope-metadata.ll new file mode 100644 index 000000000000..fd0a3d5c5b92 --- /dev/null +++ b/test/Transforms/Util/combine-alias-scope-metadata.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -S -basicaa -memcpyopt | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @test(i8* noalias dereferenceable(1) %in, i8* noalias dereferenceable(1) %out) { + %tmp = alloca i8 + %tmp2 = alloca i8 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %out, i8* %in, i64 1, i32 8, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %tmp, i8* %in, i64 1, i32 8, i1 false), !alias.scope !4 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %tmp2, i8* %tmp, i64 1, i32 8, i1 false), !alias.scope !5 + + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %out, i8* %tmp2, i64 1, i32 8, i1 false), !noalias !6 + + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i32, i1) + +!0 = !{!0} +!1 = distinct !{!1, !0, !"in"} +!2 = distinct !{!2, !0, !"tmp"} +!3 = distinct !{!3, !0, !"tmp2"} +!4 = distinct !{!1, !2} +!5 = distinct !{!2, !3} +!6 = distinct !{!1, !2}