Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1450,6 +1450,14 @@ duplicated by inlining. That implies that the function has internal linkage and only has one call site, so the original call is dead after inlining. +``nofree`` + This function attribute indicates that the function does not, directly or + indirectly, call a memory-deallocation function (free, for example). As a + result, uncaptured pointers that are known to be dereferenceable prior to a + call to a function with the ``nofree`` attribute are still known to be + dereferenceable after the call (the capturing condition is necessary in + environments where the function might communicate the pointer to another thread + which then deallocates the memory). ``noimplicitfloat`` This attributes disables implicit floating-point instructions. ``noinline`` Index: include/llvm/Analysis/MemoryBuiltins.h =================================================================== --- include/llvm/Analysis/MemoryBuiltins.h +++ include/llvm/Analysis/MemoryBuiltins.h @@ -83,6 +83,11 @@ bool isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI, bool LookThroughBitCast = false); +/// Tests if a value is a call or invoke to a library function that +/// reallocates memory (e.g., realloc). +bool isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, + bool LookThroughBitCast = false); + //===----------------------------------------------------------------------===// // malloc Call Utility Functions. // Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -606,7 +606,8 @@ ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, - ATTR_KIND_IMMARG = 60 + ATTR_KIND_IMMARG = 60, + ATTR_KIND_NOFREE = 61 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -85,6 +85,9 @@ /// Call cannot be duplicated. def NoDuplicate : EnumAttr<"noduplicate">; +/// Function does not deallocate memory. +def NoFree : EnumAttr<"nofree">; + /// Disable implicit floating point insts. def NoImplicitFloat : EnumAttr<"noimplicitfloat">; Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -564,6 +564,14 @@ addFnAttr(Attribute::Speculatable); } + /// Determine if the call might deallocate memory. + bool doesNotFreeMemory() const { + return onlyReadsMemory() || hasFnAttribute(Attribute::NoFree); + } + void setDoesNotFreeMemory() { + addFnAttr(Attribute::NoFree); + } + /// Determine if the function is known not to recurse, directly or /// indirectly. bool doesNotRecurse() const { Index: lib/Analysis/MemoryBuiltins.cpp =================================================================== --- lib/Analysis/MemoryBuiltins.cpp +++ lib/Analysis/MemoryBuiltins.cpp @@ -263,6 +263,13 @@ return getAllocationData(V, AllocLike, TLI, LookThroughBitCast).hasValue(); } +/// Tests if a value is a call or invoke to a library function that +/// reallocates memory (e.g., realloc). +bool llvm::isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, + bool LookThroughBitCast) { + return getAllocationData(V, ReallocLike, TLI, LookThroughBitCast).hasValue(); +} + /// extractMallocCall - Returns the corresponding CallInst if the instruction /// is a malloc call. Since CallInst::CreateMalloc() only creates calls, we /// ignore InvokeInst here. Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -650,6 +650,7 @@ KEYWORD(nobuiltin); KEYWORD(nocapture); KEYWORD(noduplicate); + KEYWORD(nofree); KEYWORD(noimplicitfloat); KEYWORD(noinline); KEYWORD(norecurse); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1277,6 +1277,7 @@ case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break; + case lltok::kw_nofree: B.addAttribute(Attribute::NoFree); break; case lltok::kw_noimplicitfloat: B.addAttribute(Attribute::NoImplicitFloat); break; case lltok::kw_noinline: B.addAttribute(Attribute::NoInline); break; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -194,6 +194,7 @@ kw_nobuiltin, kw_nocapture, kw_noduplicate, + kw_nofree, kw_noimplicitfloat, kw_noinline, kw_norecurse, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1191,10 +1191,11 @@ case Attribute::NoCfCheck: return 1ULL << 57; case Attribute::OptForFuzzing: return 1ULL << 58; case Attribute::ShadowCallStack: return 1ULL << 59; + case Attribute::NoFree: return 1ULL << 60; case Attribute::SpeculativeLoadHardening: - return 1ULL << 60; - case Attribute::ImmArg: return 1ULL << 61; + case Attribute::ImmArg: + return 1ULL << 62; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1353,6 +1354,8 @@ return Attribute::NoCapture; case bitc::ATTR_KIND_NO_DUPLICATE: return Attribute::NoDuplicate; + case bitc::ATTR_KIND_NOFREE: + return Attribute::NoFree; case bitc::ATTR_KIND_NO_IMPLICIT_FLOAT: return Attribute::NoImplicitFloat; case bitc::ATTR_KIND_NO_INLINE: Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -638,6 +638,8 @@ return bitc::ATTR_KIND_NO_CAPTURE; case Attribute::NoDuplicate: return bitc::ATTR_KIND_NO_DUPLICATE; + case Attribute::NoFree: + return bitc::ATTR_KIND_NOFREE; case Attribute::NoImplicitFloat: return bitc::ATTR_KIND_NO_IMPLICIT_FLOAT; case Attribute::NoInline: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -321,6 +321,8 @@ return "nocapture"; if (hasAttribute(Attribute::NoDuplicate)) return "noduplicate"; + if (hasAttribute(Attribute::NoFree)) + return "nofree"; if (hasAttribute(Attribute::NoImplicitFloat)) return "noimplicitfloat"; if (hasAttribute(Attribute::NoInline)) Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1489,6 +1489,7 @@ case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: + case Attribute::NoFree: case Attribute::AlwaysInline: case Attribute::OptimizeForSize: case Attribute::StackProtect: Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -27,7 +27,9 @@ #include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/MemoryBuiltins.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" @@ -75,6 +77,7 @@ STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); +STATISTIC(NumNoFree, "Number of functions marked as nofree"); // FIXME: This is disabled by default to avoid exposing security vulnerabilities // in C/C++ code compiled by clang: @@ -88,6 +91,10 @@ "disable-nounwind-inference", cl::Hidden, cl::desc("Stop inferring nounwind attribute during function-attrs pass")); +static cl::opt DisableNoFreeInference( + "disable-nofree-inference", cl::Hidden, + cl::desc("Stop inferring nofree attribute during function-attrs pass")); + namespace { using SCCNodeSet = SmallSetVector; @@ -1227,6 +1234,37 @@ return true; } +/// Helper for NoFree inference predicate InstrBreaksAttribute. +static bool InstrBreaksNoFree(Instruction &I, const SCCNodeSet &SCCNodes, + const TargetLibraryInfo &TLI) { + // A call to free, or similar, or realloc. + if (isFreeCall(&I, &TLI) || isReallocLikeFn(&I, &TLI)) + return true; + + if (!isa(I) && !isa(I)) + return false; + + CallSite CS(&I); + Function *Callee = CS.getCalledFunction(); + if (!Callee) + return true; + + if (Callee->doesNotFreeMemory()) + return false; + + if (SCCNodes.count(Callee) > 0) + return false; + + // A call to a function which we recognize, but does not free memory, is + // okay. Otherwise, we need to assume that the function might free memory. + LibFunc TLIFn; + StringRef FnName = Callee->getName(); + if (TLI.getLibFunc(FnName, TLIFn)) + return false; + + return true; +} + /// Infer attributes from all functions in the SCC by scanning every /// instruction for compliance to the attribute assumptions. Currently it /// does: @@ -1234,7 +1272,8 @@ /// - addition of NoUnwind attribute /// /// Returns true if any changes to function attributes were made. -static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) { +static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, + const TargetLibraryInfo &TLI) { AttributeInferer AI; @@ -1280,6 +1319,29 @@ }, /* RequiresExactDefinition= */ true}); + if (!DisableNoFreeInference) + // Request to infer nofree attribute for all the functions in the SCC if + // every callsite within the SCC does not directly or indirectly free + // memory (except for calls to functions within the SCC). Note that nofree + // attribute suffers from derefinement - results may change depending on + // how functions are optimized. Thus it can be inferred only from exact + // definitions. + AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ + Attribute::NoFree, + // Skip functions known not to free memory. + [](const Function &F) { return F.doesNotFreeMemory(); }, + // Instructions that break non-deallocating assumption. + [SCCNodes, TLI](Instruction &I) { + return InstrBreaksNoFree(I, SCCNodes, TLI); + }, + [](Function &F) { + LLVM_DEBUG(dbgs() + << "Adding nofree attr to fn " << F.getName() << "\n"); + F.setDoesNotFreeMemory(); + ++NumNoFree; + }, + /* RequiresExactDefinition= */ true}); + // Perform all the requested attribute inference actions. return AI.run(SCCNodes); } @@ -1322,7 +1384,9 @@ } template -static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, +static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, + const TargetLibraryInfo &TLI, + AARGetterT &&AARGetter, bool HasUnknownCall) { bool Changed = false; @@ -1339,7 +1403,7 @@ if (!HasUnknownCall) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= inferAttrsFromFunctionBodies(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes, TLI); Changed |= addNoRecurseAttrs(SCCNodes); } @@ -1353,6 +1417,12 @@ FunctionAnalysisManager &FAM = AM.getResult(C, CG).getManager(); + const ModuleAnalysisManager &MAM = + AM.getResult(C, CG).getManager(); + assert(C.size() > 0 && "Cannot handle an empty SCC!"); + Module &M = *C.begin()->getFunction().getParent(); + auto &TLI = *MAM.getCachedResult(M); + // We pass a lambda into functions to wire them up to the analysis manager // for getting function analyses. auto AARGetter = [&](Function &F) -> AAResults & { @@ -1360,8 +1430,8 @@ }; // Fill SCCNodes with the elements of the SCC. Also track whether there are - // any external or opt-none nodes that will prevent us from optimizing any - // part of the SCC. + // // any external or opt-none nodes that will prevent us from optimizing any + // // part of the SCC. SCCNodeSet SCCNodes; bool HasUnknownCall = false; for (LazyCallGraph::Node &N : C) { @@ -1387,7 +1457,7 @@ SCCNodes.insert(&F); } - if (deriveAttrsInPostOrder(SCCNodes, AARGetter, HasUnknownCall)) + if (deriveAttrsInPostOrder(SCCNodes, TLI, AARGetter, HasUnknownCall)) return PreservedAnalyses::none(); return PreservedAnalyses::all(); @@ -1408,6 +1478,7 @@ void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); + AU.addRequired(); AU.addRequired(); getAAResultsAnalysisUsage(AU); CallGraphSCCPass::getAnalysisUsage(AU); @@ -1419,6 +1490,7 @@ char PostOrderFunctionAttrsLegacyPass::ID = 0; INITIALIZE_PASS_BEGIN(PostOrderFunctionAttrsLegacyPass, "functionattrs", "Deduce function attributes", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) INITIALIZE_PASS_END(PostOrderFunctionAttrsLegacyPass, "functionattrs", @@ -1429,7 +1501,8 @@ } template -static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { +static bool runImpl(CallGraphSCC &SCC, const TargetLibraryInfo &TLI, + AARGetterT AARGetter) { // 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 @@ -1449,13 +1522,14 @@ SCCNodes.insert(F); } - return deriveAttrsInPostOrder(SCCNodes, AARGetter, ExternalNode); + return deriveAttrsInPostOrder(SCCNodes, TLI, AARGetter, ExternalNode); } bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { if (skipSCC(SCC)) return false; - return runImpl(SCC, LegacyAARGetter(*this)); + auto &TLI = getAnalysis().getTLI(); + return runImpl(SCC, TLI, LegacyAARGetter(*this)); } namespace { Index: lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- lib/Transforms/Utils/CodeExtractor.cpp +++ lib/Transforms/Utils/CodeExtractor.cpp @@ -813,6 +813,7 @@ case Attribute::InlineHint: case Attribute::MinSize: case Attribute::NoDuplicate: + case Attribute::NoFree: case Attribute::NoImplicitFloat: case Attribute::NoInline: case Attribute::NonLazyBind: Index: test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -73,11 +73,11 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind ; CHECK: attributes #0 = { norecurse nounwind readnone } -; CHECK: attributes #1 = { norecurse nounwind writeonly } +; CHECK: attributes #1 = { nofree norecurse nounwind writeonly } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } ; CHECK: attributes #4 = { nounwind readnone } -; CHECK: attributes #5 = { norecurse nounwind } +; CHECK: attributes #5 = { nofree norecurse nounwind } ; CHECK: attributes #6 = { argmemonly nounwind } ; Root note. Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #36 +; CHECK: call void @nobuiltin() #37 ret void; } @@ -351,6 +351,11 @@ ret void } +; CHECK: define void @f60() #36 +define void @f60() nofree { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -387,4 +392,5 @@ ; CHECK: attributes #33 = { speculatable } ; CHECK: attributes #34 = { sanitize_hwaddress } ; CHECK: attributes #35 = { shadowcallstack } -; CHECK: attributes #36 = { nobuiltin } +; CHECK: attributes #36 = { nofree } +; CHECK: attributes #37 = { nobuiltin } Index: test/CodeGen/AMDGPU/inline-attr.ll =================================================================== --- test/CodeGen/AMDGPU/inline-attr.ll +++ test/CodeGen/AMDGPU/inline-attr.ll @@ -7,13 +7,13 @@ ; GCN: %mul.i = fmul float %load, 1.500000e+01 ; UNSAFE: attributes #0 = { norecurse nounwind readnone "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } -; UNSAFE: attributes #1 = { norecurse nounwind "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } +; UNSAFE: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } ; NOINFS: attributes #0 = { norecurse nounwind readnone "no-infs-fp-math"="true" } -; NOINFS: attributes #1 = { norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } +; NOINFS: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } ; NONANS: attributes #0 = { norecurse nounwind readnone "no-nans-fp-math"="true" } -; NONANS: attributes #1 = { norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } +; NONANS: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } define float @foo(float %x) #0 { entry: Index: test/Feature/OperandBundles/function-attrs.ll =================================================================== --- test/Feature/OperandBundles/function-attrs.ll +++ test/Feature/OperandBundles/function-attrs.ll @@ -6,7 +6,7 @@ define void @test_0(i32* %x) { ; FunctionAttrs must not infer readonly / readnone for %x -; CHECK-LABEL: define void @test_0(i32* %x) { +; CHECK-LABEL: define void @test_0(i32* %x) #2 { entry: ; CHECK: call void @f_readonly() [ "foo"(i32* %x) ] call void @f_readonly() [ "foo"(i32* %x) ] @@ -16,7 +16,7 @@ define void @test_1(i32* %x) { ; FunctionAttrs must not infer readonly / readnone for %x -; CHECK-LABEL: define void @test_1(i32* %x) { +; CHECK-LABEL: define void @test_1(i32* %x) #2 { entry: ; CHECK: call void @f_readnone() [ "foo"(i32* %x) ] call void @f_readnone() [ "foo"(i32* %x) ] @@ -31,3 +31,6 @@ call void @f_readonly() [ "deopt"(i32* %x) ] ret void } + +; CHECK: attributes #2 = { nofree } + Index: test/Transforms/FunctionAttrs/atomic.ll =================================================================== --- test/Transforms/FunctionAttrs/atomic.ll +++ test/Transforms/FunctionAttrs/atomic.ll @@ -21,4 +21,4 @@ } ; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable } -; CHECK: attributes #1 = { norecurse nounwind ssp uwtable } +; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable } Index: test/Transforms/FunctionAttrs/nofree.ll =================================================================== --- test/Transforms/FunctionAttrs/nofree.ll +++ test/Transforms/FunctionAttrs/nofree.ll @@ -0,0 +1,114 @@ +; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK: define void @_Z4foo1Pi(i32* nocapture readnone %a) local_unnamed_addr #0 { +define void @_Z4foo1Pi(i32* nocapture readnone %a) local_unnamed_addr #0 { +entry: + tail call void @_Z3extv() + ret void +} + +declare void @_Z3extv() local_unnamed_addr + +; CHECK: define void @_Z4foo2Pi(i32* nocapture %a) local_unnamed_addr #1 { +define void @_Z4foo2Pi(i32* nocapture %a) local_unnamed_addr #1 { +entry: + %0 = bitcast i32* %a to i8* + tail call void @free(i8* %0) #2 + ret void +} + +declare void @free(i8* nocapture) local_unnamed_addr #2 + +; CHECK: define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 { +define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 { +entry: + %0 = load i32, i32* %a, align 4 + ret i32 %0 +} + +; CHECK: define double @_Z4foo4Pd(double* nocapture readonly %a) local_unnamed_addr #4 { +define double @_Z4foo4Pd(double* nocapture readonly %a) local_unnamed_addr #1 { +entry: + %0 = load double, double* %a, align 8 + %call = tail call double @cos(double %0) #2 + ret double %call +} + +declare double @cos(double) local_unnamed_addr #2 + +; CHECK: define noalias i32* @_Z4foo5Pm(i64* nocapture readonly %a) local_unnamed_addr #4 { +define noalias i32* @_Z4foo5Pm(i64* nocapture readonly %a) local_unnamed_addr #1 { +entry: + %0 = load i64, i64* %a, align 8 + %call = tail call noalias i8* @malloc(i64 %0) #2 + %1 = bitcast i8* %call to i32* + ret i32* %1 +} + +declare noalias i8* @malloc(i64) local_unnamed_addr #2 + +; CHECK: define noalias i64* @_Z4foo6Pm(i64* nocapture %a) local_unnamed_addr #1 { +define noalias i64* @_Z4foo6Pm(i64* nocapture %a) local_unnamed_addr #1 { +entry: + %0 = bitcast i64* %a to i8* + %1 = load i64, i64* %a, align 8 + %call = tail call i8* @realloc(i8* %0, i64 %1) #2 + %2 = bitcast i8* %call to i64* + ret i64* %2 +} + +declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #2 + +; CHECK: define void @_Z4foo7Pi(i32* %a) local_unnamed_addr #1 { +define void @_Z4foo7Pi(i32* %a) local_unnamed_addr #1 { +entry: + %isnull = icmp eq i32* %a, null + br i1 %isnull, label %delete.end, label %delete.notnull + +delete.notnull: ; preds = %entry + %0 = bitcast i32* %a to i8* + tail call void @_ZdlPv(i8* %0) #5 + br label %delete.end + +delete.end: ; preds = %delete.notnull, %entry + ret void +} + +declare void @_ZdlPv(i8*) local_unnamed_addr #4 + +; CHECK: define void @_Z4foo8Pi(i32* %a) local_unnamed_addr #1 { +define void @_Z4foo8Pi(i32* %a) local_unnamed_addr #1 { +entry: + %isnull = icmp eq i32* %a, null + br i1 %isnull, label %delete.end, label %delete.notnull + +delete.notnull: ; preds = %entry + %0 = bitcast i32* %a to i8* + tail call void @_ZdaPv(i8* %0) #5 + br label %delete.end + +delete.end: ; preds = %delete.notnull, %entry + ret void +} + +declare void @_ZdaPv(i8*) local_unnamed_addr #4 + +attributes #0 = { uwtable } +attributes #1 = { nounwind uwtable } +attributes #2 = { nounwind } +attributes #3 = { norecurse nounwind readonly uwtable } +attributes #4 = { nobuiltin nounwind } +attributes #5 = { builtin nounwind } + +; CHECK: attributes #0 = { uwtable } +; CHECK: attributes #1 = { nounwind uwtable } +; CHECK: attributes #2 = { nounwind } +; CHECK: attributes #3 = { norecurse nounwind readonly uwtable } +; CHECK: attributes #4 = { nofree nounwind uwtable } +; CHECK: attributes #5 = { nobuiltin nounwind } +; CHECK: attributes #6 = { builtin nounwind } + Index: test/Transforms/FunctionAttrs/operand-bundles-scc.ll =================================================================== --- test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ test/Transforms/FunctionAttrs/operand-bundles-scc.ll @@ -14,4 +14,4 @@ } -; CHECK: attributes #0 = { nounwind } +; CHECK: attributes #0 = { nofree nounwind } Index: test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll =================================================================== --- test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll +++ test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll @@ -30,7 +30,7 @@ ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nofree nounwind ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -41,7 +41,7 @@ ret i32* %call3 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nofree nounwind ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: @@ -70,7 +70,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nofree nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: @@ -102,7 +102,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: norecurse nounwind +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0) define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -121,7 +121,7 @@ ret i32* %w0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nofree nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: @@ -147,7 +147,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nofree nounwind ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -160,6 +160,6 @@ ; for a subset relation. ; ; CHECK-NOT: attributes # -; CHECK: attributes #{{.*}} = { nounwind } -; CHECK: attributes #{{.*}} = { norecurse nounwind } +; CHECK: attributes #{{.*}} = { nofree nounwind } +; CHECK: attributes #{{.*}} = { nofree norecurse nounwind } ; CHECK-NOT: attributes # Index: test/Transforms/InferFunctionAttrs/norecurse_debug.ll =================================================================== --- test/Transforms/InferFunctionAttrs/norecurse_debug.ll +++ test/Transforms/InferFunctionAttrs/norecurse_debug.ll @@ -52,5 +52,5 @@ !28 = !DILocation(line: 9, column: 18, scope: !2) !29 = !DILocation(line: 10, column: 1, scope: !2) -; CHECK: attributes #0 = { norecurse nounwind } +; CHECK: attributes #0 = { nofree norecurse nounwind } ; CHECK-NOT foo.coefficient1