mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
IR: Define byref parameter attribute
This allows tracking the in-memory type of a pointer argument to a function for ABI purposes. This is essentially a stripped down version of byval to remove some of the stack-copy implications in its definition. This includes the base IR changes, and some tests for places where it should be treated similarly to byval. Codegen support will be in a future patch. My original attempt at solving some of these problems was to repurpose byval with a different address space from the stack. However, it is technically permitted for the callee to introduce a write to the argument, although nothing does this in reality. There is also talk of removing and replacing the byval attribute, so a new attribute would need to take its place anyway. This is intended avoid some optimization issues with the current handling of aggregate arguments, as well as fixes inflexibilty in how frontends can specify the kernel ABI. The most honest representation of the amdgpu_kernel convention is to expose all kernel arguments as loads from constant memory. Today, these are raw, SSA Argument values and codegen is responsible for turning these into loads. Background: There currently isn't a satisfactory way to represent how arguments for the amdgpu_kernel calling convention are passed. In reality, arguments are passed in a single, flat, constant memory buffer implicitly passed to the function. It is also illegal to call this function in the IR, and this is only ever invoked by a driver of some kind. It does not make sense to have a stack passed parameter in this context as is implied by byval. It is never valid to write to the kernel arguments, as this would corrupt the inputs seen by other dispatches of the kernel. These argumets are also not in the same address space as the stack, so a copy is needed to an alloca. From a source C-like language, the kernel parameters are invisible. Semantically, a copy is always required from the constant argument memory to a mutable variable. The current clang calling convention lowering emits raw values, including aggregates into the function argument list, since using byval would not make sense. This has some unfortunate consequences for the optimizer. In the aggregate case, we end up with an aggregate store to alloca, which both SROA and instcombine turn into a store of each aggregate field. The optimizer never pieces this back together to see that this is really just a copy from constant memory, so we end up stuck with expensive stack usage. This also means the backend dictates the alignment of arguments, and arbitrarily picks the LLVM IR ABI type alignment. By allowing an explicit alignment, frontends can make better decisions. For example, there's real no advantage to an aligment higher than 4, so a frontend could choose to compact the argument layout. Similarly, there is a high penalty to using an alignment lower than 4, so a frontend could opt into more padding for small arguments. Another design consideration is when it is appropriate to expose the fact that these arguments are all really passed in adjacent memory. Currently we have a late IR optimization pass in codegen to rewrite the kernel argument values into explicit loads to enable vectorization. In most programs, unrelated argument loads can be merged together. However, exposing this property directly from the frontend has some disadvantages. We still need a way to track the original argument sizes and alignments to report to the driver. I find using some side-channel, metadata mechanism to track this unappealing. If the kernel arguments were exposed as a single buffer to begin with, alias analysis would be unaware that the padding bits betewen arguments are meaningless. Another family of problems is there are still some gaps in replacing all of the available parameter attributes with metadata equivalents once lowered to loads. The immediate plan is to start using this new attribute to handle all aggregate argumets for kernels. Long term, it makes sense to migrate all kernel arguments, including scalars, to be passed indirectly in the same manner. Additional context is in D79744.
This commit is contained in:
parent
a3033adc1a
commit
ea505ad2f6
@ -1066,6 +1066,30 @@ Currently, only the following parameter attributes are defined:
|
||||
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>)``
|
||||
|
@ -59,6 +59,9 @@ Changes to the LLVM IR
|
||||
|
||||
* ...
|
||||
|
||||
* Added the ``byref`` attribute to better represent argument passing
|
||||
for the `amdgpu_kernel` calling convention.
|
||||
|
||||
Changes to building LLVM
|
||||
------------------------
|
||||
|
||||
@ -88,6 +91,9 @@ Changes to the AMDGPU Target
|
||||
|
||||
During this release ...
|
||||
|
||||
* The new ``byref`` attribute is now the preferred method for
|
||||
representing aggregate kernel arguments.
|
||||
|
||||
Changes to the AVR Target
|
||||
-----------------------------
|
||||
|
||||
|
@ -644,6 +644,7 @@ enum AttributeKindCodes {
|
||||
ATTR_KIND_NO_MERGE = 66,
|
||||
ATTR_KIND_NULL_POINTER_IS_VALID = 67,
|
||||
ATTR_KIND_NOUNDEF = 68,
|
||||
ATTR_KIND_BYREF = 69,
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
@ -65,6 +65,9 @@ public:
|
||||
/// 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;
|
||||
|
||||
@ -80,6 +83,15 @@ public:
|
||||
/// 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.
|
||||
@ -91,6 +103,9 @@ public:
|
||||
/// 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;
|
||||
|
||||
|
@ -108,6 +108,7 @@ public:
|
||||
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);
|
||||
@ -303,6 +304,7 @@ public:
|
||||
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;
|
||||
@ -626,6 +628,9 @@ public:
|
||||
/// 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;
|
||||
|
||||
@ -729,6 +734,7 @@ class AttrBuilder {
|
||||
uint64_t DerefOrNullBytes = 0;
|
||||
uint64_t AllocSizeArgs = 0;
|
||||
Type *ByValType = nullptr;
|
||||
Type *ByRefType = nullptr;
|
||||
Type *PreallocatedType = nullptr;
|
||||
|
||||
public:
|
||||
@ -808,6 +814,9 @@ 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; }
|
||||
|
||||
@ -854,6 +863,9 @@ public:
|
||||
/// 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);
|
||||
|
||||
|
@ -39,6 +39,9 @@ def Builtin : EnumAttr<"builtin">;
|
||||
/// 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">;
|
||||
|
||||
|
@ -467,6 +467,11 @@ public:
|
||||
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.
|
||||
|
@ -676,13 +676,14 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -697,6 +697,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(writeonly);
|
||||
KEYWORD(zeroext);
|
||||
KEYWORD(immarg);
|
||||
KEYWORD(byref);
|
||||
|
||||
KEYWORD(type);
|
||||
KEYWORD(opaque);
|
||||
|
@ -1382,6 +1382,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
|
||||
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");
|
||||
@ -1675,6 +1676,13 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
|
||||
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;
|
||||
@ -1795,6 +1803,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
|
||||
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;
|
||||
|
||||
@ -2568,11 +2577,11 @@ bool LLParser::ParseByValWithOptionalType(Type *&Result) {
|
||||
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 '('");
|
||||
@ -2583,6 +2592,18 @@ bool LLParser::ParsePreallocated(Type *&Result) {
|
||||
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 ]* ']'
|
||||
|
@ -333,7 +333,10 @@ namespace llvm {
|
||||
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();
|
||||
|
@ -240,6 +240,7 @@ enum Kind {
|
||||
kw_writeonly,
|
||||
kw_zeroext,
|
||||
kw_immarg,
|
||||
kw_byref,
|
||||
|
||||
kw_type,
|
||||
kw_opaque,
|
||||
|
@ -1532,6 +1532,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||
return Attribute::Preallocated;
|
||||
case bitc::ATTR_KIND_NOUNDEF:
|
||||
return Attribute::NoUndef;
|
||||
case bitc::ATTR_KIND_BYREF:
|
||||
return Attribute::ByRef;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1649,6 +1651,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
|
||||
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]));
|
||||
}
|
||||
|
@ -733,6 +733,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||
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:
|
||||
|
@ -4269,11 +4269,14 @@ void AssemblyWriter::writeAttribute(const Attribute &Attr, bool InAttrGroup) {
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
@ -251,6 +251,7 @@ public:
|
||||
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 *;
|
||||
|
@ -172,6 +172,10 @@ Attribute Attribute::getWithByValType(LLVMContext &Context, Type *Ty) {
|
||||
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);
|
||||
}
|
||||
@ -459,9 +463,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
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);
|
||||
@ -742,6 +746,10 @@ uint64_t AttributeSet::getDereferenceableOrNullBytes() const {
|
||||
return SetNode ? SetNode->getDereferenceableOrNullBytes() : 0;
|
||||
}
|
||||
|
||||
Type *AttributeSet::getByRefType() const {
|
||||
return SetNode ? SetNode->getByRefType() : nullptr;
|
||||
}
|
||||
|
||||
Type *AttributeSet::getByValType() const {
|
||||
return SetNode ? SetNode->getByValType() : nullptr;
|
||||
}
|
||||
@ -842,6 +850,9 @@ AttributeSetNode *AttributeSetNode::get(LLVMContext &C, const AttrBuilder &B) {
|
||||
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;
|
||||
@ -928,6 +939,12 @@ Type *AttributeSetNode::getByValType() const {
|
||||
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();
|
||||
@ -1449,6 +1466,10 @@ Type *AttributeList::getParamByValType(unsigned Index) const {
|
||||
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();
|
||||
}
|
||||
@ -1534,6 +1555,7 @@ void AttrBuilder::clear() {
|
||||
DerefBytes = DerefOrNullBytes = 0;
|
||||
AllocSizeArgs = 0;
|
||||
ByValType = nullptr;
|
||||
ByRefType = nullptr;
|
||||
PreallocatedType = nullptr;
|
||||
}
|
||||
|
||||
@ -1560,6 +1582,8 @@ AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
|
||||
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)
|
||||
@ -1586,6 +1610,8 @@ AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
|
||||
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)
|
||||
@ -1676,6 +1702,12 @@ AttrBuilder &AttrBuilder::addByValAttr(Type *Ty) {
|
||||
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;
|
||||
@ -1702,6 +1734,9 @@ AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
|
||||
if (!ByValType)
|
||||
ByValType = B.ByValType;
|
||||
|
||||
if (!ByRefType)
|
||||
ByRefType = B.ByRefType;
|
||||
|
||||
if (!PreallocatedType)
|
||||
PreallocatedType = B.PreallocatedType;
|
||||
|
||||
@ -1733,6 +1768,9 @@ AttrBuilder &AttrBuilder::remove(const AttrBuilder &B) {
|
||||
if (B.ByValType)
|
||||
ByValType = nullptr;
|
||||
|
||||
if (B.ByRefType)
|
||||
ByRefType = nullptr;
|
||||
|
||||
if (B.PreallocatedType)
|
||||
PreallocatedType = nullptr;
|
||||
|
||||
@ -1796,7 +1834,7 @@ bool AttrBuilder::operator==(const AttrBuilder &B) {
|
||||
|
||||
return Alignment == B.Alignment && StackAlignment == B.StackAlignment &&
|
||||
DerefBytes == B.DerefBytes && ByValType == B.ByValType &&
|
||||
PreallocatedType == B.PreallocatedType;
|
||||
ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1825,7 +1863,8 @@ AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) {
|
||||
.addAttribute(Attribute::StructRet)
|
||||
.addAttribute(Attribute::InAlloca)
|
||||
.addPreallocatedAttr(Ty)
|
||||
.addByValAttr(Ty);
|
||||
.addByValAttr(Ty)
|
||||
.addByRefAttr(Ty);
|
||||
|
||||
return Incompatible;
|
||||
}
|
||||
|
@ -102,6 +102,12 @@ bool Argument::hasByValAttr() const {
|
||||
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);
|
||||
}
|
||||
@ -129,27 +135,52 @@ bool Argument::hasPassPointeeByValueCopyAttr() const {
|
||||
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());
|
||||
@ -165,6 +196,11 @@ Type *Argument::getParamByValType() const {
|
||||
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");
|
||||
|
@ -1652,9 +1652,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
|
||||
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) &&
|
||||
@ -1720,9 +1721,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
|
||||
SmallPtrSet<Type*, 4> Visited;
|
||||
if (!PTy->getElementType()->isSized(&Visited)) {
|
||||
Assert(!Attrs.hasAttribute(Attribute::ByVal) &&
|
||||
!Attrs.hasAttribute(Attribute::ByRef) &&
|
||||
!Attrs.hasAttribute(Attribute::InAlloca) &&
|
||||
!Attrs.hasAttribute(Attribute::Preallocated),
|
||||
"Attributes 'byval', 'inalloca', and 'preallocated' do not "
|
||||
"Attributes 'byval', 'byref', 'inalloca', and 'preallocated' do not "
|
||||
"support unsized types!",
|
||||
V);
|
||||
}
|
||||
@ -1731,10 +1733,18 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
|
||||
"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!",
|
||||
@ -1765,10 +1775,11 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
|
||||
!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);
|
||||
@ -3176,15 +3187,17 @@ 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::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;
|
||||
}
|
||||
|
@ -895,6 +895,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
||||
case Attribute::WriteOnly:
|
||||
case Attribute::ZExt:
|
||||
case Attribute::ImmArg:
|
||||
case Attribute::ByRef:
|
||||
case Attribute::EndAttrKinds:
|
||||
case Attribute::EmptyKey:
|
||||
case Attribute::TombstoneKey:
|
||||
|
6
test/Assembler/byref-parse-error-0.ll
Normal file
6
test/Assembler/byref-parse-error-0.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; RUN: not llvm-as < %s 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: <stdin>:[[@LINE+1]]:34: error: expected '('{{$}}
|
||||
define void @test_byref(i8* byref) {
|
||||
ret void
|
||||
}
|
6
test/Assembler/byref-parse-error-1.ll
Normal file
6
test/Assembler/byref-parse-error-1.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-10.ll
Normal file
6
test/Assembler/byref-parse-error-10.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
7
test/Assembler/byref-parse-error-2.ll
Normal file
7
test/Assembler/byref-parse-error-2.ll
Normal file
@ -0,0 +1,7 @@
|
||||
; 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
|
||||
}
|
||||
|
6
test/Assembler/byref-parse-error-3.ll
Normal file
6
test/Assembler/byref-parse-error-3.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-4.ll
Normal file
6
test/Assembler/byref-parse-error-4.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-5.ll
Normal file
6
test/Assembler/byref-parse-error-5.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-6.ll
Normal file
6
test/Assembler/byref-parse-error-6.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-7.ll
Normal file
6
test/Assembler/byref-parse-error-7.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-8.ll
Normal file
6
test/Assembler/byref-parse-error-8.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
6
test/Assembler/byref-parse-error-9.ll
Normal file
6
test/Assembler/byref-parse-error-9.ll
Normal file
@ -0,0 +1,6 @@
|
||||
; 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
|
||||
}
|
@ -392,6 +392,12 @@ define noundef i32 @f66(i32 noundef %a)
|
||||
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 }
|
||||
|
20
test/CodeGen/X86/byref.ll
Normal file
20
test/CodeGen/X86/byref.ll
Normal file
@ -0,0 +1,20 @@
|
||||
; 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
|
||||
}
|
20
test/Instrumentation/AddressSanitizer/byref-args.ll
Normal file
20
test/Instrumentation/AddressSanitizer/byref-args.ll
Normal file
@ -0,0 +1,20 @@
|
||||
; 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)
|
22
test/Transforms/DeadArgElim/byref.ll
Normal file
22
test/Transforms/DeadArgElim/byref.ll
Normal file
@ -0,0 +1,22 @@
|
||||
; 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
|
||||
}
|
52
test/Transforms/Inline/byref-align.ll
Normal file
52
test/Transforms/Inline/byref-align.ll
Normal file
@ -0,0 +1,52 @@
|
||||
; 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 }
|
@ -89,3 +89,11 @@ define i64 @test_objectsize_byval_arg([42 x i8]* byval([42 x i8]) %ptr) {
|
||||
%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
|
||||
}
|
||||
|
100
test/Verifier/byref.ll
Normal file
100
test/Verifier/byref.ll
Normal file
@ -0,0 +1,100 @@
|
||||
; 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user