1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(attributes);

KEYWORD(alwaysinline);
KEYWORD(allocsize);
KEYWORD(argmemonly);
KEYWORD(builtin);
KEYWORD(byval);
Expand Down
39 changes: 39 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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: ::=
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace lltok {

// Attributes:
kw_attributes,
kw_allocsize,
kw_alwaysinline,
kw_argmemonly,
kw_sanitize_address,
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/IR/AttributeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -120,7 +121,8 @@ class IntAttributeImpl : public EnumAttributeImpl {
: 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!");
}

Expand Down Expand Up @@ -188,6 +190,7 @@ class AttributeSetNode final
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;
Expand Down
133 changes: 128 additions & 5 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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 "";

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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("");
}
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand All @@ -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())
Expand All @@ -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())
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 29 additions & 1 deletion llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
9 changes: 7 additions & 2 deletions llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
16 changes: 14 additions & 2 deletions llvm/test/Bitcode/attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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 changes: 29 additions & 0 deletions llvm/test/Transforms/InstCombine/allocsize-32.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 141 additions & 0 deletions llvm/test/Transforms/InstCombine/allocsize.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions llvm/test/Verifier/alloc-size-failedparse.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions llvm/test/Verifier/allocsize.ll
Original file line number Diff line number Diff line change
@@ -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)