Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -29,6 +29,7 @@ #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" @@ -41,6 +42,8 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Type.h" @@ -211,7 +214,7 @@ ReadsMemory |= I->mayReadFromMemory(); } - if (WritesMemory) { + if (WritesMemory) { if (!ReadsMemory) return MAK_WriteOnly; else @@ -261,7 +264,7 @@ bool MadeChange = false; assert(!(ReadsMemory && WritesMemory) && - "Function marked read-only and write-only"); + "Function marked read-only and write-only"); for (Function *F : SCCNodes) { if (F->doesNotAccessMemory()) // Already perfect! @@ -613,6 +616,110 @@ return Changed; } +static void setNonNullParamAndFunctionArg(CallInst *CI, unsigned ArgNo) { + CI->addParamAttr(ArgNo, Attribute::NonNull); + if (auto *FArg = dyn_cast(CI->getArgOperand(ArgNo))) + FArg->addAttr(Attribute::NonNull); +} + +template +static bool isPositiveSize(T *I, unsigned ArgNo) { + Value *Arg = I->getArgOperand(ArgNo); + return isa(Arg) && !cast(Arg)->isZero(); +} + +static bool addArgumentAttrsFromKnownCallsites(Function &F, + TargetLibraryInfo &TLI) { + if (F.nullPointerIsDefined()) + return false; + bool Changed = false; + BasicBlock &Entry = F.getEntryBlock(); + LibFunc Func; + + for (Instruction &I : Entry) { + if (IntrinsicInst *II = dyn_cast(&I)) { + switch (II->getIntrinsicID()) { + case Intrinsic::memmove: + case Intrinsic::memcpy: + if (isPositiveSize(II, 2)) { + setNonNullParamAndFunctionArg(II, 0); + setNonNullParamAndFunctionArg(II, 1); + } + break; + case Intrinsic::memset: + if (isPositiveSize(II, 2)) + setNonNullParamAndFunctionArg(II, 0); + break; + default: + break; + } + } else if (CallInst *CI = dyn_cast(&I)) { + if (Function *Callee = CI->getCalledFunction()) { + if (!TLI.getLibFunc(*Callee, Func) || !TLI.has(Func)) + continue; + + switch (Func) { + case LibFunc_strchr: + case LibFunc_strrchr: + case LibFunc_strlen: + case LibFunc_wcslen: + case LibFunc_strtol: + case LibFunc_strtod: + case LibFunc_strtof: + case LibFunc_strtoul: + case LibFunc_strtoll: + case LibFunc_strtold: + case LibFunc_atoi: + case LibFunc_atol: + case LibFunc_atof: + case LibFunc_atoll: + case LibFunc_strdup: + case LibFunc_puts: + case LibFunc_printf: + setNonNullParamAndFunctionArg(CI, 0); + break; + + case LibFunc_strncat: + case LibFunc_strncpy: + case LibFunc_stpncpy: + case LibFunc_strncmp: + case LibFunc_memcmp: + if (isPositiveSize(CI, 2)) { + setNonNullParamAndFunctionArg(CI, 0); + setNonNullParamAndFunctionArg(CI, 1); + } + break; + + case LibFunc_strcmp: + case LibFunc_strspn: + case LibFunc_strcspn: + case LibFunc_strcoll: + case LibFunc_strstr: + case LibFunc_strpbrk: + case LibFunc_sscanf: + setNonNullParamAndFunctionArg(CI, 0); + setNonNullParamAndFunctionArg(CI, 1); + break; + + case LibFunc_strtok: + case LibFunc_strtok_r: + case LibFunc_snprintf: + setNonNullParamAndFunctionArg(CI, 2); + break; + default: + continue; + } + + Changed = true; + } + } + if (!isGuaranteedToTransferExecutionToSuccessor(&I)) + break; + } + + return Changed; +} + /// If a callsite has arguments that are also arguments to the parent function, /// try to propagate attributes from the callsite's arguments to the parent's /// arguments. This may be important because inlining can cause information loss @@ -657,7 +764,8 @@ } /// Deduce nocapture attributes for the SCC. -static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { +static bool addArgumentAttrs(const SCCNodeSet &SCCNodes, + TargetLibraryInfo &TLI) { bool Changed = false; ArgumentGraph AG; @@ -671,6 +779,7 @@ if (!F->hasExactDefinition()) continue; + Changed |= addArgumentAttrsFromKnownCallsites(*F, TLI); Changed |= addArgumentAttrsFromCallsites(*F); // Functions that are readonly (or readnone) and nounwind and don't return @@ -931,8 +1040,7 @@ bool MadeChange = false; for (Function *F : SCCNodes) { - if (F->returnDoesNotAlias() || - !F->getReturnType()->isPointerTy()) + if (F->returnDoesNotAlias() || !F->getReturnType()->isPointerTy()) continue; F->setReturnDoesNotAlias(); @@ -1323,7 +1431,8 @@ template static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, - bool HasUnknownCall) { + bool HasUnknownCall, + TargetLibraryInfo &TLI) { bool Changed = false; // Bail if the SCC only contains optnone functions. @@ -1332,7 +1441,7 @@ Changed |= addArgumentReturnedAttrs(SCCNodes); Changed |= addReadAttrs(SCCNodes, AARGetter); - Changed |= addArgumentAttrs(SCCNodes); + Changed |= addArgumentAttrs(SCCNodes, TLI); // If we have no external nodes participating in the SCC, we can deduce some // more precise attributes as well. @@ -1352,6 +1461,7 @@ CGSCCUpdateResult &) { FunctionAnalysisManager &FAM = AM.getResult(C, CG).getManager(); + TargetLibraryInfo &TLI = AM.getResult(C, CG); // We pass a lambda into functions to wire them up to the analysis manager // for getting function analyses. @@ -1388,7 +1498,7 @@ SCCNodes.insert(&F); } - if (deriveAttrsInPostOrder(SCCNodes, AARGetter, HasUnknownCall)) + if (deriveAttrsInPostOrder(SCCNodes, AARGetter, HasUnknownCall, TLI)) return PreservedAnalyses::none(); return PreservedAnalyses::all(); @@ -1421,6 +1531,7 @@ INITIALIZE_PASS_BEGIN(PostOrderFunctionAttrsLegacyPass, "functionattrs", "Deduce function attributes", false, false) INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) INITIALIZE_PASS_END(PostOrderFunctionAttrsLegacyPass, "functionattrs", "Deduce function attributes", false, false) @@ -1430,7 +1541,8 @@ } template -static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { +static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter, + TargetLibraryInfo &TLI) { // Fill SCCNodes with the elements of the SCC. Used for quickly looking up // whether a given CallGraphNode is in this SCC. Also track whether there are @@ -1451,13 +1563,15 @@ SCCNodes.insert(F); } - return deriveAttrsInPostOrder(SCCNodes, AARGetter, ExternalNode); + return deriveAttrsInPostOrder(SCCNodes, AARGetter, ExternalNode, TLI); } bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { if (skipSCC(SCC)) return false; - return runImpl(SCC, LegacyAARGetter(*this)); + + auto &TLI = getAnalysis().getTLI(); + return runImpl(SCC, LegacyAARGetter(*this), TLI); } namespace { @@ -1484,11 +1598,11 @@ char ReversePostOrderFunctionAttrsLegacyPass::ID = 0; -INITIALIZE_PASS_BEGIN(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", +INITIALIZE_PASS_BEGIN(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", "Deduce function attributes in RPO", false, false) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) -INITIALIZE_PASS_END(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", - "Deduce function attributes in RPO", false, false) +INITIALIZE_PASS_END(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", + "Deduce function attributes in RPO", false, false) Pass *llvm::createReversePostOrderFunctionAttrsPass() { return new ReversePostOrderFunctionAttrsLegacyPass();