1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 04:32:44 +01:00

Add, and infer, a nofree function attribute

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
This commit is contained in:
Brian Homerding 2019-07-08 15:57:56 +00:00
parent 98551a8d55
commit ed0ab9dc76
30 changed files with 594 additions and 332 deletions

View File

@ -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``

View File

@ -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);

View File

@ -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)

View File

@ -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 {

View File

@ -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">;

View File

@ -564,6 +564,14 @@ public:
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 {

View File

@ -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 nullptr;
if (FTy->getParamType(0) != Type::getInt8PtrTy(Callee->getContext()))
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;
return dyn_cast<CallInst>(I);
StringRef FnName = Callee->getName();
LibFunc TLIFn;
if (!TLI || !TLI->getLibFunc(FnName, TLIFn) || !TLI->has(TLIFn))
return nullptr;
return isLibFreeFunction(Callee, TLIFn) ? dyn_cast<CallInst>(I) : nullptr;
}
//===----------------------------------------------------------------------===//
// Utility functions to compute size of objects.
//

View File

@ -650,6 +650,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nobuiltin);
KEYWORD(nocapture);
KEYWORD(noduplicate);
KEYWORD(nofree);
KEYWORD(noimplicitfloat);
KEYWORD(noinline);
KEYWORD(norecurse);

View File

@ -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;

View File

@ -195,6 +195,7 @@ enum Kind {
kw_nobuiltin,
kw_nocapture,
kw_noduplicate,
kw_nofree,
kw_noimplicitfloat,
kw_noinline,
kw_norecurse,

View File

@ -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:

View File

@ -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:

View File

@ -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))

View File

@ -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:

View File

@ -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 & {

View File

@ -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);

View File

@ -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:

View File

@ -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.

View File

@ -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 }

View File

@ -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:

View File

@ -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}

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

View File

@ -14,4 +14,4 @@ define void @g() {
}
; CHECK: attributes #0 = { nounwind }
; CHECK: attributes #0 = { nofree nounwind }

View File

@ -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 #

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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*)

View File

@ -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 }