From 647946fa14febdbacf44c3a422e83b52304eca3f Mon Sep 17 00:00:00 2001 From: Manoj Gupta Date: Mon, 9 Jul 2018 22:27:23 +0000 Subject: [PATCH] llvm: Add support for "-fno-delete-null-pointer-checks" Summary: Support for this option is needed for building Linux kernel. This is a very frequently requested feature by kernel developers. More details : https://lkml.org/lkml/2018/4/4/601 GCC option description for -fdelete-null-pointer-checks: This Assume that programs cannot safely dereference null pointers, and that no code or data element resides at address zero. -fno-delete-null-pointer-checks is the inverse of this implying that null pointer dereferencing is not undefined. This feature is implemented in LLVM IR in this CL as the function attribute "null-pointer-is-valid"="true" in IR (Under review at D47894). The CL updates several passes that assumed null pointer dereferencing is undefined to not optimize when the "null-pointer-is-valid"="true" attribute is present. Reviewers: t.p.northover, efriedma, jyknight, chandlerc, rnk, srhines, void, george.burgess.iv Reviewed By: efriedma, george.burgess.iv Subscribers: eraman, haicheng, george.burgess.iv, drinkcat, theraven, reames, sanjoy, xbolva00, llvm-commits Differential Revision: https://reviews.llvm.org/D47895 llvm-svn: 336613 --- docs/LangRef.rst | 8 ++ include/llvm/Analysis/BasicAliasAnalysis.h | 21 +-- include/llvm/IR/CallSite.h | 3 +- include/llvm/IR/Function.h | 13 ++ lib/Analysis/BasicAliasAnalysis.cpp | 37 +++--- lib/Analysis/ConstantFolding.cpp | 15 ++- lib/Analysis/InlineCost.cpp | 5 + lib/Analysis/InstructionSimplify.cpp | 7 +- lib/Analysis/LazyValueInfo.cpp | 14 +- lib/Analysis/LoopAccessAnalysis.cpp | 28 ++-- lib/Analysis/ValueTracking.cpp | 7 +- lib/IR/ConstantFold.cpp | 12 +- lib/IR/Function.cpp | 19 ++- lib/Transforms/IPO/GlobalOpt.cpp | 19 ++- .../InstCombine/InstCombineCalls.cpp | 4 +- .../InstCombineLoadStoreAlloca.cpp | 17 ++- .../Scalar/DeadStoreElimination.cpp | 26 ++-- lib/Transforms/Scalar/SCCP.cpp | 8 +- lib/Transforms/Utils/Local.cpp | 11 +- lib/Transforms/Utils/SimplifyCFG.cpp | 9 +- test/Analysis/MemorySSA/cyclicphi.ll | 32 +++++ .../CorrelatedValuePropagation/non-null.ll | 67 ++++++++++ test/Transforms/FunctionAttrs/nonnull.ll | 9 ++ .../2018-06-08-pre-load-dbgloc-no-null-opt.ll | 82 ++++++++++++ .../MallocSROA-section-no-null-opt.ll | 34 +++++ .../GlobalOpt/heap-sra-1-no-null-opt.ll | 40 ++++++ test/Transforms/GlobalOpt/heap-sra-1.ll | 7 + .../GlobalOpt/heap-sra-2-no-null-opt.ll | 39 ++++++ test/Transforms/GlobalOpt/heap-sra-2.ll | 7 + .../GlobalOpt/heap-sra-3-no-null-opt.ll | 41 ++++++ test/Transforms/GlobalOpt/heap-sra-3.ll | 7 + .../GlobalOpt/heap-sra-4-no-null-opt.ll | 44 +++++++ test/Transforms/GlobalOpt/heap-sra-4.ll | 8 ++ .../GlobalOpt/heap-sra-phi-no-null-opt.ll | 54 ++++++++ test/Transforms/GlobalOpt/heap-sra-phi.ll | 8 ++ .../load-store-global-no-null-opt.ll | 28 ++++ .../GlobalOpt/malloc-promote-1-no-null-opt.ll | 31 +++++ test/Transforms/GlobalOpt/malloc-promote-1.ll | 10 +- .../GlobalOpt/malloc-promote-2-no-null-opt.ll | 24 ++++ test/Transforms/GlobalOpt/malloc-promote-2.ll | 8 ++ .../storepointer-compare-no-null-opt.ll | 40 ++++++ .../GlobalOpt/storepointer-no-null-opt.ll | 27 ++++ test/Transforms/IPConstantProp/PR26044.ll | 31 +++++ test/Transforms/Inline/attributes.ll | 44 +++++++ test/Transforms/InstCombine/atomic.ll | 44 +++++++ .../Transforms/InstCombine/invariant.group.ll | 15 +++ test/Transforms/InstCombine/invoke.ll | 21 +++ .../InstCombine/lifetime-no-null-opt.ll | 94 +++++++++++++ test/Transforms/InstCombine/load.ll | 10 ++ .../InstCombine/memcpy-addrspace.ll | 40 ++++++ .../InstCombine/memcpy-from-global.ll | 22 ++++ test/Transforms/InstCombine/select.ll | 25 ++++ test/Transforms/InstCombine/store.ll | 11 ++ test/Transforms/InstCombine/strcpy_chk-64.ll | 13 ++ test/Transforms/InstCombine/strlen-1.ll | 25 ++++ test/Transforms/InstCombine/wcslen-1.ll | 28 ++++ test/Transforms/InstSimplify/compare.ll | 83 ++++++++++++ test/Transforms/LoopIdiom/pr28196.ll | 27 ++++ test/Transforms/LoopVersioning/lcssa.ll | 37 ++++++ .../SimplifyCFG/UnreachableEliminate.ll | 59 +++++++++ test/Transforms/SimplifyCFG/invoke.ll | 23 ++++ .../SimplifyCFG/phi-undef-loadstore.ll | 124 ++++++++++++++++++ .../SimplifyCFG/trap-no-null-opt-debugloc.ll | 24 ++++ .../SimplifyCFG/trapping-load-unreachable.ll | 40 +++++- unittests/Analysis/AliasAnalysisTest.cpp | 2 +- unittests/Analysis/MemorySSA.cpp | 2 +- 66 files changed, 1688 insertions(+), 86 deletions(-) create mode 100644 test/Transforms/GVN/PRE/2018-06-08-pre-load-dbgloc-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/MallocSROA-section-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/heap-sra-1-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/heap-sra-2-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/heap-sra-3-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/heap-sra-4-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/heap-sra-phi-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/load-store-global-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/malloc-promote-1-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/malloc-promote-2-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/storepointer-compare-no-null-opt.ll create mode 100644 test/Transforms/GlobalOpt/storepointer-no-null-opt.ll create mode 100644 test/Transforms/InstCombine/lifetime-no-null-opt.ll create mode 100644 test/Transforms/SimplifyCFG/trap-no-null-opt-debugloc.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 57abe7f9067..4e70bae3168 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -1461,6 +1461,14 @@ example: trap or generate asynchronous exceptions. Exception handling schemes that are recognized by LLVM to handle asynchronous exceptions, such as SEH, will still provide their implementation defined semantics. +``"null-pointer-is-valid"`` + If ``"null-pointer-is-valid"`` is set to ``"true"``, then ``null`` address + in address-space 0 is considered to be a valid address for memory loads and + stores. Any analysis or optimization should not treat dereferencing a + pointer to ``null`` as undefined behavior in this function. + Note: Comparing address of a global variable to ``null`` may still + evaluate to false because of a limitation in querying this attribute inside + constant expressions. ``optforfuzzing`` This attribute indicates that this function should be optimized for maximum fuzzing signal. diff --git a/include/llvm/Analysis/BasicAliasAnalysis.h b/include/llvm/Analysis/BasicAliasAnalysis.h index 8fb3c3a63f2..fa81539a9d6 100644 --- a/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/include/llvm/Analysis/BasicAliasAnalysis.h @@ -55,26 +55,27 @@ class BasicAAResult : public AAResultBase { friend AAResultBase; const DataLayout &DL; + const Function &F; const TargetLibraryInfo &TLI; AssumptionCache &AC; DominatorTree *DT; LoopInfo *LI; public: - BasicAAResult(const DataLayout &DL, const TargetLibraryInfo &TLI, - AssumptionCache &AC, DominatorTree *DT = nullptr, - LoopInfo *LI = nullptr) - : AAResultBase(), DL(DL), TLI(TLI), AC(AC), DT(DT), LI(LI) {} + BasicAAResult(const DataLayout &DL, const Function &F, + const TargetLibraryInfo &TLI, AssumptionCache &AC, + DominatorTree *DT = nullptr, LoopInfo *LI = nullptr) + : AAResultBase(), DL(DL), F(F), TLI(TLI), AC(AC), DT(DT), LI(LI) {} BasicAAResult(const BasicAAResult &Arg) - : AAResultBase(Arg), DL(Arg.DL), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT), - LI(Arg.LI) {} - BasicAAResult(BasicAAResult &&Arg) - : AAResultBase(std::move(Arg)), DL(Arg.DL), TLI(Arg.TLI), AC(Arg.AC), + : AAResultBase(Arg), DL(Arg.DL), F(Arg.F), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT), LI(Arg.LI) {} + BasicAAResult(BasicAAResult &&Arg) + : AAResultBase(std::move(Arg)), DL(Arg.DL), F(Arg.F), TLI(Arg.TLI), + AC(Arg.AC), DT(Arg.DT), LI(Arg.LI) {} /// Handle invalidation events in the new pass manager. - bool invalidate(Function &F, const PreservedAnalyses &PA, + bool invalidate(Function &Fn, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &Inv); AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB); @@ -94,7 +95,7 @@ public: /// Returns the behavior when calling the given function. For use when the /// call site is not known. - FunctionModRefBehavior getModRefBehavior(const Function *F); + FunctionModRefBehavior getModRefBehavior(const Function *Fn); private: // A linear transformation of a Value; this class represents ZExt(SExt(V, diff --git a/include/llvm/IR/CallSite.h b/include/llvm/IR/CallSite.h index 5b10da8f2ae..2162ccb982b 100644 --- a/include/llvm/IR/CallSite.h +++ b/include/llvm/IR/CallSite.h @@ -637,7 +637,8 @@ public: if (hasRetAttr(Attribute::NonNull)) return true; else if (getDereferenceableBytes(AttributeList::ReturnIndex) > 0 && - getType()->getPointerAddressSpace() == 0) + !NullPointerIsDefined(getCaller(), + getType()->getPointerAddressSpace())) return true; return false; diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h index 029172e7ba8..c8d6b0776fb 100644 --- a/include/llvm/IR/Function.h +++ b/include/llvm/IR/Function.h @@ -781,6 +781,12 @@ public: /// Returns true if we should emit debug info for profiling. bool isDebugInfoForProfiling() const; + /// Check if null pointer dereferencing is considered undefined behavior for + /// the function. + /// Return value: false => null pointer dereference is undefined. + /// Return value: true => null pointer dereference is not undefined. + bool nullPointerIsDefined() const; + private: void allocHungoffUselist(); template void setHungoffOperand(Constant *C); @@ -793,6 +799,13 @@ private: void setValueSubclassDataBit(unsigned Bit, bool On); }; +/// Check whether null pointer dereferencing is considered undefined behavior +/// for a given function or an address space. +/// Null pointer access in non-zero address space is not considered undefined. +/// Return value: false => null pointer dereference is undefined. +/// Return value: true => null pointer dereference is not undefined. +bool NullPointerIsDefined(const Function *F, unsigned AS = 0); + template <> struct OperandTraits : public HungoffOperandTraits<3> {}; diff --git a/lib/Analysis/BasicAliasAnalysis.cpp b/lib/Analysis/BasicAliasAnalysis.cpp index 883462a6fcb..96326347b71 100644 --- a/lib/Analysis/BasicAliasAnalysis.cpp +++ b/lib/Analysis/BasicAliasAnalysis.cpp @@ -85,15 +85,15 @@ const unsigned MaxNumPhiBBsValueReachabilityCheck = 20; // depth otherwise the algorithm in aliasGEP will assert. static const unsigned MaxLookupSearchDepth = 6; -bool BasicAAResult::invalidate(Function &F, const PreservedAnalyses &PA, +bool BasicAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &Inv) { // We don't care if this analysis itself is preserved, it has no state. But // we need to check that the analyses it depends on have been. Note that we // may be created without handles to some analyses and in that case don't // depend on them. - if (Inv.invalidate(F, PA) || - (DT && Inv.invalidate(F, PA)) || - (LI && Inv.invalidate(F, PA))) + if (Inv.invalidate(Fn, PA) || + (DT && Inv.invalidate(Fn, PA)) || + (LI && Inv.invalidate(Fn, PA))) return true; // Otherwise this analysis result remains valid. @@ -150,10 +150,12 @@ static bool isEscapeSource(const Value *V) { /// Returns the size of the object specified by V or UnknownSize if unknown. static uint64_t getObjectSize(const Value *V, const DataLayout &DL, const TargetLibraryInfo &TLI, + bool NullIsValidLoc, bool RoundToAlign = false) { uint64_t Size; ObjectSizeOpts Opts; Opts.RoundToAlign = RoundToAlign; + Opts.NullIsUnknownSize = NullIsValidLoc; if (getObjectSize(V, Size, DL, &TLI, Opts)) return Size; return MemoryLocation::UnknownSize; @@ -163,7 +165,8 @@ static uint64_t getObjectSize(const Value *V, const DataLayout &DL, /// Size. static bool isObjectSmallerThan(const Value *V, uint64_t Size, const DataLayout &DL, - const TargetLibraryInfo &TLI) { + const TargetLibraryInfo &TLI, + bool NullIsValidLoc) { // Note that the meanings of the "object" are slightly different in the // following contexts: // c1: llvm::getObjectSize() @@ -195,15 +198,16 @@ static bool isObjectSmallerThan(const Value *V, uint64_t Size, // This function needs to use the aligned object size because we allow // reads a bit past the end given sufficient alignment. - uint64_t ObjectSize = getObjectSize(V, DL, TLI, /*RoundToAlign*/ true); + uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc, + /*RoundToAlign*/ true); return ObjectSize != MemoryLocation::UnknownSize && ObjectSize < Size; } /// Returns true if we can prove that the object specified by V has size Size. static bool isObjectSize(const Value *V, uint64_t Size, const DataLayout &DL, - const TargetLibraryInfo &TLI) { - uint64_t ObjectSize = getObjectSize(V, DL, TLI); + const TargetLibraryInfo &TLI, bool NullIsValidLoc) { + uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc); return ObjectSize != MemoryLocation::UnknownSize && ObjectSize == Size; } @@ -1623,10 +1627,10 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, // Null values in the default address space don't point to any object, so they // don't alias any other pointer. if (const ConstantPointerNull *CPN = dyn_cast(O1)) - if (CPN->getType()->getAddressSpace() == 0) + if (!NullPointerIsDefined(&F, CPN->getType()->getAddressSpace())) return NoAlias; if (const ConstantPointerNull *CPN = dyn_cast(O2)) - if (CPN->getType()->getAddressSpace() == 0) + if (!NullPointerIsDefined(&F, CPN->getType()->getAddressSpace())) return NoAlias; if (O1 != O2) { @@ -1662,10 +1666,11 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, // If the size of one access is larger than the entire object on the other // side, then we know such behavior is undefined and can assume no alias. + bool NullIsValidLocation = NullPointerIsDefined(&F); if ((V1Size != MemoryLocation::UnknownSize && - isObjectSmallerThan(O2, V1Size, DL, TLI)) || + isObjectSmallerThan(O2, V1Size, DL, TLI, NullIsValidLocation)) || (V2Size != MemoryLocation::UnknownSize && - isObjectSmallerThan(O1, V2Size, DL, TLI))) + isObjectSmallerThan(O1, V2Size, DL, TLI, NullIsValidLocation))) return NoAlias; // Check the cache before climbing up use-def chains. This also terminates @@ -1725,8 +1730,8 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, if (O1 == O2) if (V1Size != MemoryLocation::UnknownSize && V2Size != MemoryLocation::UnknownSize && - (isObjectSize(O1, V1Size, DL, TLI) || - isObjectSize(O2, V2Size, DL, TLI))) + (isObjectSize(O1, V1Size, DL, TLI, NullIsValidLocation) || + isObjectSize(O2, V2Size, DL, TLI, NullIsValidLocation))) return AliasCache[Locs] = PartialAlias; // Recurse back into the best AA results we have, potentially with refined @@ -1870,6 +1875,7 @@ AnalysisKey BasicAA::Key; BasicAAResult BasicAA::run(Function &F, FunctionAnalysisManager &AM) { return BasicAAResult(F.getParent()->getDataLayout(), + F, AM.getResult(F), AM.getResult(F), &AM.getResult(F), @@ -1902,7 +1908,7 @@ bool BasicAAWrapperPass::runOnFunction(Function &F) { auto &DTWP = getAnalysis(); auto *LIWP = getAnalysisIfAvailable(); - Result.reset(new BasicAAResult(F.getParent()->getDataLayout(), TLIWP.getTLI(), + Result.reset(new BasicAAResult(F.getParent()->getDataLayout(), F, TLIWP.getTLI(), ACT.getAssumptionCache(F), &DTWP.getDomTree(), LIWP ? &LIWP->getLoopInfo() : nullptr)); @@ -1919,6 +1925,7 @@ void BasicAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { BasicAAResult llvm::createLegacyPMBasicAAResult(Pass &P, Function &F) { return BasicAAResult( F.getParent()->getDataLayout(), + F, P.getAnalysis().getTLI(), P.getAnalysis().getAssumptionCache(F)); } diff --git a/lib/Analysis/ConstantFolding.cpp b/lib/Analysis/ConstantFolding.cpp index 38e018f6db0..a49007ee499 100644 --- a/lib/Analysis/ConstantFolding.cpp +++ b/lib/Analysis/ConstantFolding.cpp @@ -1589,7 +1589,8 @@ double getValueAsDouble(ConstantFP *Op) { Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, Type *Ty, ArrayRef Operands, - const TargetLibraryInfo *TLI) { + const TargetLibraryInfo *TLI, + ImmutableCallSite CS) { if (Operands.size() == 1) { if (isa(Operands[0])) { // cosine(arg) is between -1 and 1. cosine(invalid arg) is NaN @@ -1603,7 +1604,8 @@ Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, Type *Ty, } if (isa(Operands[0]) && - Operands[0]->getType()->getPointerAddressSpace() == 0) { + !NullPointerIsDefined( + CS.getCaller(), Operands[0]->getType()->getPointerAddressSpace())) { // launder(null) == null == strip(null) iff in addrspace 0 if (IntrinsicID == Intrinsic::launder_invariant_group || IntrinsicID == Intrinsic::strip_invariant_group) @@ -2007,7 +2009,8 @@ Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, Type *Ty, Constant *ConstantFoldVectorCall(StringRef Name, unsigned IntrinsicID, VectorType *VTy, ArrayRef Operands, const DataLayout &DL, - const TargetLibraryInfo *TLI) { + const TargetLibraryInfo *TLI, + ImmutableCallSite CS) { SmallVector Result(VTy->getNumElements()); SmallVector Lane(Operands.size()); Type *Ty = VTy->getElementType(); @@ -2070,7 +2073,7 @@ Constant *ConstantFoldVectorCall(StringRef Name, unsigned IntrinsicID, } // Use the regular scalar folding to simplify this column. - Constant *Folded = ConstantFoldScalarCall(Name, IntrinsicID, Ty, Lane, TLI); + Constant *Folded = ConstantFoldScalarCall(Name, IntrinsicID, Ty, Lane, TLI, CS); if (!Folded) return nullptr; Result[I] = Folded; @@ -2095,9 +2098,9 @@ llvm::ConstantFoldCall(ImmutableCallSite CS, Function *F, if (auto *VTy = dyn_cast(Ty)) return ConstantFoldVectorCall(Name, F->getIntrinsicID(), VTy, Operands, - F->getParent()->getDataLayout(), TLI); + F->getParent()->getDataLayout(), TLI, CS); - return ConstantFoldScalarCall(Name, F->getIntrinsicID(), Ty, Operands, TLI); + return ConstantFoldScalarCall(Name, F->getIntrinsicID(), Ty, Operands, TLI, CS); } bool llvm::isMathLibCallNoop(CallSite CS, const TargetLibraryInfo *TLI) { diff --git a/lib/Analysis/InlineCost.cpp b/lib/Analysis/InlineCost.cpp index 7a28ad431f6..a6cccc3b591 100644 --- a/lib/Analysis/InlineCost.cpp +++ b/lib/Analysis/InlineCost.cpp @@ -1994,6 +1994,11 @@ InlineCost llvm::getInlineCost( if (Caller->hasFnAttribute(Attribute::OptimizeNone)) return llvm::InlineCost::getNever(); + // Don't inline a function that treats null pointer as valid into a caller + // that does not have this attribute. + if (!Caller->nullPointerIsDefined() && Callee->nullPointerIsDefined()) + return llvm::InlineCost::getNever(); + // Don't inline functions which can be interposed at link-time. Don't inline // functions marked noinline or call sites marked noinline. // Note: inlining non-exact non-interposable functions is fine, since we know diff --git a/lib/Analysis/InstructionSimplify.cpp b/lib/Analysis/InstructionSimplify.cpp index c15f649e9c4..061ce8f0bd1 100644 --- a/lib/Analysis/InstructionSimplify.cpp +++ b/lib/Analysis/InstructionSimplify.cpp @@ -2120,9 +2120,12 @@ computePointerICmp(const DataLayout &DL, const TargetLibraryInfo *TLI, ConstantInt *LHSOffsetCI = dyn_cast(LHSOffset); ConstantInt *RHSOffsetCI = dyn_cast(RHSOffset); uint64_t LHSSize, RHSSize; + ObjectSizeOpts Opts; + Opts.NullIsUnknownSize = + NullPointerIsDefined(cast(LHS)->getFunction()); if (LHSOffsetCI && RHSOffsetCI && - getObjectSize(LHS, LHSSize, DL, TLI) && - getObjectSize(RHS, RHSSize, DL, TLI)) { + getObjectSize(LHS, LHSSize, DL, TLI, Opts) && + getObjectSize(RHS, RHSSize, DL, TLI, Opts)) { const APInt &LHSOffsetValue = LHSOffsetCI->getValue(); const APInt &RHSOffsetValue = RHSOffsetCI->getValue(); if (!LHSOffsetValue.isNegative() && diff --git a/lib/Analysis/LazyValueInfo.cpp b/lib/Analysis/LazyValueInfo.cpp index a133357979b..435b6f20519 100644 --- a/lib/Analysis/LazyValueInfo.cpp +++ b/lib/Analysis/LazyValueInfo.cpp @@ -704,9 +704,11 @@ bool LazyValueInfoImpl::solveBlockValueNonLocal(ValueLatticeElement &BBLV, assert(isa(Val) && "Unknown live-in to the entry block"); // Before giving up, see if we can prove the pointer non-null local to // this particular block. - if (Val->getType()->isPointerTy() && - (isKnownNonZero(Val, DL) || isObjectDereferencedInBlock(Val, BB))) { - PointerType *PTy = cast(Val->getType()); + PointerType *PTy = dyn_cast(Val->getType()); + if (PTy && + (isKnownNonZero(Val, DL) || + (isObjectDereferencedInBlock(Val, BB) && + !NullPointerIsDefined(BB->getParent(), PTy->getAddressSpace())))) { Result = ValueLatticeElement::getNot(ConstantPointerNull::get(PTy)); } else { Result = ValueLatticeElement::getOverdefined(); @@ -739,9 +741,9 @@ bool LazyValueInfoImpl::solveBlockValueNonLocal(ValueLatticeElement &BBLV, << "' - overdefined because of pred (non local).\n"); // Before giving up, see if we can prove the pointer non-null local to // this particular block. - if (Val->getType()->isPointerTy() && - isObjectDereferencedInBlock(Val, BB)) { - PointerType *PTy = cast(Val->getType()); + PointerType *PTy = dyn_cast(Val->getType()); + if (PTy && isObjectDereferencedInBlock(Val, BB) && + !NullPointerIsDefined(BB->getParent(), PTy->getAddressSpace())) { Result = ValueLatticeElement::getNot(ConstantPointerNull::get(PTy)); } diff --git a/lib/Analysis/LoopAccessAnalysis.cpp b/lib/Analysis/LoopAccessAnalysis.cpp index 17b13802e1d..c6175bf9bee 100644 --- a/lib/Analysis/LoopAccessAnalysis.cpp +++ b/lib/Analysis/LoopAccessAnalysis.cpp @@ -500,11 +500,11 @@ public: typedef PointerIntPair MemAccessInfo; typedef SmallVector MemAccessInfoList; - AccessAnalysis(const DataLayout &Dl, AliasAnalysis *AA, LoopInfo *LI, - MemoryDepChecker::DepCandidates &DA, + AccessAnalysis(const DataLayout &Dl, Loop *TheLoop, AliasAnalysis *AA, + LoopInfo *LI, MemoryDepChecker::DepCandidates &DA, PredicatedScalarEvolution &PSE) - : DL(Dl), AST(*AA), LI(LI), DepCands(DA), IsRTCheckAnalysisNeeded(false), - PSE(PSE) {} + : DL(Dl), TheLoop(TheLoop), AST(*AA), LI(LI), DepCands(DA), + IsRTCheckAnalysisNeeded(false), PSE(PSE) {} /// Register a load and whether it is only read from. void addLoad(MemoryLocation &Loc, bool IsReadOnly) { @@ -579,6 +579,9 @@ private: const DataLayout &DL; + /// The loop being checked. + const Loop *TheLoop; + /// List of accesses that need a further dependence check. MemAccessInfoList CheckDeps; @@ -910,7 +913,10 @@ void AccessAnalysis::processMemAccesses() { for (Value *UnderlyingObj : TempObjects) { // nullptr never alias, don't join sets for pointer that have "null" // in their UnderlyingObjects list. - if (isa(UnderlyingObj)) + if (isa(UnderlyingObj) && + !NullPointerIsDefined( + TheLoop->getHeader()->getParent(), + UnderlyingObj->getType()->getPointerAddressSpace())) continue; UnderlyingObjToAccessMap::iterator Prev = @@ -1026,8 +1032,9 @@ int64_t llvm::getPtrStride(PredicatedScalarEvolution &PSE, Value *Ptr, bool IsNoWrapAddRec = !ShouldCheckWrap || PSE.hasNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW) || isNoWrapAddRec(Ptr, AR, PSE, Lp); - bool IsInAddressSpaceZero = PtrTy->getAddressSpace() == 0; - if (!IsNoWrapAddRec && !IsInBoundsGEP && !IsInAddressSpaceZero) { + if (!IsNoWrapAddRec && !IsInBoundsGEP && + NullPointerIsDefined(Lp->getHeader()->getParent(), + PtrTy->getAddressSpace())) { if (Assume) { PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW); IsNoWrapAddRec = true; @@ -1073,8 +1080,9 @@ int64_t llvm::getPtrStride(PredicatedScalarEvolution &PSE, Value *Ptr, // If the SCEV could wrap but we have an inbounds gep with a unit stride we // know we can't "wrap around the address space". In case of address space // zero we know that this won't happen without triggering undefined behavior. - if (!IsNoWrapAddRec && (IsInBoundsGEP || IsInAddressSpaceZero) && - Stride != 1 && Stride != -1) { + if (!IsNoWrapAddRec && Stride != 1 && Stride != -1 && + (IsInBoundsGEP || !NullPointerIsDefined(Lp->getHeader()->getParent(), + PtrTy->getAddressSpace()))) { if (Assume) { // We can avoid this case by adding a run-time check. LLVM_DEBUG(dbgs() << "LAA: Non unit strided pointer which is not either " @@ -1845,7 +1853,7 @@ void LoopAccessInfo::analyzeLoop(AliasAnalysis *AA, LoopInfo *LI, MemoryDepChecker::DepCandidates DependentAccesses; AccessAnalysis Accesses(TheLoop->getHeader()->getModule()->getDataLayout(), - AA, LI, DependentAccesses, *PSE); + TheLoop, AA, LI, DependentAccesses, *PSE); // Holds the analyzed pointers. We don't want to call GetUnderlyingObjects // multiple times on the same object. If the ptr is accessed twice, once diff --git a/lib/Analysis/ValueTracking.cpp b/lib/Analysis/ValueTracking.cpp index 7ff1d5236a3..bdfd1783236 100644 --- a/lib/Analysis/ValueTracking.cpp +++ b/lib/Analysis/ValueTracking.cpp @@ -1769,7 +1769,12 @@ bool isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, unsigned Depth, /// Currently this routine does not support vector GEPs. static bool isGEPKnownNonNull(const GEPOperator *GEP, unsigned Depth, const Query &Q) { - if (!GEP->isInBounds() || GEP->getPointerAddressSpace() != 0) + const Function *F = nullptr; + if (const Instruction *I = dyn_cast(GEP)) + F = I->getFunction(); + + if (!GEP->isInBounds() || + NullPointerIsDefined(F, GEP->getPointerAddressSpace())) return false; // FIXME: Support vector-GEPs. diff --git a/lib/IR/ConstantFold.cpp b/lib/IR/ConstantFold.cpp index 9be9d3adf69..90a8366d169 100644 --- a/lib/IR/ConstantFold.cpp +++ b/lib/IR/ConstantFold.cpp @@ -1500,8 +1500,12 @@ static ICmpInst::Predicate evaluateICmpRelation(Constant *V1, Constant *V2, assert(isa(V2) && "Canonicalization guarantee!"); // GlobalVals can never be null unless they have external weak linkage. // We don't try to evaluate aliases here. + // NOTE: We should not be doing this constant folding if null pointer + // is considered valid for the function. But currently there is no way to + // query it from the Constant type. if (!GV->hasExternalWeakLinkage() && !isa(GV) && - GV->getType()->getAddressSpace() == 0) + !NullPointerIsDefined(nullptr /* F */, + GV->getType()->getAddressSpace())) return ICmpInst::ICMP_NE; } } else if (const BlockAddress *BA = dyn_cast(V1)) { @@ -1731,7 +1735,8 @@ Constant *llvm::ConstantFoldCompareInstruction(unsigned short pred, if (const GlobalValue *GV = dyn_cast(C2)) // Don't try to evaluate aliases. External weak GV can be null. if (!isa(GV) && !GV->hasExternalWeakLinkage() && - GV->getType()->getAddressSpace() == 0) { + !NullPointerIsDefined(nullptr /* F */, + GV->getType()->getAddressSpace())) { if (pred == ICmpInst::ICMP_EQ) return ConstantInt::getFalse(C1->getContext()); else if (pred == ICmpInst::ICMP_NE) @@ -1742,7 +1747,8 @@ Constant *llvm::ConstantFoldCompareInstruction(unsigned short pred, if (const GlobalValue *GV = dyn_cast(C1)) // Don't try to evaluate aliases. External weak GV can be null. if (!isa(GV) && !GV->hasExternalWeakLinkage() && - GV->getType()->getAddressSpace() == 0) { + !NullPointerIsDefined(nullptr /* F */, + GV->getType()->getAddressSpace())) { if (pred == ICmpInst::ICMP_EQ) return ConstantInt::getFalse(C1->getContext()); else if (pred == ICmpInst::ICMP_NE) diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp index 49582c4debc..aba329b8050 100644 --- a/lib/IR/Function.cpp +++ b/lib/IR/Function.cpp @@ -79,7 +79,8 @@ bool Argument::hasNonNullAttr() const { if (getParent()->hasParamAttribute(getArgNo(), Attribute::NonNull)) return true; else if (getDereferenceableBytes() > 0 && - getType()->getPointerAddressSpace() == 0) + !NullPointerIsDefined(getParent(), + getType()->getPointerAddressSpace())) return true; return false; } @@ -1415,3 +1416,19 @@ Optional Function::getSectionPrefix() const { } return None; } + +bool Function::nullPointerIsDefined() const { + return getFnAttribute("null-pointer-is-valid") + .getValueAsString() + .equals("true"); +} + +bool llvm::NullPointerIsDefined(const Function *F, unsigned AS) { + if (F && F->nullPointerIsDefined()) + return true; + + if (AS != 0) + return true; + + return false; +} diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp index 6f3796f4a80..1af7e689477 100644 --- a/lib/Transforms/IPO/GlobalOpt.cpp +++ b/lib/Transforms/IPO/GlobalOpt.cpp @@ -636,7 +636,13 @@ static GlobalVariable *SRAGlobal(GlobalVariable *GV, const DataLayout &DL) { /// reprocessing them. static bool AllUsesOfValueWillTrapIfNull(const Value *V, SmallPtrSetImpl &PHIs) { - for (const User *U : V->users()) + for (const User *U : V->users()) { + if (const Instruction *I = dyn_cast(U)) { + // If null pointer is considered valid, then all uses are non-trapping. + // Non address-space 0 globals have already been pruned by the caller. + if (NullPointerIsDefined(I->getFunction())) + return false; + } if (isa(U)) { // Will trap. } else if (const StoreInst *SI = dyn_cast(U)) { @@ -670,7 +676,7 @@ static bool AllUsesOfValueWillTrapIfNull(const Value *V, //cerr << "NONTRAPPING USE: " << *U; return false; } - + } return true; } @@ -697,6 +703,10 @@ static bool OptimizeAwayTrappingUsesOfValue(Value *V, Constant *NewV) { bool Changed = false; for (auto UI = V->user_begin(), E = V->user_end(); UI != E; ) { Instruction *I = cast(*UI++); + // Uses are non-trapping if null pointer is considered valid. + // Non address-space 0 globals are already pruned by the caller. + if (NullPointerIsDefined(I->getFunction())) + return false; if (LoadInst *LI = dyn_cast(I)) { LI->setOperand(0, NewV); Changed = true; @@ -1584,7 +1594,10 @@ static bool optimizeOnceStoredGlobal(GlobalVariable *GV, Value *StoredOnceVal, // users of the loaded value (often calls and loads) that would trap if the // value was null. if (GV->getInitializer()->getType()->isPointerTy() && - GV->getInitializer()->isNullValue()) { + GV->getInitializer()->isNullValue() && + !NullPointerIsDefined( + nullptr /* F */, + GV->getInitializer()->getType()->getPointerAddressSpace())) { if (Constant *SOVC = dyn_cast(StoredOnceVal)) { if (GV->getInitializer()->getType() != SOVC->getType()) SOVC = ConstantExpr::getBitCast(SOVC, GV->getInitializer()->getType()); diff --git a/lib/Transforms/InstCombine/InstCombineCalls.cpp b/lib/Transforms/InstCombine/InstCombineCalls.cpp index 78c2d312939..29ae67af7b7 100644 --- a/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -4020,7 +4020,9 @@ Instruction *InstCombiner::visitCallSite(CallSite CS) { } } - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(CS.getInstruction()->getFunction())) || + isa(Callee)) { // If CS does not return void then replaceAllUsesWith undef. // This allows ValueHandlers and custom metadata to adjust itself. if (!CS.getInstruction()->getType()->isVoidTy()) diff --git a/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp index 473d88e416a..742caf64900 100644 --- a/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -964,23 +964,26 @@ static Instruction *replaceGEPIdxWithZero(InstCombiner &IC, Value *Ptr, } static bool canSimplifyNullStoreOrGEP(StoreInst &SI) { - if (SI.getPointerAddressSpace() != 0) + if (NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace())) return false; auto *Ptr = SI.getPointerOperand(); if (GetElementPtrInst *GEPI = dyn_cast(Ptr)) Ptr = GEPI->getOperand(0); - return isa(Ptr); + return (isa(Ptr) && + !NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace())); } static bool canSimplifyNullLoadOrGEP(LoadInst &LI, Value *Op) { if (GetElementPtrInst *GEPI = dyn_cast(Op)) { const Value *GEPI0 = GEPI->getOperand(0); - if (isa(GEPI0) && GEPI->getPointerAddressSpace() == 0) + if (isa(GEPI0) && + !NullPointerIsDefined(LI.getFunction(), GEPI->getPointerAddressSpace())) return true; } if (isa(Op) || - (isa(Op) && LI.getPointerAddressSpace() == 0)) + (isa(Op) && + !NullPointerIsDefined(LI.getFunction(), LI.getPointerAddressSpace()))) return true; return false; } @@ -1076,14 +1079,16 @@ Instruction *InstCombiner::visitLoadInst(LoadInst &LI) { // load (select (cond, null, P)) -> load P if (isa(SI->getOperand(1)) && - LI.getPointerAddressSpace() == 0) { + !NullPointerIsDefined(SI->getFunction(), + LI.getPointerAddressSpace())) { LI.setOperand(0, SI->getOperand(2)); return &LI; } // load (select (cond, P, null)) -> load P if (isa(SI->getOperand(2)) && - LI.getPointerAddressSpace() == 0) { + !NullPointerIsDefined(SI->getFunction(), + LI.getPointerAddressSpace())) { LI.setOperand(0, SI->getOperand(1)); return &LI; } diff --git a/lib/Transforms/Scalar/DeadStoreElimination.cpp b/lib/Transforms/Scalar/DeadStoreElimination.cpp index f99941210e9..dd1a2a6adb8 100644 --- a/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -310,9 +310,13 @@ static Value *getStoredPointerOperand(Instruction *I) { } static uint64_t getPointerSize(const Value *V, const DataLayout &DL, - const TargetLibraryInfo &TLI) { + const TargetLibraryInfo &TLI, + const Function *F) { uint64_t Size; - if (getObjectSize(V, Size, DL, &TLI)) + ObjectSizeOpts Opts; + Opts.NullIsUnknownSize = NullPointerIsDefined(F); + + if (getObjectSize(V, Size, DL, &TLI, Opts)) return Size; return MemoryLocation::UnknownSize; } @@ -343,7 +347,8 @@ static OverwriteResult isOverwrite(const MemoryLocation &Later, int64_t &EarlierOff, int64_t &LaterOff, Instruction *DepWrite, InstOverlapIntervalsTy &IOL, - AliasAnalysis &AA) { + AliasAnalysis &AA, + const Function *F) { // If we don't know the sizes of either access, then we can't do a comparison. if (Later.Size == MemoryLocation::UnknownSize || Earlier.Size == MemoryLocation::UnknownSize) @@ -372,7 +377,7 @@ static OverwriteResult isOverwrite(const MemoryLocation &Later, return OW_Unknown; // If the "Later" store is to a recognizable object, get its size. - uint64_t ObjectSize = getPointerSize(UO2, DL, TLI); + uint64_t ObjectSize = getPointerSize(UO2, DL, TLI, F); if (ObjectSize != MemoryLocation::UnknownSize) if (ObjectSize == Later.Size && ObjectSize >= Earlier.Size) return OW_Complete; @@ -710,7 +715,8 @@ static bool handleFree(CallInst *F, AliasAnalysis *AA, static void removeAccessedObjects(const MemoryLocation &LoadedLoc, SmallSetVector &DeadStackObjects, const DataLayout &DL, AliasAnalysis *AA, - const TargetLibraryInfo *TLI) { + const TargetLibraryInfo *TLI, + const Function *F) { const Value *UnderlyingPointer = GetUnderlyingObject(LoadedLoc.Ptr, DL); // A constant can't be in the dead pointer set. @@ -727,7 +733,7 @@ static void removeAccessedObjects(const MemoryLocation &LoadedLoc, // Remove objects that could alias LoadedLoc. DeadStackObjects.remove_if([&](Value *I) { // See if the loaded location could alias the stack location. - MemoryLocation StackLoc(I, getPointerSize(I, DL, *TLI)); + MemoryLocation StackLoc(I, getPointerSize(I, DL, *TLI, F)); return !AA->isNoAlias(StackLoc, LoadedLoc); }); } @@ -841,7 +847,8 @@ static bool handleEndBlock(BasicBlock &BB, AliasAnalysis *AA, // the call is live. DeadStackObjects.remove_if([&](Value *I) { // See if the call site touches the value. - return isRefSet(AA->getModRefInfo(CS, I, getPointerSize(I, DL, *TLI))); + return isRefSet(AA->getModRefInfo(CS, I, getPointerSize(I, DL, *TLI, + BB.getParent()))); }); // If all of the allocas were clobbered by the call then we're not going @@ -880,7 +887,7 @@ static bool handleEndBlock(BasicBlock &BB, AliasAnalysis *AA, // Remove any allocas from the DeadPointer set that are loaded, as this // makes any stores above the access live. - removeAccessedObjects(LoadedLoc, DeadStackObjects, DL, AA, TLI); + removeAccessedObjects(LoadedLoc, DeadStackObjects, DL, AA, TLI, BB.getParent()); // If all of the allocas were clobbered by the access then we're not going // to find anything else to process. @@ -1176,7 +1183,8 @@ static bool eliminateDeadStores(BasicBlock &BB, AliasAnalysis *AA, !isPossibleSelfRead(Inst, Loc, DepWrite, *TLI, *AA)) { int64_t InstWriteOffset, DepWriteOffset; OverwriteResult OR = isOverwrite(Loc, DepLoc, DL, *TLI, DepWriteOffset, - InstWriteOffset, DepWrite, IOL, *AA); + InstWriteOffset, DepWrite, IOL, *AA, + BB.getParent()); if (OR == OW_Complete) { LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *DepWrite << "\n KILLER: " << *Inst << '\n'); diff --git a/lib/Transforms/Scalar/SCCP.cpp b/lib/Transforms/Scalar/SCCP.cpp index 81a429fe618..6f3d798f704 100644 --- a/lib/Transforms/Scalar/SCCP.cpp +++ b/lib/Transforms/Scalar/SCCP.cpp @@ -1133,8 +1133,12 @@ void SCCPSolver::visitLoadInst(LoadInst &I) { Constant *Ptr = PtrVal.getConstant(); // load null is undefined. - if (isa(Ptr) && I.getPointerAddressSpace() == 0) - return; + if (isa(Ptr)) { + if (NullPointerIsDefined(I.getFunction(), I.getPointerAddressSpace())) + return (void)markOverdefined(IV, &I); + else + return; + } // Transform load (constant global) into the value loaded. if (auto *GV = dyn_cast(Ptr)) { diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp index 4081e0465da..ff5f77c6098 100644 --- a/lib/Transforms/Utils/Local.cpp +++ b/lib/Transforms/Utils/Local.cpp @@ -2050,7 +2050,9 @@ static bool markAliveBlocks(Function &F, if (auto *CI = dyn_cast(&I)) { Value *Callee = CI->getCalledValue(); - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(CI->getFunction())) || + isa(Callee)) { changeToUnreachable(CI, /*UseLLVMTrap=*/false, false, DDT); Changed = true; break; @@ -2079,7 +2081,8 @@ static bool markAliveBlocks(Function &F, if (isa(Ptr) || (isa(Ptr) && - SI->getPointerAddressSpace() == 0)) { + !NullPointerIsDefined(SI->getFunction(), + SI->getPointerAddressSpace()))) { changeToUnreachable(SI, true, false, DDT); Changed = true; break; @@ -2091,7 +2094,9 @@ static bool markAliveBlocks(Function &F, if (auto *II = dyn_cast(Terminator)) { // Turn invokes that call 'nounwind' functions into ordinary calls. Value *Callee = II->getCalledValue(); - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(BB->getParent())) || + isa(Callee)) { changeToUnreachable(II, true, false, DDT); Changed = true; } else if (II->doesNotThrow() && canSimplifyInvokeNoUnwind(&F)) { diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp index 083e83fdb7d..c87b5c16ffc 100644 --- a/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/lib/Transforms/Utils/SimplifyCFG.cpp @@ -5950,17 +5950,20 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I) { // Load from null is undefined. if (LoadInst *LI = dyn_cast(Use)) if (!LI->isVolatile()) - return LI->getPointerAddressSpace() == 0; + return !NullPointerIsDefined(LI->getFunction(), + LI->getPointerAddressSpace()); // Store to null is undefined. if (StoreInst *SI = dyn_cast(Use)) if (!SI->isVolatile()) - return SI->getPointerAddressSpace() == 0 && + return (!NullPointerIsDefined(SI->getFunction(), + SI->getPointerAddressSpace())) && SI->getPointerOperand() == I; // A call to null is undefined. if (auto CS = CallSite(Use)) - return CS.getCalledValue() == I; + return !NullPointerIsDefined(CS->getFunction()) && + CS.getCalledValue() == I; } return false; } diff --git a/test/Analysis/MemorySSA/cyclicphi.ll b/test/Analysis/MemorySSA/cyclicphi.ll index 39b35b0f793..b6434890854 100644 --- a/test/Analysis/MemorySSA/cyclicphi.ll +++ b/test/Analysis/MemorySSA/cyclicphi.ll @@ -33,6 +33,36 @@ bb77: ; preds = %bb68, %bb26 br label %bb26 } +define hidden void @quux_no_null_opt(%struct.hoge *%f) align 2 #0 { +; CHECK-LABEL: quux_no_null_opt( + %tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0 + %tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1 + %tmp25 = bitcast %struct.widget* %tmp24 to i64** + br label %bb26 + +bb26: ; preds = %bb77, %0 +; CHECK: 3 = MemoryPhi({%0,liveOnEntry},{bb77,2}) +; CHECK-NEXT: br i1 undef, label %bb68, label %bb77 + br i1 undef, label %bb68, label %bb77 + +bb68: ; preds = %bb26 +; CHECK: MemoryUse(3) +; CHECK-NEXT: %tmp69 = load i64, i64* null, align 8 + %tmp69 = load i64, i64* null, align 8 +; CHECK: 1 = MemoryDef(3) +; CHECK-NEXT: store i64 %tmp69, i64* %tmp, align 8 + store i64 %tmp69, i64* %tmp, align 8 + br label %bb77 + +bb77: ; preds = %bb68, %bb26 +; CHECK: 2 = MemoryPhi({bb26,3},{bb68,1}) +; CHECK: MemoryUse(2) +; CHECK-NEXT: %tmp78 = load i64*, i64** %tmp25, align 8 + %tmp78 = load i64*, i64** %tmp25, align 8 + %tmp79 = getelementptr inbounds i64, i64* %tmp78, i64 undef + br label %bb26 +} + ; CHECK-LABEL: define void @quux_skip define void @quux_skip(%struct.hoge* noalias %f, i64* noalias %g) align 2 { %tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0 @@ -121,3 +151,5 @@ bb77: ; preds = %bb68, %bb26 ; CHECK-NEXT: br label %bb26 br label %bb26 } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/CorrelatedValuePropagation/non-null.ll b/test/Transforms/CorrelatedValuePropagation/non-null.ll index e5882f12565..a5cd90524fa 100644 --- a/test/Transforms/CorrelatedValuePropagation/non-null.ll +++ b/test/Transforms/CorrelatedValuePropagation/non-null.ll @@ -10,6 +10,16 @@ bb: ret void } +define void @test1_no_null_opt(i8* %ptr) #0 { +; CHECK: test1_no_null_opt + %A = load i8, i8* %ptr + br label %bb +bb: + icmp ne i8* %ptr, null +; CHECK: icmp ne i8* %ptr, null + ret void +} + define void @test2(i8* %ptr) { ; CHECK: test2 store i8 0, i8* %ptr @@ -20,6 +30,16 @@ bb: ret void } +define void @test2_no_null_opt(i8* %ptr) #0 { +; CHECK: test2_no_null_opt + store i8 0, i8* %ptr + br label %bb +bb: + icmp ne i8* %ptr, null +; CHECK: icmp ne i8* %ptr, null + ret void +} + define void @test3() { ; CHECK: test3 %ptr = alloca i8 @@ -30,6 +50,17 @@ bb: ret void } +;; OK to remove icmp here since ptr is coming from alloca. +define void @test3_no_null_opt() #0 { +; CHECK: test3 + %ptr = alloca i8 + br label %bb +bb: + icmp ne i8* %ptr, null +; CHECK-NOT: icmp + ret void +} + declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) define void @test4(i8* %dest, i8* %src) { ; CHECK: test4 @@ -42,6 +73,18 @@ bb: ret void } +define void @test4_no_null_opt(i8* %dest, i8* %src) #0 { +; CHECK: test4_no_null_opt + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 1, i1 false) + br label %bb +bb: + icmp ne i8* %dest, null + icmp ne i8* %src, null +; CHECK: icmp ne i8* %dest, null +; CHECK: icmp ne i8* %src, null + ret void +} + declare void @llvm.memmove.p0i8.p0i8.i32(i8*, i8*, i32, i1) define void @test5(i8* %dest, i8* %src) { ; CHECK: test5 @@ -54,6 +97,18 @@ bb: ret void } +define void @test5_no_null_opt(i8* %dest, i8* %src) #0 { +; CHECK: test5_no_null_opt + call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 1, i1 false) + br label %bb +bb: + icmp ne i8* %dest, null + icmp ne i8* %src, null +; CHECK: icmp ne i8* %dest, null +; CHECK: icmp ne i8* %src, null + ret void +} + declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i1) define void @test6(i8* %dest) { ; CHECK: test6 @@ -65,6 +120,16 @@ bb: ret void } +define void @test6_no_null_opt(i8* %dest) #0 { +; CHECK: test6_no_null_opt + call void @llvm.memset.p0i8.i32(i8* %dest, i8 255, i32 1, i1 false) + br label %bb +bb: + icmp ne i8* %dest, null +; CHECK: icmp ne i8* %dest, null + ret void +} + define void @test7(i8* %dest, i8* %src, i32 %len) { ; CHECK: test7 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false) @@ -161,3 +226,5 @@ merge: ; CHECK: call void @test12_helper(i8* nonnull %merged_arg) ret void } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/FunctionAttrs/nonnull.ll b/test/Transforms/FunctionAttrs/nonnull.ll index 7de65d8a0fd..7029be9691d 100644 --- a/test/Transforms/FunctionAttrs/nonnull.ll +++ b/test/Transforms/FunctionAttrs/nonnull.ll @@ -224,8 +224,17 @@ define i32* @gep1(i32* %p) { ret i32* %q } +define i32* @gep1_no_null_opt(i32* %p) #0 { +; Should't be able to derive nonnull based on gep. +; CHECK: define i32* @gep1_no_null_opt( + %q = getelementptr inbounds i32, i32* %p, i32 1 + ret i32* %q +} + ; CHECK: define i32 addrspace(3)* @gep2( define i32 addrspace(3)* @gep2(i32 addrspace(3)* %p) { %q = getelementptr inbounds i32, i32 addrspace(3)* %p, i32 1 ret i32 addrspace(3)* %q } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GVN/PRE/2018-06-08-pre-load-dbgloc-no-null-opt.ll b/test/Transforms/GVN/PRE/2018-06-08-pre-load-dbgloc-no-null-opt.ll new file mode 100644 index 00000000000..88896a4e97e --- /dev/null +++ b/test/Transforms/GVN/PRE/2018-06-08-pre-load-dbgloc-no-null-opt.ll @@ -0,0 +1,82 @@ +; This test checks if debug loc is propagated to load/store created by GVN/Instcombine. +; RUN: opt < %s -gvn -S | FileCheck %s --check-prefixes=ALL +; RUN: opt < %s -gvn -instcombine -S | FileCheck %s --check-prefixes=ALL + +; struct node { +; int *v; +; struct desc *descs; +; }; + +; struct desc { +; struct node *node; +; }; + +; extern int bar(void *v, void* n); + +; int test(struct desc *desc) +; { +; void *v, *n; +; v = !desc ? ((void *)0) : desc->node->v; // Line 15 +; n = &desc->node->descs[0]; // Line 16 +; return bar(v, n); +; } + +; Line 16, Column 13: +; n = &desc->node->descs[0]; +; ^ + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-gnu" + +%struct.desc = type { %struct.node* } +%struct.node = type { i32*, %struct.desc* } + +define i32 @test_no_null_opt(%struct.desc* readonly %desc) local_unnamed_addr #0 !dbg !4 { +entry: + %tobool = icmp eq %struct.desc* %desc, null + br i1 %tobool, label %cond.end, label %cond.false, !dbg !9 +; ALL: br i1 %tobool, label %entry.cond.end_crit_edge, label %cond.false, !dbg [[LOC_15_6:![0-9]+]] +; ALL: entry.cond.end_crit_edge: +; ALL: load %struct.node*, %struct.node** null, align {{[0-9]+}}, !dbg [[LOC_16_13:![0-9]+]] + +cond.false: + %0 = bitcast %struct.desc* %desc to i8***, !dbg !11 + %1 = load i8**, i8*** %0, align 8, !dbg !11 + %2 = load i8*, i8** %1, align 8 + br label %cond.end, !dbg !9 + +cond.end: +; ALL: phi %struct.node* [ %3, %cond.false ], [ %.pre, %entry.cond.end_crit_edge ] +; ALL: phi i8* [ %2, %cond.false ], [ null, %entry.cond.end_crit_edge ] + + %3 = phi i8* [ %2, %cond.false ], [ null, %entry ], !dbg !9 + %node2 = getelementptr inbounds %struct.desc, %struct.desc* %desc, i64 0, i32 0 + %4 = load %struct.node*, %struct.node** %node2, align 8, !dbg !10 + %descs = getelementptr inbounds %struct.node, %struct.node* %4, i64 0, i32 1 + %5 = bitcast %struct.desc** %descs to i8** + %6 = load i8*, i8** %5, align 8 + %call = tail call i32 @bar(i8* %3, i8* %6) + ret i32 %call +} +attributes #0 = { "null-pointer-is-valid"="true" } + +declare i32 @bar(i8*, i8*) local_unnamed_addr #1 +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) +!1 = !DIFile(filename: "test.c", directory: ".") +!2 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "test_no_null_opt", scope: !1, file: !1, line: 12, type: !5, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !8) +!5 = !DISubroutineType(types: !6) +!6 = !{!7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{} +!9 = !DILocation(line: 15, column: 6, scope: !4) +!10 = !DILocation(line: 16, column: 13, scope: !4) +!11 = !DILocation(line: 15, column: 34, scope: !4) + +;ALL: [[SCOPE:![0-9]+]] = distinct !DISubprogram(name: "test_no_null_opt",{{.*}} +;ALL: [[LOC_15_6]] = !DILocation(line: 15, column: 6, scope: [[SCOPE]]) +;ALL: [[LOC_16_13]] = !DILocation(line: 16, column: 13, scope: [[SCOPE]]) diff --git a/test/Transforms/GlobalOpt/MallocSROA-section-no-null-opt.ll b/test/Transforms/GlobalOpt/MallocSROA-section-no-null-opt.ll new file mode 100644 index 00000000000..c9b3f6fc1cd --- /dev/null +++ b/test/Transforms/GlobalOpt/MallocSROA-section-no-null-opt.ll @@ -0,0 +1,34 @@ +; RUN: opt -globalopt -S < %s | FileCheck %s +; CHECK: @Y +; CHECK: section ".foo" + +%struct.xyz = type { double, i32 } + +@Y = internal global %struct.xyz* null ,section ".foo" ; <%struct.xyz**> [#uses=2] +@numf2s = external global i32 ; [#uses=1] + +define void @init_net() #0 { +; CHECK-LABEL: init_net( +; CHECK: load i32, i32* @numf2s +; CHECK: call i8* @malloc +; CHECK: store %struct.xyz* {{.*}}, %struct.xyz** @Y +entry: + %0 = load i32, i32* @numf2s, align 4 ; [#uses=1] + %mallocsize2 = shl i32 %0, 4 ; [#uses=1] + %malloccall3 = tail call i8* @malloc(i32 %mallocsize2) ; [#uses=1] + %1 = bitcast i8* %malloccall3 to %struct.xyz* ; <%struct.xyz*> [#uses=1] + store %struct.xyz* %1, %struct.xyz** @Y, align 8 + ret void +} + +define %struct.xyz* @load_train() #0 { +; CHECK-LABEL: load_train( +; CHECK: load %struct.xyz*, %struct.xyz** @Y +entry: + %0 = load %struct.xyz*, %struct.xyz** @Y, align 8 ; <%struct.xyz*> [#uses=0] + ret %struct.xyz* %0 +} + +declare noalias i8* @malloc(i32) + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-1-no-null-opt.ll b/test/Transforms/GlobalOpt/heap-sra-1-no-null-opt.ll new file mode 100644 index 00000000000..c826e7f7a04 --- /dev/null +++ b/test/Transforms/GlobalOpt/heap-sra-1-no-null-opt.ll @@ -0,0 +1,40 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +%struct.foo = type { i32, i32 } +@X = internal global %struct.foo* null +; CHECK: @X +; CHECK-NOT: @X.f0 + +define void @bar(i64 %Size) nounwind noinline #0 { +entry: + %mallocsize = mul i64 %Size, 8 ; [#uses=1] + %malloccall = tail call i8* @malloc(i64 %mallocsize) ; [#uses=1] + %.sub = bitcast i8* %malloccall to %struct.foo* ; <%struct.foo*> [#uses=1] + store %struct.foo* %.sub, %struct.foo** @X, align 4 + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @baz() nounwind readonly noinline #0 { +bb1.thread: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + br label %bb1 + +bb1: ; preds = %bb1, %bb1.thread + %i.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %indvar.next, %bb1 ] + %sum.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %3, %bb1 ] + %1 = getelementptr %struct.foo, %struct.foo* %0, i32 %i.0.reg2mem.0, i32 0 + %2 = load i32, i32* %1, align 4 + %3 = add i32 %2, %sum.0.reg2mem.0 + %indvar.next = add i32 %i.0.reg2mem.0, 1 + %exitcond = icmp eq i32 %indvar.next, 1200 + br i1 %exitcond, label %bb2, label %bb1 + +bb2: ; preds = %bb1 + ret i32 %3 +} + +attributes #0 = { "null-pointer-is-valid"="true" } + diff --git a/test/Transforms/GlobalOpt/heap-sra-1.ll b/test/Transforms/GlobalOpt/heap-sra-1.ll index 6035eaaefcc..28a20ada03f 100644 --- a/test/Transforms/GlobalOpt/heap-sra-1.ll +++ b/test/Transforms/GlobalOpt/heap-sra-1.ll @@ -36,3 +36,10 @@ bb2: ; preds = %bb1 ret i32 %3 } +define void @bam(i64 %Size) nounwind noinline #0 { +entry: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-2-no-null-opt.ll b/test/Transforms/GlobalOpt/heap-sra-2-no-null-opt.ll new file mode 100644 index 00000000000..c33bcba9921 --- /dev/null +++ b/test/Transforms/GlobalOpt/heap-sra-2-no-null-opt.ll @@ -0,0 +1,39 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +%struct.foo = type { i32, i32 } +@X = internal global %struct.foo* null ; <%struct.foo**> [#uses=2] +; CHECK: @X +; CHECK-NOT: @X.f0 + +define void @bar(i32 %Size) nounwind noinline #0 { +entry: + %malloccall = tail call i8* @malloc(i64 8000000) ; [#uses=1] + %0 = bitcast i8* %malloccall to [1000000 x %struct.foo]* ; <[1000000 x %struct.foo]*> [#uses=1] + %.sub = getelementptr [1000000 x %struct.foo], [1000000 x %struct.foo]* %0, i32 0, i32 0 ; <%struct.foo*> [#uses=1] + store %struct.foo* %.sub, %struct.foo** @X, align 4 + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @baz() nounwind readonly noinline #0 { +bb1.thread: + %0 = load %struct.foo*, %struct.foo** @X, align 4 ; <%struct.foo*> [#uses=1] + br label %bb1 + +bb1: ; preds = %bb1, %bb1.thread + %i.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %indvar.next, %bb1 ] ; [#uses=2] + %sum.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %3, %bb1 ] ; [#uses=1] + %1 = getelementptr %struct.foo, %struct.foo* %0, i32 %i.0.reg2mem.0, i32 0 ; [#uses=1] + %2 = load i32, i32* %1, align 4 ; [#uses=1] + %3 = add i32 %2, %sum.0.reg2mem.0 ; [#uses=2] + %indvar.next = add i32 %i.0.reg2mem.0, 1 ; [#uses=2] + %exitcond = icmp eq i32 %indvar.next, 1200 ; [#uses=1] + br i1 %exitcond, label %bb2, label %bb1 + +bb2: ; preds = %bb1 + ret i32 %3 +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-2.ll b/test/Transforms/GlobalOpt/heap-sra-2.ll index d66c627b184..ec05b22b33b 100644 --- a/test/Transforms/GlobalOpt/heap-sra-2.ll +++ b/test/Transforms/GlobalOpt/heap-sra-2.ll @@ -36,3 +36,10 @@ bb2: ; preds = %bb1 ret i32 %3 } +define void @bam(i64 %Size) nounwind noinline #0 { +entry: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-3-no-null-opt.ll b/test/Transforms/GlobalOpt/heap-sra-3-no-null-opt.ll new file mode 100644 index 00000000000..ba3b0a418a1 --- /dev/null +++ b/test/Transforms/GlobalOpt/heap-sra-3-no-null-opt.ll @@ -0,0 +1,41 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +%struct.foo = type { i32, i32 } +@X = internal global %struct.foo* null +; CHECK: @X +; CHECK-NOT: @X.f0 + +define void @bar(i64 %Size) nounwind noinline #0 { +entry: + %mallocsize = mul i64 8, %Size ; [#uses=1] +; CHECK: mul i64 8, %Size + %malloccall = tail call i8* @malloc(i64 %mallocsize) ; [#uses=1] + %.sub = bitcast i8* %malloccall to %struct.foo* ; <%struct.foo*> [#uses=1] + store %struct.foo* %.sub, %struct.foo** @X, align 4 + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @baz() nounwind readonly noinline #0 { +bb1.thread: +; CHECK: load %struct.foo*, %struct.foo** @X, align 4 + %0 = load %struct.foo*, %struct.foo** @X, align 4 + br label %bb1 + +bb1: ; preds = %bb1, %bb1.thread + %i.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %indvar.next, %bb1 ] + %sum.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %3, %bb1 ] + %1 = getelementptr %struct.foo, %struct.foo* %0, i32 %i.0.reg2mem.0, i32 0 + %2 = load i32, i32* %1, align 4 + %3 = add i32 %2, %sum.0.reg2mem.0 + %indvar.next = add i32 %i.0.reg2mem.0, 1 + %exitcond = icmp eq i32 %indvar.next, 1200 + br i1 %exitcond, label %bb2, label %bb1 + +bb2: ; preds = %bb1 + ret i32 %3 +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-3.ll b/test/Transforms/GlobalOpt/heap-sra-3.ll index 6a34364b49f..67058c8aba2 100644 --- a/test/Transforms/GlobalOpt/heap-sra-3.ll +++ b/test/Transforms/GlobalOpt/heap-sra-3.ll @@ -37,3 +37,10 @@ bb2: ; preds = %bb1 ret i32 %3 } +define void @bam(i64 %Size) nounwind noinline #0 { +entry: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-4-no-null-opt.ll b/test/Transforms/GlobalOpt/heap-sra-4-no-null-opt.ll new file mode 100644 index 00000000000..607c93d1e8a --- /dev/null +++ b/test/Transforms/GlobalOpt/heap-sra-4-no-null-opt.ll @@ -0,0 +1,44 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +%struct.foo = type { i32, i32 } + +@X = internal global %struct.foo* null +; CHECK: @X +; CHECK-NOT: @X.f0 + +define void @bar(i64 %Size) nounwind noinline #0 { +entry: + %mallocsize = shl i64 %Size, 3 ; [#uses=1] + %malloccall = tail call i8* @malloc(i64 %mallocsize) ; [#uses=1] +; CHECK: shl i64 %Size, 3 + %.sub = bitcast i8* %malloccall to %struct.foo* ; <%struct.foo*> [#uses=1] + store %struct.foo* %.sub, %struct.foo** @X, align 4 + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @baz() nounwind readonly noinline #0 { +; CHECK-LABEL: @baz( +bb1.thread: +; CHECK: load %struct.foo*, %struct.foo** @X, align 4 + %0 = load %struct.foo*, %struct.foo** @X, align 4 + br label %bb1 + +bb1: ; preds = %bb1, %bb1.thread + %i.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %indvar.next, %bb1 ] + %sum.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %3, %bb1 ] + %1 = getelementptr %struct.foo, %struct.foo* %0, i32 %i.0.reg2mem.0, i32 0 + %2 = load i32, i32* %1, align 4 +; CHECK: load i32, i32* %1, align 4 + %3 = add i32 %2, %sum.0.reg2mem.0 + %indvar.next = add i32 %i.0.reg2mem.0, 1 + %exitcond = icmp eq i32 %indvar.next, 1200 + br i1 %exitcond, label %bb2, label %bb1 + +bb2: ; preds = %bb1 + ret i32 %3 +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-4.ll b/test/Transforms/GlobalOpt/heap-sra-4.ll index 2176b9fcbee..71832f34923 100644 --- a/test/Transforms/GlobalOpt/heap-sra-4.ll +++ b/test/Transforms/GlobalOpt/heap-sra-4.ll @@ -37,3 +37,11 @@ bb2: ; preds = %bb1 ret i32 %3 } +define void @bam(i64 %Size) nounwind noinline #0 { +entry: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } + diff --git a/test/Transforms/GlobalOpt/heap-sra-phi-no-null-opt.ll b/test/Transforms/GlobalOpt/heap-sra-phi-no-null-opt.ll new file mode 100644 index 00000000000..06c74e5bfc4 --- /dev/null +++ b/test/Transforms/GlobalOpt/heap-sra-phi-no-null-opt.ll @@ -0,0 +1,54 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +%struct.foo = type { i32, i32 } + +@X = internal global %struct.foo* null ; <%struct.foo**> [#uses=2] +; CHECK: @X +; CHECK-NOT: @X.f0 + +define void @bar(i32 %Size) nounwind noinline #0 { +; CHECK-LABEL: @bar( +entry: + %malloccall = tail call i8* @malloc(i64 8000000) ; [#uses=1] + %tmp = bitcast i8* %malloccall to [1000000 x %struct.foo]* ; <[1000000 x %struct.foo]*> [#uses=1] + %.sub = getelementptr [1000000 x %struct.foo], [1000000 x %struct.foo]* %tmp, i32 0, i32 0 ; <%struct.foo*> [#uses=1] + store %struct.foo* %.sub, %struct.foo** @X, align 4 + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @baz() nounwind readonly noinline #0 { +; CHECK-LABEL: @baz( +bb1.thread: + %tmpLD1 = load %struct.foo*, %struct.foo** @X, align 4 ; <%struct.foo*> [#uses=1] +; CHECK: load %struct.foo*, %struct.foo** @X, align 4 + br label %bb1 + +bb1: ; preds = %bb1, %bb1.thread + %tmp = phi %struct.foo* [%tmpLD1, %bb1.thread ], [ %tmpLD2, %bb1 ] ; [#uses=2] +; CHECK: %tmp = phi %struct.foo* [ %tmpLD1, %bb1.thread ], [ %tmpLD2, %bb1 ] + %i.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %indvar.next, %bb1 ] ; [#uses=2] + %sum.0.reg2mem.0 = phi i32 [ 0, %bb1.thread ], [ %tmp3, %bb1 ] ; [#uses=1] + %tmp1 = getelementptr %struct.foo, %struct.foo* %tmp, i32 %i.0.reg2mem.0, i32 0 ; [#uses=1] + %tmp2 = load i32, i32* %tmp1, align 4 ; [#uses=1] +; CHECK: load i32, i32* %tmp1, align 4 + %tmp6 = add i32 %tmp2, %sum.0.reg2mem.0 ; [#uses=2] + %tmp4 = getelementptr %struct.foo, %struct.foo* %tmp, i32 %i.0.reg2mem.0, i32 1 ; [#uses=1] + %tmp5 = load i32 , i32 * %tmp4 +; CHECK: load i32, i32* %tmp4 + %tmp3 = add i32 %tmp5, %tmp6 + %indvar.next = add i32 %i.0.reg2mem.0, 1 ; [#uses=2] + + %tmpLD2 = load %struct.foo*, %struct.foo** @X, align 4 ; <%struct.foo*> [#uses=1] +; CHECK: load %struct.foo*, %struct.foo** @X, align 4 + + %exitcond = icmp eq i32 %indvar.next, 1200 ; [#uses=1] + br i1 %exitcond, label %bb2, label %bb1 + +bb2: ; preds = %bb1 + ret i32 %tmp3 +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/heap-sra-phi.ll b/test/Transforms/GlobalOpt/heap-sra-phi.ll index 995f9dc142c..770220dd07b 100644 --- a/test/Transforms/GlobalOpt/heap-sra-phi.ll +++ b/test/Transforms/GlobalOpt/heap-sra-phi.ll @@ -42,3 +42,11 @@ bb1: ; preds = %bb1, %bb1.thread bb2: ; preds = %bb1 ret i32 %tmp3 } + +define void @bam(i64 %Size) nounwind noinline #0 { +entry: + %0 = load %struct.foo*, %struct.foo** @X, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/load-store-global-no-null-opt.ll b/test/Transforms/GlobalOpt/load-store-global-no-null-opt.ll new file mode 100644 index 00000000000..d319d162ff4 --- /dev/null +++ b/test/Transforms/GlobalOpt/load-store-global-no-null-opt.ll @@ -0,0 +1,28 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s + +@a = internal global i64* null, align 8 +; CHECK: @a + +; PR13968 +define void @qux_no_null_opt() nounwind #0 { +; CHECK-LABEL: @qux_no_null_opt( +; CHECK: getelementptr i64*, i64** @a, i32 1 +; CHECK: store i64* inttoptr (i64 1 to i64*), i64** @a + %b = bitcast i64** @a to i8* + %g = getelementptr i64*, i64** @a, i32 1 + %cmp = icmp ne i8* null, %b + %cmp2 = icmp eq i8* null, %b + %cmp3 = icmp eq i64** null, %g + store i64* inttoptr (i64 1 to i64*), i64** @a, align 8 + %l = load i64*, i64** @a, align 8 + ret void +} + +define i64* @bar() { + %X = load i64*, i64** @a, align 8 + ret i64* %X +; CHECK-LABEL: @bar( +; CHECK: load +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/malloc-promote-1-no-null-opt.ll b/test/Transforms/GlobalOpt/malloc-promote-1-no-null-opt.ll new file mode 100644 index 00000000000..fc6dab31640 --- /dev/null +++ b/test/Transforms/GlobalOpt/malloc-promote-1-no-null-opt.ll @@ -0,0 +1,31 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +@G = internal global i32* null ; [#uses=3] +; CHECK: global + +define void @init() #0 { +; CHECK-LABEL: @init( +; CHECK: store +; CHECK: load + %malloccall = tail call i8* @malloc(i64 4) ; [#uses=1] + %P = bitcast i8* %malloccall to i32* ; [#uses=1] + store i32* %P, i32** @G + %GV = load i32*, i32** @G ; [#uses=1] + store i32 0, i32* %GV + ret void +} + +declare noalias i8* @malloc(i64) + +define i32 @get() #0 { +; CHECK-LABEL: @get( +; CHECK: load i32*, i32** @G +; CHECK-NEXT: load i32, i32* %GV + %GV = load i32*, i32** @G ; [#uses=1] + %V = load i32, i32* %GV ; [#uses=1] + ret i32 %V +; CHECK: ret i32 %V +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/malloc-promote-1.ll b/test/Transforms/GlobalOpt/malloc-promote-1.ll index d5087dea344..a8f127474e3 100644 --- a/test/Transforms/GlobalOpt/malloc-promote-1.ll +++ b/test/Transforms/GlobalOpt/malloc-promote-1.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -globalopt -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" -@G = internal global i32* null ; [#uses=3] +@G = internal global i32* null ; [#uses=4] ; CHECK-NOT: global define void @init() { @@ -22,3 +22,11 @@ define i32 @get() { ; CHECK: ret i32 0 } +define void @foo(i64 %Size) nounwind noinline #0 { +entry: + %0 = load i32*, i32** @G, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } + diff --git a/test/Transforms/GlobalOpt/malloc-promote-2-no-null-opt.ll b/test/Transforms/GlobalOpt/malloc-promote-2-no-null-opt.ll new file mode 100644 index 00000000000..009a33435fd --- /dev/null +++ b/test/Transforms/GlobalOpt/malloc-promote-2-no-null-opt.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" + +@G = internal global i32* null + +define void @t() #0 { +; CHECK: @t() +; CHECK: call i8* @malloc +; CHECK: bitcast +; CHECK: store +; CHECK: load +; CHECK: getelementptr +; CHECK: store + %malloccall = tail call i8* @malloc(i64 mul (i64 100, i64 4)) + %P = bitcast i8* %malloccall to i32* + store i32* %P, i32** @G + %GV = load i32*, i32** @G + %GVe = getelementptr i32, i32* %GV, i32 40 + store i32 20, i32* %GVe + ret void +} + +declare noalias i8* @malloc(i64) +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/malloc-promote-2.ll b/test/Transforms/GlobalOpt/malloc-promote-2.ll index 335ed82a8cf..64f379365c7 100644 --- a/test/Transforms/GlobalOpt/malloc-promote-2.ll +++ b/test/Transforms/GlobalOpt/malloc-promote-2.ll @@ -17,3 +17,11 @@ define void @t() { } declare noalias i8* @malloc(i64) + +define void @foo(i64 %Size) nounwind noinline #0 { +entry: + %0 = load i32*, i32** @G, align 4 + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/storepointer-compare-no-null-opt.ll b/test/Transforms/GlobalOpt/storepointer-compare-no-null-opt.ll new file mode 100644 index 00000000000..709df17e0b5 --- /dev/null +++ b/test/Transforms/GlobalOpt/storepointer-compare-no-null-opt.ll @@ -0,0 +1,40 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s +; CHECK: global + +@G = internal global void ()* null ; [#uses=2] + +define internal void @Actual() { +; CHECK-LABEL: Actual( + ret void +} + +define void @init() { +; CHECK-LABEL: init( +; CHECK: store void ()* @Actual, void ()** @G + store void ()* @Actual, void ()** @G + ret void +} + +define void @doit() #0 { +; CHECK-LABEL: doit( + %FP = load void ()*, void ()** @G ; [#uses=2] +; CHECK: %FP = load void ()*, void ()** @G + %CC = icmp eq void ()* %FP, null ; [#uses=1] +; CHECK: %CC = icmp eq void ()* %FP, null + br i1 %CC, label %isNull, label %DoCall +; CHECK: br i1 %CC, label %isNull, label %DoCall + +DoCall: ; preds = %0 +; CHECK: DoCall: +; CHECK: call void %FP() +; CHECK: ret void + call void %FP( ) + ret void + +isNull: ; preds = %0 +; CHECK: isNull: +; CHECK: ret void + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/GlobalOpt/storepointer-no-null-opt.ll b/test/Transforms/GlobalOpt/storepointer-no-null-opt.ll new file mode 100644 index 00000000000..c9a63f0080d --- /dev/null +++ b/test/Transforms/GlobalOpt/storepointer-no-null-opt.ll @@ -0,0 +1,27 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s + +@G = internal global void ()* null ; [#uses=2] +; CHECK: global + +define internal void @Actual() { +; CHECK-LABEL: Actual( + ret void +} + +define void @init() { +; CHECK-LABEL: init( +; CHECK: store void ()* @Actual, void ()** @G + store void ()* @Actual, void ()** @G + ret void +} + +define void @doit() #0 { +; CHECK-LABEL: doit( +; CHECK: %FP = load void ()*, void ()** @G +; CHECK: call void %FP() + %FP = load void ()*, void ()** @G ; [#uses=1] + call void %FP( ) + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/IPConstantProp/PR26044.ll b/test/Transforms/IPConstantProp/PR26044.ll index 9e8c61eb53b..6c608868210 100644 --- a/test/Transforms/IPConstantProp/PR26044.ll +++ b/test/Transforms/IPConstantProp/PR26044.ll @@ -23,9 +23,40 @@ entry: ret i32 %cond } +define void @fn_no_null_opt() #0 { +entry: + br label %if.end + +for.cond1: ; preds = %if.end, %for.end + br i1 undef, label %if.end, label %if.end + +if.end: ; preds = %lbl, %for.cond1 + %e.2 = phi i32* [ undef, %entry ], [ null, %for.cond1 ], [ null, %for.cond1 ] + %0 = load i32, i32* %e.2, align 4 + %call = call i32 @fn0(i32 %0) + br label %for.cond1 +} + +define internal i32 @fn0(i32 %p1) { +entry: + %tobool = icmp ne i32 %p1, 0 + %cond = select i1 %tobool, i32 %p1, i32 %p1 + ret i32 %cond +} + +attributes #0 = { "null-pointer-is-valid"="true" } + ; CHECK-LABEL: define void @fn2( ; CHECK: call i32 @fn1(i32 undef) ; CHECK-LABEL: define internal i32 @fn1( ; CHECK:%[[COND:.*]] = select i1 undef, i32 undef, i32 undef ; CHECK: ret i32 %[[COND]] + +; CHECK-LABEL: define void @fn_no_null_opt( +; CHECK: call i32 @fn0(i32 %0) + +; CHECK-LABEL: define internal i32 @fn0( +; CHECK:%[[TOBOOL:.*]] = icmp ne i32 %p1, 0 +; CHECK:%[[COND:.*]] = select i1 %[[TOBOOL]], i32 %p1, i32 %p1 +; CHECK: ret i32 %[[COND]] diff --git a/test/Transforms/Inline/attributes.ll b/test/Transforms/Inline/attributes.ll index 0df3cfa9a52..c2808ba8c03 100644 --- a/test/Transforms/Inline/attributes.ll +++ b/test/Transforms/Inline/attributes.ll @@ -333,6 +333,50 @@ define i32 @test_no-use-jump-tables3(i32 %i) "no-jump-tables"="true" { ; CHECK-NEXT: ret i32 } +; Calle with "null-pointer-is-valid"="true" attribute should not be inlined +; into a caller without this attribute. Exception: alwaysinline callee +; can still be inlined. + +define i32 @null-pointer-is-valid_callee0(i32 %i) "null-pointer-is-valid"="true" { + ret i32 %i +; CHECK: @null-pointer-is-valid_callee0(i32 %i) +; CHECK-NEXT: ret i32 +} + +define i32 @null-pointer-is-valid_callee1(i32 %i) alwaysinline "null-pointer-is-valid"="true" { + ret i32 %i +; CHECK: @null-pointer-is-valid_callee1(i32 %i) +; CHECK-NEXT: ret i32 +} + +define i32 @null-pointer-is-valid_callee2(i32 %i) { + ret i32 %i +; CHECK: @null-pointer-is-valid_callee2(i32 %i) +; CHECK-NEXT: ret i32 +} + +define i32 @test_null-pointer-is-valid0(i32 %i) { + %1 = call i32 @null-pointer-is-valid_callee0(i32 %i) + ret i32 %1 +; CHECK: @test_null-pointer-is-valid0( +; CHECK: call i32 @null-pointer-is-valid_callee0 +; CHECK-NEXT: ret i32 +} + +define i32 @test_null-pointer-is-valid1(i32 %i) { + %1 = call i32 @null-pointer-is-valid_callee1(i32 %i) + ret i32 %1 +; CHECK: @test_null-pointer-is-valid1( +; CHECK-NEXT: ret i32 +} + +define i32 @test_null-pointer-is-valid2(i32 %i) "null-pointer-is-valid"="true" { + %1 = call i32 @null-pointer-is-valid_callee2(i32 %i) + ret i32 %1 +; CHECK: @test_null-pointer-is-valid2( +; CHECK-NEXT: ret i32 +} + ; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" } ; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" } ; CHECK: attributes [[NOIMPLICITFLOAT]] = { noimplicitfloat } diff --git a/test/Transforms/InstCombine/atomic.ll b/test/Transforms/InstCombine/atomic.ll index 4b2f4323154..0f9752657fa 100644 --- a/test/Transforms/InstCombine/atomic.ll +++ b/test/Transforms/InstCombine/atomic.ll @@ -102,6 +102,13 @@ define i32 @test9() { ret i32 %x } +define i32 @test9_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test9_no_null_opt( +; CHECK: load atomic i32, i32* null unordered + %x = load atomic i32, i32* null unordered, align 4 + ret i32 %x +} + ; FIXME: Could also fold define i32 @test10() { ; CHECK-LABEL: define i32 @test10( @@ -110,6 +117,13 @@ define i32 @test10() { ret i32 %x } +define i32 @test10_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test10_no_null_opt( +; CHECK: load atomic i32, i32* null monotonic + %x = load atomic i32, i32* null monotonic, align 4 + ret i32 %x +} + ; Would this be legal to fold? Probably? define i32 @test11() { ; CHECK-LABEL: define i32 @test11( @@ -118,6 +132,13 @@ define i32 @test11() { ret i32 %x } +define i32 @test11_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test11_no_null_opt( +; CHECK: load atomic i32, i32* null seq_cst + %x = load atomic i32, i32* null seq_cst, align 4 + ret i32 %x +} + ; An unordered access to null is still unreachable. There's no ; ordering imposed. define i32 @test12() { @@ -127,6 +148,13 @@ define i32 @test12() { ret i32 0 } +define i32 @test12_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test12_no_null_opt( +; CHECK: store atomic i32 0, i32* null unordered + store atomic i32 0, i32* null unordered, align 4 + ret i32 0 +} + ; FIXME: Could also fold define i32 @test13() { ; CHECK-LABEL: define i32 @test13( @@ -135,6 +163,13 @@ define i32 @test13() { ret i32 0 } +define i32 @test13_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test13_no_null_opt( +; CHECK: store atomic i32 0, i32* null monotonic + store atomic i32 0, i32* null monotonic, align 4 + ret i32 0 +} + ; Would this be legal to fold? Probably? define i32 @test14() { ; CHECK-LABEL: define i32 @test14( @@ -143,6 +178,13 @@ define i32 @test14() { ret i32 0 } +define i32 @test14_no_null_opt() #0 { +; CHECK-LABEL: define i32 @test14_no_null_opt( +; CHECK: store atomic i32 0, i32* null seq_cst + store atomic i32 0, i32* null seq_cst, align 4 + ret i32 0 +} + @a = external global i32 @b = external global i32 @@ -287,3 +329,5 @@ define void @no_atomic_vector_store(<2 x float> %p, i8* %p2) { store atomic i64 %1, i64* %2 unordered, align 8 ret void } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/invariant.group.ll b/test/Transforms/InstCombine/invariant.group.ll index ada45e3417c..4d36e706df5 100644 --- a/test/Transforms/InstCombine/invariant.group.ll +++ b/test/Transforms/InstCombine/invariant.group.ll @@ -8,6 +8,13 @@ define i8* @simplifyNullLaunder() { ret i8* %b2 } +; CHECK-LABEL: define i8* @dontSimplifyNullLaunderNoNullOpt() +define i8* @dontSimplifyNullLaunderNoNullOpt() #0 { +; CHECK-NEXT: call i8* @llvm.launder.invariant.group.p0i8(i8* null) + %b2 = call i8* @llvm.launder.invariant.group.p0i8(i8* null) + ret i8* %b2 +} + ; CHECK-LABEL: define i8 addrspace(42)* @dontsimplifyNullLaunderForDifferentAddrspace() define i8 addrspace(42)* @dontsimplifyNullLaunderForDifferentAddrspace() { ; CHECK: %b2 = call i8 addrspace(42)* @llvm.launder.invariant.group.p42i8(i8 addrspace(42)* null) @@ -41,6 +48,13 @@ define i8* @simplifyNullStrip() { ret i8* %b2 } +; CHECK-LABEL: define i8* @dontSimplifyNullStripNonNullOpt() +define i8* @dontSimplifyNullStripNonNullOpt() #0 { +; CHECK-NEXT: call i8* @llvm.strip.invariant.group.p0i8(i8* null) + %b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* null) + ret i8* %b2 +} + ; CHECK-LABEL: define i8 addrspace(42)* @dontsimplifyNullStripForDifferentAddrspace() define i8 addrspace(42)* @dontsimplifyNullStripForDifferentAddrspace() { ; CHECK: %b2 = call i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)* null) @@ -66,3 +80,4 @@ define i8 addrspace(42)* @simplifyUndefStrip2() { declare i8* @llvm.strip.invariant.group.p0i8(i8*) declare i8 addrspace(42)* @llvm.strip.invariant.group.p42i8(i8 addrspace(42)*) +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/invoke.ll b/test/Transforms/InstCombine/invoke.ll index ee08ae14887..fb54c4f77c4 100644 --- a/test/Transforms/InstCombine/invoke.ll +++ b/test/Transforms/InstCombine/invoke.ll @@ -47,6 +47,27 @@ lpad: unreachable } +; CHECK-LABEL: @f2_no_null_opt( +define i64 @f2_no_null_opt() nounwind uwtable ssp #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: +; CHECK: invoke noalias i8* null() + %call = invoke noalias i8* null() + to label %invoke.cont unwind label %lpad + +invoke.cont: +; CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %call, i1 false, i1 false) + %0 = tail call i64 @llvm.objectsize.i64(i8* %call, i1 false) + ret i64 %0 + +lpad: + %1 = landingpad { i8*, i32 } + filter [0 x i8*] zeroinitializer + %2 = extractvalue { i8*, i32 } %1, 0 + tail call void @__cxa_call_unexpected(i8* %2) noreturn nounwind + unreachable +} +attributes #0 = { "null-pointer-is-valid"="true" } + ; CHECK-LABEL: @f3( define void @f3() nounwind uwtable ssp personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { ; CHECK: invoke void @llvm.donothing() diff --git a/test/Transforms/InstCombine/lifetime-no-null-opt.ll b/test/Transforms/InstCombine/lifetime-no-null-opt.ll new file mode 100644 index 00000000000..ee3668b3d80 --- /dev/null +++ b/test/Transforms/InstCombine/lifetime-no-null-opt.ll @@ -0,0 +1,94 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare void @llvm.dbg.declare(metadata, metadata, metadata) +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) +declare void @foo(i8* nocapture, i8* nocapture) + +define void @bar(i1 %flag) #0 !dbg !4 { +entry: +; CHECK-LABEL: @bar( +; CHECK: %[[T:[^ ]+]] = getelementptr inbounds [1 x i8], [1 x i8]* %text +; CHECK: %[[B:[^ ]+]] = getelementptr inbounds [1 x i8], [1 x i8]* %buff +; CHECK: if: +; CHECK-NEXT: br label %bb2 +; CHECK: bb2: +; CHECK-NEXT: br label %bb3 +; CHECK: bb3: +; CHECK-NEXT: call void @llvm.dbg.declare +; CHECK-NEXT: br label %fin +; CHECK: call void @llvm.lifetime.start.p0i8(i64 1, i8* %[[T]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* %[[B]]) +; CHECK-NEXT: call void @foo(i8* %[[B]], i8* %[[T]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* %[[B]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* %[[T]]) + %text = alloca [1 x i8], align 1 + %buff = alloca [1 x i8], align 1 + %0 = getelementptr inbounds [1 x i8], [1 x i8]* %text, i64 0, i64 0 + %1 = getelementptr inbounds [1 x i8], [1 x i8]* %buff, i64 0, i64 0 + br i1 %flag, label %if, label %else + +if: + call void @llvm.lifetime.start.p0i8(i64 1, i8* %0) + call void @llvm.lifetime.start.p0i8(i64 1, i8* %1) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %1) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %0) + br label %bb2 + +bb2: + call void @llvm.lifetime.start.p0i8(i64 1, i8* %0) + call void @llvm.lifetime.start.p0i8(i64 1, i8* %1) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %0) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %1) + br label %bb3 + +bb3: + call void @llvm.lifetime.start.p0i8(i64 1, i8* %0) + call void @llvm.dbg.declare(metadata [1 x i8]* %text, metadata !14, metadata !25), !dbg !26 + call void @llvm.lifetime.end.p0i8(i64 1, i8* %0) + br label %fin + +else: + call void @llvm.lifetime.start.p0i8(i64 1, i8* %0) + call void @llvm.lifetime.start.p0i8(i64 1, i8* %1) + call void @foo(i8* %1, i8* %0) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %1) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %0) + br label %fin + +fin: + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!22, !23} +!llvm.ident = !{!24} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 248826) (llvm/trunk 248827)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "test.cpp", directory: "/home/user") +!2 = !{} +!4 = distinct !DISubprogram(name: "bar", linkageName: "bar", scope: !1, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !8) +!5 = !DISubroutineType(types: !6) +!6 = !{null, !7} +!7 = !DIBasicType(name: "bool", size: 8, align: 8, encoding: DW_ATE_boolean) +!8 = !{!9, !11, !12, !14, !21} +!9 = !DILocalVariable(name: "Size", arg: 1, scope: !4, file: !1, line: 2, type: !10) +!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !DILocalVariable(name: "flag", arg: 2, scope: !4, file: !1, line: 2, type: !7) +!12 = !DILocalVariable(name: "i", scope: !13, file: !1, line: 3, type: !10) +!13 = distinct !DILexicalBlock(scope: !4, file: !1, line: 3, column: 3) +!14 = !DILocalVariable(name: "text", scope: !15, file: !1, line: 4, type: !17) +!15 = distinct !DILexicalBlock(scope: !16, file: !1, line: 3, column: 30) +!16 = distinct !DILexicalBlock(scope: !13, file: !1, line: 3, column: 3) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8, align: 8, elements: !19) +!18 = !DIBasicType(name: "char", size: 8, align: 8, encoding: DW_ATE_signed_char) +!19 = !{!20} +!20 = !DISubrange(count: 1) +!21 = !DILocalVariable(name: "buff", scope: !15, file: !1, line: 5, type: !17) +!22 = !{i32 2, !"Dwarf Version", i32 4} +!23 = !{i32 2, !"Debug Info Version", i32 3} +!24 = !{!"clang version 3.8.0 (trunk 248826) (llvm/trunk 248827)"} +!25 = !DIExpression() +!26 = !DILocation(line: 4, column: 10, scope: !15) diff --git a/test/Transforms/InstCombine/load.ll b/test/Transforms/InstCombine/load.ll index 49ed897fd2e..028bc4b3fb7 100644 --- a/test/Transforms/InstCombine/load.ll +++ b/test/Transforms/InstCombine/load.ll @@ -60,6 +60,16 @@ define i32 @test7(i32 %X) { ret i32 %R } +; CHECK-LABEL: @test7_no_null_opt( +; CHECK: %V = getelementptr i32, i32* null +; CHECK: %R = load i32, i32* %V +define i32 @test7_no_null_opt(i32 %X) #0 { + %V = getelementptr i32, i32* null, i32 %X ; [#uses=1] + %R = load i32, i32* %V ; [#uses=1] + ret i32 %R +} +attributes #0 = { "null-pointer-is-valid"="true" } + ; CHECK-LABEL: @test8( ; CHECK-NOT: load define i32 @test8(i32* %P) { diff --git a/test/Transforms/InstCombine/memcpy-addrspace.ll b/test/Transforms/InstCombine/memcpy-addrspace.ll index 18615889dd8..b57a24e58b0 100644 --- a/test/Transforms/InstCombine/memcpy-addrspace.ll +++ b/test/Transforms/InstCombine/memcpy-addrspace.ll @@ -58,6 +58,23 @@ entry: ret void } +; CHECK-LABEL: test_call_no_null_opt +; CHECK: alloca +; CHECK: call void @llvm.memcpy.p0i8.p2i8.i64 +; CHECK-NOT: addrspacecast +; CHECK: call i32 @foo(i32* %{{.*}}) +define void @test_call_no_null_opt(i32 addrspace(1)* %out, i64 %x) #0 { +entry: + %data = alloca [8 x i32], align 4 + %0 = bitcast [8 x i32]* %data to i8* + call void @llvm.memcpy.p0i8.p2i8.i64(i8* align 4 %0, i8 addrspace(2)* align 4 bitcast ([8 x i32] addrspace(2)* @test.data to i8 addrspace(2)*), i64 32, i1 false) + %arrayidx = getelementptr inbounds [8 x i32], [8 x i32]* %data, i64 0, i64 %x + %1 = call i32 @foo(i32* %arrayidx) + %arrayidx1 = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %x + store i32 %1, i32 addrspace(1)* %arrayidx1, align 4 + ret void +} + ; CHECK-LABEL: test_load_and_call ; CHECK: alloca ; CHECK: call void @llvm.memcpy.p0i8.p2i8.i64 @@ -80,6 +97,29 @@ entry: ret void } +; CHECK-LABEL: test_load_and_call_no_null_opt +; CHECK: alloca +; CHECK: call void @llvm.memcpy.p0i8.p2i8.i64 +; CHECK: load i32, i32* %{{.*}} +; CHECK: call i32 @foo(i32* %{{.*}}) +; CHECK-NOT: addrspacecast +; CHECK-NOT: load i32, i32 addrspace(2)* +define void @test_load_and_call_no_null_opt(i32 addrspace(1)* %out, i64 %x, i64 %y) #0 { +entry: + %data = alloca [8 x i32], align 4 + %0 = bitcast [8 x i32]* %data to i8* + call void @llvm.memcpy.p0i8.p2i8.i64(i8* align 4 %0, i8 addrspace(2)* align 4 bitcast ([8 x i32] addrspace(2)* @test.data to i8 addrspace(2)*), i64 32, i1 false) + %arrayidx = getelementptr inbounds [8 x i32], [8 x i32]* %data, i64 0, i64 %x + %1 = load i32, i32* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %x + store i32 %1, i32 addrspace(1)* %arrayidx1, align 4 + %2 = call i32 @foo(i32* %arrayidx) + %arrayidx2 = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %y + store i32 %2, i32 addrspace(1)* %arrayidx2, align 4 + ret void +} declare void @llvm.memcpy.p0i8.p2i8.i64(i8* nocapture writeonly, i8 addrspace(2)* nocapture readonly, i64, i1) declare i32 @foo(i32* %x) + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/memcpy-from-global.ll b/test/Transforms/InstCombine/memcpy-from-global.ll index 2aff969891b..c14b1a70fd0 100644 --- a/test/Transforms/InstCombine/memcpy-from-global.ll +++ b/test/Transforms/InstCombine/memcpy-from-global.ll @@ -67,6 +67,26 @@ define void @test2() { ret void } +define void @test2_no_null_opt() #0 { + %A = alloca %T + %B = alloca %T + %a = bitcast %T* %A to i8* + %b = bitcast %T* %B to i8* + +; CHECK-LABEL: @test2_no_null_opt( + +; %A alloca is deleted +; CHECK-NEXT: alloca [124 x i8] +; CHECK-NEXT: getelementptr inbounds [124 x i8], [124 x i8]* + +; use @G instead of %A +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %{{.*}}, i8* align 16 getelementptr inbounds (%T, %T* @G, i64 0, i32 0) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %a, i8* align 4 bitcast (%T* @G to i8*), i64 124, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %b, i8* align 4 %a, i64 124, i1 false) + call void @bar(i8* %b) + ret void +} + define void @test2_addrspacecast() { %A = alloca %T %B = alloca %T @@ -235,3 +255,5 @@ entry: call void @llvm.memcpy.p0i8.p0i8.i64(i8* getelementptr inbounds ([1000000 x i8], [1000000 x i8]* @bbb, i32 0, i32 0), i8* %arraydecay, i64 3, i1 false) ret void } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/select.ll b/test/Transforms/InstCombine/select.ll index cc7d1035e46..b42f15714b3 100644 --- a/test/Transforms/InstCombine/select.ll +++ b/test/Transforms/InstCombine/select.ll @@ -385,6 +385,31 @@ define i32 @test16_neg2(i1 %C, i32 addrspace(1)* %P) { ret i32 %V } +;; It may be legal to load from a null address with null pointer valid attribute. +define i32 @test16_no_null_opt(i1 %C, i32* %P) #0 { +; CHECK-LABEL: @test16_no_null_opt( +; CHECK-NEXT: [[P2:%.*]] = select i1 [[C:%.*]], i32* [[P:%.*]], i32* null +; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[P2]], align 4 +; CHECK-NEXT: ret i32 [[V]] +; + %P2 = select i1 %C, i32* %P, i32* null + %V = load i32, i32* %P2 + ret i32 %V +} + +define i32 @test16_no_null_opt_2(i1 %C, i32* %P) #0 { +; CHECK-LABEL: @test16_no_null_opt_2( +; CHECK-NEXT: [[P2:%.*]] = select i1 [[C:%.*]], i32* null, i32* [[P:%.*]] +; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[P2]], align 4 +; CHECK-NEXT: ret i32 [[V]] +; + %P2 = select i1 %C, i32* null, i32* %P + %V = load i32, i32* %P2 + ret i32 %V +} + +attributes #0 = { "null-pointer-is-valid"="true" } + define i1 @test17(i32* %X, i1 %C) { ; CHECK-LABEL: @test17( ; CHECK-NEXT: [[RV1:%.*]] = icmp eq i32* [[X:%.*]], null diff --git a/test/Transforms/InstCombine/store.ll b/test/Transforms/InstCombine/store.ll index 5bf3683d5ea..52ce52768a6 100644 --- a/test/Transforms/InstCombine/store.ll +++ b/test/Transforms/InstCombine/store.ll @@ -28,6 +28,17 @@ define void @store_at_gep_off_null(i64 %offset) { ret void } +define void @store_at_gep_off_no_null_opt(i64 %offset) #0 { + %ptr = getelementptr i32, i32 *null, i64 %offset + store i32 24, i32* %ptr + ret void +; CHECK-LABEL: @store_at_gep_off_no_null_opt(i64 %offset) +; CHECK-NEXT: %ptr = getelementptr i32, i32* null, i64 %offset +; CHECK-NEXT: store i32 24, i32* %ptr +} + +attributes #0 = { "null-pointer-is-valid"="true" } + ;; Simple sinking tests ; "if then else" diff --git a/test/Transforms/InstCombine/strcpy_chk-64.ll b/test/Transforms/InstCombine/strcpy_chk-64.ll index 8cfb649b634..6ff063b2b2b 100644 --- a/test/Transforms/InstCombine/strcpy_chk-64.ll +++ b/test/Transforms/InstCombine/strcpy_chk-64.ll @@ -13,6 +13,19 @@ entry: ret void } +define void @func_no_null_opt(i8* %i) nounwind ssp #0 { +; CHECK-LABEL: @func_no_null_opt( +; CHECK: @__strcpy_chk(i8* %arraydecay, i8* %i, i64 32) +entry: + %s = alloca [32 x i8], align 16 + %arraydecay = getelementptr inbounds [32 x i8], [32 x i8]* %s, i32 0, i32 0 + %call = call i8* @__strcpy_chk(i8* %arraydecay, i8* %i, i64 32) + call void @func2(i8* %arraydecay) + ret void +} + declare i8* @__strcpy_chk(i8*, i8*, i64) nounwind declare void @func2(i8*) + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/strlen-1.ll b/test/Transforms/InstCombine/strlen-1.ll index 88a416be0d4..5d8c4e028b5 100644 --- a/test/Transforms/InstCombine/strlen-1.ll +++ b/test/Transforms/InstCombine/strlen-1.ll @@ -162,6 +162,17 @@ define i32 @test_no_simplify2(i32 %x) { ret i32 %hello_l } +define i32 @test_no_simplify2_no_null_opt(i32 %x) #0 { +; CHECK-LABEL: @test_no_simplify2_no_null_opt( +; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [7 x i8], [7 x i8]* @null_hello, i32 0, i32 %x +; CHECK-NEXT: [[HELLO_L:%.*]] = call i32 @strlen(i8* [[HELLO_P]]) +; CHECK-NEXT: ret i32 [[HELLO_L]] +; + %hello_p = getelementptr inbounds [7 x i8], [7 x i8]* @null_hello, i32 0, i32 %x + %hello_l = call i32 @strlen(i8* %hello_p) + ret i32 %hello_l +} + ; strlen(@null_hello_mid + (x & 15)) should not be simplified to a sub instruction. define i32 @test_no_simplify3(i32 %x) { @@ -177,3 +188,17 @@ define i32 @test_no_simplify3(i32 %x) { ret i32 %hello_l } +define i32 @test_no_simplify3_on_null_opt(i32 %x) #0 { +; CHECK-LABEL: @test_no_simplify3_on_null_opt( +; CHECK-NEXT: [[AND:%.*]] = and i32 %x, 15 +; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [13 x i8], [13 x i8]* @null_hello_mid, i32 0, i32 [[AND]] +; CHECK-NEXT: [[HELLO_L:%.*]] = call i32 @strlen(i8* [[HELLO_P]]) +; CHECK-NEXT: ret i32 [[HELLO_L]] +; + %and = and i32 %x, 15 + %hello_p = getelementptr inbounds [13 x i8], [13 x i8]* @null_hello_mid, i32 0, i32 %and + %hello_l = call i32 @strlen(i8* %hello_p) + ret i32 %hello_l +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstCombine/wcslen-1.ll b/test/Transforms/InstCombine/wcslen-1.ll index 22da95ef4ad..f8f8ec12dbb 100644 --- a/test/Transforms/InstCombine/wcslen-1.ll +++ b/test/Transforms/InstCombine/wcslen-1.ll @@ -166,6 +166,18 @@ define i64 @test_no_simplify2(i32 %x) { ret i64 %hello_l } +define i64 @test_no_simplify2_no_null_opt(i32 %x) #0 { +; CHECK-LABEL: @test_no_simplify2_no_null_opt( +; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[X:%.*]] to i64 +; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [7 x i32], [7 x i32]* @null_hello, i64 0, i64 [[TMP1]] +; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i32* [[HELLO_P]]) +; CHECK-NEXT: ret i64 [[HELLO_L]] +; + %hello_p = getelementptr inbounds [7 x i32], [7 x i32]* @null_hello, i32 0, i32 %x + %hello_l = call i64 @wcslen(i32* %hello_p) + ret i64 %hello_l +} + ; wcslen(@null_hello_mid + (x & 15)) should not be simplified to a sub instruction. define i64 @test_no_simplify3(i32 %x) { @@ -182,6 +194,20 @@ define i64 @test_no_simplify3(i32 %x) { ret i64 %hello_l } +define i64 @test_no_simplify3_no_null_opt(i32 %x) #0 { +; CHECK-LABEL: @test_no_simplify3_no_null_opt( +; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 15 +; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[AND]] to i64 +; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [13 x i32], [13 x i32]* @null_hello_mid, i64 0, i64 [[TMP1]] +; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i32* [[HELLO_P]]) +; CHECK-NEXT: ret i64 [[HELLO_L]] +; + %and = and i32 %x, 15 + %hello_p = getelementptr inbounds [13 x i32], [13 x i32]* @null_hello_mid, i32 0, i32 %and + %hello_l = call i64 @wcslen(i32* %hello_p) + ret i64 %hello_l +} + @str16 = constant [1 x i16] [i16 0] define i64 @test_no_simplify4() { @@ -192,3 +218,5 @@ define i64 @test_no_simplify4() { %l = call i64 @wcslen(i32* bitcast ([1 x i16]* @str16 to i32*)) ret i64 %l } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/InstSimplify/compare.ll b/test/Transforms/InstSimplify/compare.ll index d84e4986dbb..899f198d48a 100644 --- a/test/Transforms/InstSimplify/compare.ll +++ b/test/Transforms/InstSimplify/compare.ll @@ -185,6 +185,17 @@ define i1 @gep13(i8* %ptr) { ; CHECK-NEXT: ret i1 false } +define i1 @gep13_no_null_opt(i8* %ptr) #0 { +; We can't prove this GEP is non-null. +; CHECK-LABEL: @gep13_no_null_opt( +; CHECK: getelementptr +; CHECK: icmp +; CHECK: ret + %x = getelementptr inbounds i8, i8* %ptr, i32 1 + %cmp = icmp eq i8* %x, null + ret i1 %cmp +} + define i1 @gep14({ {}, i8 }* %ptr) { ; CHECK-LABEL: @gep14( ; We can't simplify this because the offset of one in the GEP actually doesn't @@ -205,6 +216,17 @@ define i1 @gep15({ {}, [4 x {i8, i8}]}* %ptr, i32 %y) { ; CHECK-NEXT: ret i1 false } +define i1 @gep15_no_null_opt({ {}, [4 x {i8, i8}]}* %ptr, i32 %y) #0 { +; We can't prove this GEP is non-null. +; CHECK-LABEL: @gep15_no_null_opt( +; CHECK: getelementptr +; CHECK: icmp +; CHECK: ret + %x = getelementptr inbounds { {}, [4 x {i8, i8}]}, { {}, [4 x {i8, i8}]}* %ptr, i32 0, i32 1, i32 %y, i32 1 + %cmp = icmp eq i8* %x, null + ret i1 %cmp +} + define i1 @gep16(i8* %ptr, i32 %a) { ; CHECK-LABEL: @gep16( ; We can prove this GEP is non-null because it is inbounds and because we know @@ -216,6 +238,18 @@ define i1 @gep16(i8* %ptr, i32 %a) { ; CHECK-NEXT: ret i1 false } +define i1 @gep16_no_null_opt(i8* %ptr, i32 %a) #0 { +; We can't prove this GEP is non-null. +; CHECK-LABEL: @gep16_no_null_opt( +; CHECK getelementptr inbounds i8, i8* %ptr, i32 %b +; CHECK: %cmp = icmp eq i8* %x, null +; CHECK-NEXT: ret i1 %cmp + %b = or i32 %a, 1 + %x = getelementptr inbounds i8, i8* %ptr, i32 %b + %cmp = icmp eq i8* %x, null + ret i1 %cmp +} + define i1 @gep17() { ; CHECK-LABEL: @gep17( %alloca = alloca i32, align 4 @@ -712,6 +746,17 @@ define i1 @alloca_compare(i64 %idx) { ; CHECK: ret i1 false } +define i1 @alloca_compare_no_null_opt(i64 %idx) #0 { +; CHECK-LABEL: alloca_compare_no_null_opt( +; CHECK: %sv = alloca { i32, i32, [124 x i32] } +; CHECK: %cmp = getelementptr inbounds { i32, i32, [124 x i32] }, { i32, i32, [124 x i32] }* %sv, i32 0, i32 2, i64 %idx +; CHECK: %X = icmp eq i32* %cmp, null +; CHECK: ret i1 %X + %sv = alloca { i32, i32, [124 x i32] } + %cmp = getelementptr inbounds { i32, i32, [124 x i32] }, { i32, i32, [124 x i32] }* %sv, i32 0, i32 2, i64 %idx + %X = icmp eq i32* %cmp, null + ret i1 %X +} ; PR12075 define i1 @infinite_gep() { ret i1 1 @@ -768,6 +813,19 @@ define i1 @alloca_gep(i64 %a, i64 %b) { ; CHECK-NEXT: ret i1 false } +define i1 @alloca_gep_no_null_opt(i64 %a, i64 %b) #0 { +; CHECK-LABEL: @alloca_gep_no_null_opt( +; We can't prove this GEP is non-null. +; CHECK: alloca +; CHECK: getelementptr +; CHECK: icmp +; CHECK: ret + %strs = alloca [1000 x [1001 x i8]], align 16 + %x = getelementptr inbounds [1000 x [1001 x i8]], [1000 x [1001 x i8]]* %strs, i64 0, i64 %a, i64 %b + %cmp = icmp eq i8* %x, null + ret i1 %cmp +} + define i1 @non_inbounds_gep_compare(i64* %a) { ; CHECK-LABEL: @non_inbounds_gep_compare( ; Equality compares with non-inbounds GEPs can be folded. @@ -865,6 +923,13 @@ define i1 @nonnull_arg(i32* nonnull %i) { ; CHECK: ret i1 false } +define i1 @nonnull_arg_no_null_opt(i32* nonnull %i) #0 { + %cmp = icmp eq i32* %i, null + ret i1 %cmp +; CHECK-LABEL: @nonnull_arg_no_null_opt +; CHECK: ret i1 false +} + define i1 @nonnull_deref_arg(i32* dereferenceable(4) %i) { %cmp = icmp eq i32* %i, null ret i1 %cmp @@ -872,6 +937,13 @@ define i1 @nonnull_deref_arg(i32* dereferenceable(4) %i) { ; CHECK: ret i1 false } +define i1 @nonnull_deref_arg_no_null_opt(i32* dereferenceable(4) %i) #0 { + %cmp = icmp eq i32* %i, null + ret i1 %cmp +; CHECK-LABEL: @nonnull_deref_arg_no_null_opt +; CHECK-NEXT: icmp +; CHECK: ret +} define i1 @nonnull_deref_as_arg(i32 addrspace(1)* dereferenceable(4) %i) { %cmp = icmp eq i32 addrspace(1)* %i, null ret i1 %cmp @@ -898,6 +970,15 @@ define i1 @returns_nonnull_deref() { ; CHECK: ret i1 false } +define i1 @returns_nonnull_deref_no_null_opt () #0 { + %call = call dereferenceable(4) i32* @returns_nonnull_deref_helper() + %cmp = icmp eq i32* %call, null + ret i1 %cmp +; CHECK-LABEL: @returns_nonnull_deref_no_null_opt +; CHECK: icmp +; CHECK: ret +} + declare dereferenceable(4) i32 addrspace(1)* @returns_nonnull_deref_as_helper() define i1 @returns_nonnull_as_deref() { %call = call dereferenceable(4) i32 addrspace(1)* @returns_nonnull_deref_as_helper() @@ -1276,3 +1357,5 @@ define i1 @constant_fold_null_inttoptr() { %x = icmp eq i32* null, inttoptr (i64 32 to i32*) ret i1 %x } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/LoopIdiom/pr28196.ll b/test/Transforms/LoopIdiom/pr28196.ll index 9057bdbb103..d65acec2214 100644 --- a/test/Transforms/LoopIdiom/pr28196.ll +++ b/test/Transforms/LoopIdiom/pr28196.ll @@ -24,3 +24,30 @@ for.body: ; preds = %for.body, %for.body ; CHECK-LABEL: define void @test1( ; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 null, i8* align 4 inttoptr (i64 4 to i8*), i64 24, i1 false) ; CHECK-NOT: store + +define void @test1_no_null_opt() #0 { +entry: + br label %for.body.preheader + +for.body.preheader: ; preds = %for.cond + br label %for.body + +for.body: ; preds = %for.body, %for.body.preheader + %indvars.iv = phi i32 [ 0, %for.body.preheader ], [ %indvars.iv.next, %for.body ] + %add.ptr3 = getelementptr inbounds i32, i32* null, i32 %indvars.iv + %add.ptr4 = getelementptr inbounds i32, i32* %add.ptr3, i32 1 + %0 = load i32, i32* %add.ptr4, align 4 + store i32 %0, i32* %add.ptr3, align 4 + %indvars.iv.next = add nsw i32 %indvars.iv, 1 + %exitcond = icmp ne i32 %indvars.iv.next, 6 + br i1 %exitcond, label %for.body, label %for.body.preheader +} + +; CHECK-LABEL: define void @test1_no_null_opt( +; CHECK-NOT: call void @llvm.memcpy +; CHECK: getelementptr +; CHECK: getelementptr +; CHECK: load +; CHECK: store + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/LoopVersioning/lcssa.ll b/test/Transforms/LoopVersioning/lcssa.ll index 2cd4662c371..64993061008 100644 --- a/test/Transforms/LoopVersioning/lcssa.ll +++ b/test/Transforms/LoopVersioning/lcssa.ll @@ -33,3 +33,40 @@ bb3.loopexit: bb3: ret void } + +define void @fill_no_null_opt(i8** %ls1.20, i8** %ls2.21, i8* %cse3.22) #0 { +; CHECK-LABEL: fill_no_null_opt( +; CHECK: bb1.lver.check: +; CHECK: %lver.safe = or i1 %memcheck.conflict, %{{.*}} +; CHECK: br i1 %lver.safe, label %bb1.ph.lver.orig, label %bb1.ph +bb1.ph: + %ls1.20.promoted = load i8*, i8** %ls1.20 + %ls2.21.promoted = load i8*, i8** %ls2.21 + br label %bb1 + +bb1: + %_tmp302 = phi i8* [ %ls2.21.promoted, %bb1.ph ], [ %_tmp30, %bb1 ] + %_tmp281 = phi i8* [ %ls1.20.promoted, %bb1.ph ], [ %_tmp28, %bb1 ] + %_tmp14 = getelementptr i8, i8* %_tmp281, i16 -1 + %_tmp15 = load i8, i8* %_tmp14 + %add = add i8 %_tmp15, 1 + store i8 %add, i8* %_tmp281 + store i8 %add, i8* %_tmp302 + %_tmp28 = getelementptr i8, i8* %_tmp281, i16 1 + %_tmp30 = getelementptr i8, i8* %_tmp302, i16 1 + br i1 false, label %bb1, label %bb3.loopexit + +bb3.loopexit: + %_tmp30.lcssa = phi i8* [ %_tmp30, %bb1 ] + %_tmp15.lcssa = phi i8 [ %_tmp15, %bb1 ] + %_tmp28.lcssa = phi i8* [ %_tmp28, %bb1 ] + store i8* %_tmp28.lcssa, i8** %ls1.20 + store i8 %_tmp15.lcssa, i8* %cse3.22 + store i8* %_tmp30.lcssa, i8** %ls2.21 + br label %bb3 + +bb3: + ret void +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/test/Transforms/SimplifyCFG/UnreachableEliminate.ll index be612b288b7..f994477d6ac 100644 --- a/test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ b/test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -78,6 +78,28 @@ bb2: ret void } +define void @test5_no_null_opt(i1 %cond, i8* %ptr) #0 { + +; CHECK-LABEL: test5_no_null_opt +; CHECK: entry: +; CHECK: %[[SEL:.*]] = select i1 %cond, i8* null, i8* %ptr +; CHECK: store i8 2, i8* %[[SEL]] + +entry: + br i1 %cond, label %bb1, label %bb3 + +bb3: + br label %bb2 + +bb1: + br label %bb2 + +bb2: + %ptr.2 = phi i8* [ %ptr, %bb3 ], [ null, %bb1 ] + store i8 2, i8* %ptr.2, align 8 + ret void +} + ; CHECK-LABEL: test6 ; CHECK: entry: ; CHECK-NOT: select @@ -97,6 +119,25 @@ bb2: ret void } +; CHECK-LABEL: test6_no_null_opt +; CHECK: entry: +; CHECK: %[[SEL:.*]] = select i1 %cond, i8* null, i8* %ptr +; CHECK: store i8 2, i8* %[[SEL]] + +define void @test6_no_null_opt(i1 %cond, i8* %ptr) #0 { +entry: + br i1 %cond, label %bb1, label %bb2 + +bb1: + br label %bb2 + +bb2: + %ptr.2 = phi i8* [ %ptr, %entry ], [ null, %bb1 ] + store i8 2, i8* %ptr.2, align 8 + ret void +} + + define i32 @test7(i1 %X) { entry: br i1 %X, label %if, label %else @@ -127,3 +168,21 @@ else: } ; CHECK-LABEL: define void @test8( ; CHECK: call void %Y( + +define void @test8_no_null_opt(i1 %X, void ()* %Y) #0 { +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi void ()* [ %Y, %entry ], [ null, %if ] + call void %phi() + ret void +} +attributes #0 = { "null-pointer-is-valid"="true" } + +; CHECK-LABEL: define void @test8_no_null_opt( +; CHECK: %[[SEL:.*]] = select i1 %X, void ()* null, void ()* %Y +; CHECK: call void %[[SEL]] diff --git a/test/Transforms/SimplifyCFG/invoke.ll b/test/Transforms/SimplifyCFG/invoke.ll index b7fd7d62ccf..a58e8005793 100644 --- a/test/Transforms/SimplifyCFG/invoke.ll +++ b/test/Transforms/SimplifyCFG/invoke.ll @@ -47,6 +47,27 @@ lpad: unreachable } +; CHECK-LABEL: @f2_no_null_opt( +define i8* @f2_no_null_opt() nounwind uwtable ssp #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: +; CHECK: invoke noalias i8* null() + %call = invoke noalias i8* null() + to label %invoke.cont unwind label %lpad + +; CHECK: invoke.cont: +; CHECK: ret i8* %call +invoke.cont: + ret i8* %call + +lpad: + %0 = landingpad { i8*, i32 } + filter [0 x i8*] zeroinitializer + %1 = extractvalue { i8*, i32 } %0, 0 + tail call void @__cxa_call_unexpected(i8* %1) noreturn nounwind +; CHECK: unreachable + unreachable +} + ; CHECK-LABEL: @f3( define i32 @f3() nounwind uwtable ssp personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { ; CHECK-NEXT: entry @@ -137,3 +158,5 @@ lpad: cleanup ret void } + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/SimplifyCFG/phi-undef-loadstore.ll b/test/Transforms/SimplifyCFG/phi-undef-loadstore.ll index c0f0046a1d2..e7e9e727346 100644 --- a/test/Transforms/SimplifyCFG/phi-undef-loadstore.ll +++ b/test/Transforms/SimplifyCFG/phi-undef-loadstore.ll @@ -31,6 +31,37 @@ if.end7: ; preds = %if.else, %if.then4, ; CHECK: phi i32* [ %a, %if.then ], [ %c, %if.else ] } +define i32 @test1_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ %c, %if.then4 ], [ null, %if.else ] + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 + +; CHECK-LABEL: @test1_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: br label %if.end7 +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ %c, %if.then4 ], [ null, %if.else ] +} + define i32 @test2(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -59,6 +90,35 @@ if.end7: ; preds = %if.else, %if.then4, ; CHECK-NOT: phi } +define i32 @test2_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 +; CHECK-LABEL: @test2_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + define i32 @test3(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -86,6 +146,36 @@ if.end7: ; preds = %if.else, %if.then4, ; CHECK: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] } +define i32 @test3_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + tail call void @bar() nounwind + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 +; CHECK-LABEL: @test3_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + define i32 @test4(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -113,3 +203,37 @@ if.end7: ; preds = %if.else, %if.then4, ; CHECK-LABEL: @test4( ; CHECK-NOT: phi } + +define i32 @test4_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + %gep = getelementptr i32, i32* %x.0, i32 10 + %tmp9 = load i32, i32* %gep + %tmp10 = or i32 %tmp9, 1 + store i32 %tmp10, i32* %gep + ret i32 %tmp9 +; CHECK-LABEL: @test4_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/test/Transforms/SimplifyCFG/trap-no-null-opt-debugloc.ll b/test/Transforms/SimplifyCFG/trap-no-null-opt-debugloc.ll new file mode 100644 index 00000000000..b3c104d7ae5 --- /dev/null +++ b/test/Transforms/SimplifyCFG/trap-no-null-opt-debugloc.ll @@ -0,0 +1,24 @@ +; RUN: opt -S -simplifycfg < %s | FileCheck %s +define void @foo() nounwind ssp #0 !dbg !0 { +; CHECK: store i32 42, i32* null +; CHECK-NOT: call void @llvm.trap() +; CHECK: ret void + store i32 42, i32* null, !dbg !5 + ret void, !dbg !7 +} + +attributes #0 = { "null-pointer-is-valid"="true" } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!10} + +!0 = distinct !DISubprogram(name: "foo", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, isOptimized: false, unit: !2, file: !8, scope: !1, type: !3) +!1 = !DIFile(filename: "foo.c", directory: "/private/tmp") +!2 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "Apple clang version 3.0 (tags/Apple/clang-206.1) (based on LLVM 3.0svn)", isOptimized: true, emissionKind: FullDebug, file: !8, enums: !{}, retainedTypes: !{}) +!3 = !DISubroutineType(types: !4) +!4 = !{null} +!5 = !DILocation(line: 4, column: 2, scope: !6) +!6 = distinct !DILexicalBlock(line: 3, column: 12, file: !8, scope: !0) +!7 = !DILocation(line: 5, column: 1, scope: !6) +!8 = !DIFile(filename: "foo.c", directory: "/private/tmp") +!10 = !{i32 1, !"Debug Info Version", i32 3} diff --git a/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll b/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll index 5881367d961..b074393e453 100644 --- a/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll +++ b/test/Transforms/SimplifyCFG/trapping-load-unreachable.ll @@ -21,17 +21,44 @@ return: ; preds = %entry ; CHECK: load volatile } +define void @test1_no_null_opt(i32 %x) nounwind #0 { +entry: + %0 = icmp eq i32 %x, 0 ; [#uses=1] + br i1 %0, label %bb, label %return + +bb: ; preds = %entry + %1 = load volatile i32, i32* null + unreachable + + br label %return +return: ; preds = %entry + ret void +; CHECK-LABEL: @test1_no_null_opt( +; CHECK: load volatile +; CHECK: unreachable +} + ; rdar://7958343 define void @test2() nounwind { entry: store i32 4,i32* null ret void - + ; CHECK-LABEL: @test2( ; CHECK: call void @llvm.trap ; CHECK: unreachable } +define void @test2_no_null_opt() nounwind #0 { +entry: + store i32 4,i32* null + ret void +; CHECK-LABEL: @test2_no_null_opt( +; CHECK: store i32 4, i32* null +; CHECK-NOT: call void @llvm.trap +; CHECK: ret +} + ; PR7369 define void @test3() nounwind { entry: @@ -43,6 +70,16 @@ entry: ; CHECK: ret } +define void @test3_no_null_opt() nounwind #0 { +entry: + store volatile i32 4, i32* null + ret void + +; CHECK-LABEL: @test3_no_null_opt( +; CHECK: store volatile i32 4, i32* null +; CHECK: ret +} + ; Check store before unreachable. define void @test4(i1 %C, i32* %P) { ; CHECK-LABEL: @test4( @@ -85,3 +122,4 @@ F: ret void } +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/unittests/Analysis/AliasAnalysisTest.cpp b/unittests/Analysis/AliasAnalysisTest.cpp index f1294eb5b7e..0f0d44f6c78 100644 --- a/unittests/Analysis/AliasAnalysisTest.cpp +++ b/unittests/Analysis/AliasAnalysisTest.cpp @@ -156,7 +156,7 @@ protected: // Build the various AA results and register them. AC.reset(new AssumptionCache(F)); - BAR.reset(new BasicAAResult(M.getDataLayout(), TLI, *AC)); + BAR.reset(new BasicAAResult(M.getDataLayout(), F, TLI, *AC)); AAR->addAAResult(*BAR); return *AAR; diff --git a/unittests/Analysis/MemorySSA.cpp b/unittests/Analysis/MemorySSA.cpp index c93c0d34307..1a1675faca1 100644 --- a/unittests/Analysis/MemorySSA.cpp +++ b/unittests/Analysis/MemorySSA.cpp @@ -50,7 +50,7 @@ protected: TestAnalyses(MemorySSATest &Test) : DT(*Test.F), AC(*Test.F), AA(Test.TLI), - BAA(Test.DL, Test.TLI, AC, &DT) { + BAA(Test.DL, *Test.F, Test.TLI, AC, &DT) { AA.addAAResult(BAA); MSSA = make_unique(*Test.F, &AA, &DT); Walker = MSSA->getWalker();