site. If the alignment is not specified, then the code generator
makes a target-specific assumption.
+.. _attr_byref:
+
+``byref(<ty>)``
+
+ The ``byref`` argument attribute allows specifying the pointee
+ memory type of an argument. This is similar to ``byval``, but does
+ not imply a copy is made anywhere, or that the argument is passed
+ on the stack. This implies the pointer is dereferenceable up to
+ the storage size of the type.
+
+ It is not generally permissible to introduce a write to an
+ ``byref`` pointer. The pointer may have any address space and may
+ be read only.
+
+ This is not a valid attribute for return values.
+
+ The alignment for an ``byref`` parameter can be explicitly
+ specified by combining it with the ``align`` attribute, similar to
+ ``byval``. If the alignment is not specified, then the code generator
+ makes a target-specific assumption.
+
+ This is intended for representing ABI constraints, and is not
+ intended to be inferred for optimization use.
+
.. _attr_preallocated:
``preallocated(<ty>)``
* ...
+* Added the ``byref`` attribute to better represent argument passing
+ for the `amdgpu_kernel` calling convention.
+
Changes to building LLVM
------------------------
During this release ...
+* The new ``byref`` attribute is now the preferred method for
+ representing aggregate kernel arguments.
+
Changes to the AVR Target
-----------------------------
ATTR_KIND_NO_MERGE = 66,
ATTR_KIND_NULL_POINTER_IS_VALID = 67,
ATTR_KIND_NOUNDEF = 68,
+ ATTR_KIND_BYREF = 69,
};
enum ComdatSelectionKindCodes {
/// Return true if this argument has the byval attribute.
bool hasByValAttr() const;
+ /// Return true if this argument has the byref attribute.
+ bool hasByRefAttr() const;
+
/// Return true if this argument has the swiftself attribute.
bool hasSwiftSelfAttr() const;
/// in-memory ABI size copied to the stack for the call. Otherwise, return 0.
uint64_t getPassPointeeByValueCopySize(const DataLayout &DL) const;
+ /// Return true if this argument has the byval, inalloca, preallocated, or
+ /// byref attribute. These attributes represent arguments being passed by
+ /// value (which may or may not involve a stack copy)
+ bool hasPointeeInMemoryValueAttr() const;
+
+ /// If hasPointeeInMemoryValueAttr returns true, the in-memory ABI type is
+ /// returned. Otherwise, nullptr.
+ Type *getPointeeInMemoryValueType() const;
+
/// If this is a byval or inalloca argument, return its alignment.
/// FIXME: Remove this function once transition to Align is over.
/// Use getParamAlign() instead.
/// If this is a byval argument, return its type.
Type *getParamByValType() const;
+ /// If this is a byref argument, return its type.
+ Type *getParamByRefType() const;
+
/// Return true if this argument has the nest attribute.
bool hasNestAttr() const;
unsigned ElemSizeArg,
const Optional<unsigned> &NumElemsArg);
static Attribute getWithByValType(LLVMContext &Context, Type *Ty);
+ static Attribute getWithByRefType(LLVMContext &Context, Type *Ty);
static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty);
static Attribute::AttrKind getAttrKindFromName(StringRef AttrName);
uint64_t getDereferenceableBytes() const;
uint64_t getDereferenceableOrNullBytes() const;
Type *getByValType() const;
+ Type *getByRefType() const;
Type *getPreallocatedType() const;
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
std::string getAsString(bool InAttrGrp = false) const;
/// Return the byval type for the specified function parameter.
Type *getParamByValType(unsigned ArgNo) const;
+ /// Return the byref type for the specified function parameter.
+ Type *getParamByRefType(unsigned ArgNo) const;
+
/// Return the preallocated type for the specified function parameter.
Type *getParamPreallocatedType(unsigned ArgNo) const;
uint64_t DerefOrNullBytes = 0;
uint64_t AllocSizeArgs = 0;
Type *ByValType = nullptr;
+ Type *ByRefType = nullptr;
Type *PreallocatedType = nullptr;
public:
/// Retrieve the byval type.
Type *getByValType() const { return ByValType; }
+ /// Retrieve the byref type.
+ Type *getByRefType() const { return ByRefType; }
+
/// Retrieve the preallocated type.
Type *getPreallocatedType() const { return PreallocatedType; }
/// This turns a byval type into the form used internally in Attribute.
AttrBuilder &addByValAttr(Type *Ty);
+ /// This turns a byref type into the form used internally in Attribute.
+ AttrBuilder &addByRefAttr(Type *Ty);
+
/// This turns a preallocated type into the form used internally in Attribute.
AttrBuilder &addPreallocatedAttr(Type *Ty);
/// Pass structure by value.
def ByVal : TypeAttr<"byval">;
+/// Mark in-memory ABI type.
+def ByRef : TypeAttr<"byref">;
+
/// Parameter or return value may not contain uninitialized or poison bits.
def NoUndef : EnumAttr<"noundef">;
return Ty ? Ty : (arg_begin() + ArgNo)->getType()->getPointerElementType();
}
+ /// Extract the byref type for a parameter.
+ Type *getParamByRefType(unsigned ArgNo) const {
+ return AttributeSets.getParamByRefType(ArgNo);
+ }
+
/// Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
/// @param i AttributeList index, referring to a return value or argument.
}
SizeOffsetType ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
+ Type *MemoryTy = A.getPointeeInMemoryValueType();
// No interprocedural analysis is done at the moment.
- if (!A.hasPassPointeeByValueCopyAttr()) {
+ if (!MemoryTy) {
++ObjectVisitorArgument;
return unknown();
}
- PointerType *PT = cast<PointerType>(A.getType());
- APInt Size(IntTyBits, DL.getTypeAllocSize(PT->getElementType()));
+
+ APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
return std::make_pair(align(Size, A.getParamAlignment()), Zero);
}
KEYWORD(writeonly);
KEYWORD(zeroext);
KEYWORD(immarg);
+ KEYWORD(byref);
KEYWORD(type);
KEYWORD(opaque);
case lltok::kw_swifterror:
case lltok::kw_swiftself:
case lltok::kw_immarg:
+ case lltok::kw_byref:
HaveError |=
Error(Lex.getLoc(),
"invalid use of parameter-only attribute on a function");
B.addDereferenceableOrNullAttr(Bytes);
continue;
}
+ case lltok::kw_byref: {
+ Type *Ty;
+ if (ParseByRef(Ty))
+ return true;
+ B.addByRefAttr(Ty);
+ continue;
+ }
case lltok::kw_inalloca: B.addAttribute(Attribute::InAlloca); break;
case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break;
case lltok::kw_nest: B.addAttribute(Attribute::Nest); break;
case lltok::kw_swifterror:
case lltok::kw_swiftself:
case lltok::kw_immarg:
+ case lltok::kw_byref:
HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute");
break;
return false;
}
-/// ParsePreallocated
-/// ::= preallocated(<ty>)
-bool LLParser::ParsePreallocated(Type *&Result) {
+/// ParseRequiredTypeAttr
+/// ::= attrname(<ty>)
+bool LLParser::ParseRequiredTypeAttr(Type *&Result, lltok::Kind AttrName) {
Result = nullptr;
- if (!EatIfPresent(lltok::kw_preallocated))
+ if (!EatIfPresent(AttrName))
return true;
if (!EatIfPresent(lltok::lparen))
return Error(Lex.getLoc(), "expected '('");
return false;
}
+/// ParsePreallocated
+/// ::= preallocated(<ty>)
+bool LLParser::ParsePreallocated(Type *&Result) {
+ return ParseRequiredTypeAttr(Result, lltok::kw_preallocated);
+}
+
+/// ParseByRef
+/// ::= byref(<type>)
+bool LLParser::ParseByRef(Type *&Result) {
+ return ParseRequiredTypeAttr(Result, lltok::kw_byref);
+}
+
/// ParseOptionalOperandBundles
/// ::= /*empty*/
/// ::= '[' OperandBundle [, OperandBundle ]* ']'
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool ParseByValWithOptionalType(Type *&Result);
+
+ bool ParseRequiredTypeAttr(Type *&Result, lltok::Kind AttrName);
bool ParsePreallocated(Type *&Result);
+ bool ParseByRef(Type *&Result);
// Module Summary Index Parsing.
bool SkipModuleSummaryEntry();
kw_writeonly,
kw_zeroext,
kw_immarg,
+ kw_byref,
kw_type,
kw_opaque,
return Attribute::Preallocated;
case bitc::ATTR_KIND_NOUNDEF:
return Attribute::NoUndef;
+ case bitc::ATTR_KIND_BYREF:
+ return Attribute::ByRef;
}
}
return Err;
if (Kind == Attribute::ByVal) {
B.addByValAttr(HasType ? getTypeByID(Record[++i]) : nullptr);
+ } else if (Kind == Attribute::ByRef) {
+ B.addByRefAttr(getTypeByID(Record[++i]));
} else if (Kind == Attribute::Preallocated) {
B.addPreallocatedAttr(getTypeByID(Record[++i]));
}
return bitc::ATTR_KIND_PREALLOCATED;
case Attribute::NoUndef:
return bitc::ATTR_KIND_NOUNDEF;
+ case Attribute::ByRef:
+ return bitc::ATTR_KIND_BYREF;
case Attribute::EndAttrKinds:
llvm_unreachable("Can not encode end-attribute kinds marker.");
case Attribute::None:
}
assert((Attr.hasAttribute(Attribute::ByVal) ||
+ Attr.hasAttribute(Attribute::ByRef) ||
Attr.hasAttribute(Attribute::Preallocated)) &&
"unexpected type attr");
if (Attr.hasAttribute(Attribute::ByVal)) {
Out << "byval";
+ } else if (Attr.hasAttribute(Attribute::ByRef)) {
+ Out << "byref";
} else {
Out << "preallocated";
}
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
std::string getAsString(bool InAttrGrp) const;
Type *getByValType() const;
+ Type *getByRefType() const;
Type *getPreallocatedType() const;
using iterator = const Attribute *;
return get(Context, ByVal, Ty);
}
+Attribute Attribute::getWithByRefType(LLVMContext &Context, Type *Ty) {
+ return get(Context, ByRef, Ty);
+}
+
Attribute Attribute::getWithPreallocatedType(LLVMContext &Context, Type *Ty) {
return get(Context, Preallocated, Ty);
}
return Result;
}
- if (hasAttribute(Attribute::Preallocated)) {
- std::string Result;
- Result += "preallocated";
+ const bool IsByRef = hasAttribute(Attribute::ByRef);
+ if (IsByRef || hasAttribute(Attribute::Preallocated)) {
+ std::string Result = IsByRef ? "byref" : "preallocated";
raw_string_ostream OS(Result);
Result += '(';
getValueAsType()->print(OS, false, true);
return SetNode ? SetNode->getDereferenceableOrNullBytes() : 0;
}
+Type *AttributeSet::getByRefType() const {
+ return SetNode ? SetNode->getByRefType() : nullptr;
+}
+
Type *AttributeSet::getByValType() const {
return SetNode ? SetNode->getByValType() : nullptr;
}
case Attribute::ByVal:
Attr = Attribute::getWithByValType(C, B.getByValType());
break;
+ case Attribute::ByRef:
+ Attr = Attribute::getWithByRefType(C, B.getByRefType());
+ break;
case Attribute::Preallocated:
Attr = Attribute::getWithPreallocatedType(C, B.getPreallocatedType());
break;
return nullptr;
}
+Type *AttributeSetNode::getByRefType() const {
+ if (auto A = findEnumAttribute(Attribute::ByRef))
+ return A->getValueAsType();
+ return nullptr;
+}
+
Type *AttributeSetNode::getPreallocatedType() const {
if (auto A = findEnumAttribute(Attribute::Preallocated))
return A->getValueAsType();
return getAttributes(Index+FirstArgIndex).getByValType();
}
+Type *AttributeList::getParamByRefType(unsigned Index) const {
+ return getAttributes(Index + FirstArgIndex).getByRefType();
+}
+
Type *AttributeList::getParamPreallocatedType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getPreallocatedType();
}
DerefBytes = DerefOrNullBytes = 0;
AllocSizeArgs = 0;
ByValType = nullptr;
+ ByRefType = nullptr;
PreallocatedType = nullptr;
}
StackAlignment = Attr.getStackAlignment();
else if (Kind == Attribute::ByVal)
ByValType = Attr.getValueAsType();
+ else if (Kind == Attribute::ByRef)
+ ByRefType = Attr.getValueAsType();
else if (Kind == Attribute::Preallocated)
PreallocatedType = Attr.getValueAsType();
else if (Kind == Attribute::Dereferenceable)
StackAlignment.reset();
else if (Val == Attribute::ByVal)
ByValType = nullptr;
+ else if (Val == Attribute::ByRef)
+ ByRefType = nullptr;
else if (Val == Attribute::Preallocated)
PreallocatedType = nullptr;
else if (Val == Attribute::Dereferenceable)
return *this;
}
+AttrBuilder &AttrBuilder::addByRefAttr(Type *Ty) {
+ Attrs[Attribute::ByRef] = true;
+ ByRefType = Ty;
+ return *this;
+}
+
AttrBuilder &AttrBuilder::addPreallocatedAttr(Type *Ty) {
Attrs[Attribute::Preallocated] = true;
PreallocatedType = Ty;
if (!ByValType)
ByValType = B.ByValType;
+ if (!ByRefType)
+ ByRefType = B.ByRefType;
+
if (!PreallocatedType)
PreallocatedType = B.PreallocatedType;
if (B.ByValType)
ByValType = nullptr;
+ if (B.ByRefType)
+ ByRefType = nullptr;
+
if (B.PreallocatedType)
PreallocatedType = nullptr;
return Alignment == B.Alignment && StackAlignment == B.StackAlignment &&
DerefBytes == B.DerefBytes && ByValType == B.ByValType &&
- PreallocatedType == B.PreallocatedType;
+ ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType;
}
//===----------------------------------------------------------------------===//
.addAttribute(Attribute::StructRet)
.addAttribute(Attribute::InAlloca)
.addPreallocatedAttr(Ty)
- .addByValAttr(Ty);
+ .addByValAttr(Ty)
+ .addByRefAttr(Ty);
return Incompatible;
}
return hasAttribute(Attribute::ByVal);
}
+bool Argument::hasByRefAttr() const {
+ if (!getType()->isPointerTy())
+ return false;
+ return hasAttribute(Attribute::ByRef);
+}
+
bool Argument::hasSwiftSelfAttr() const {
return getParent()->hasParamAttribute(getArgNo(), Attribute::SwiftSelf);
}
Attrs.hasParamAttribute(getArgNo(), Attribute::Preallocated);
}
-uint64_t Argument::getPassPointeeByValueCopySize(const DataLayout &DL) const {
- AttributeSet ParamAttrs
- = getParent()->getAttributes().getParamAttributes(getArgNo());
+bool Argument::hasPointeeInMemoryValueAttr() const {
+ if (!getType()->isPointerTy())
+ return false;
+ AttributeList Attrs = getParent()->getAttributes();
+ return Attrs.hasParamAttribute(getArgNo(), Attribute::ByVal) ||
+ Attrs.hasParamAttribute(getArgNo(), Attribute::InAlloca) ||
+ Attrs.hasParamAttribute(getArgNo(), Attribute::Preallocated) ||
+ Attrs.hasParamAttribute(getArgNo(), Attribute::ByRef);
+}
+/// For a byval, inalloca, or preallocated parameter, get the in-memory
+/// parameter type.
+static Type *getMemoryParamAllocType(AttributeSet ParamAttrs, Type *ArgTy) {
// FIXME: All the type carrying attributes are mutually exclusive, so there
// should be a single query to get the stored type that handles any of them.
if (Type *ByValTy = ParamAttrs.getByValType())
- return DL.getTypeAllocSize(ByValTy);
+ return ByValTy;
+ if (Type *ByRefTy = ParamAttrs.getByRefType())
+ return ByRefTy;
if (Type *PreAllocTy = ParamAttrs.getPreallocatedType())
- return DL.getTypeAllocSize(PreAllocTy);
+ return PreAllocTy;
// FIXME: inalloca always depends on pointee element type. It's also possible
// for byval to miss it.
if (ParamAttrs.hasAttribute(Attribute::InAlloca) ||
ParamAttrs.hasAttribute(Attribute::ByVal) ||
ParamAttrs.hasAttribute(Attribute::Preallocated))
- return DL.getTypeAllocSize(cast<PointerType>(getType())->getElementType());
+ return cast<PointerType>(ArgTy)->getElementType();
+
+ return nullptr;
+}
+uint64_t Argument::getPassPointeeByValueCopySize(const DataLayout &DL) const {
+ AttributeSet ParamAttrs =
+ getParent()->getAttributes().getParamAttributes(getArgNo());
+ if (Type *MemTy = getMemoryParamAllocType(ParamAttrs, getType()))
+ return DL.getTypeAllocSize(MemTy);
return 0;
}
+Type *Argument::getPointeeInMemoryValueType() const {
+ AttributeSet ParamAttrs =
+ getParent()->getAttributes().getParamAttributes(getArgNo());
+ return getMemoryParamAllocType(ParamAttrs, getType());
+}
+
unsigned Argument::getParamAlignment() const {
assert(getType()->isPointerTy() && "Only pointers have alignments");
return getParent()->getParamAlignment(getArgNo());
return getParent()->getParamByValType(getArgNo());
}
+Type *Argument::getParamByRefType() const {
+ assert(getType()->isPointerTy() && "Only pointers have byval types");
+ return getParent()->getParamByRefType(getArgNo());
+}
+
uint64_t Argument::getDereferenceableBytes() const {
assert(getType()->isPointerTy() &&
"Only pointers have dereferenceable bytes");
AttrCount += Attrs.hasAttribute(Attribute::StructRet) ||
Attrs.hasAttribute(Attribute::InReg);
AttrCount += Attrs.hasAttribute(Attribute::Nest);
+ AttrCount += Attrs.hasAttribute(Attribute::ByRef);
Assert(AttrCount <= 1,
"Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', "
- "and 'sret' are incompatible!",
+ "'byref', and 'sret' are incompatible!",
V);
Assert(!(Attrs.hasAttribute(Attribute::InAlloca) &&
SmallPtrSet<Type*, 4> Visited;
if (!PTy->getElementType()->isSized(&Visited)) {
Assert(!Attrs.hasAttribute(Attribute::ByVal) &&
- !Attrs.hasAttribute(Attribute::InAlloca) &&
- !Attrs.hasAttribute(Attribute::Preallocated),
- "Attributes 'byval', 'inalloca', and 'preallocated' do not "
+ !Attrs.hasAttribute(Attribute::ByRef) &&
+ !Attrs.hasAttribute(Attribute::InAlloca) &&
+ !Attrs.hasAttribute(Attribute::Preallocated),
+ "Attributes 'byval', 'byref', 'inalloca', and 'preallocated' do not "
"support unsized types!",
V);
}
"Attribute 'swifterror' only applies to parameters "
"with pointer to pointer type!",
V);
+
+ if (Attrs.hasAttribute(Attribute::ByRef)) {
+ Assert(Attrs.getByRefType() == PTy->getElementType(),
+ "Attribute 'byref' type does not match parameter!", V);
+ }
} else {
Assert(!Attrs.hasAttribute(Attribute::ByVal),
"Attribute 'byval' only applies to parameters with pointer type!",
V);
+ Assert(!Attrs.hasAttribute(Attribute::ByRef),
+ "Attribute 'byref' only applies to parameters with pointer type!",
+ V);
Assert(!Attrs.hasAttribute(Attribute::SwiftError),
"Attribute 'swifterror' only applies to parameters "
"with pointer type!",
!RetAttrs.hasAttribute(Attribute::Returned) &&
!RetAttrs.hasAttribute(Attribute::InAlloca) &&
!RetAttrs.hasAttribute(Attribute::Preallocated) &&
+ !RetAttrs.hasAttribute(Attribute::ByRef) &&
!RetAttrs.hasAttribute(Attribute::SwiftSelf) &&
!RetAttrs.hasAttribute(Attribute::SwiftError)),
- "Attributes 'byval', 'inalloca', 'preallocated', 'nest', 'sret', "
- "'nocapture', 'nofree', "
+ "Attributes 'byval', 'inalloca', 'preallocated', 'byref', "
+ "'nest', 'sret', 'nocapture', 'nofree', "
"'returned', 'swiftself', and 'swifterror' do not apply to return "
"values!",
V);
static AttrBuilder getParameterABIAttributes(int I, AttributeList Attrs) {
static const Attribute::AttrKind ABIAttrs[] = {
- Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca,
- Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError,
- Attribute::Preallocated};
+ Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca,
+ Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError,
+ Attribute::Preallocated, Attribute::ByRef};
AttrBuilder Copy;
for (auto AK : ABIAttrs) {
if (Attrs.hasParamAttribute(I, AK))
Copy.addAttribute(AK);
}
- // `align` is ABI-affecting only in combination with `byval`.
+
+ // `align` is ABI-affecting only in combination with `byval` or `byref`.
if (Attrs.hasParamAttribute(I, Attribute::Alignment) &&
- Attrs.hasParamAttribute(I, Attribute::ByVal))
+ (Attrs.hasParamAttribute(I, Attribute::ByVal) ||
+ Attrs.hasParamAttribute(I, Attribute::ByRef)))
Copy.addAlignmentAttr(Attrs.getParamAlignment(I));
return Copy;
}
case Attribute::WriteOnly:
case Attribute::ZExt:
case Attribute::ImmArg:
+ case Attribute::ByRef:
case Attribute::EndAttrKinds:
case Attribute::EmptyKey:
case Attribute::TombstoneKey:
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:34: error: expected '('{{$}}
+define void @test_byref(i8* byref) {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref() {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref(4) {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref()) {
+ ret void
+}
+
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref(-1)) {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref(0)) {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref i8* @test_byref() {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref 8 i8* @test_byref() {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref(8) i8* @test_byref() {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref {
+ ret void
+}
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref=4 {
+ ret void
+}
ret i32 %a
}
+; CHECK: define void @f67(i32* byref(i32) %a)
+define void @f67(i32* byref(i32) %a)
+{
+ ret void
+}
+
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
--- /dev/null
+; RUN: llc < %s -mtriple=i686-pc-win32 | FileCheck %s
+
+%Foo = type { i32, i32 }
+
+declare x86_stdcallcc void @foo_byref_stdcall_p(%Foo* byref(%Foo))
+declare x86_stdcallcc void @i(i32)
+
+; byref does not imply a stack copy, so this should append 4 bytes,
+; not 8.
+define void @stdcall(%Foo* %value) {
+; CHECK-LABEL: _stdcall:
+; CHECK: pushl 4(%esp)
+; CHECK: calll _foo_byref_stdcall_p@4
+ call x86_stdcallcc void @foo_byref_stdcall_p(%Foo* byref(%Foo) %value)
+; CHECK-NOT: %esp
+; CHECK: pushl
+; CHECK: calll _i@4
+ call x86_stdcallcc void @i(i32 0)
+ ret void
+}
--- /dev/null
+; RUN: opt < %s -asan -S | FileCheck %s
+
+; Test that for call instructions, the byref arguments are not
+; instrumented, as no copy is implied.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.bar = type { %struct.foo }
+%struct.foo = type { i8*, i8*, i8* }
+
+; CHECK-LABEL: @func2
+; CHECK-NEXT: tail call void @func1(
+; CHECK-NEXT: ret void
+define dso_local void @func2(%struct.foo* %foo) sanitize_address {
+ tail call void @func1(%struct.foo* byref(%struct.foo) align 8 %foo) #2
+ ret void
+}
+
+declare dso_local void @func1(%struct.foo* byref(%struct.foo) align 8)
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -deadargelim -S | FileCheck %s
+
+declare void @sideeffect()
+
+define void @unused_byref_arg(i32* byref(i32) %dead_arg) {
+; CHECK-LABEL: @unused_byref_arg(
+; CHECK-NEXT: tail call void @sideeffect()
+; CHECK-NEXT: ret void
+;
+ tail call void @sideeffect()
+ ret void
+}
+
+define void @dont_replace_by_undef(i32* %ptr) {
+; CHECK-LABEL: @dont_replace_by_undef(
+; CHECK-NEXT: call void @unused_byref_arg(i32* undef)
+; CHECK-NEXT: ret void
+;
+ call void @unused_byref_arg(i32* %ptr)
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
+; RUN: opt -inline -preserve-alignment-assumptions-during-inlining -S < %s | FileCheck %s
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Test behavior of inserted alignment assumptions with byref. There is
+; no implied copy to a higher alignment, so an alignment assume call
+; should be inserted.
+define void @byref_callee(float* align(128) byref(float) nocapture %a, float* %b) #0 {
+; CHECK-LABEL: define {{[^@]+}}@byref_callee
+; CHECK-SAME: (float* nocapture byref(float) align 128 [[A:%.*]], float* [[B:%.*]]) #0
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[LOAD:%.*]] = load float, float* [[A]], align 4
+; CHECK-NEXT: [[B_IDX:%.*]] = getelementptr inbounds float, float* [[B]], i64 8
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[LOAD]], 2.000000e+00
+; CHECK-NEXT: store float [[ADD]], float* [[B_IDX]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %load = load float, float* %a, align 4
+ %b.idx = getelementptr inbounds float, float* %b, i64 8
+ %add = fadd float %load, 2.0
+ store float %add, float* %b.idx, align 4
+ ret void
+}
+
+define void @byref_caller(float* nocapture align 64 %a, float* %b) #0 {
+; CHECK-LABEL: define {{[^@]+}}@byref_caller
+; CHECK-SAME: (float* nocapture align 64 [[A:%.*]], float* [[B:%.*]]) #0
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTRINT:%.*]] = ptrtoint float* [[A]] to i64
+; CHECK-NEXT: [[MASKEDPTR:%.*]] = and i64 [[PTRINT]], 127
+; CHECK-NEXT: [[MASKCOND:%.*]] = icmp eq i64 [[MASKEDPTR]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[MASKCOND]])
+; CHECK-NEXT: [[LOAD_I:%.*]] = load float, float* [[A]], align 4
+; CHECK-NEXT: [[B_IDX_I:%.*]] = getelementptr inbounds float, float* [[B]], i64 8
+; CHECK-NEXT: [[ADD_I:%.*]] = fadd float [[LOAD_I]], 2.000000e+00
+; CHECK-NEXT: store float [[ADD_I]], float* [[B_IDX_I]], align 4
+; CHECK-NEXT: [[CALLER_LOAD:%.*]] = load float, float* [[B]], align 4
+; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7
+; CHECK-NEXT: store float [[CALLER_LOAD]], float* [[ARRAYIDX]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ call void @byref_callee(float* align(128) byref(float) %a, float* %b)
+ %caller.load = load float, float* %b, align 4
+ %arrayidx = getelementptr inbounds float, float* %a, i64 7
+ store float %caller.load, float* %arrayidx, align 4
+ ret void
+}
+
+attributes #0 = { nounwind uwtable }
%size = tail call i64 @llvm.objectsize.i64(i8* %cast, i1 true, i1 false, i1 false)
ret i64 %size
}
+
+; CHECK-LABEL: @test_objectsize_byref_arg(
+; CHECK: ret i64 42
+define i64 @test_objectsize_byref_arg([42 x i8]* byref([42 x i8]) %ptr) {
+ %cast = bitcast [42 x i8]* %ptr to i8*
+ %size = tail call i64 @llvm.objectsize.i64(i8* %cast, i1 true, i1 false, i1 false)
+ ret i64 %size
+}
--- /dev/null
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: Attribute 'byref' type does not match parameter!
+; CHECK-NEXT: void (i32*)* @byref_mismatched_pointee_type0
+define void @byref_mismatched_pointee_type0(i32* byref(i8)) {
+ ret void
+}
+
+; CHECK: Attribute 'byref' type does not match parameter!
+; CHECK-NEXT: void (i8*)* @byref_mismatched_pointee_type1
+define void @byref_mismatched_pointee_type1(i8* byref(i32)) {
+ ret void
+}
+
+%opaque.ty = type opaque
+
+; CHECK: Attributes 'byval', 'byref', 'inalloca', and 'preallocated' do not support unsized types!
+; CHECK-NEXT: void (%opaque.ty*)* @byref_unsized
+define void @byref_unsized(%opaque.ty* byref(%opaque.ty)) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_byval
+define void @byref_byval(i32* byref(i32) byval(i32)) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_inalloca
+define void @byref_inalloca(i32* byref(i32) inalloca) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_preallocated
+define void @byref_preallocated(i32* byref(i32) preallocated(i32)) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_sret
+define void @byref_sret(i32* byref(i32) sret) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_inreg
+define void @byref_inreg(i32* byref(i32) inreg) {
+ ret void
+}
+
+; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible!
+; CHECK-NEXT: void (i32*)* @byref_nest
+define void @byref_nest(i32* byref(i32) nest) {
+ ret void
+}
+
+; CHECK: Wrong types for attribute: inalloca nest noalias nocapture nonnull readnone readonly sret byref(i32) byval(i32) preallocated(i32) dereferenceable(1) dereferenceable_or_null(1)
+; CHECK-NEXT: void (i32)* @byref_non_pointer
+define void @byref_non_pointer(i32 byref(i32)) {
+ ret void
+}
+
+define void @byref_callee([64 x i8]* byref([64 x i8])) {
+ ret void
+}
+
+define void @no_byref_callee(i8*) {
+ ret void
+}
+
+; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes
+; CHECK-NEXT: musttail call void @byref_callee([64 x i8]* byref([64 x i8]) %cast)
+; CHECK-NEXT: i8* %ptr
+define void @musttail_byref_caller(i8* %ptr) {
+ %cast = bitcast i8* %ptr to [64 x i8]*
+ musttail call void @byref_callee([64 x i8]* byref([64 x i8]) %cast)
+ ret void
+}
+
+; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes
+; CHECK-NEXT: musttail call void @byref_callee([64 x i8]* %ptr)
+; CHECK-NEXT: [64 x i8]* %ptr
+define void @musttail_byref_callee([64 x i8]* byref([64 x i8]) %ptr) {
+ musttail call void @byref_callee([64 x i8]* %ptr)
+ ret void
+}
+
+define void @byref_callee_align32(i8* byref([64 x i8]) align 32) {
+ ret void
+}
+
+; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes
+; CHECK-NEXT: musttail call void @byref_callee_align32(i8* byref([64 x i8]) align 32 %ptr)
+; CHECK-NEXT: i8* %ptr
+define void @musttail_byref_caller_mismatched_align(i8* byref([64 x i8]) align 16 %ptr) {
+ musttail call void @byref_callee_align32(i8* byref([64 x i8]) align 32 %ptr)
+ ret void
+}