Skip to content

Commit

Permalink
Add, and infer, a nofree function attribute
Browse files Browse the repository at this point in the history
This patch adds a function attribute, nofree, to indicate that a function does
not, directly or indirectly, call a memory-deallocation function (e.g., free,
C++'s operator delete).

Reviewers: jdoerfert

Differential Revision: https://reviews.llvm.org/D49165

llvm-svn: 365336
homerdin committed Jul 8, 2019
1 parent b52a0c0 commit b4b21d8
Showing 30 changed files with 593 additions and 331 deletions.
8 changes: 8 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
@@ -1453,6 +1453,14 @@ example:
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``
13 changes: 13 additions & 0 deletions llvm/include/llvm/Analysis/MemoryBuiltins.h
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/TargetFolder.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstVisitor.h"
@@ -83,6 +84,15 @@ bool isMallocOrCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
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);

/// Tests if a function is a call or invoke to a library function that
/// reallocates memory (e.g., realloc).
bool isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI);

//===----------------------------------------------------------------------===//
// malloc Call Utility Functions.
//
@@ -134,6 +144,9 @@ inline CallInst *extractCallocCall(Value *I, const TargetLibraryInfo *TLI) {
// free Call Utility Functions.
//

/// isLibFreeFunction - Returns true if the function is a builtin free()
bool isLibFreeFunction(const Function *F, const LibFunc TLIFn);

/// isFreeCall - Returns non-null if the value is a call to the builtin free()
const CallInst *isFreeCall(const Value *I, const TargetLibraryInfo *TLI);

9 changes: 9 additions & 0 deletions llvm/include/llvm/Analysis/TargetLibraryInfo.def
Original file line number Diff line number Diff line change
@@ -11,6 +11,15 @@
// Which is defined depends on whether TLI_DEFINE_ENUM is defined or
// TLI_DEFINE_STRING is defined. Only one should be defined at a time.

// NOTE: The nofree attribute is added to Libfuncs which are not
// listed as free or realloc functions in MemoryBuiltins.cpp
//
// When adding a function which frees memory include the LibFunc
// in lib/Analysis/MemoryBuiltins.cpp "isLibFreeFunction".
//
// When adding a LibFunc which reallocates memory include the LibFunc
// in lib/Analysis/MemoryBuiltins.cpp "AllocationFnData[]".

#if !(defined(TLI_DEFINE_ENUM) || defined(TLI_DEFINE_STRING))
#error "Must define TLI_DEFINE_ENUM or TLI_DEFINE_STRING for TLI .def."
#elif defined(TLI_DEFINE_ENUM) && defined(TLI_DEFINE_STRING)
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
@@ -629,6 +629,7 @@ enum AttributeKindCodes {
ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
ATTR_KIND_IMMARG = 60,
ATTR_KIND_WILLRETURN = 61,
ATTR_KIND_NOFREE = 62
};

enum ComdatSelectionKindCodes {
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Original file line number Diff line number Diff line change
@@ -85,6 +85,9 @@ def NoCapture : EnumAttr<"nocapture">;
/// 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">;

8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/Function.h
Original file line number Diff line number Diff line change
@@ -564,6 +564,14 @@ class Function : public GlobalObject, public ilist_node<Function> {
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 {
55 changes: 37 additions & 18 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Original file line number Diff line number Diff line change
@@ -263,6 +263,19 @@ bool llvm::isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
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();
}

/// Tests if a functions is a call or invoke to a library function that
/// reallocates memory (e.g., realloc).
bool llvm::isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI) {
return getAllocationDataForFunction(F, ReallocLike, TLI).hasValue();
}

/// extractMallocCall - Returns the corresponding CallInst if the instruction
/// is a malloc call. Since CallInst::CreateMalloc() only creates calls, we
/// ignore InvokeInst here.
@@ -358,19 +371,8 @@ const CallInst *llvm::extractCallocCall(const Value *I,
return isCallocLikeFn(I, TLI) ? cast<CallInst>(I) : nullptr;
}

/// isFreeCall - Returns non-null if the value is a call to the builtin free()
const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
bool IsNoBuiltinCall;
const Function *Callee =
getCalledFunction(I, /*LookThroughBitCast=*/false, IsNoBuiltinCall);
if (Callee == nullptr || IsNoBuiltinCall)
return nullptr;

StringRef FnName = Callee->getName();
LibFunc TLIFn;
if (!TLI || !TLI->getLibFunc(FnName, TLIFn) || !TLI->has(TLIFn))
return nullptr;

/// isLibFreeFunction - Returns true if the function is a builtin free()
bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) {
unsigned ExpectedNumParams;
if (TLIFn == LibFunc_free ||
TLIFn == LibFunc_ZdlPv || // operator delete(void*)
@@ -401,22 +403,39 @@ const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
TLIFn == LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t) // delete[](void*, align_val_t, nothrow)
ExpectedNumParams = 3;
else
return nullptr;
return false;

// Check free prototype.
// FIXME: workaround for PR5130, this will be obsolete when a nobuiltin
// attribute will exist.
FunctionType *FTy = Callee->getFunctionType();
FunctionType *FTy = F->getFunctionType();
if (!FTy->getReturnType()->isVoidTy())
return nullptr;
return false;
if (FTy->getNumParams() != ExpectedNumParams)
return false;
if (FTy->getParamType(0) != Type::getInt8PtrTy(F->getContext()))
return false;

return true;
}

/// isFreeCall - Returns non-null if the value is a call to the builtin free()
const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
bool IsNoBuiltinCall;
const Function *Callee =
getCalledFunction(I, /*LookThroughBitCast=*/false, IsNoBuiltinCall);
if (Callee == nullptr || IsNoBuiltinCall)
return nullptr;
if (FTy->getParamType(0) != Type::getInt8PtrTy(Callee->getContext()))

StringRef FnName = Callee->getName();
LibFunc TLIFn;
if (!TLI || !TLI->getLibFunc(FnName, TLIFn) || !TLI->has(TLIFn))
return nullptr;

return dyn_cast<CallInst>(I);
return isLibFreeFunction(Callee, TLIFn) ? dyn_cast<CallInst>(I) : nullptr;
}


