Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1026,11 +1026,15 @@ ``byval`` parameters). This is not a valid attribute for return values. - The byval attribute also supports specifying an alignment with the - align attribute. It indicates the alignment of the stack slot to - form and the known alignment of the pointer specified to the call - site. If the alignment is not specified, then the code generator - makes a target-specific assumption. + The byval attribute also supports specifying an alignment with the ``align`` + attribute, and a size with the ``size`` attribute. They indicate the + alignment and size of the stack slot to form and the known alignment of the + pointer specified to the call site. If they are not specified, then the code + generator makes a target-specific assumption. + +``size `` + This must be used in conjunction with ``byval`` and indicates the in-memory + size of the specified parameter. .. _attr_inalloca: Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -606,7 +606,8 @@ ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, - ATTR_KIND_IMMARG = 60 + ATTR_KIND_IMMARG = 60, + ATTR_KIND_SIZE = 61, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/CodeGen/TargetLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetLowering.h +++ llvm/include/llvm/CodeGen/TargetLowering.h @@ -187,6 +187,7 @@ bool IsReturned : 1; bool IsSwiftSelf : 1; bool IsSwiftError : 1; + uint32_t Size = 0; uint16_t Alignment = 0; ArgListEntry() Index: llvm/include/llvm/IR/Argument.h =================================================================== --- llvm/include/llvm/IR/Argument.h +++ llvm/include/llvm/IR/Argument.h @@ -78,6 +78,9 @@ /// If this is a byval or inalloca argument, return its alignment. unsigned getParamAlignment() const; + /// If this is a byval argument, return its size. + unsigned getParamSize() const; + /// Return true if this argument has the nest attribute. bool hasNestAttr() const; Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -94,6 +94,7 @@ /// Return a uniquified Attribute object that has the specific /// alignment set. static Attribute getWithAlignment(LLVMContext &Context, uint64_t Align); + static Attribute getWithSize(LLVMContext &Context, uint64_t Align); static Attribute getWithStackAlignment(LLVMContext &Context, uint64_t Align); static Attribute getWithDereferenceableBytes(LLVMContext &Context, uint64_t Bytes); @@ -147,6 +148,9 @@ /// alignment value. unsigned getStackAlignment() const; + /// Returns the size field of an attribute as a byte value. + unsigned getSize() const; + /// Returns the number of dereferenceable bytes from the /// dereferenceable attribute. uint64_t getDereferenceableBytes() const; @@ -277,6 +281,7 @@ unsigned getAlignment() const; unsigned getStackAlignment() const; + unsigned getSize() const; uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; std::pair> getAllocSizeArgs() const; @@ -601,6 +606,9 @@ /// Get the stack alignment. unsigned getStackAlignment(unsigned Index) const; + /// Return the size for the specified byval function parameter. + unsigned getParamSize(unsigned ArgNo) const; + /// Get the number of dereferenceable bytes (or zero if unknown). uint64_t getDereferenceableBytes(unsigned Index) const; @@ -693,6 +701,7 @@ std::bitset Attrs; std::map TargetDepAttrs; uint64_t Alignment = 0; + uint64_t Size = 0; uint64_t StackAlignment = 0; uint64_t DerefBytes = 0; uint64_t DerefOrNullBytes = 0; @@ -764,6 +773,9 @@ /// Retrieve the stack alignment attribute, if it exists. uint64_t getStackAlignment() const { return StackAlignment; } + /// Retrieve the alignment attribute, if it exists. + uint64_t getSize() const { return Size; } + /// Retrieve the number of dereferenceable bytes, if the /// dereferenceable attribute exists (zero is returned otherwise). uint64_t getDereferenceableBytes() const { return DerefBytes; } @@ -784,6 +796,9 @@ /// the form used internally in Attribute. AttrBuilder &addStackAlignmentAttr(unsigned Align); + /// This turns an int size into the form used internally in Attribute. + AttrBuilder &addSizeAttr(unsigned Size); + /// This turns the number of dereferenceable bytes into the form used /// internally in Attribute. AttrBuilder &addDereferenceableAttr(uint64_t Bytes); Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -16,6 +16,9 @@ /// 0 means unaligned (different from align(1)). def Alignment : EnumAttr<"align">; +/// Size of byval parameter. +def Size : EnumAttr<"size">; + /// The result of the function is guaranteed to point to a number of bytes that /// we can determine if we know the value of the function's arguments. def AllocSize : EnumAttr<"allocsize">; Index: llvm/include/llvm/IR/CallSite.h =================================================================== --- llvm/include/llvm/IR/CallSite.h +++ llvm/include/llvm/IR/CallSite.h @@ -415,6 +415,11 @@ CALLSITE_DELEGATE_GETTER(getParamAlignment(ArgNo)); } + /// Extract the size for a call or parameter (0=unknown). + unsigned getParamSize(unsigned ArgNo) const { + CALLSITE_DELEGATE_GETTER(getParamSize(ArgNo)); + } + /// Extract the number of dereferenceable bytes for a call or parameter /// (0=unknown). uint64_t getDereferenceableBytes(unsigned i) const { Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -431,6 +431,11 @@ return AttributeSets.getParamAlignment(ArgNo); } + /// Extract the size for a call or parameter (0=unknown). + unsigned getParamSize(unsigned ArgNo) const { + return AttributeSets.getParamSize(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. Index: llvm/include/llvm/IR/InstrTypes.h =================================================================== --- llvm/include/llvm/IR/InstrTypes.h +++ llvm/include/llvm/IR/InstrTypes.h @@ -1551,6 +1551,11 @@ return Attrs.getParamAlignment(ArgNo); } + /// Extract the size for a call or parameter (0=unknown). + unsigned getParamSize(unsigned ArgNo) const { + return Attrs.getParamSize(ArgNo); + } + /// Extract the number of dereferenceable bytes for a call or /// parameter (0=unknown). uint64_t getDereferenceableBytes(unsigned i) const { Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -632,6 +632,7 @@ KEYWORD(argmemonly); KEYWORD(builtin); KEYWORD(byval); + KEYWORD(size); KEYWORD(inalloca); KEYWORD(cold); KEYWORD(convergent); Index: llvm/lib/AsmParser/LLParser.h =================================================================== --- llvm/lib/AsmParser/LLParser.h +++ llvm/lib/AsmParser/LLParser.h @@ -282,6 +282,7 @@ void ParseOptionalDLLStorageClass(unsigned &Res); bool ParseOptionalCallingConv(unsigned &CC); bool ParseOptionalAlignment(unsigned &Alignment); + bool ParseSize(unsigned &Size); bool ParseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes); bool ParseScopeAndOrdering(bool isAtomic, SyncScope::ID &SSID, AtomicOrdering &Ordering); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1303,6 +1303,7 @@ "invalid use of attribute on a function"); break; case lltok::kw_byval: + case lltok::kw_size: case lltok::kw_dereferenceable: case lltok::kw_dereferenceable_or_null: case lltok::kw_inalloca: @@ -1578,6 +1579,13 @@ B.addAlignmentAttr(Alignment); continue; } + case lltok::kw_size: { + unsigned Size; + if (ParseSize(Size)) + return true; + B.addSizeAttr(Size); + continue; + } case lltok::kw_byval: B.addAttribute(Attribute::ByVal); break; case lltok::kw_dereferenceable: { uint64_t Bytes; @@ -2041,6 +2049,16 @@ return false; } +/// ParseSize +/// ::= 'size' 4 +bool LLParser::ParseSize(unsigned &Size) { + Size = 0; + if (!EatIfPresent(lltok::kw_size)) + return false; + if (ParseUInt32(Size)) return true; + return false; +} + /// ParseOptionalDerefAttrBytes /// ::= /* empty */ /// ::= AttrKind '(' 4 ')' Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -176,6 +176,7 @@ kw_sanitize_hwaddress, kw_builtin, kw_byval, + kw_size, kw_inalloca, kw_cold, kw_convergent, Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1204,6 +1204,9 @@ case Attribute::AllocSize: llvm_unreachable("allocsize not supported in raw format"); break; + case Attribute::Size: + llvm_unreachable("size not supported in raw format"); + break; } llvm_unreachable("Unsupported attribute type"); } @@ -1216,7 +1219,8 @@ if (I == Attribute::Dereferenceable || I == Attribute::DereferenceableOrNull || I == Attribute::ArgMemOnly || - I == Attribute::AllocSize) + I == Attribute::AllocSize || + I == Attribute::Size) continue; if (uint64_t A = (Val & getRawAttributeMask(I))) { if (I == Attribute::Alignment) @@ -1319,6 +1323,8 @@ return Attribute::Builtin; case bitc::ATTR_KIND_BY_VAL: return Attribute::ByVal; + case bitc::ATTR_KIND_SIZE: + return Attribute::Size; case bitc::ATTR_KIND_IN_ALLOCA: return Attribute::InAlloca; case bitc::ATTR_KIND_COLD: @@ -1501,6 +1507,8 @@ B.addAlignmentAttr(Record[++i]); else if (Kind == Attribute::StackAlignment) B.addStackAlignmentAttr(Record[++i]); + else if (Kind == Attribute::Size) + B.addSizeAttr(Record[++i]); else if (Kind == Attribute::Dereferenceable) B.addDereferenceableAttr(Record[++i]); else if (Kind == Attribute::DereferenceableOrNull) Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -598,6 +598,8 @@ switch (Kind) { case Attribute::Alignment: return bitc::ATTR_KIND_ALIGNMENT; + case Attribute::Size: + return bitc::ATTR_KIND_SIZE; case Attribute::AllocSize: return bitc::ATTR_KIND_ALLOC_SIZE; case Attribute::AlwaysInline: Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -9080,8 +9080,14 @@ if (Args[i].IsByVal || Args[i].IsInAlloca) { PointerType *Ty = cast(Args[i].Ty); Type *ElementTy = Ty->getElementType(); - Flags.setByValSize(DL.getTypeAllocSize(ElementTy)); - // For ByVal, alignment should come from FE. BE will guess if this + + unsigned FrameSize; + if (Args[i].Size) + FrameSize = Args[i].Size; + else + FrameSize = DL.getTypeAllocSize(ElementTy); + Flags.setByValSize(FrameSize); + // info is not there but there are cases it cannot get right. unsigned FrameAlign; if (Args[i].Alignment) @@ -9578,9 +9584,17 @@ if (Flags.isByVal() || Flags.isInAlloca()) { PointerType *Ty = cast(Arg.getType()); Type *ElementTy = Ty->getElementType(); - Flags.setByValSize(DL.getTypeAllocSize(ElementTy)); - // For ByVal, alignment should be passed from FE. BE will guess if - // this info is not there but there are cases it cannot get right. + + // For ByVal, size and alignment should be passed from FE. BE will + // guess if this info is not there but there are cases it cannot get + // right. + unsigned FrameSize; + if (Arg.getParamSize()) + FrameSize = Arg.getParamSize(); + else + FrameSize = DL.getTypeAllocSize(ElementTy); + Flags.setByValSize(FrameSize); + unsigned FrameAlign; if (Arg.getParamAlignment()) FrameAlign = Arg.getParamAlignment(); Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -112,6 +112,7 @@ IsSwiftSelf = Call->paramHasAttr(ArgIdx, Attribute::SwiftSelf); IsSwiftError = Call->paramHasAttr(ArgIdx, Attribute::SwiftError); Alignment = Call->getParamAlignment(ArgIdx); + Size = Call->getParamSize(ArgIdx); } /// Generate a libcall taking the given operands as arguments and returning a Index: llvm/lib/IR/AttributeImpl.h =================================================================== --- llvm/lib/IR/AttributeImpl.h +++ llvm/lib/IR/AttributeImpl.h @@ -122,7 +122,7 @@ IntAttributeImpl(Attribute::AttrKind Kind, uint64_t Val) : EnumAttributeImpl(IntAttrEntry, Kind), Val(Val) { assert((Kind == Attribute::Alignment || Kind == Attribute::StackAlignment || - Kind == Attribute::Dereferenceable || + Kind == Attribute::Size || Kind == Attribute::Dereferenceable || Kind == Attribute::DereferenceableOrNull || Kind == Attribute::AllocSize) && "Wrong kind for int attribute!"); @@ -185,6 +185,7 @@ unsigned getAlignment() const; unsigned getStackAlignment() const; + unsigned getSize() const; uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; std::pair> getAllocSizeArgs() const; Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -134,6 +134,10 @@ return get(Context, StackAlignment, Align); } +Attribute Attribute::getWithSize(LLVMContext &Context, uint64_t Size) { + return get(Context, Attribute::Size, Size); +} + Attribute Attribute::getWithDereferenceableBytes(LLVMContext &Context, uint64_t Bytes) { assert(Bytes && "Bytes must be non-zero."); @@ -219,6 +223,12 @@ return pImpl->getValueAsInt(); } +unsigned Attribute::getSize() const { + assert(hasAttribute(Attribute::Size) && + "Trying to get size from non-size attribute!"); + return pImpl->getValueAsInt(); +} + uint64_t Attribute::getDereferenceableBytes() const { assert(hasAttribute(Attribute::Dereferenceable) && "Trying to get dereferenceable bytes from " @@ -366,6 +376,14 @@ return Result; } + if (hasAttribute(Attribute::Size)) { + std::string Result; + Result += "size"; + Result += (InAttrGrp) ? "=" : " "; + Result += utostr(getValueAsInt()); + return Result; + } + auto AttrWithBytesToString = [&](const char *Name) { std::string Result; Result += Name; @@ -600,6 +618,10 @@ return SetNode ? SetNode->getStackAlignment() : 0; } +unsigned AttributeSet::getSize() const { + return SetNode ? SetNode->getSize() : 0; +} + uint64_t AttributeSet::getDereferenceableBytes() const { return SetNode ? SetNode->getDereferenceableBytes() : 0; } @@ -697,6 +719,9 @@ case Attribute::StackAlignment: Attr = Attribute::getWithStackAlignment(C, B.getStackAlignment()); break; + case Attribute::Size: + Attr = Attribute::getWithSize(C, B.getSize()); + break; case Attribute::Dereferenceable: Attr = Attribute::getWithDereferenceableBytes( C, B.getDereferenceableBytes()); @@ -760,6 +785,13 @@ return 0; } +unsigned AttributeSetNode::getSize() const { + for (const auto I : *this) + if (I.hasAttribute(Attribute::Size)) + return I.getSize(); + return 0; +} + uint64_t AttributeSetNode::getDereferenceableBytes() const { for (const auto I : *this) if (I.hasAttribute(Attribute::Dereferenceable)) @@ -1262,6 +1294,10 @@ return getAttributes(Index).getStackAlignment(); } +unsigned AttributeList::getParamSize(unsigned ArgNo) const { + return getAttributes(ArgNo + FirstArgIndex).getSize(); +} + uint64_t AttributeList::getDereferenceableBytes(unsigned Index) const { return getAttributes(Index).getDereferenceableBytes(); } @@ -1334,14 +1370,15 @@ void AttrBuilder::clear() { Attrs.reset(); TargetDepAttrs.clear(); - Alignment = StackAlignment = DerefBytes = DerefOrNullBytes = 0; + Alignment = StackAlignment = Size = DerefBytes = DerefOrNullBytes = 0; AllocSizeArgs = 0; } AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) { assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!"); assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment && - Val != Attribute::Dereferenceable && Val != Attribute::AllocSize && + Val != Attribute::Size && Val != Attribute::Dereferenceable && + Val != Attribute::AllocSize && "Adding integer attribute without adding a value!"); Attrs[Val] = true; return *this; @@ -1360,6 +1397,8 @@ Alignment = Attr.getAlignment(); else if (Kind == Attribute::StackAlignment) StackAlignment = Attr.getStackAlignment(); + else if (Kind == Attribute::Size) + Size = Attr.getSize(); else if (Kind == Attribute::Dereferenceable) DerefBytes = Attr.getDereferenceableBytes(); else if (Kind == Attribute::DereferenceableOrNull) @@ -1382,6 +1421,8 @@ Alignment = 0; else if (Val == Attribute::StackAlignment) StackAlignment = 0; + else if (Val == Attribute::Size) + Size = 0; else if (Val == Attribute::Dereferenceable) DerefBytes = 0; else if (Val == Attribute::DereferenceableOrNull) @@ -1431,6 +1472,14 @@ return *this; } +AttrBuilder &AttrBuilder::addSizeAttr(unsigned Size) { + if (Size == 0) return *this; + + Attrs[Attribute::Size] = true; + this->Size = Size; + return *this; +} + AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) { if (Bytes == 0) return *this; Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -113,6 +113,11 @@ return getParent()->getParamAlignment(getArgNo()); } +unsigned Argument::getParamSize() const { + assert(getType()->isPointerTy() && "Only pointers have sizes"); + return getParent()->getParamSize(getArgNo()); +} + uint64_t Argument::getDereferenceableBytes() const { assert(getType()->isPointerTy() && "Only pointers have dereferenceable bytes"); Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1635,6 +1635,10 @@ AttributeSet::get(Context, IncompatibleAttrs).getAsString(), V); + if (Attrs.hasAttribute(Attribute::Size)) + Assert(Attrs.hasAttribute(Attribute::ByVal), + "Attribute 'size' only valid with 'byval'!", V); + if (PointerType *PTy = dyn_cast(Ty)) { SmallPtrSet Visited; if (!PTy->getElementType()->isSized(&Visited)) { Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -775,6 +775,7 @@ case Attribute::ArgMemOnly: case Attribute::Builtin: case Attribute::ByVal: + case Attribute::Size: case Attribute::Convergent: case Attribute::Dereferenceable: case Attribute::DereferenceableOrNull: Index: llvm/test/Assembler/invalid-size.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/invalid-size.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: Attribute 'size' only valid with 'byval'! +declare void @bare_size(i8* size 4) Index: llvm/test/Assembler/invalid-size1.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/invalid-size1.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: error: expected function name +declare i8* size 4 @ret_size() Index: llvm/test/Assembler/invalid-size2.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/invalid-size2.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: error: invalid use of parameter-only attribute on a function +declare i8* @ret_size() size 4 Index: llvm/test/Assembler/size-param-attr.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/size-param-attr.ll @@ -0,0 +1,17 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +; CHECK: define void @foo(i32* byval align 4 size 4) +define void @foo(i32* byval size 4 align 4) { + ret void +} + +; CHECK: define void @bar(i32* byval align 4 size 8) +define void @bar(i32* byval size 8 align 4) { + ret void +} + +; CHECK: declare void @baz({ i32, i8 }* byval size 10) +declare void @baz({i32, i8}* byval size 10) + +; CHECK: declare void @big({ i32, i8 }* byval size 4294967295) +declare void @big({i32, i8}* byval size 4294967295) Index: llvm/test/CodeGen/AArch64/byval-size.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/byval-size.ll @@ -0,0 +1,62 @@ +; RUN: llc -mtriple=aarch64-linux-gnu %s -o - | FileCheck %s + +define i8 @byval_match(i8* byval size 1 align 1, i8* byval %ptr) { +; CHECK-LABEL: byval_match: +; CHECK: ldrb w0, [sp, #8] + %res = load i8, i8* %ptr + ret i8 %res +} + +define void @caller_match(i8* %p0, i8* %p1) { +; CHECK-LABEL: caller_match: +; CHECK: ldrb [[P1:w[0-9]+]], [x1] +; CHECK: strb [[P1]], [sp, #8] +; CHECK: ldrb [[P0:w[0-9]+]], [x0] +; CHECK: strb [[P0]], [sp] +; CHECK: bl byval_match + call i8 @byval_match(i8* byval size 1 align 1 %p0, i8* byval %p1) + ret void +} + +define i8 @byval_large(i8* byval size 24 align 8, i8* byval %ptr) { +; CHECK-LABEL: byval_large: +; CHECK: ldrb w0, [sp, #24] + %res = load i8, i8* %ptr + ret i8 %res +} + +define void @caller_large(i8* %p0, i8* %p1) { +; CHECK-LABEL: caller_large: +; CHECK: ldr [[P0HI:x[0-9]+]], [x0, #16] +; CHECK: ldr [[P0LO:q[0-9]+]], [x0] +; CHECK: str [[P0HI]], [sp, #16] +; CHECK: str [[P0LO]], [sp] +; CHECK: bl byval_large + call i8 @byval_large(i8* byval size 24 align 8 %p0, i8* byval %p1) + ret void +} + +; Slot too small for type, but the backend does what it's told anyway. +define i128 @byval_small(i128* byval size 4 align 1 %p0, i128* byval align 1 %p1) { +; CHECK-LABEL: byval_small: +; CHECK: ldp [[LHS_LO:x[0-9]+]], [[LHS_HI:x[0-9]+]], [sp] +; CHECK: ldp [[RHS_LO:x[0-9]+]], [[RHS_HI:x[0-9]+]], [sp, #8] +; CHECK: adds x0, [[LHS_LO]], [[RHS_LO]] +; CHECK: adcs x1, [[LHS_HI]], [[RHS_HI]] + + %lhs = load i128, i128* %p0 + %rhs = load i128, i128* %p1 + %sum = add i128 %lhs, %rhs + ret i128 %sum +} + +define void @caller_small(i128* %p0, i128* %p1) { +; CHECK-LABEL: caller_small: +; CHECK: ldr [[P1:q[0-9]+]], [x1] +; CHECK: stur [[P1]], [sp, #8] +; CHECK: ldr [[P0:w[0-9]+]], [x0] +; CHECK: str [[P0]], [sp] +; CHECK: bl byval_small + call i128 @byval_small(i128* byval size 4 align 1 %p0, i128* byval align 1 %p1) + ret void +}