1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

Reapply "OpaquePtr: Add type to sret attribute"

This reverts commit eb9f7c28e5fe6d75fed3587023e17f2997c8024b.

Previously this was incorrectly handling linking of the contained
type, so this merges the fixes from D88973.
This commit is contained in:
Matt Arsenault 2020-09-29 09:33:55 -04:00
parent 45720e3d8e
commit e3bfefd3cc
34 changed files with 273 additions and 84 deletions

View File

@ -1057,8 +1057,8 @@ Currently, only the following parameter attributes are defined:
``byval`` parameters). This is not a valid attribute for return
values.
The byval attribute also supports an optional type argument, which must be
the same as the pointee type of the argument.
The byval attribute also supports an optional type argument, which
must be the same as the pointee type of the argument.
The byval attribute also supports specifying an alignment with the
align attribute. It indicates the alignment of the stack slot to
@ -1144,7 +1144,7 @@ Currently, only the following parameter attributes are defined:
See :doc:`InAlloca` for more information on how to use this
attribute.
``sret``
``sret`` or ``sret(<ty>)``
This indicates that the pointer parameter specifies the address of a
structure that is the return value of the function in the source
program. This pointer must be guaranteed by the caller to be valid:
@ -1152,6 +1152,10 @@ Currently, only the following parameter attributes are defined:
to trap and to be properly aligned. This is not a valid attribute
for return values.
The sret attribute also supports an optional type argument, which
must be the same as the pointee type of the argument. In the
future this will be required.
.. _attr_align:
``align <n>`` or ``align(<n>)``

View File

@ -63,10 +63,14 @@ Changes to the LLVM IR
* Added the ``byref`` attribute to better represent argument passing
for the `amdgpu_kernel` calling convention.
* Added type parameter to the ``sret`` attribute to continue work on
removing pointer element types.
* The ``llvm.experimental.vector.reduce`` family of intrinsics have been renamed
to drop the "experimental" from the name, reflecting their now fully supported
status in the IR.
Changes to building LLVM
------------------------

View File