//===----------------------------------------------------------------------===//
// Utility functions to compute size of objects.
//
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
@@ -650,6 +650,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nobuiltin);
KEYWORD(nocapture);
KEYWORD(noduplicate);
KEYWORD(nofree);
KEYWORD(noimplicitfloat);
KEYWORD(noinline);
KEYWORD(norecurse);
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
@@ -1280,6 +1280,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
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;
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
@@ -195,6 +195,7 @@ enum Kind {
kw_nobuiltin,
kw_nocapture,
kw_noduplicate,
kw_nofree,
kw_noimplicitfloat,
kw_noinline,
kw_norecurse,
4 changes: 4 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
@@ -1278,6 +1278,8 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
return 1ULL << 61;
case Attribute::WillReturn:
return 1ULL << 62;
case Attribute::NoFree:
return 1ULL << 63;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
break;
@@ -1442,6 +1444,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
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:
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
@@ -639,6 +639,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
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:
2 changes: 2 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
@@ -321,6 +321,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "nocapture";
if (hasAttribute(Attribute::NoDuplicate))
return "noduplicate";
if (hasAttribute(Attribute::NoFree))
return "nofree";
if (hasAttribute(Attribute::NoImplicitFloat))
return "noimplicitfloat";
if (hasAttribute(Attribute::NoInline))
1 change: 1 addition & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
@@ -1497,6 +1497,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
case Attribute::NoCfCheck:
case Attribute::NoUnwind:
case Attribute::NoInline:
case Attribute::NoFree:
case Attribute::AlwaysInline:
case Attribute::OptimizeForSize:
case Attribute::StackProtect:
56 changes: 55 additions & 1 deletion llvm/lib/Transforms/IPO/FunctionAttrs.cpp
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
#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/ValueTracking.h"
#include "llvm/IR/Argument.h"
@@ -75,6 +76,7 @@ STATISTIC(NumNoAlias, "Number of function returns marked noalias");
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 +90,10 @@ static cl::opt<bool> DisableNoUnwindInference(
"disable-nounwind-inference", cl::Hidden,
cl::desc("Stop inferring nounwind attribute during function-attrs pass"));

static cl::opt<bool> DisableNoFreeInference(
"disable-nofree-inference", cl::Hidden,
cl::desc("Stop inferring nofree attribute during function-attrs pass"));

namespace {

using SCCNodeSet = SmallSetVector<Function *, 8>;
@@ -1227,6 +1233,25 @@ static bool InstrBreaksNonThrowing(Instruction &I, const SCCNodeSet &SCCNodes) {
return true;
}

/// Helper for NoFree inference predicate InstrBreaksAttribute.
static bool InstrBreaksNoFree(Instruction &I, const SCCNodeSet &SCCNodes) {
CallSite CS(&I);
if (!CS)
return false;

Function *Callee = CS.getCalledFunction();
if (!Callee)
return true;

if (Callee->doesNotFreeMemory())
return false;

if (SCCNodes.count(Callee) > 0)
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:
@@ -1280,6 +1305,29 @@ static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) {
},
/* 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](Instruction &I) {
return InstrBreaksNoFree(I, SCCNodes);
},
[](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 +1370,8 @@ static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) {
}

template <typename AARGetterT>
static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, AARGetterT &&AARGetter,
static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes,
AARGetterT &&AARGetter,
bool HasUnknownCall) {
bool Changed = false;

@@ -1353,6 +1402,11 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();

const ModuleAnalysisManager &MAM =
AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager();
assert(C.size() > 0 && "Cannot handle an empty SCC!");
Module &M = *C.begin()->getFunction().getParent();

// We pass a lambda into functions to wire them up to the analysis manager
// for getting function analyses.
auto AARGetter = [&](Function &F) -> AAResults & {
11 changes: 11 additions & 0 deletions llvm/lib/Transforms/Utils/BuildLibCalls.cpp
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/Analysis/MemoryBuiltins.h"

using namespace llvm;

@@ -120,6 +121,13 @@ static bool setNonLazyBind(Function &F) {
return true;
}

static bool setDoesNotFreeMemory(Function &F) {
if (F.hasFnAttribute(Attribute::NoFree))
return false;
F.addFnAttr(Attribute::NoFree);
return true;
}

bool llvm::inferLibFuncAttributes(Module *M, StringRef Name,
const TargetLibraryInfo &TLI) {
Function *F = M->getFunction(Name);
@@ -135,6 +143,9 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {

bool Changed = false;

if(!isLibFreeFunction(&F, TheLibFunc) && !isReallocLikeFn(&F, &TLI))
Changed |= setDoesNotFreeMemory(F);

if (F.getParent() != nullptr && F.getParent()->getRtLibUseGOT())
Changed |= setNonLazyBind(F);

1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Original file line number Diff line number Diff line change
@@ -814,6 +814,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::InlineHint:
case Attribute::MinSize:
case Attribute::NoDuplicate:
case Attribute::NoFree:
case Attribute::NoImplicitFloat:
case Attribute::NoInline:
case Attribute::NonLazyBind:
4 changes: 2 additions & 2 deletions llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
Original file line number Diff line number Diff line change
@@ -73,11 +73,11 @@ declare void @callee(i32* %p) nounwind
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.
10 changes: 8 additions & 2 deletions llvm/test/Bitcode/attributes.ll
Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ define void @f34()
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #37
; CHECK: call void @nobuiltin() #38
ret void;
}

@@ -357,6 +357,11 @@ define void @f60() willreturn
ret void
}

; CHECK: define void @f61() #37
define void @f61() nofree {
ret void
}

; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@@ -394,4 +399,5 @@ define void @f60() willreturn
; CHECK: attributes #34 = { sanitize_hwaddress }
; CHECK: attributes #35 = { shadowcallstack }
; CHECK: attributes #36 = { willreturn }
; CHECK: attributes #37 = { nobuiltin }
; CHECK: attributes #37 = { nofree }
; CHECK: attributes #38 = { nobuiltin }
6 changes: 3 additions & 3 deletions llvm/test/CodeGen/AMDGPU/inline-attr.ll
Original file line number Diff line number Diff line change
@@ -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:
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/X86/no-plt-libcalls.ll
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ define void @printf_call() {
ret void
}

; CHECK: Function Attrs: nounwind nonlazybind
; CHECK: Function Attrs: nofree nounwind nonlazybind
; CHECK-NEXT: declare i32 @puts(i8* nocapture readonly)

!llvm.module.flags = !{!0}
7 changes: 5 additions & 2 deletions llvm/test/Feature/OperandBundles/function-attrs.ll
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ declare void @f_readnone() readnone
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_0(i32* %x) {
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 @@ define void @test_2(i32* %x) {
call void @f_readonly() [ "deopt"(i32* %x) ]
ret void
}

; CHECK: attributes #2 = { nofree }

2 changes: 1 addition & 1 deletion llvm/test/Transforms/FunctionAttrs/atomic.ll
Original file line number Diff line number Diff line change
@@ -21,4 +21,4 @@ entry:
}

; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable }
; CHECK: attributes #1 = { norecurse nounwind ssp uwtable }
; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable }
113 changes: 113 additions & 0 deletions llvm/test/Transforms/FunctionAttrs/nofree.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
; 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 #1 {
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 #1 {
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 = { nobuiltin nounwind }
; CHECK: attributes #5 = { builtin nounwind }

2 changes: 1 addition & 1 deletion llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
Original file line number Diff line number Diff line change
@@ -14,4 +14,4 @@ define void @g() {
}


; CHECK: attributes #0 = { nounwind }
; CHECK: attributes #0 = { nofree nounwind }
Original file line number Diff line number Diff line change
@@ -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 @@ entry:
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 @@ return: ; preds = %if.end, %if.then
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 @@ return: ; preds = %if.end, %if.then
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 @@ return: ; preds = %if.end, %if.then
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 @@ return: ; preds = %if.end, %if.then
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 @@ entry:
; 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 #
579 changes: 290 additions & 289 deletions llvm/test/Transforms/InferFunctionAttrs/annotate.ll

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll
Original file line number Diff line number Diff line change
@@ -52,5 +52,5 @@ attributes #1 = { nounwind readnone speculatable }
!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
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LICM/strlen.ll
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ loop:
}

; CHECK: declare i64 @strlen(i8* nocapture) #0
; CHECK: attributes #0 = { argmemonly nounwind readonly }
; CHECK: attributes #0 = { argmemonly nofree nounwind readonly }
declare i64 @strlen(i8*)


2 changes: 1 addition & 1 deletion llvm/test/Transforms/LoopIdiom/basic.ll
Original file line number Diff line number Diff line change
@@ -709,4 +709,4 @@ exit:

; Validate that "memset_pattern" has the proper attributes.
; CHECK: declare void @memset_pattern16(i8* nocapture, i8* nocapture readonly, i64) [[ATTRS:#[0-9]+]]
; CHECK: [[ATTRS]] = { argmemonly }
; CHECK: [[ATTRS]] = { argmemonly nofree }

0 comments on commit b4b21d8

Please sign in to comment.