mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-28 06:00:28 +00:00
Add the allocsize attribute to LLVM.
`allocsize` is a function attribute that allows users to request that LLVM treat arbitrary functions as allocation functions. This patch makes LLVM accept the `allocsize` attribute, and makes `@llvm.objectsize` recognize said attribute. The review for this was split into two patches for ease of reviewing: D18974 and D14933. As promised on the revisions, I'm landing both patches as a single commit. Differential Revision: http://reviews.llvm.org/D14933 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@266032 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
c13dfaa1cd
commit
274105b8b5
@ -1269,6 +1269,15 @@ example:
|
||||
epilogue, the backend should forcibly align the stack pointer.
|
||||
Specify the desired alignment, which must be a power of two, in
|
||||
parentheses.
|
||||
``allocsize(<EltSizeParam>[, <NumEltsParam>])``
|
||||
This attribute indicates that the annotated function will always return at
|
||||
least a given number of bytes (or null). Its arguments are zero-indexed
|
||||
parameter numbers; if one argument is provided, then it's assumed that at
|
||||
least ``CallSite.Args[EltSizeParam]`` bytes will be available at the
|
||||
returned pointer. If two are provided, then it's assumed that
|
||||
``CallSite.Args[EltSizeParam] * CallSite.Args[NumEltsParam]`` bytes are
|
||||
available. The referenced parameters must be integer types. No assumptions
|
||||
are made about the contents of the returned block of memory.
|
||||
``alwaysinline``
|
||||
This attribute indicates that the inliner should attempt to inline
|
||||
this function into callers whenever possible, ignoring any active
|
||||
|
@ -513,7 +513,8 @@ enum AttributeKindCodes {
|
||||
ATTR_KIND_SWIFT_ERROR = 47,
|
||||
ATTR_KIND_NO_RECURSE = 48,
|
||||
ATTR_KIND_INACCESSIBLEMEM_ONLY = 49,
|
||||
ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50
|
||||
ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50,
|
||||
ATTR_KIND_ALLOC_SIZE = 51
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/PointerLikeTypeTraits.h"
|
||||
#include <bitset>
|
||||
@ -94,6 +95,9 @@ public:
|
||||
uint64_t Bytes);
|
||||
static Attribute getWithDereferenceableOrNullBytes(LLVMContext &Context,
|
||||
uint64_t Bytes);
|
||||
static Attribute getWithAllocSizeArgs(LLVMContext &Context,
|
||||
unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Attribute Accessors
|
||||
@ -147,6 +151,10 @@ public:
|
||||
/// dereferenceable_or_null attribute.
|
||||
uint64_t getDereferenceableOrNullBytes() const;
|
||||
|
||||
/// Returns the argument numbers for the allocsize attribute (or pair(0, 0)
|
||||
/// if not known).
|
||||
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
|
||||
|
||||
/// \brief The Attribute is converted to a string of equivalent mnemonic. This
|
||||
/// is, presumably, for writing out the mnemonics for the assembly writer.
|
||||
std::string getAsString(bool InAttrGrp = false) const;
|
||||
@ -267,6 +275,12 @@ public:
|
||||
AttributeSet addDereferenceableOrNullAttr(LLVMContext &C, unsigned Index,
|
||||
uint64_t Bytes) const;
|
||||
|
||||
/// Add the allocsize attribute to the attribute set at the given index.
|
||||
/// Because attribute sets are immutable, this returns a new set.
|
||||
AttributeSet addAllocSizeAttr(LLVMContext &C, unsigned Index,
|
||||
unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// AttributeSet Accessors
|
||||
//===--------------------------------------------------------------------===//
|
||||
@ -319,6 +333,10 @@ public:
|
||||
/// unknown).
|
||||
uint64_t getDereferenceableOrNullBytes(unsigned Index) const;
|
||||
|
||||
/// Get the allocsize argument numbers (or pair(0, 0) if unknown).
|
||||
std::pair<unsigned, Optional<unsigned>>
|
||||
getAllocSizeArgs(unsigned Index) const;
|
||||
|
||||
/// \brief Return the attributes at the index as a string.
|
||||
std::string getAsString(unsigned Index, bool InAttrGrp = false) const;
|
||||
|
||||
@ -400,19 +418,20 @@ class AttrBuilder {
|
||||
uint64_t StackAlignment;
|
||||
uint64_t DerefBytes;
|
||||
uint64_t DerefOrNullBytes;
|
||||
uint64_t AllocSizeArgs;
|
||||
|
||||
public:
|
||||
AttrBuilder()
|
||||
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0),
|
||||
DerefOrNullBytes(0) {}
|
||||
DerefOrNullBytes(0), AllocSizeArgs(0) {}
|
||||
explicit AttrBuilder(uint64_t Val)
|
||||
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0),
|
||||
DerefOrNullBytes(0) {
|
||||
DerefOrNullBytes(0), AllocSizeArgs(0) {
|
||||
addRawValue(Val);
|
||||
}
|
||||
AttrBuilder(const Attribute &A)
|
||||
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0),
|
||||
DerefOrNullBytes(0) {
|
||||
DerefOrNullBytes(0), AllocSizeArgs(0) {
|
||||
addAttribute(A);
|
||||
}
|
||||
AttrBuilder(AttributeSet AS, unsigned Idx);
|
||||
@ -481,6 +500,10 @@ public:
|
||||
/// dereferenceable_or_null attribute exists (zero is returned otherwise).
|
||||
uint64_t getDereferenceableOrNullBytes() const { return DerefOrNullBytes; }
|
||||
|
||||
/// Retrieve the allocsize args, if the allocsize attribute exists. If it
|
||||
/// doesn't exist, pair(0, 0) is returned.
|
||||
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
|
||||
|
||||
/// \brief This turns an int alignment (which must be a power of 2) into the
|
||||
/// form used internally in Attribute.
|
||||
AttrBuilder &addAlignmentAttr(unsigned Align);
|
||||
@ -497,6 +520,14 @@ public:
|
||||
/// form used internally in Attribute.
|
||||
AttrBuilder &addDereferenceableOrNullAttr(uint64_t Bytes);
|
||||
|
||||
/// This turns one (or two) ints into the form used internally in Attribute.
|
||||
AttrBuilder &addAllocSizeAttr(unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg);
|
||||
|
||||
/// Add an allocsize attribute, using the representation returned by
|
||||
/// Attribute.getIntValue().
|
||||
AttrBuilder &addAllocSizeAttrFromRawRepr(uint64_t RawAllocSizeRepr);
|
||||
|
||||
/// \brief Return true if the builder contains no target-independent
|
||||
/// attributes.
|
||||
bool empty() const { return Attrs.none(); }
|
||||
|
@ -16,6 +16,10 @@ class StrBoolAttr<string S> : Attr<S>;
|
||||
/// 0 means unaligned (different from align(1)).
|
||||
def Alignment : EnumAttr<"align">;
|
||||
|
||||
/// The result of the function is guaranteed to point to a number of bytes that
|
||||
/// we can determine if we know the value of the function's arguments.
|
||||
def AllocSize : EnumAttr<"allocsize">;
|
||||
|
||||
/// inline=always.
|
||||
def AlwaysInline : EnumAttr<"alwaysinline">;
|
||||
|
||||
|
@ -42,39 +42,38 @@ enum AllocType : uint8_t {
|
||||
};
|
||||
|
||||
struct AllocFnsTy {
|
||||
LibFunc::Func Func;
|
||||
AllocType AllocTy;
|
||||
unsigned char NumParams;
|
||||
unsigned NumParams;
|
||||
// First and Second size parameters (or -1 if unused)
|
||||
signed char FstParam, SndParam;
|
||||
int FstParam, SndParam;
|
||||
};
|
||||
|
||||
// FIXME: certain users need more information. E.g., SimplifyLibCalls needs to
|
||||
// know which functions are nounwind, noalias, nocapture parameters, etc.
|
||||
static const AllocFnsTy AllocationFnData[] = {
|
||||
{LibFunc::malloc, MallocLike, 1, 0, -1},
|
||||
{LibFunc::valloc, MallocLike, 1, 0, -1},
|
||||
{LibFunc::Znwj, OpNewLike, 1, 0, -1}, // new(unsigned int)
|
||||
{LibFunc::ZnwjRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new(unsigned int, nothrow)
|
||||
{LibFunc::Znwm, OpNewLike, 1, 0, -1}, // new(unsigned long)
|
||||
{LibFunc::ZnwmRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new(unsigned long, nothrow)
|
||||
{LibFunc::Znaj, OpNewLike, 1, 0, -1}, // new[](unsigned int)
|
||||
{LibFunc::ZnajRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new[](unsigned int, nothrow)
|
||||
{LibFunc::Znam, OpNewLike, 1, 0, -1}, // new[](unsigned long)
|
||||
{LibFunc::ZnamRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new[](unsigned long, nothrow)
|
||||
{LibFunc::msvc_new_int, OpNewLike, 1, 0, -1}, // new(unsigned int)
|
||||
{LibFunc::msvc_new_int_nothrow, MallocLike, 2, 0, -1}, // new(unsigned int, nothrow)
|
||||
{LibFunc::msvc_new_longlong, OpNewLike, 1, 0, -1}, // new(unsigned long long)
|
||||
{LibFunc::msvc_new_longlong_nothrow, MallocLike, 2, 0, -1}, // new(unsigned long long, nothrow)
|
||||
{LibFunc::msvc_new_array_int, OpNewLike, 1, 0, -1}, // new[](unsigned int)
|
||||
{LibFunc::msvc_new_array_int_nothrow, MallocLike, 2, 0, -1}, // new[](unsigned int, nothrow)
|
||||
{LibFunc::msvc_new_array_longlong, OpNewLike, 1, 0, -1}, // new[](unsigned long long)
|
||||
{LibFunc::msvc_new_array_longlong_nothrow, MallocLike, 2, 0, -1}, // new[](unsigned long long, nothrow)
|
||||
{LibFunc::calloc, CallocLike, 2, 0, 1},
|
||||
{LibFunc::realloc, ReallocLike, 2, 1, -1},
|
||||
{LibFunc::reallocf, ReallocLike, 2, 1, -1},
|
||||
{LibFunc::strdup, StrDupLike, 1, -1, -1},
|
||||
{LibFunc::strndup, StrDupLike, 2, 1, -1}
|
||||
static const std::pair<LibFunc::Func, AllocFnsTy> AllocationFnData[] = {
|
||||
{LibFunc::malloc, {MallocLike, 1, 0, -1}},
|
||||
{LibFunc::valloc, {MallocLike, 1, 0, -1}},
|
||||
{LibFunc::Znwj, {OpNewLike, 1, 0, -1}}, // new(unsigned int)
|
||||
{LibFunc::ZnwjRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new(unsigned int, nothrow)
|
||||
{LibFunc::Znwm, {OpNewLike, 1, 0, -1}}, // new(unsigned long)
|
||||
{LibFunc::ZnwmRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new(unsigned long, nothrow)
|
||||
{LibFunc::Znaj, {OpNewLike, 1, 0, -1}}, // new[](unsigned int)
|
||||
{LibFunc::ZnajRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new[](unsigned int, nothrow)
|
||||
{LibFunc::Znam, {OpNewLike, 1, 0, -1}}, // new[](unsigned long)
|
||||
{LibFunc::ZnamRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new[](unsigned long, nothrow)
|
||||
{LibFunc::msvc_new_int, {OpNewLike, 1, 0, -1}}, // new(unsigned int)
|
||||
{LibFunc::msvc_new_int_nothrow, {MallocLike, 2, 0, -1}}, // new(unsigned int, nothrow)
|
||||
{LibFunc::msvc_new_longlong, {OpNewLike, 1, 0, -1}}, // new(unsigned long long)
|
||||
{LibFunc::msvc_new_longlong_nothrow, {MallocLike, 2, 0, -1}}, // new(unsigned long long, nothrow)
|
||||
{LibFunc::msvc_new_array_int, {OpNewLike, 1, 0, -1}}, // new[](unsigned int)
|
||||
{LibFunc::msvc_new_array_int_nothrow, {MallocLike, 2, 0, -1}}, // new[](unsigned int, nothrow)
|
||||
{LibFunc::msvc_new_array_longlong, {OpNewLike, 1, 0, -1}}, // new[](unsigned long long)
|
||||
{LibFunc::msvc_new_array_longlong_nothrow, {MallocLike, 2, 0, -1}}, // new[](unsigned long long, nothrow)
|
||||
{LibFunc::calloc, {CallocLike, 2, 0, 1}},
|
||||
{LibFunc::realloc, {ReallocLike, 2, 1, -1}},
|
||||
{LibFunc::reallocf, {ReallocLike, 2, 1, -1}},
|
||||
{LibFunc::strdup, {StrDupLike, 1, -1, -1}},
|
||||
{LibFunc::strndup, {StrDupLike, 2, 1, -1}}
|
||||
// TODO: Handle "int posix_memalign(void **, size_t, size_t)"
|
||||
};
|
||||
|
||||
@ -96,34 +95,57 @@ static Function *getCalledFunction(const Value *V, bool LookThroughBitCast) {
|
||||
return Callee;
|
||||
}
|
||||
|
||||
/// \brief Returns the allocation data for the given value if it is a call to a
|
||||
/// known allocation function, and NULL otherwise.
|
||||
static const AllocFnsTy *getAllocationData(const Value *V, AllocType AllocTy,
|
||||
const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast = false) {
|
||||
/// Returns the allocation data for the given value if it's either a call to a
|
||||
/// known allocation function, or a call to a function with the allocsize
|
||||
/// attribute.
|
||||
static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy,
|
||||
const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast = false) {
|
||||
// Skip intrinsics
|
||||
if (isa<IntrinsicInst>(V))
|
||||
return nullptr;
|
||||
return None;
|
||||
|
||||
Function *Callee = getCalledFunction(V, LookThroughBitCast);
|
||||
const Function *Callee = getCalledFunction(V, LookThroughBitCast);
|
||||
if (!Callee)
|
||||
return nullptr;
|
||||
return None;
|
||||
|
||||
// If it has allocsize, we can skip checking if it's a known function.
|
||||
//
|
||||
// MallocLike is chosen here because allocsize makes no guarantees about the
|
||||
// nullness of the result of the function, nor does it deal with strings, nor
|
||||
// does it require that the memory returned is zeroed out.
|
||||
LLVM_CONSTEXPR auto AllocSizeAllocTy = MallocLike;
|
||||
if ((AllocTy & AllocSizeAllocTy) == AllocSizeAllocTy &&
|
||||
Callee->hasFnAttribute(Attribute::AllocSize)) {
|
||||
Attribute Attr = Callee->getFnAttribute(Attribute::AllocSize);
|
||||
std::pair<unsigned, Optional<unsigned>> Args = Attr.getAllocSizeArgs();
|
||||
|
||||
AllocFnsTy Result;
|
||||
Result.AllocTy = AllocSizeAllocTy;
|
||||
Result.NumParams = Callee->getNumOperands();
|
||||
Result.FstParam = Args.first;
|
||||
Result.SndParam = Args.second.getValueOr(-1);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Make sure that the function is available.
|
||||
StringRef FnName = Callee->getName();
|
||||
LibFunc::Func TLIFn;
|
||||
if (!TLI || !TLI->getLibFunc(FnName, TLIFn) || !TLI->has(TLIFn))
|
||||
return nullptr;
|
||||
return None;
|
||||
|
||||
const AllocFnsTy *FnData =
|
||||
const auto *Iter =
|
||||
std::find_if(std::begin(AllocationFnData), std::end(AllocationFnData),
|
||||
[TLIFn](const AllocFnsTy &Fn) { return Fn.Func == TLIFn; });
|
||||
[TLIFn](const std::pair<LibFunc::Func, AllocFnsTy> &P) {
|
||||
return P.first == TLIFn;
|
||||
});
|
||||
|
||||
if (FnData == std::end(AllocationFnData))
|
||||
return nullptr;
|
||||
if (Iter == std::end(AllocationFnData))
|
||||
return None;
|
||||
|
||||
const AllocFnsTy *FnData = &Iter->second;
|
||||
if ((FnData->AllocTy & AllocTy) != FnData->AllocTy)
|
||||
return nullptr;
|
||||
return None;
|
||||
|
||||
// Check function prototype.
|
||||
int FstParam = FnData->FstParam;
|
||||
@ -138,8 +160,8 @@ static const AllocFnsTy *getAllocationData(const Value *V, AllocType AllocTy,
|
||||
(SndParam < 0 ||
|
||||
FTy->getParamType(SndParam)->isIntegerTy(32) ||
|
||||
FTy->getParamType(SndParam)->isIntegerTy(64)))
|
||||
return FnData;
|
||||
return nullptr;
|
||||
return *FnData;
|
||||
return None;
|
||||
}
|
||||
|
||||
static bool hasNoAliasAttr(const Value *V, bool LookThroughBitCast) {
|
||||
@ -153,7 +175,7 @@ static bool hasNoAliasAttr(const Value *V, bool LookThroughBitCast) {
|
||||
/// like).
|
||||
bool llvm::isAllocationFn(const Value *V, const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast) {
|
||||
return getAllocationData(V, AnyAlloc, TLI, LookThroughBitCast);
|
||||
return getAllocationData(V, AnyAlloc, TLI, LookThroughBitCast).hasValue();
|
||||
}
|
||||
|
||||
/// \brief Tests if a value is a call or invoke to a function that returns a
|
||||
@ -170,21 +192,21 @@ bool llvm::isNoAliasFn(const Value *V, const TargetLibraryInfo *TLI,
|
||||
/// allocates uninitialized memory (such as malloc).
|
||||
bool llvm::isMallocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast) {
|
||||
return getAllocationData(V, MallocLike, TLI, LookThroughBitCast);
|
||||
return getAllocationData(V, MallocLike, TLI, LookThroughBitCast).hasValue();
|
||||
}
|
||||
|
||||
/// \brief Tests if a value is a call or invoke to a library function that
|
||||
/// allocates zero-filled memory (such as calloc).
|
||||
bool llvm::isCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast) {
|
||||
return getAllocationData(V, CallocLike, TLI, LookThroughBitCast);
|
||||
return getAllocationData(V, CallocLike, TLI, LookThroughBitCast).hasValue();
|
||||
}
|
||||
|
||||
/// \brief Tests if a value is a call or invoke to a library function that
|
||||
/// allocates memory (either malloc, calloc, or strdup like).
|
||||
bool llvm::isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
|
||||
bool LookThroughBitCast) {
|
||||
return getAllocationData(V, AllocLike, TLI, LookThroughBitCast);
|
||||
return getAllocationData(V, AllocLike, TLI, LookThroughBitCast).hasValue();
|
||||
}
|
||||
|
||||
/// extractMallocCall - Returns the corresponding CallInst if the instruction
|
||||
@ -454,8 +476,8 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
|
||||
}
|
||||
|
||||
SizeOffsetType ObjectSizeOffsetVisitor::visitCallSite(CallSite CS) {
|
||||
const AllocFnsTy *FnData = getAllocationData(CS.getInstruction(), AnyAlloc,
|
||||
TLI);
|
||||
Optional<AllocFnsTy> FnData =
|
||||
getAllocationData(CS.getInstruction(), AnyAlloc, TLI);
|
||||
if (!FnData)
|
||||
return unknown();
|
||||
|
||||
@ -467,7 +489,8 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitCallSite(CallSite CS) {
|
||||
|
||||
// strndup limits strlen
|
||||
if (FnData->FstParam > 0) {
|
||||
ConstantInt *Arg= dyn_cast<ConstantInt>(CS.getArgument(FnData->FstParam));
|
||||
ConstantInt *Arg =
|
||||
dyn_cast<ConstantInt>(CS.getArgument(FnData->FstParam));
|
||||
if (!Arg)
|
||||
return unknown();
|
||||
|
||||
@ -482,7 +505,25 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitCallSite(CallSite CS) {
|
||||
if (!Arg)
|
||||
return unknown();
|
||||
|
||||
APInt Size = Arg->getValue().zextOrSelf(IntTyBits);
|
||||
// When we're compiling N-bit code, and the user uses parameters that are
|
||||
// greater than N bits (e.g. uint64_t on a 32-bit build), we can run into
|
||||
// trouble with APInt size issues. This function handles resizing + overflow
|
||||
// checks for us.
|
||||
auto CheckedZextOrTrunc = [&](APInt &I) {
|
||||
// More bits than we can handle. Checking the bit width isn't necessary, but
|
||||
// it's faster than checking active bits, and should give `false` in the
|
||||
// vast majority of cases.
|
||||
if (I.getBitWidth() > IntTyBits && I.getActiveBits() > IntTyBits)
|
||||
return false;
|
||||
if (I.getBitWidth() != IntTyBits)
|
||||
I = I.zextOrTrunc(IntTyBits);
|
||||
return true;
|
||||
};
|
||||
|
||||
APInt Size = Arg->getValue();
|
||||
if (!CheckedZextOrTrunc(Size))
|
||||
return unknown();
|
||||
|
||||
// size determined by just 1 parameter
|
||||
if (FnData->SndParam < 0)
|
||||
return std::make_pair(Size, Zero);
|
||||
@ -491,8 +532,13 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitCallSite(CallSite CS) {
|
||||
if (!Arg)
|
||||
return unknown();
|
||||
|
||||
Size *= Arg->getValue().zextOrSelf(IntTyBits);
|
||||
return std::make_pair(Size, Zero);
|
||||
APInt NumElems = Arg->getValue();
|
||||
if (!CheckedZextOrTrunc(NumElems))
|
||||
return unknown();
|
||||
|
||||
bool Overflow;
|
||||
Size = Size.umul_ov(NumElems, Overflow);
|
||||
return Overflow ? unknown() : std::make_pair(Size, Zero);
|
||||
|
||||
// TODO: handle more standard functions (+ wchar cousins):
|
||||
// - strdup / strndup
|
||||
@ -670,8 +716,8 @@ SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitAllocaInst(AllocaInst &I) {
|
||||
}
|
||||
|
||||
SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitCallSite(CallSite CS) {
|
||||
const AllocFnsTy *FnData = getAllocationData(CS.getInstruction(), AnyAlloc,
|
||||
TLI);
|
||||
Optional<AllocFnsTy> FnData =
|
||||
getAllocationData(CS.getInstruction(), AnyAlloc, TLI);
|
||||
if (!FnData)
|
||||
return unknown();
|
||||
|
||||
|
@ -611,6 +611,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(attributes);
|
||||
|
||||
KEYWORD(alwaysinline);
|
||||
KEYWORD(allocsize);
|
||||
KEYWORD(argmemonly);
|
||||
KEYWORD(builtin);
|
||||
KEYWORD(byval);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "LLParser.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/AsmParser/SlotMapping.h"
|
||||
#include "llvm/IR/AutoUpgrade.h"
|
||||
#include "llvm/IR/CallingConv.h"
|
||||
@ -1051,6 +1052,15 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
|
||||
B.addStackAlignmentAttr(Alignment);
|
||||
continue;
|
||||
}
|
||||
case lltok::kw_allocsize: {
|
||||
unsigned ElemSizeArg;
|
||||
Optional<unsigned> NumElemsArg;
|
||||
// inAttrGrp doesn't matter; we only support allocsize(a[, b])
|
||||
if (parseAllocSizeArguments(ElemSizeArg, NumElemsArg))
|
||||
return true;
|
||||
B.addAllocSizeAttr(ElemSizeArg, NumElemsArg);
|
||||
continue;
|
||||
}
|
||||
case lltok::kw_alwaysinline: B.addAttribute(Attribute::AlwaysInline); break;
|
||||
case lltok::kw_argmemonly: B.addAttribute(Attribute::ArgMemOnly); break;
|
||||
case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break;
|
||||
@ -1790,6 +1800,35 @@ bool LLParser::ParseOptionalCommaAlign(unsigned &Alignment,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLParser::parseAllocSizeArguments(unsigned &BaseSizeArg,
|
||||
Optional<unsigned> &HowManyArg) {
|
||||
Lex.Lex();
|
||||
|
||||
auto StartParen = Lex.getLoc();
|
||||
if (!EatIfPresent(lltok::lparen))
|
||||
return Error(StartParen, "expected '('");
|
||||
|
||||
if (ParseUInt32(BaseSizeArg))
|
||||
return true;
|
||||
|
||||
if (EatIfPresent(lltok::comma)) {
|
||||
auto HowManyAt = Lex.getLoc();
|
||||
unsigned HowMany;
|
||||
if (ParseUInt32(HowMany))
|
||||
return true;
|
||||
if (HowMany == BaseSizeArg)
|
||||
return Error(HowManyAt,
|
||||
"'allocsize' indices can't refer to the same parameter");
|
||||
HowManyArg = HowMany;
|
||||
} else
|
||||
HowManyArg = None;
|
||||
|
||||
auto EndParen = Lex.getLoc();
|
||||
if (!EatIfPresent(lltok::rparen))
|
||||
return Error(EndParen, "expected ')'");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// ParseScopeAndOrdering
|
||||
/// if isAtomic: ::= 'singlethread'? AtomicOrdering
|
||||
/// else: ::=
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "LLLexer.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
@ -247,7 +248,10 @@ namespace llvm {
|
||||
bool ParseOptionalStackAlignment(unsigned &Alignment);
|
||||
bool ParseOptionalCommaAlign(unsigned &Alignment, bool &AteExtraComma);
|
||||
bool ParseOptionalCommaInAlloca(bool &IsInAlloca);
|
||||
bool ParseIndexList(SmallVectorImpl<unsigned> &Indices,bool &AteExtraComma);
|
||||
bool parseAllocSizeArguments(unsigned &ElemSizeArg,
|
||||
Optional<unsigned> &HowManyArg);
|
||||
bool ParseIndexList(SmallVectorImpl<unsigned> &Indices,
|
||||
bool &AteExtraComma);
|
||||
bool ParseIndexList(SmallVectorImpl<unsigned> &Indices) {
|
||||
bool AteExtraComma;
|
||||
if (ParseIndexList(Indices, AteExtraComma)) return true;
|
||||
|
@ -114,6 +114,7 @@ namespace lltok {
|
||||
|
||||
// Attributes:
|
||||
kw_attributes,
|
||||
kw_allocsize,
|
||||
kw_alwaysinline,
|
||||
kw_argmemonly,
|
||||
kw_sanitize_address,
|
||||
|
@ -1288,6 +1288,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||
return Attribute::Dereferenceable;
|
||||
case bitc::ATTR_KIND_DEREFERENCEABLE_OR_NULL:
|
||||
return Attribute::DereferenceableOrNull;
|
||||
case bitc::ATTR_KIND_ALLOC_SIZE:
|
||||
return Attribute::AllocSize;
|
||||
case bitc::ATTR_KIND_NO_RED_ZONE:
|
||||
return Attribute::NoRedZone;
|
||||
case bitc::ATTR_KIND_NO_RETURN:
|
||||
@ -1412,6 +1414,8 @@ std::error_code BitcodeReader::parseAttributeGroupBlock() {
|
||||
B.addDereferenceableAttr(Record[++i]);
|
||||
else if (Kind == Attribute::DereferenceableOrNull)
|
||||
B.addDereferenceableOrNullAttr(Record[++i]);
|
||||
else if (Kind == Attribute::AllocSize)
|
||||
B.addAllocSizeAttrFromRawRepr(Record[++i]);
|
||||
} else { // String attribute
|
||||
assert((Record[i] == 3 || Record[i] == 4) &&
|
||||
"Invalid attribute group entry");
|
||||
|
@ -164,6 +164,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||
switch (Kind) {
|
||||
case Attribute::Alignment:
|
||||
return bitc::ATTR_KIND_ALIGNMENT;
|
||||
case Attribute::AllocSize:
|
||||
return bitc::ATTR_KIND_ALLOC_SIZE;
|
||||
case Attribute::AlwaysInline:
|
||||
return bitc::ATTR_KIND_ALWAYS_INLINE;
|
||||
case Attribute::ArgMemOnly:
|
||||
|
@ -17,6 +17,7 @@
|
||||
#define LLVM_LIB_IR_ATTRIBUTEIMPL_H
|
||||
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include "llvm/Support/TrailingObjects.h"
|
||||
@ -120,7 +121,8 @@ public:
|
||||
: EnumAttributeImpl(IntAttrEntry, Kind), Val(Val) {
|
||||
assert((Kind == Attribute::Alignment || Kind == Attribute::StackAlignment ||
|
||||
Kind == Attribute::Dereferenceable ||
|
||||
Kind == Attribute::DereferenceableOrNull) &&
|
||||
Kind == Attribute::DereferenceableOrNull ||
|
||||
Kind == Attribute::AllocSize) &&
|
||||
"Wrong kind for int attribute!");
|
||||
}
|
||||
|
||||
@ -188,6 +190,7 @@ public:
|
||||
unsigned getStackAlignment() const;
|
||||
uint64_t getDereferenceableBytes() const;
|
||||
uint64_t getDereferenceableOrNullBytes() const;
|
||||
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
|
||||
std::string getAsString(bool InAttrGrp) const;
|
||||
|
||||
typedef const Attribute *iterator;
|
||||
|
@ -32,6 +32,36 @@ using namespace llvm;
|
||||
// Attribute Construction Methods
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// allocsize has two integer arguments, but because they're both 32 bits, we can
|
||||
// pack them into one 64-bit value, at the cost of making said value
|
||||
// nonsensical.
|
||||
//
|
||||
// In order to do this, we need to reserve one value of the second (optional)
|
||||
// allocsize argument to signify "not present."
|
||||
LLVM_CONSTEXPR static unsigned AllocSizeNumElemsNotPresent =
|
||||
std::numeric_limits<unsigned>::max();
|
||||
|
||||
static uint64_t packAllocSizeArgs(unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg) {
|
||||
assert((!NumElemsArg.hasValue() ||
|
||||
*NumElemsArg != AllocSizeNumElemsNotPresent) &&
|
||||
"Attempting to pack a reserved value");
|
||||
|
||||
return uint64_t(ElemSizeArg) << 32 |
|
||||
NumElemsArg.getValueOr(AllocSizeNumElemsNotPresent);
|
||||
}
|
||||
|
||||
static std::pair<unsigned, Optional<unsigned>>
|
||||
unpackAllocSizeArgs(uint64_t Num) {
|
||||
unsigned NumElems = Num & std::numeric_limits<unsigned>::max();
|
||||
unsigned ElemSizeArg = Num >> 32;
|
||||
|
||||
Optional<unsigned> NumElemsArg;
|
||||
if (NumElems != AllocSizeNumElemsNotPresent)
|
||||
NumElemsArg = NumElems;
|
||||
return std::make_pair(ElemSizeArg, NumElemsArg);
|
||||
}
|
||||
|
||||
Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
|
||||
uint64_t Val) {
|
||||
LLVMContextImpl *pImpl = Context.pImpl;
|
||||
@ -101,6 +131,14 @@ Attribute Attribute::getWithDereferenceableOrNullBytes(LLVMContext &Context,
|
||||
return get(Context, DereferenceableOrNull, Bytes);
|
||||
}
|
||||
|
||||
Attribute
|
||||
Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg) {
|
||||
assert(!(ElemSizeArg == 0 && NumElemsArg && *NumElemsArg == 0) &&
|
||||
"Invalid allocsize arguments -- given allocsize(0, 0)");
|
||||
return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Attribute Accessor Methods
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -180,6 +218,12 @@ uint64_t Attribute::getDereferenceableOrNullBytes() const {
|
||||
return pImpl->getValueAsInt();
|
||||
}
|
||||
|
||||
std::pair<unsigned, Optional<unsigned>> Attribute::getAllocSizeArgs() const {
|
||||
assert(hasAttribute(Attribute::AllocSize) &&
|
||||
"Trying to get allocsize args from non-allocsize attribute");
|
||||
return unpackAllocSizeArgs(pImpl->getValueAsInt());
|
||||
}
|
||||
|
||||
std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
if (!pImpl) return "";
|
||||
|
||||
@ -312,6 +356,21 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
if (hasAttribute(Attribute::DereferenceableOrNull))
|
||||
return AttrWithBytesToString("dereferenceable_or_null");
|
||||
|
||||
if (hasAttribute(Attribute::AllocSize)) {
|
||||
unsigned ElemSize;
|
||||
Optional<unsigned> NumElems;
|
||||
std::tie(ElemSize, NumElems) = getAllocSizeArgs();
|
||||
|
||||
std::string Result = "allocsize(";
|
||||
Result += utostr(ElemSize);
|
||||
if (NumElems.hasValue()) {
|
||||
Result += ',';
|
||||
Result += utostr(*NumElems);
|
||||
}
|
||||
Result += ')';
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Convert target-dependent attributes to strings of the form:
|
||||
//
|
||||
// "kind"
|
||||
@ -468,6 +527,9 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
|
||||
case Attribute::ArgMemOnly:
|
||||
llvm_unreachable("argmemonly attribute not supported in raw format");
|
||||
break;
|
||||
case Attribute::AllocSize:
|
||||
llvm_unreachable("allocsize not supported in raw format");
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("Unsupported attribute type");
|
||||
}
|
||||
@ -559,6 +621,14 @@ uint64_t AttributeSetNode::getDereferenceableOrNullBytes() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::pair<unsigned, Optional<unsigned>>
|
||||
AttributeSetNode::getAllocSizeArgs() const {
|
||||
for (iterator I = begin(), E = end(); I != E; ++I)
|
||||
if (I->hasAttribute(Attribute::AllocSize))
|
||||
return I->getAllocSizeArgs();
|
||||
return std::make_pair(0, 0);
|
||||
}
|
||||
|
||||
std::string AttributeSetNode::getAsString(bool InAttrGrp) const {
|
||||
std::string Str;
|
||||
for (iterator I = begin(), E = end(); I != E; ++I) {
|
||||
@ -594,6 +664,8 @@ uint64_t AttributeSetImpl::Raw(unsigned Index) const {
|
||||
Mask |= (Log2_32(ASN->getStackAlignment()) + 1) << 26;
|
||||
else if (Kind == Attribute::Dereferenceable)
|
||||
llvm_unreachable("dereferenceable not supported in bit mask");
|
||||
else if (Kind == Attribute::AllocSize)
|
||||
llvm_unreachable("allocsize not supported in bit mask");
|
||||
else
|
||||
Mask |= AttributeImpl::getAttrMask(Kind);
|
||||
}
|
||||
@ -709,6 +781,11 @@ AttributeSet AttributeSet::get(LLVMContext &C, unsigned Index,
|
||||
Attr = Attribute::getWithDereferenceableOrNullBytes(
|
||||
C, B.getDereferenceableOrNullBytes());
|
||||
break;
|
||||
case Attribute::AllocSize: {
|
||||
auto A = B.getAllocSizeArgs();
|
||||
Attr = Attribute::getWithAllocSizeArgs(C, A.first, A.second);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Attr = Attribute::get(C, Kind);
|
||||
}
|
||||
@ -960,6 +1037,15 @@ AttributeSet AttributeSet::addDereferenceableOrNullAttr(LLVMContext &C,
|
||||
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
||||
}
|
||||
|
||||
AttributeSet
|
||||
AttributeSet::addAllocSizeAttr(LLVMContext &C, unsigned Index,
|
||||
unsigned ElemSizeArg,
|
||||
const Optional<unsigned> &NumElemsArg) {
|
||||
llvm::AttrBuilder B;
|
||||
B.addAllocSizeAttr(ElemSizeArg, NumElemsArg);
|
||||
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AttributeSet Accessor Methods
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1057,8 +1143,13 @@ uint64_t AttributeSet::getDereferenceableOrNullBytes(unsigned Index) const {
|
||||
return ASN ? ASN->getDereferenceableOrNullBytes() : 0;
|
||||
}
|
||||
|
||||
std::string AttributeSet::getAsString(unsigned Index,
|
||||
bool InAttrGrp) const {
|
||||
std::pair<unsigned, Optional<unsigned>>
|
||||
AttributeSet::getAllocSizeArgs(unsigned Index) const {
|
||||
AttributeSetNode *ASN = getAttributes(Index);
|
||||
return ASN ? ASN->getAllocSizeArgs() : std::make_pair(0, 0);
|
||||
}
|
||||
|
||||
std::string AttributeSet::getAsString(unsigned Index, bool InAttrGrp) const {
|
||||
AttributeSetNode *ASN = getAttributes(Index);
|
||||
return ASN ? ASN->getAsString(InAttrGrp) : std::string("");
|
||||
}
|
||||
@ -1133,7 +1224,7 @@ LLVM_DUMP_METHOD void AttributeSet::dump() const {
|
||||
|
||||
AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index)
|
||||
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0),
|
||||
DerefOrNullBytes(0) {
|
||||
DerefOrNullBytes(0), AllocSizeArgs(0) {
|
||||
AttributeSetImpl *pImpl = AS.pImpl;
|
||||
if (!pImpl) return;
|
||||
|
||||
@ -1152,12 +1243,13 @@ void AttrBuilder::clear() {
|
||||
Attrs.reset();
|
||||
TargetDepAttrs.clear();
|
||||
Alignment = StackAlignment = DerefBytes = DerefOrNullBytes = 0;
|
||||
AllocSizeArgs = 0;
|
||||
}
|
||||
|
||||
AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) {
|
||||
assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!");
|
||||
assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment &&
|
||||
Val != Attribute::Dereferenceable &&
|
||||
Val != Attribute::Dereferenceable && Val != Attribute::AllocSize &&
|
||||
"Adding integer attribute without adding a value!");
|
||||
Attrs[Val] = true;
|
||||
return *this;
|
||||
@ -1180,6 +1272,8 @@ AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
|
||||
DerefBytes = Attr.getDereferenceableBytes();
|
||||
else if (Kind == Attribute::DereferenceableOrNull)
|
||||
DerefOrNullBytes = Attr.getDereferenceableOrNullBytes();
|
||||
else if (Kind == Attribute::AllocSize)
|
||||
AllocSizeArgs = Attr.getValueAsInt();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -1200,6 +1294,8 @@ AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
|
||||
DerefBytes = 0;
|
||||
else if (Val == Attribute::DereferenceableOrNull)
|
||||
DerefOrNullBytes = 0;
|
||||
else if (Val == Attribute::AllocSize)
|
||||
AllocSizeArgs = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -1234,6 +1330,10 @@ AttrBuilder &AttrBuilder::removeAttribute(StringRef A) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<unsigned, Optional<unsigned>> AttrBuilder::getAllocSizeArgs() const {
|
||||
return unpackAllocSizeArgs(AllocSizeArgs);
|
||||
}
|
||||
|
||||
AttrBuilder &AttrBuilder::addAlignmentAttr(unsigned Align) {
|
||||
if (Align == 0) return *this;
|
||||
|
||||
@ -1274,6 +1374,22 @@ AttrBuilder &AttrBuilder::addDereferenceableOrNullAttr(uint64_t Bytes) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrBuilder &AttrBuilder::addAllocSizeAttr(unsigned ElemSize,
|
||||
const Optional<unsigned> &NumElems) {
|
||||
return addAllocSizeAttrFromRawRepr(packAllocSizeArgs(ElemSize, NumElems));
|
||||
}
|
||||
|
||||
AttrBuilder &AttrBuilder::addAllocSizeAttrFromRawRepr(uint64_t RawArgs) {
|
||||
// (0, 0) is our "not present" value, so we need to check for it here.
|
||||
assert(RawArgs && "Invalid allocsize arguments -- given allocsize(0, 0)");
|
||||
|
||||
Attrs[Attribute::AllocSize] = true;
|
||||
// Reuse existing machinery to store this as a single 64-bit integer so we can
|
||||
// save a few bytes over using a pair<unsigned, Optional<unsigned>>.
|
||||
AllocSizeArgs = RawArgs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
|
||||
// FIXME: What if both have alignments, but they don't match?!
|
||||
if (!Alignment)
|
||||
@ -1288,6 +1404,9 @@ AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
|
||||
if (!DerefOrNullBytes)
|
||||
DerefOrNullBytes = B.DerefOrNullBytes;
|
||||
|
||||
if (!AllocSizeArgs)
|
||||
AllocSizeArgs = B.AllocSizeArgs;
|
||||
|
||||
Attrs |= B.Attrs;
|
||||
|
||||
for (auto I : B.td_attrs())
|
||||
@ -1310,6 +1429,9 @@ AttrBuilder &AttrBuilder::remove(const AttrBuilder &B) {
|
||||
if (B.DerefOrNullBytes)
|
||||
DerefOrNullBytes = 0;
|
||||
|
||||
if (B.AllocSizeArgs)
|
||||
AllocSizeArgs = 0;
|
||||
|
||||
Attrs &= ~B.Attrs;
|
||||
|
||||
for (auto I : B.td_attrs())
|
||||
@ -1388,7 +1510,8 @@ AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) {
|
||||
I = Attribute::AttrKind(I + 1)) {
|
||||
if (I == Attribute::Dereferenceable ||
|
||||
I == Attribute::DereferenceableOrNull ||
|
||||
I == Attribute::ArgMemOnly)
|
||||
I == Attribute::ArgMemOnly ||
|
||||
I == Attribute::AllocSize)
|
||||
continue;
|
||||
if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) {
|
||||
Attrs[I] = true;
|
||||
|
@ -1313,7 +1313,8 @@ void Verifier::verifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
|
||||
I->getKindAsEnum() == Attribute::ArgMemOnly ||
|
||||
I->getKindAsEnum() == Attribute::NoRecurse ||
|
||||
I->getKindAsEnum() == Attribute::InaccessibleMemOnly ||
|
||||
I->getKindAsEnum() == Attribute::InaccessibleMemOrArgMemOnly) {
|
||||
I->getKindAsEnum() == Attribute::InaccessibleMemOrArgMemOnly ||
|
||||
I->getKindAsEnum() == Attribute::AllocSize) {
|
||||
if (!isFunction) {
|
||||
CheckFailed("Attribute '" + I->getAsString() +
|
||||
"' only applies to functions!", V);
|
||||
@ -1545,6 +1546,33 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
|
||||
Assert(GV->hasUnnamedAddr(),
|
||||
"Attribute 'jumptable' requires 'unnamed_addr'", V);
|
||||
}
|
||||
|
||||
if (Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::AllocSize)) {
|
||||
std::pair<unsigned, Optional<unsigned>> Args =
|
||||
Attrs.getAllocSizeArgs(AttributeSet::FunctionIndex);
|
||||
|
||||
auto CheckParam = [&](StringRef Name, unsigned ParamNo) {
|
||||
if (ParamNo >= FT->getNumParams()) {
|
||||
CheckFailed("'allocsize' " + Name + " argument is out of bounds", V);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FT->getParamType(ParamNo)->isIntegerTy()) {
|
||||
CheckFailed("'allocsize' " + Name +
|
||||
" argument must refer to an integer parameter",
|
||||
V);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!CheckParam("element size", Args.first))
|
||||
return;
|
||||
|
||||
if (Args.second && !CheckParam("number of elements", *Args.second))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Verifier::verifyFunctionMetadata(
|
||||
|
@ -996,8 +996,13 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
|
||||
default: break;
|
||||
case Intrinsic::objectsize: {
|
||||
uint64_t Size;
|
||||
if (getObjectSize(II->getArgOperand(0), Size, DL, TLI))
|
||||
return replaceInstUsesWith(CI, ConstantInt::get(CI.getType(), Size));
|
||||
if (getObjectSize(II->getArgOperand(0), Size, DL, TLI)) {
|
||||
APInt APSize(II->getType()->getIntegerBitWidth(), Size);
|
||||
// Equality check to be sure that `Size` can fit in a value of type
|
||||
// `II->getType()`
|
||||
if (APSize == Size)
|
||||
return replaceInstUsesWith(CI, ConstantInt::get(II->getType(), APSize));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
case Intrinsic::bswap: {
|
||||
|
@ -204,7 +204,7 @@ define void @f34()
|
||||
; CHECK: define void @f34()
|
||||
{
|
||||
call void @nobuiltin() nobuiltin
|
||||
; CHECK: call void @nobuiltin() #30
|
||||
; CHECK: call void @nobuiltin() #32
|
||||
ret void;
|
||||
}
|
||||
|
||||
@ -318,6 +318,16 @@ entry:
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; CHECK: define i8* @f54(i32) #30
|
||||
define i8* @f54(i32) allocsize(0) {
|
||||
ret i8* null
|
||||
}
|
||||
|
||||
; CHECK: define i8* @f55(i32, i32) #31
|
||||
define i8* @f55(i32, i32) allocsize(0, 1) {
|
||||
ret i8* null
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { noreturn }
|
||||
; CHECK: attributes #1 = { nounwind }
|
||||
; CHECK: attributes #2 = { readnone }
|
||||
@ -348,4 +358,6 @@ entry:
|
||||
; CHECK: attributes #27 = { norecurse }
|
||||
; CHECK: attributes #28 = { inaccessiblememonly }
|
||||
; CHECK: attributes #29 = { inaccessiblemem_or_argmemonly }
|
||||
; CHECK: attributes #30 = { nobuiltin }
|
||||
; CHECK: attributes #30 = { allocsize(0) }
|
||||
; CHECK: attributes #31 = { allocsize(0,1) }
|
||||
; CHECK: attributes #32 = { nobuiltin }
|
||||
|
29
test/Transforms/InstCombine/allocsize-32.ll
Normal file
29
test/Transforms/InstCombine/allocsize-32.ll
Normal file
@ -0,0 +1,29 @@
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
;
|
||||
; The idea is that we want to have sane semantics (e.g. not assertion failures)
|
||||
; when given an allocsize function that takes a 64-bit argument in the face of
|
||||
; 32-bit pointers.
|
||||
|
||||
target datalayout="e-p:32:32:32"
|
||||
|
||||
declare i8* @my_malloc(i8*, i64) allocsize(1)
|
||||
|
||||
define void @test_malloc(i8** %p, i32* %r) {
|
||||
%1 = call i8* @my_malloc(i8* null, i64 100)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
%2 = call i32 @llvm.objectsize.i32.p0i8(i8* %1, i1 false)
|
||||
; CHECK: store i32 100
|
||||
store i32 %2, i32* %r, align 8
|
||||
|
||||
; Big number is 5 billion.
|
||||
%3 = call i8* @my_malloc(i8* null, i64 5000000000)
|
||||
store i8* %3, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
; CHECK: call i32 @llvm.objectsize
|
||||
%4 = call i32 @llvm.objectsize.i32.p0i8(i8* %3, i1 false)
|
||||
store i32 %4, i32* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @llvm.objectsize.i32.p0i8(i8*, i1)
|
141
test/Transforms/InstCombine/allocsize.ll
Normal file
141
test/Transforms/InstCombine/allocsize.ll
Normal file
@ -0,0 +1,141 @@
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
;
|
||||
; Test that instcombine folds allocsize function calls properly.
|
||||
; Dummy arguments are inserted to verify that allocsize is picking the right
|
||||
; args, and to prove that arbitrary unfoldable values don't interfere with
|
||||
; allocsize if they're not used by allocsize.
|
||||
|
||||
declare i8* @my_malloc(i8*, i32) allocsize(1)
|
||||
declare i8* @my_calloc(i8*, i8*, i32, i32) allocsize(2, 3)
|
||||
|
||||
; CHECK-LABEL: define void @test_malloc
|
||||
define void @test_malloc(i8** %p, i64* %r) {
|
||||
%1 = call i8* @my_malloc(i8* null, i32 100)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
|
||||
; CHECK: store i64 100
|
||||
store i64 %2, i64* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test_calloc
|
||||
define void @test_calloc(i8** %p, i64* %r) {
|
||||
%1 = call i8* @my_calloc(i8* null, i8* null, i32 100, i32 5)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
|
||||
; CHECK: store i64 500
|
||||
store i64 %2, i64* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Failure cases with non-constant values...
|
||||
; CHECK-LABEL: define void @test_malloc_fails
|
||||
define void @test_malloc_fails(i8** %p, i64* %r, i32 %n) {
|
||||
%1 = call i8* @my_malloc(i8* null, i32 %n)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
; CHECK: @llvm.objectsize.i64.p0i8
|
||||
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
|
||||
store i64 %2, i64* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test_calloc_fails
|
||||
define void @test_calloc_fails(i8** %p, i64* %r, i32 %n) {
|
||||
%1 = call i8* @my_calloc(i8* null, i8* null, i32 %n, i32 5)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
; CHECK: @llvm.objectsize.i64.p0i8
|
||||
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
|
||||
store i64 %2, i64* %r, align 8
|
||||
|
||||
|
||||
%3 = call i8* @my_calloc(i8* null, i8* null, i32 100, i32 %n)
|
||||
store i8* %3, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
; CHECK: @llvm.objectsize.i64.p0i8
|
||||
%4 = call i64 @llvm.objectsize.i64.p0i8(i8* %3, i1 false)
|
||||
store i64 %4, i64* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i8* @my_malloc_outofline(i8*, i32) #0
|
||||
declare i8* @my_calloc_outofline(i8*, i8*, i32, i32) #1
|
||||
|
||||
; Verifying that out of line allocsize is parsed correctly
|
||||
; CHECK-LABEL: define void @test_outofline
|
||||
define void @test_outofline(i8** %p, i64* %r) {
|
||||
%1 = call i8* @my_malloc_outofline(i8* null, i32 100)
|
||||
store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
|
||||
; CHECK: store i64 100
|
||||
store i64 %2, i64* %r, align 8
|
||||
|
||||
|
||||
%3 = call i8* @my_calloc_outofline(i8* null, i8* null, i32 100, i32 5)
|
||||
store i8* %3, i8** %p, align 8 ; To ensure objectsize isn't killed
|
||||
|
||||
%4 = call i64 @llvm.objectsize.i64.p0i8(i8* %3, i1 false)
|
||||
; CHECK: store i64 500
|
||||
store i64 %4, i64* %r, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i8* @my_malloc_i64(i8*, i64) #0
|
||||
declare i8* @my_tiny_calloc(i8*, i8*, i8, i8) #1
|
||||
declare i8* @my_varied_calloc(i8*, i8*, i32, i8) #1
|
||||
|
||||
; CHECK-LABEL: define void @test_overflow
|
||||
define void @test_overflow(i8** %p, i32* %r) {
|
||||
%r64 = bitcast i32* %r to i64*
|
||||
|
||||
; (2**31 + 1) * 2 > 2**31. So overflow. Yay.
|
||||
%big_malloc = call i8* @my_calloc(i8* null, i8* null, i32 2147483649, i32 2)
|
||||
store i8* %big_malloc, i8** %p, align 8
|
||||
|
||||
; CHECK: @llvm.objectsize
|
||||
%1 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_malloc, i1 false)
|
||||
store i32 %1, i32* %r, align 4
|
||||
|
||||
|
||||
%big_little_malloc = call i8* @my_tiny_calloc(i8* null, i8* null, i8 127, i8 4)
|
||||
store i8* %big_little_malloc, i8** %p, align 8
|
||||
|
||||
; CHECK: store i32 508
|
||||
%2 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_little_malloc, i1 false)
|
||||
store i32 %2, i32* %r, align 4
|
||||
|
||||
|
||||
; malloc(2**33)
|
||||
%big_malloc_i64 = call i8* @my_malloc_i64(i8* null, i64 8589934592)
|
||||
store i8* %big_malloc_i64, i8** %p, align 8
|
||||
|
||||
; CHECK: @llvm.objectsize
|
||||
%3 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_malloc_i64, i1 false)
|
||||
store i32 %3, i32* %r, align 4
|
||||
|
||||
|
||||
%4 = call i64 @llvm.objectsize.i64.p0i8(i8* %big_malloc_i64, i1 false)
|
||||
; CHECK: store i64 8589934592
|
||||
store i64 %4, i64* %r64, align 8
|
||||
|
||||
|
||||
; Just intended to ensure that we properly handle args of different types...
|
||||
%varied_calloc = call i8* @my_varied_calloc(i8* null, i8* null, i32 1000, i8 5)
|
||||
store i8* %varied_calloc, i8** %p, align 8
|
||||
|
||||
; CHECK: store i32 5000
|
||||
%5 = call i32 @llvm.objectsize.i32.p0i8(i8* %varied_calloc, i1 false)
|
||||
store i32 %5, i32* %r, align 4
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { allocsize(1) }
|
||||
attributes #1 = { allocsize(2, 3) }
|
||||
|
||||
declare i32 @llvm.objectsize.i32.p0i8(i8*, i1)
|
||||
declare i64 @llvm.objectsize.i64.p0i8(i8*, i1)
|
7
test/Verifier/alloc-size-failedparse.ll
Normal file
7
test/Verifier/alloc-size-failedparse.ll
Normal file
@ -0,0 +1,7 @@
|
||||
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
|
||||
;
|
||||
; We handle allocsize with identical args in the parser, rather than the
|
||||
; verifier. So, a seperate test is needed.
|
||||
|
||||
; CHECK: 'allocsize' indices can't refer to the same parameter
|
||||
declare i8* @a(i32, i32) allocsize(0, 0)
|
16
test/Verifier/allocsize.ll
Normal file
16
test/Verifier/allocsize.ll
Normal file
@ -0,0 +1,16 @@
|
||||
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: 'allocsize' element size argument is out of bounds
|
||||
declare i8* @a(i32) allocsize(1)
|
||||
|
||||
; CHECK: 'allocsize' element size argument must refer to an integer parameter
|
||||
declare i8* @b(i32*) allocsize(0)
|
||||
|
||||
; CHECK: 'allocsize' number of elements argument is out of bounds
|
||||
declare i8* @c(i32) allocsize(0, 1)
|
||||
|
||||
; CHECK: 'allocsize' number of elements argument must refer to an integer parameter
|
||||
declare i8* @d(i32, i32*) allocsize(0, 1)
|
||||
|
||||
; CHECK: 'allocsize' number of elements argument is out of bounds
|
||||
declare i8* @e(i32, i32) allocsize(1, 2)
|
Loading…
Reference in New Issue
Block a user