@ -108,9 +108,17 @@ public:
unsigned ElemSizeArg,
const Optional<unsigned> &NumElemsArg);
static Attribute getWithByValType(LLVMContext &Context, Type *Ty);
static Attribute getWithStructRetType(LLVMContext &Context, Type *Ty);
static Attribute getWithByRefType(LLVMContext &Context, Type *Ty);
static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty);
/// For a typed attribute, return the equivalent attribute with the type
/// changed to \p ReplacementTy.
Attribute getWithNewType(LLVMContext &Context, Type *ReplacementTy) {
assert(isTypeAttribute() && "this requires a typed attribute");
return get(Context, getKindAsEnum(), ReplacementTy);
}
static Attribute::AttrKind getAttrKindFromName(StringRef AttrName);
static StringRef getNameFromAttrKind(Attribute::AttrKind AttrKind);
@ -307,6 +315,7 @@ public:
uint64_t getDereferenceableBytes() const;
uint64_t getDereferenceableOrNullBytes() const;
Type *getByValType() const;
Type *getStructRetType() const;
Type *getByRefType() const;
Type *getPreallocatedType() const;
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
@ -508,6 +517,17 @@ public:
return removeAttributes(C, ArgNo + FirstArgIndex);
}
/// Replace the type contained by attribute \p AttrKind at index \p ArgNo wih
/// \p ReplacementTy, preserving all other attributes.
LLVM_NODISCARD AttributeList replaceAttributeType(LLVMContext &C,
unsigned ArgNo,
Attribute::AttrKind Kind,
Type *ReplacementTy) const {
Attribute Attr = getAttribute(ArgNo, Kind);
auto Attrs = removeAttribute(C, ArgNo, Kind);
return Attrs.addAttribute(C, ArgNo, Attr.getWithNewType(C, ReplacementTy));
}
/// \brief Add the dereferenceable attribute to the attribute set at the given
/// index. Returns a new list because attribute lists are immutable.
LLVM_NODISCARD AttributeList addDereferenceableAttr(LLVMContext &C,
@ -631,6 +651,9 @@ public:
/// Return the byval type for the specified function parameter.
Type *getParamByValType(unsigned ArgNo) const;
/// Return the sret type for the specified function parameter.
Type *getParamStructRetType(unsigned ArgNo) const;
/// Return the byref type for the specified function parameter.
Type *getParamByRefType(unsigned ArgNo) const;
@ -737,6 +760,7 @@ class AttrBuilder {
uint64_t DerefOrNullBytes = 0;
uint64_t AllocSizeArgs = 0;
Type *ByValType = nullptr;
Type *StructRetType = nullptr;
Type *ByRefType = nullptr;
Type *PreallocatedType = nullptr;
@ -824,6 +848,9 @@ public:
/// Retrieve the byval type.
Type *getByValType() const { return ByValType; }
/// Retrieve the sret type.
Type *getStructRetType() const { return StructRetType; }
/// Retrieve the byref type.
Type *getByRefType() const { return ByRefType; }
@ -873,6 +900,9 @@ public:
/// This turns a byval type into the form used internally in Attribute.
AttrBuilder &addByValAttr(Type *Ty);
/// This turns a sret type into the form used internally in Attribute.
AttrBuilder &addStructRetAttr(Type *Ty);
/// This turns a byref type into the form used internally in Attribute.
AttrBuilder &addByRefAttr(Type *Ty);

View File

@ -192,7 +192,7 @@ def StackProtectStrong : EnumAttr<"sspstrong">;
def StrictFP : EnumAttr<"strictfp">;
/// Hidden pointer to structure to return.
def StructRet : EnumAttr<"sret">;
def StructRet : TypeAttr<"sret">;
/// AddressSanitizer is on.
def SanitizeAddress : EnumAttr<"sanitize_address">;

View File

@ -474,8 +474,8 @@ public:
/// Extract the sret type for a parameter.
Type *getParamStructRetType(unsigned ArgNo) const {
// FIXME: Add type to attribute like byval
return (arg_begin() + ArgNo)->getType()->getPointerElementType();
Type *Ty = AttributeSets.getParamStructRetType(ArgNo);
return Ty ? Ty : (arg_begin() + ArgNo)->getType()->getPointerElementType();
}
/// Extract the byref type for a parameter.

View File

@ -1657,11 +1657,18 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
}
case lltok::kw_byval: {
Type *Ty;
if (ParseByValWithOptionalType(Ty))
if (ParseOptionalTypeAttr(Ty, lltok::kw_byval))
return true;
B.addByValAttr(Ty);
continue;
}
case lltok::kw_sret: {
Type *Ty;
if (ParseOptionalTypeAttr(Ty, lltok::kw_sret))
return true;
B.addStructRetAttr(Ty);
continue;
}
case lltok::kw_preallocated: {
Type *Ty;
if (ParsePreallocated(Ty))
@ -1704,7 +1711,6 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_readonly: B.addAttribute(Attribute::ReadOnly); break;
case lltok::kw_returned: B.addAttribute(Attribute::Returned); break;
case lltok::kw_signext: B.addAttribute(Attribute::SExt); break;
case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break;
case lltok::kw_swifterror: B.addAttribute(Attribute::SwiftError); break;
case lltok::kw_swiftself: B.addAttribute(Attribute::SwiftSelf); break;
case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break;
@ -2571,9 +2577,9 @@ bool LLParser::ParseParameterList(SmallVectorImpl<ParamInfo> &ArgList,
/// ParseByValWithOptionalType
/// ::= byval
/// ::= byval(<ty>)
bool LLParser::ParseByValWithOptionalType(Type *&Result) {
bool LLParser::ParseOptionalTypeAttr(Type *&Result, lltok::Kind AttrName) {
Result = nullptr;
if (!EatIfPresent(lltok::kw_byval))
if (!EatIfPresent(AttrName))
return true;
if (!EatIfPresent(lltok::lparen))
return false;

View File

@ -332,8 +332,7 @@ namespace llvm {
bool ParseFnAttributeValuePairs(AttrBuilder &B,
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool ParseByValWithOptionalType(Type *&Result);
bool ParseOptionalTypeAttr(Type *&Result, lltok::Kind AttrName);
bool ParseRequiredTypeAttr(Type *&Result, lltok::Kind AttrName);
bool ParsePreallocated(Type *&Result);
bool ParseByRef(Type *&Result);

View File

@ -715,9 +715,9 @@ private:
return getFnValueByID(ValNo, Ty);
}
/// Upgrades old-style typeless byval attributes by adding the corresponding
/// argument's pointee type.
void propagateByValTypes(CallBase *CB, ArrayRef<Type *> ArgsFullTys);
/// Upgrades old-style typeless byval or sret attributes by adding the
/// corresponding argument's pointee type.
void propagateByValSRetTypes(CallBase *CB, ArrayRef<Type *> ArgsFullTys);
/// Converts alignment exponent (i.e. power of two (or zero)) to the
/// corresponding alignment to use. If alignment is too large, returns
@ -1609,6 +1609,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
// this AttributeList with a function.
if (Kind == Attribute::ByVal)
B.addByValAttr(nullptr);
else if (Kind == Attribute::StructRet)
B.addStructRetAttr(nullptr);
B.addAttribute(Kind);
} else if (Record[i] == 1) { // Integer attribute
@ -1652,6 +1654,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
return Err;
if (Kind == Attribute::ByVal) {
B.addByValAttr(HasType ? getTypeByID(Record[++i]) : nullptr);
} else if (Kind == Attribute::StructRet) {
B.addStructRetAttr(HasType ? getTypeByID(Record[++i]) : nullptr);
} else if (Kind == Attribute::ByRef) {
B.addByRefAttr(getTypeByID(Record[++i]));
} else if (Kind == Attribute::Preallocated) {
@ -3286,17 +3290,24 @@ Error BitcodeReader::parseFunctionRecord(ArrayRef<uint64_t> Record) {
Func->setLinkage(getDecodedLinkage(RawLinkage));
Func->setAttributes(getAttributes(Record[4]));
// Upgrade any old-style byval without a type by propagating the argument's
// pointee type. There should be no opaque pointers where the byval type is
// implicit.
// Upgrade any old-style byval or sret without a type by propagating the
// argument's pointee type. There should be no opaque pointers where the byval
// type is implicit.
for (unsigned i = 0; i != Func->arg_size(); ++i) {
if (!Func->hasParamAttribute(i, Attribute::ByVal))
continue;
for (Attribute::AttrKind Kind : {Attribute::ByVal, Attribute::StructRet}) {
if (!Func->hasParamAttribute(i, Kind))
continue;
Type *PTy = cast<FunctionType>(FullFTy)->getParamType(i);
Func->removeParamAttr(i, Attribute::ByVal);
Func->addParamAttr(i, Attribute::getWithByValType(
Context, getPointerElementFlatType(PTy)));
Func->removeParamAttr(i, Kind);
Type *PTy = cast<FunctionType>(FullFTy)->getParamType(i);
Type *PtrEltTy = getPointerElementFlatType(PTy);
Attribute NewAttr =
Kind == Attribute::ByVal
? Attribute::getWithByValType(Context, PtrEltTy)
: Attribute::getWithStructRetType(Context, PtrEltTy);
Func->addParamAttr(i, NewAttr);
}
}
MaybeAlign Alignment;
@ -3757,16 +3768,22 @@ Error BitcodeReader::typeCheckLoadStoreInst(Type *ValType, Type *PtrType) {
return Error::success();
}
void BitcodeReader::propagateByValTypes(CallBase *CB,
ArrayRef<Type *> ArgsFullTys) {
void BitcodeReader::propagateByValSRetTypes(CallBase *CB,
ArrayRef<Type *> ArgsFullTys) {
for (unsigned i = 0; i != CB->arg_size(); ++i) {
if (!CB->paramHasAttr(i, Attribute::ByVal))
continue;
for (Attribute::AttrKind Kind : {Attribute::ByVal, Attribute::StructRet}) {
if (!CB->paramHasAttr(i, Kind))
continue;
CB->removeParamAttr(i, Attribute::ByVal);
CB->addParamAttr(
i, Attribute::getWithByValType(
Context, getPointerElementFlatType(ArgsFullTys[i])));
CB->removeParamAttr(i, Kind);
Type *PtrEltTy = getPointerElementFlatType(ArgsFullTys[i]);
Attribute NewAttr =
Kind == Attribute::ByVal
? Attribute::getWithByValType(Context, PtrEltTy)
: Attribute::getWithStructRetType(Context, PtrEltTy);
CB->addParamAttr(i, NewAttr);
}
}
}
@ -4618,7 +4635,7 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
cast<InvokeInst>(I)->setCallingConv(
static_cast<CallingConv::ID>(CallingConv::MaxID & CCInfo));
cast<InvokeInst>(I)->setAttributes(PAL);
propagateByValTypes(cast<CallBase>(I), ArgsFullTys);
propagateByValSRetTypes(cast<CallBase>(I), ArgsFullTys);
break;
}
@ -5225,7 +5242,7 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
TCK = CallInst::TCK_NoTail;
cast<CallInst>(I)->setTailCallKind(TCK);
cast<CallInst>(I)->setAttributes(PAL);
propagateByValTypes(cast<CallBase>(I), ArgsFullTys);
propagateByValSRetTypes(cast<CallBase>(I), ArgsFullTys);
if (FMF.any()) {
if (!isa<FPMathOperator>(I))
return error("Fast-math-flags specified for call without "

View File

@ -973,6 +973,8 @@ void ValueEnumerator::incorporateFunction(const Function &F) {
EnumerateValue(&I);
if (I.hasAttribute(Attribute::ByVal))
EnumerateType(I.getParamByValType());
else if (I.hasAttribute(Attribute::StructRet))
EnumerateType(I.getParamStructRetType());
}
FirstFuncConstantID = Values.size();

View File

@ -4303,12 +4303,15 @@ void AssemblyWriter::writeAttribute(const Attribute &Attr, bool InAttrGroup) {
}
assert((Attr.hasAttribute(Attribute::ByVal) ||
Attr.hasAttribute(Attribute::StructRet) ||
Attr.hasAttribute(Attribute::ByRef) ||
Attr.hasAttribute(Attribute::Preallocated)) &&
"unexpected type attr");
if (Attr.hasAttribute(Attribute::ByVal)) {
Out << "byval";
} else if (Attr.hasAttribute(Attribute::StructRet)) {
Out << "sret";
} else if (Attr.hasAttribute(Attribute::ByRef)) {
Out << "byref";
} else {

View File

@ -254,6 +254,7 @@ public:
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
std::string getAsString(bool InAttrGrp) const;
Type *getByValType() const;
Type *getStructRetType() const;
Type *getByRefType() const;
Type *getPreallocatedType() const;

View File

@ -172,6 +172,10 @@ Attribute Attribute::getWithByValType(LLVMContext &Context, Type *Ty) {
return get(Context, ByVal, Ty);
}
Attribute Attribute::getWithStructRetType(LLVMContext &Context, Type *Ty) {
return get(Context, StructRet, Ty);
}
Attribute Attribute::getWithByRefType(LLVMContext &Context, Type *Ty) {
return get(Context, ByRef, Ty);
}
@ -433,8 +437,6 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "shadowcallstack";
if (hasAttribute(Attribute::StrictFP))
return "strictfp";
if (hasAttribute(Attribute::StructRet))
return "sret";
if (hasAttribute(Attribute::SanitizeThread))
return "sanitize_thread";
if (hasAttribute(Attribute::SanitizeMemory))
@ -450,9 +452,10 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
if (hasAttribute(Attribute::NoUndef))
return "noundef";
if (hasAttribute(Attribute::ByVal)) {
const bool IsByVal = hasAttribute(Attribute::ByVal);
if (IsByVal || hasAttribute(Attribute::StructRet)) {
std::string Result;
Result += "byval";
Result += IsByVal ? "byval" : "sret";
if (Type *Ty = getValueAsType()) {
raw_string_ostream OS(Result);
Result += '(';
@ -754,6 +757,10 @@ Type *AttributeSet::getByValType() const {
return SetNode ? SetNode->getByValType() : nullptr;
}
Type *AttributeSet::getStructRetType() const {
return SetNode ? SetNode->getStructRetType() : nullptr;
}
Type *AttributeSet::getPreallocatedType() const {
return SetNode ? SetNode->getPreallocatedType() : nullptr;
}
@ -850,6 +857,9 @@ AttributeSetNode *AttributeSetNode::get(LLVMContext &C, const AttrBuilder &B) {
case Attribute::ByVal:
Attr = Attribute::getWithByValType(C, B.getByValType());
break;
case Attribute::StructRet:
Attr = Attribute::getWithStructRetType(C, B.getStructRetType());
break;
case Attribute::ByRef:
Attr = Attribute::getWithByRefType(C, B.getByRefType());
break;
@ -939,6 +949,12 @@ Type *AttributeSetNode::getByValType() const {
return nullptr;
}
Type *AttributeSetNode::getStructRetType() const {
if (auto A = findEnumAttribute(Attribute::StructRet))
return A->getValueAsType();
return nullptr;
}
Type *AttributeSetNode::getByRefType() const {
if (auto A = findEnumAttribute(Attribute::ByRef))
return A->getValueAsType();
@ -1466,6 +1482,10 @@ Type *AttributeList::getParamByValType(unsigned Index) const {
return getAttributes(Index+FirstArgIndex).getByValType();
}
Type *AttributeList::getParamStructRetType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getStructRetType();
}
Type *AttributeList::getParamByRefType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getByRefType();
}
@ -1555,6 +1575,7 @@ void AttrBuilder::clear() {
DerefBytes = DerefOrNullBytes = 0;
AllocSizeArgs = 0;
ByValType = nullptr;
StructRetType = nullptr;
ByRefType = nullptr;
PreallocatedType = nullptr;
}
@ -1574,6 +1595,8 @@ AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
StackAlignment = Attr.getStackAlignment();
else if (Kind == Attribute::ByVal)
ByValType = Attr.getValueAsType();
else if (Kind == Attribute::StructRet)
StructRetType = Attr.getValueAsType();
else if (Kind == Attribute::ByRef)
ByRefType = Attr.getValueAsType();
else if (Kind == Attribute::Preallocated)
@ -1602,6 +1625,8 @@ AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
StackAlignment.reset();
else if (Val == Attribute::ByVal)
ByValType = nullptr;
else if (Val == Attribute::StructRet)
StructRetType = nullptr;
else if (Val == Attribute::ByRef)
ByRefType = nullptr;
else if (Val == Attribute::Preallocated)
@ -1694,6 +1719,12 @@ AttrBuilder &AttrBuilder::addByValAttr(Type *Ty) {
return *this;
}
AttrBuilder &AttrBuilder::addStructRetAttr(Type *Ty) {
Attrs[Attribute::StructRet] = true;
StructRetType = Ty;
return *this;
}
AttrBuilder &AttrBuilder::addByRefAttr(Type *Ty) {
Attrs[Attribute::ByRef] = true;
ByRefType = Ty;
@ -1726,6 +1757,9 @@ AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
if (!ByValType)
ByValType = B.ByValType;
if (!StructRetType)
StructRetType = B.StructRetType;
if (!ByRefType)
ByRefType = B.ByRefType;
@ -1760,6 +1794,9 @@ AttrBuilder &AttrBuilder::remove(const AttrBuilder &B) {
if (B.ByValType)
ByValType = nullptr;
if (B.StructRetType)
StructRetType = nullptr;
if (B.ByRefType)
ByRefType = nullptr;
@ -1826,7 +1863,8 @@ bool AttrBuilder::operator==(const AttrBuilder &B) {
return Alignment == B.Alignment && StackAlignment == B.StackAlignment &&
DerefBytes == B.DerefBytes && ByValType == B.ByValType &&
ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType;
StructRetType == B.StructRetType && ByRefType == B.ByRefType &&
PreallocatedType == B.PreallocatedType;
}
//===----------------------------------------------------------------------===//
@ -1853,10 +1891,10 @@ AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) {
.addDereferenceableOrNullAttr(1) // the int here is ignored
.addAttribute(Attribute::ReadNone)
.addAttribute(Attribute::ReadOnly)
.addAttribute(Attribute::StructRet)
.addAttribute(Attribute::InAlloca)
.addPreallocatedAttr(Ty)
.addByValAttr(Ty)
.addStructRetAttr(Ty)
.addByRefAttr(Ty);
// Some attributes can apply to all "values" but there are no `void` values.

View File

@ -146,6 +146,11 @@ LLVMAttributeRef LLVMCreateEnumAttribute(LLVMContextRef C, unsigned KindID,
return wrap(Attribute::getWithByValType(Ctx, NULL));
}
if (AttrKind == Attribute::AttrKind::StructRet) {
// Same as byval.
return wrap(Attribute::getWithStructRetType(Ctx, NULL));
}
return wrap(Attribute::get(Ctx, AttrKind, Val));
}

View File

@ -638,14 +638,14 @@ GlobalVariable *IRLinker::copyGlobalVariableProto(const GlobalVariable *SGVar) {
AttributeList IRLinker::mapAttributeTypes(LLVMContext &C, AttributeList Attrs) {
for (unsigned i = 0; i < Attrs.getNumAttrSets(); ++i) {
if (Attrs.hasAttribute(i, Attribute::ByVal)) {
Type *Ty = Attrs.getAttribute(i, Attribute::ByVal).getValueAsType();
if (!Ty)
continue;
Attrs = Attrs.removeAttribute(C, i, Attribute::ByVal);
Attrs = Attrs.addAttribute(
C, i, Attribute::getWithByValType(C, TypeMap.get(Ty)));
for (Attribute::AttrKind TypedAttr :
{Attribute::ByVal, Attribute::StructRet}) {
if (Attrs.hasAttribute(i, TypedAttr)) {
if (Type *Ty = Attrs.getAttribute(i, TypedAttr).getValueAsType()) {
Attrs = Attrs.replaceAttributeType(C, i, TypedAttr, TypeMap.get(Ty));
break;
}
}
}
}
return Attrs;

View File

@ -900,14 +900,13 @@ void Mapper::remapInstruction(Instruction *I) {
LLVMContext &C = CB->getContext();
AttributeList Attrs = CB->getAttributes();
for (unsigned i = 0; i < Attrs.getNumAttrSets(); ++i) {
if (Attrs.hasAttribute(i, Attribute::ByVal)) {
Type *Ty = Attrs.getAttribute(i, Attribute::ByVal).getValueAsType();
if (!Ty)
continue;
Attrs = Attrs.removeAttribute(C, i, Attribute::ByVal);
Attrs = Attrs.addAttribute(
C, i, Attribute::getWithByValType(C, TypeMapper->remapType(Ty)));
for (Attribute::AttrKind TypedAttr :
{Attribute::ByVal, Attribute::StructRet}) {
if (Type *Ty = Attrs.getAttribute(i, TypedAttr).getValueAsType()) {
Attrs = Attrs.replaceAttributeType(C, i, TypedAttr,
TypeMapper->remapType(Ty));
break;
}
}
}
CB->setAttributes(Attrs);

View File

@ -0,0 +1,43 @@
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
; CHECK: define void @foo(i32* sret(i32) align 4 %0)
define void @foo(i32* sret(i32) align 4 %0) {
ret void
}
; CHECK: define void @bar({ i32*, i8 }* sret({ i32*, i8 }) align 4 %0)
define void @bar({i32*, i8}* sret({i32*, i8}) align 4 %0) {
ret void
}
define void @caller({ i32*, i8 }* %ptr) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
; CHECK: call void @bar({ i32*, i8 }* sret({ i32*, i8 }) %ptr)
; CHECK: invoke void @bar({ i32*, i8 }* sret({ i32*, i8 }) %ptr)
call void @bar({i32*, i8}* sret %ptr)
invoke void @bar({i32*, i8}* sret %ptr) to label %success unwind label %fail
success:
ret void
fail:
landingpad { i8*, i32 } cleanup
ret void
}
; CHECK: declare void @baz([8 x i8]* sret([8 x i8]))
%named_type = type [8 x i8]
declare void @baz(%named_type* sret(%named_type))
declare i32 @__gxx_personality_v0(...)
%0 = type opaque
; CHECK: define void @anon({ %0* }* sret({ %0* }) %arg)
; CHECK: call void @anon_callee({ %0* }* sret({ %0* }) %arg)
define void @anon({ %0* }* sret({ %0* }) %arg) {
call void @anon_callee({ %0* }* sret({ %0* }) %arg)
ret void
}
; CHECK: declare void @anon_callee({ %0* }* sret({ %0* }))
declare void @anon_callee({ %0* }* sret({ %0* }))

View File

@ -30,7 +30,7 @@ define void @f4(i8 inreg %0)
}
define void @f5(i8* sret %0)
; CHECK: define void @f5(i8* sret %0)
; CHECK: define void @f5(i8* sret(i8) %0)
{
ret void;
}

View File

@ -27,7 +27,7 @@ define void @f4(i8 inreg %0)
}
define void @f5(i8* sret %0)
; CHECK: define void @f5(i8* sret %0)
; CHECK: define void @f5(i8* sret(i8) %0)
{
ret void;
}

View File

@ -408,7 +408,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -414,7 +414,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -439,7 +439,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -508,7 +508,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -508,7 +508,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -512,7 +512,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -519,7 +519,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -533,7 +533,7 @@ declare void @f.param.byval({ i8, i8 }* byval)
declare void @f.param.inalloca(i8* inalloca)
; CHECK: declare void @f.param.inalloca(i8* inalloca)
declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret)
; CHECK: declare void @f.param.sret(i8* sret(i8))
declare void @f.param.noalias(i8* noalias)
; CHECK: declare void @f.param.noalias(i8* noalias)
declare void @f.param.nocapture(i8* nocapture)

View File

@ -35,7 +35,7 @@ module asm "some assembly"
declare void @ParamAttr1(i8 zeroext)
; CHECK: declare void @ParamAttr2(i8* nest)
declare void @ParamAttr2(i8* nest)
; CHECK: declare void @ParamAttr3(i8* sret)
; CHECK: declare void @ParamAttr3(i8* sret(i8))
declare void @ParamAttr3(i8* sret)
; CHECK: declare void @ParamAttr4(i8 signext)
declare void @ParamAttr4(i8 signext)

View File

@ -0,0 +1,13 @@
%a = type { i64 }
%struct = type { i32, i8 }
define void @g(%a* sret(%a)) {
ret void
}
declare void @baz(%struct* sret(%struct))
define void @foo(%struct* sret(%struct) %a) {
call void @baz(%struct* sret(%struct) %a)
ret void
}

25
test/Linker/sret-types.ll Normal file
View File

@ -0,0 +1,25 @@
; RUN: llvm-link %s %p/Inputs/sret-type-input.ll -S | FileCheck %s
%a = type { i64 }
%struct = type { i32, i8 }
; CHECK-LABEL: define void @f(%a* sret(%a) %0)
define void @f(%a* sret(%a)) {
ret void
}
; CHECK-LABEL: define void @bar(
; CHECK: call void @foo(%struct* sret(%struct) %ptr)
define void @bar() {
%ptr = alloca %struct
call void @foo(%struct* sret(%struct) %ptr)
ret void
}
; CHECK-LABEL: define void @g(%a* sret(%a) %0)
; CHECK-LABEL: define void @foo(%struct* sret(%struct) %a)
; CHECK-NEXT: call void @baz(%struct* sret(%struct) %a)
declare void @foo(%struct* sret(%struct) %a)
; CHECK: declare void @baz(%struct* sret(%struct))

View File

@ -7,11 +7,11 @@
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
define internal void @add({i32, i32}* %this, i32* sret %r) {
define internal void @add({i32, i32}* %this, i32* sret(i32) %r) {
;
; IS__TUNIT_OPM: Function Attrs: argmemonly nofree nosync nounwind willreturn
; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@add
; IS__TUNIT_OPM-SAME: ({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__TUNIT_OPM-SAME: ({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__TUNIT_OPM-NEXT: [[AP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 0
; IS__TUNIT_OPM-NEXT: [[BP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 1
; IS__TUNIT_OPM-NEXT: [[A:%.*]] = load i32, i32* [[AP]], align 8
@ -22,7 +22,7 @@ define internal void @add({i32, i32}* %this, i32* sret %r) {
;
; IS__TUNIT_NPM: Function Attrs: argmemonly nofree nosync nounwind willreturn
; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@add
; IS__TUNIT_NPM-SAME: ({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* noalias nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__TUNIT_NPM-SAME: ({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* noalias nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__TUNIT_NPM-NEXT: [[AP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 0
; IS__TUNIT_NPM-NEXT: [[BP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 1
; IS__TUNIT_NPM-NEXT: [[A:%.*]] = load i32, i32* [[AP]], align 8
@ -33,7 +33,7 @@ define internal void @add({i32, i32}* %this, i32* sret %r) {
;
; IS__CGSCC_OPM: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@add
; IS__CGSCC_OPM-SAME: ({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__CGSCC_OPM-SAME: ({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__CGSCC_OPM-NEXT: [[AP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 0
; IS__CGSCC_OPM-NEXT: [[BP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 1
; IS__CGSCC_OPM-NEXT: [[A:%.*]] = load i32, i32* [[AP]], align 8
@ -44,7 +44,7 @@ define internal void @add({i32, i32}* %this, i32* sret %r) {
;
; IS__CGSCC_NPM: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@add
; IS__CGSCC_NPM-SAME: ({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* noalias nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__CGSCC_NPM-SAME: ({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[THIS:%.*]], i32* noalias nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R:%.*]]) [[ATTR0:#.*]] {
; IS__CGSCC_NPM-NEXT: [[AP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 0
; IS__CGSCC_NPM-NEXT: [[BP:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[THIS]], i32 0, i32 1
; IS__CGSCC_NPM-NEXT: [[A:%.*]] = load i32, i32* [[AP]], align 8
@ -68,7 +68,7 @@ define void @f() {
; IS__TUNIT_OPM-SAME: () [[ATTR1:#.*]] {
; IS__TUNIT_OPM-NEXT: [[R:%.*]] = alloca i32, align 4
; IS__TUNIT_OPM-NEXT: [[PAIR:%.*]] = alloca { i32, i32 }, align 8
; IS__TUNIT_OPM-NEXT: call void @add({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__TUNIT_OPM-NEXT: call void @add({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__TUNIT_OPM-NEXT: ret void
;
; IS__TUNIT_NPM: Function Attrs: nofree nosync nounwind readnone willreturn
@ -76,7 +76,7 @@ define void @f() {
; IS__TUNIT_NPM-SAME: () [[ATTR1:#.*]] {
; IS__TUNIT_NPM-NEXT: [[R:%.*]] = alloca i32, align 4
; IS__TUNIT_NPM-NEXT: [[PAIR:%.*]] = alloca { i32, i32 }, align 8
; IS__TUNIT_NPM-NEXT: call void @add({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* noalias nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__TUNIT_NPM-NEXT: call void @add({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* noalias nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__TUNIT_NPM-NEXT: ret void
;
; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
@ -84,7 +84,7 @@ define void @f() {
; IS__CGSCC_OPM-SAME: () [[ATTR1:#.*]] {
; IS__CGSCC_OPM-NEXT: [[R:%.*]] = alloca i32, align 4
; IS__CGSCC_OPM-NEXT: [[PAIR:%.*]] = alloca { i32, i32 }, align 8
; IS__CGSCC_OPM-NEXT: call void @add({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__CGSCC_OPM-NEXT: call void @add({ i32, i32 }* nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__CGSCC_OPM-NEXT: ret void
;
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
@ -92,12 +92,12 @@ define void @f() {
; IS__CGSCC_NPM-SAME: () [[ATTR1:#.*]] {
; IS__CGSCC_NPM-NEXT: [[R:%.*]] = alloca i32, align 4
; IS__CGSCC_NPM-NEXT: [[PAIR:%.*]] = alloca { i32, i32 }, align 8
; IS__CGSCC_NPM-NEXT: call void @add({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* noalias nocapture nofree noundef nonnull sret writeonly align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__CGSCC_NPM-NEXT: call void @add({ i32, i32 }* noalias nocapture nofree noundef nonnull readonly align 8 dereferenceable(8) [[PAIR]], i32* noalias nocapture nofree noundef nonnull writeonly sret(i32) align 4 dereferenceable(4) [[R]]) [[ATTR2:#.*]]
; IS__CGSCC_NPM-NEXT: ret void
;
%r = alloca i32
%pair = alloca {i32, i32}
call void @add({i32, i32}* %pair, i32* sret %r)
call void @add({i32, i32}* %pair, i32* sret(i32) %r)
ret void
}

View File

@ -412,17 +412,17 @@ define i32* @complicated_args_preallocated() {
ret i32* %call
}
define internal void @test_sret(%struct.X* sret %a, %struct.X** %b) {
define internal void @test_sret(%struct.X* sret(%struct.X) %a, %struct.X** %b) {
;
; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
; IS__TUNIT____-LABEL: define {{[^@]+}}@test_sret
; IS__TUNIT____-SAME: (%struct.X* noalias nofree noundef nonnull sret writeonly align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) [[ATTR2:#.*]] {
; IS__TUNIT____-SAME: (%struct.X* noalias nofree noundef nonnull writeonly sret(%struct.X) align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) [[ATTR2:#.*]] {
; IS__TUNIT____-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8
; IS__TUNIT____-NEXT: ret void
;
; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC____-LABEL: define {{[^@]+}}@test_sret
; IS__CGSCC____-SAME: (%struct.X* noalias nofree noundef nonnull sret writeonly align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) [[ATTR2:#.*]] {
; IS__CGSCC____-SAME: (%struct.X* noalias nofree noundef nonnull writeonly sret(%struct.X) align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) [[ATTR2:#.*]] {
; IS__CGSCC____-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8
; IS__CGSCC____-NEXT: ret void
;

View File

@ -1,12 +1,12 @@
; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture nonnull readnone readonly sret byref(i32) byval(i32) preallocated(i32) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture nonnull readnone readonly byref(i32) byval(i32) preallocated(i32) sret(i32) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK-NEXT: @align_non_pointer1
define void @align_non_pointer1(i32 align 4 %a) {
ret void
}
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture noundef nonnull readnone readonly signext sret zeroext byref(void) byval(void) preallocated(void) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture noundef nonnull readnone readonly signext zeroext byref(void) byval(void) preallocated(void) sret(void) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK-NEXT: @align_non_pointer2
define align 4 void @align_non_pointer2(i32 %a) {
ret void

View File

@ -56,7 +56,7 @@ 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) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture nonnull readnone readonly byref(i32) byval(i32) preallocated(i32) sret(i32) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK-NEXT: void (i32)* @byref_non_pointer
define void @byref_non_pointer(i32 byref(i32)) {
ret void

View File

@ -1,6 +1,6 @@
; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture noundef nonnull readnone readonly signext sret zeroext byref(void) byval(void) preallocated(void) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK: Wrong types for attribute: inalloca nest noalias nocapture noundef nonnull readnone readonly signext zeroext byref(void) byval(void) preallocated(void) sret(void) align 1 dereferenceable(1) dereferenceable_or_null(1)
; CHECK-NEXT: @noundef_void
define noundef void @noundef_void() {
ret void