diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h --- a/clang/include/clang/CodeGen/CGFunctionInfo.h +++ b/clang/include/clang/CodeGen/CGFunctionInfo.h @@ -94,12 +94,17 @@ llvm::Type *UnpaddedCoerceAndExpandType; // isCoerceAndExpand() }; union { - unsigned DirectOffset; // isDirect() || isExtend() - unsigned IndirectAlign; // isIndirect() + struct { + unsigned Offset; + unsigned Align; + } DirectAttr; // isDirect() || isExtend() + struct { + unsigned Align; + unsigned AddrSpace; + } IndirectAttr; // isIndirect() unsigned AllocaFieldIndex; // isInAlloca() }; Kind TheKind; - unsigned IndirectAddrSpace : 24; // isIndirect() bool PaddingInReg : 1; bool InAllocaSRet : 1; // isInAlloca() bool InAllocaIndirect : 1;// isInAlloca() @@ -126,19 +131,20 @@ public: ABIArgInfo(Kind K = Direct) - : TypeData(nullptr), PaddingType(nullptr), DirectOffset(0), TheKind(K), - IndirectAddrSpace(0), PaddingInReg(false), InAllocaSRet(false), + : TypeData(nullptr), PaddingType(nullptr), DirectAttr{0, 0}, TheKind(K), + PaddingInReg(false), InAllocaSRet(false), InAllocaIndirect(false), IndirectByVal(false), IndirectRealign(false), SRetAfterThis(false), InReg(false), CanBeFlattened(false), SignExt(false) {} static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0, llvm::Type *Padding = nullptr, - bool CanBeFlattened = true) { + bool CanBeFlattened = true, unsigned Align = 0) { auto AI = ABIArgInfo(Direct); AI.setCoerceToType(T); AI.setPaddingType(Padding); AI.setDirectOffset(Offset); + AI.setDirectAlign(Align); AI.setCanBeFlattened(CanBeFlattened); return AI; } @@ -154,6 +160,7 @@ AI.setCoerceToType(T); AI.setPaddingType(nullptr); AI.setDirectOffset(0); + AI.setDirectAlign(0); AI.setSignExt(true); return AI; } @@ -164,6 +171,7 @@ AI.setCoerceToType(T); AI.setPaddingType(nullptr); AI.setDirectOffset(0); + AI.setDirectAlign(0); AI.setSignExt(false); return AI; } @@ -299,11 +307,20 @@ // Direct/Extend accessors unsigned getDirectOffset() const { assert((isDirect() || isExtend()) && "Not a direct or extend kind"); - return DirectOffset; + return DirectAttr.Offset; } void setDirectOffset(unsigned Offset) { assert((isDirect() || isExtend()) && "Not a direct or extend kind"); - DirectOffset = Offset; + DirectAttr.Offset = Offset; + } + + unsigned getDirectAlign() const { + assert((isDirect() || isExtend()) && "Not a direct or extend kind"); + return DirectAttr.Align; + } + void setDirectAlign(unsigned Align) { + assert((isDirect() || isExtend()) && "Not a direct or extend kind"); + DirectAttr.Align = Align; } bool isSignExt() const { @@ -369,11 +386,11 @@ // Indirect accessors CharUnits getIndirectAlign() const { assert((isIndirect() || isIndirectAliased()) && "Invalid kind!"); - return CharUnits::fromQuantity(IndirectAlign); + return CharUnits::fromQuantity(IndirectAttr.Align); } void setIndirectAlign(CharUnits IA) { assert((isIndirect() || isIndirectAliased()) && "Invalid kind!"); - IndirectAlign = IA.getQuantity(); + IndirectAttr.Align = IA.getQuantity(); } bool getIndirectByVal() const { @@ -387,12 +404,12 @@ unsigned getIndirectAddrSpace() const { assert(isIndirectAliased() && "Invalid kind!"); - return IndirectAddrSpace; + return IndirectAttr.AddrSpace; } void setIndirectAddrSpace(unsigned AddrSpace) { assert(isIndirectAliased() && "Invalid kind!"); - IndirectAddrSpace = AddrSpace; + IndirectAttr.AddrSpace = AddrSpace; } bool getIndirectRealign() const { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2363,6 +2363,7 @@ Attrs.addAttribute(llvm::Attribute::Nest); else if (AI.getInReg()) Attrs.addAttribute(llvm::Attribute::InReg); + Attrs.addStackAlignmentAttr(llvm::MaybeAlign(AI.getDirectAlign())); break; case ABIArgInfo::Indirect: { diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -5690,8 +5690,19 @@ // In variadic functions on Windows, all composite types are treated alike, // no special handling of HFAs/HVAs. if (!IsWinVariadic && isHomogeneousAggregate(Ty, Base, Members)) { + if (Kind != AArch64ABIInfo::AAPCS) + return ABIArgInfo::getDirect( + llvm::ArrayType::get(CGT.ConvertType(QualType(Base, 0)), Members)); + + // For alignment adjusted HFAs, cap the argument alignment to 16, leave it + // default otherwise. + unsigned Align = + getContext().getTypeUnadjustedAlignInChars(Ty).getQuantity(); + unsigned BaseAlign = getContext().getTypeAlignInChars(Base).getQuantity(); + Align = (Align > BaseAlign && Align >= 16) ? 16 : 0; return ABIArgInfo::getDirect( - llvm::ArrayType::get(CGT.ConvertType(QualType(Base, 0)), Members)); + llvm::ArrayType::get(CGT.ConvertType(QualType(Base, 0)), Members), 0, + nullptr, true, Align); } // Aggregates <= 16 bytes are passed directly in registers or on the stack. diff --git a/clang/test/CodeGen/aarch64-args-hfa.c b/clang/test/CodeGen/aarch64-args-hfa.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-args-hfa.c @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -triple aarch64-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-AAPCS +// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DARWIN +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - -x c %s | FileCheck %s --check-prefixes=CHECK,CHECK-AAPCS + +typedef struct { + float v[2]; +} S0; + +// CHECK: define{{.*}} float @f0([2 x float] %h.coerce) +float f0(S0 h) { + return h.v[0]; +} + +// CHECK: define{{.*}} float @f0_call() +// CHECK: %call = call float @f0([2 x float] %1) +float f0_call() { + S0 h = {1.0f, 2.0f}; + return f0(h); +} +typedef struct { + double v[2]; +} S1; + +// CHECK: define{{.*}} double @f1([2 x double] %h.coerce) +double f1(S1 h) { + return h.v[0]; +} + +// CHECK: define{{.*}} double @f1_call() +// CHECK: %call = call double @f1([2 x double] %1 +double f1_call() { + S1 h = {1.0, 2.0}; + return f1(h); +} +typedef struct { + __attribute__((__aligned__(16))) double v[2]; +} S2; + +// CHECK-AAPCS: define{{.*}} double @f2([2 x double] alignstack(16) %h.coerce) +// CHECK-DARWIN: define{{.*}} double @f2([2 x double] %h.coerce) +double f2(S2 h) { + return h.v[0]; +} + +// CHECK: define{{.*}} double @f2_call() +// CHECK-AAPCS: %call = call double @f2([2 x double] alignstack(16) %1) +// CHECK-DARWIN: %call = call double @f2([2 x double] %1 +double f2_call() { + S2 h = {1.0, 2.0}; + return f2(h); +} + +typedef struct { + __attribute__((__aligned__(32))) double v[4]; +} S3; + +// CHECK-AAPCS: define{{.*}} double @f3([4 x double] alignstack(16) %h.coerce) +// CHECK-DARWIN: define{{.*}} double @f3([4 x double] %h.coerce) +double f3(S3 h) { + return h.v[0]; +} + +// CHECK: define{{.*}} double @f3_call() +// CHECK-AAPCS: %call = call double @f3([4 x double] alignstack(16) %1) +// CHECK-DARWIN: %call = call double @f3([4 x double] %1 +double f3_call() { + S3 h = {1.0, 2.0}; + return f3(h); +} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1300,6 +1300,15 @@ undefined. Note that this does not refer to padding introduced by the type's storage representation. +``alignstack()`` + This indicates the alignment that should be considered by the backend when + assigning this parameter to a stack slot during calling convention + lowering. The enforcement of the specified alignment is target-dependent, + as target-specific calling convention rules may override this value. This + attribute serves the purpose of carrying language specific alignment + information that is not mapped to base types in the backend (for example, + over-alignment specification through language attributes). + .. _gc: Garbage Collector Strategy Names diff --git a/llvm/include/llvm/CodeGen/TargetCallingConv.h b/llvm/include/llvm/CodeGen/TargetCallingConv.h --- a/llvm/include/llvm/CodeGen/TargetCallingConv.h +++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -44,7 +44,8 @@ unsigned IsHva : 1; ///< HVA field for unsigned IsHvaStart : 1; ///< HVA structure start unsigned IsSecArgPass : 1; ///< Second argument - unsigned ByValOrByRefAlign : 4; ///< Log 2 of byval/byref alignment + unsigned MemAlign : 4; ///< Log 2 of alignment when arg is passed in memory + ///< (including byval/byref) unsigned OrigAlign : 5; ///< Log 2 of original alignment unsigned IsInConsecutiveRegsLast : 1; unsigned IsInConsecutiveRegs : 1; @@ -55,18 +56,12 @@ unsigned PointerAddrSpace; ///< Address space of pointer argument - /// Set the alignment used by byref or byval parameters. - void setAlignImpl(Align A) { - ByValOrByRefAlign = encode(A); - assert(getNonZeroByValAlign() == A && "bitfield overflow"); - } - public: ArgFlagsTy() : IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsByRef(0), IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0), IsPreallocated(0), IsSplitEnd(0), IsSwiftSelf(0), IsSwiftError(0), IsCFGuardTarget(0), - IsHva(0), IsHvaStart(0), IsSecArgPass(0), ByValOrByRefAlign(0), + IsHva(0), IsHvaStart(0), IsSecArgPass(0), MemAlign(0), OrigAlign(0), IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0), IsCopyElisionCandidate(0), IsPointer(0), ByValOrByRefSize(0), PointerAddrSpace(0) { @@ -141,24 +136,26 @@ bool isPointer() const { return IsPointer; } void setPointer() { IsPointer = 1; } - Align getNonZeroByValAlign() const { - MaybeAlign A = decodeMaybeAlign(ByValOrByRefAlign); - assert(A && "ByValAlign must be defined"); - return *A; + Align getNonZeroMemAlign() const { + return decodeMaybeAlign(MemAlign).valueOrOne(); } - void setByValAlign(Align A) { - assert(isByVal() && !isByRef()); - setAlignImpl(A); + + void setMemAlign(Align A) { + MemAlign = encode(A); + assert(getNonZeroMemAlign() == A && "bitfield overflow"); } - void setByRefAlign(Align A) { - assert(!isByVal() && isByRef()); - setAlignImpl(A); + Align getNonZeroByValAlign() const { + assert(isByVal()); + MaybeAlign A = decodeMaybeAlign(MemAlign); + assert(A && "ByValAlign must be defined"); + return *A; } Align getNonZeroOrigAlign() const { return decodeMaybeAlign(OrigAlign).valueOrOne(); } + void setOrigAlign(Align A) { OrigAlign = encode(A); assert(getNonZeroOrigAlign() == A && "bitfield overflow"); diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -102,6 +102,8 @@ /// If this is a byval or inalloca argument, return its alignment. MaybeAlign getParamAlign() const; + MaybeAlign getParamStackAlign() const; + /// If this is a byval argument, return its type. Type *getParamByValType() const; diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -674,6 +674,9 @@ /// Return the alignment for the specified function parameter. MaybeAlign getParamAlignment(unsigned ArgNo) const; + /// Return the stack alignment for the specified function parameter. + MaybeAlign getParamStackAlignment(unsigned ArgNo) const; + /// Return the byval type for the specified function parameter. Type *getParamByValType(unsigned ArgNo) const; diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -483,6 +483,10 @@ return AttributeSets.getParamAlignment(ArgNo); } + MaybeAlign getParamStackAlign(unsigned ArgNo) const { + return AttributeSets.getParamStackAlignment(ArgNo); + } + /// Extract the byval type for a parameter. Type *getParamByValType(unsigned ArgNo) const { return AttributeSets.getParamByValType(ArgNo); diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1731,6 +1731,10 @@ return Attrs.getParamAlignment(ArgNo); } + MaybeAlign getParamStackAlign(unsigned ArgNo) const { + return Attrs.getParamStackAlignment(ArgNo); + } + /// Extract the byval type for a call or parameter. Type *getParamByValType(unsigned ArgNo) const { Type *Ty = Attrs.getParamByValType(ArgNo); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1715,6 +1715,13 @@ B.addAlignmentAttr(Alignment); continue; } + case lltok::kw_alignstack: { + unsigned Alignment; + if (parseOptionalStackAlignment(Alignment)) + return true; + B.addStackAlignmentAttr(Alignment); + continue; + } case lltok::kw_byval: { Type *Ty; if (parseRequiredTypeAttr(Ty, lltok::kw_byval)) @@ -1783,7 +1790,6 @@ case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break; case lltok::kw_immarg: B.addAttribute(Attribute::ImmArg); break; - case lltok::kw_alignstack: case lltok::kw_alwaysinline: case lltok::kw_argmemonly: case lltok::kw_builtin: diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -154,6 +154,7 @@ const AttributeList &Attrs = FuncInfo.getAttributes(); addArgFlagsFromAttributes(Flags, Attrs, OpIdx); + Align MemAlign; if (Flags.isByVal() || Flags.isInAlloca() || Flags.isPreallocated()) { Type *ElementTy = cast(Arg.Ty)->getElementType(); @@ -162,13 +163,18 @@ // 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. - Align FrameAlign; - if (auto ParamAlign = FuncInfo.getParamAlign(OpIdx - 1)) - FrameAlign = *ParamAlign; + if (auto ParamAlign = FuncInfo.getParamStackAlign(OpIdx - 1)) + MemAlign = *ParamAlign; + else if ((ParamAlign = FuncInfo.getParamAlign(OpIdx - 1))) + MemAlign = *ParamAlign; else - FrameAlign = Align(getTLI()->getByValTypeAlignment(ElementTy, DL)); - Flags.setByValAlign(FrameAlign); + MemAlign = Align(getTLI()->getByValTypeAlignment(ElementTy, DL)); + } else if (auto ParamAlign = FuncInfo.getParamStackAlign(OpIdx - 1)) { + MemAlign = *ParamAlign; + } else { + MemAlign = Align(DL.getABITypeAlign(Arg.Ty)); } + Flags.setMemAlign(MemAlign); Flags.setOrigAlign(DL.getABITypeAlign(Arg.Ty)); // Don't try to use the returned attribute if the argument is marked as diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1072,6 +1072,7 @@ // preallocated handling in the various CC lowering callbacks. Flags.setByVal(); } + MaybeAlign MemAlign = Arg.Alignment; if (Arg.IsByVal || Arg.IsInAlloca || Arg.IsPreallocated) { PointerType *Ty = cast(Arg.Ty); Type *ElementTy = Ty->getElementType(); @@ -1080,18 +1081,18 @@ // For ByVal, alignment should come from FE. BE will guess if this info // is not there, but there are cases it cannot get right. - MaybeAlign FrameAlign = Arg.Alignment; - if (!FrameAlign) - FrameAlign = Align(TLI.getByValTypeAlignment(ElementTy, DL)); + if (!MemAlign) + MemAlign = Align(TLI.getByValTypeAlignment(ElementTy, DL)); Flags.setByValSize(FrameSize); - Flags.setByValAlign(*FrameAlign); + } else if (!MemAlign) { + MemAlign = DL.getABITypeAlign(Arg.Ty); } + Flags.setMemAlign(*MemAlign); if (Arg.IsNest) Flags.setNest(); if (NeedsRegBlock) Flags.setInConsecutiveRegs(); Flags.setOrigAlign(DL.getABITypeAlign(Arg.Ty)); - CLI.OutVals.push_back(Arg.Val); CLI.OutFlags.push_back(Flags); } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -9425,6 +9425,7 @@ // for a type depending on the context. Give the target a chance to // specify the alignment it wants. const Align OriginalAlignment(getABIAlignmentForCallingConv(ArgTy, DL)); + Flags.setOrigAlign(OriginalAlignment); if (Args[i].Ty->isPointerTy()) { Flags.setPointer(); @@ -9478,6 +9479,7 @@ // in the various CC lowering callbacks. Flags.setByVal(); } + Align MemAlign; if (Args[i].IsByVal || Args[i].IsInAlloca || Args[i].IsPreallocated) { PointerType *Ty = cast(Args[i].Ty); Type *ElementTy = Ty->getElementType(); @@ -9487,18 +9489,20 @@ Flags.setByValSize(FrameSize); // info is not there but there are cases it cannot get right. - Align FrameAlign; if (auto MA = Args[i].Alignment) - FrameAlign = *MA; + MemAlign = *MA; else - FrameAlign = Align(getByValTypeAlignment(ElementTy, DL)); - Flags.setByValAlign(FrameAlign); + MemAlign = Align(getByValTypeAlignment(ElementTy, DL)); + } else if (auto MA = Args[i].Alignment) { + MemAlign = *MA; + } else { + MemAlign = OriginalAlignment; } + Flags.setMemAlign(MemAlign); if (Args[i].IsNest) Flags.setNest(); if (NeedsRegBlock) Flags.setInConsecutiveRegs(); - Flags.setOrigAlign(OriginalAlignment); MVT PartVT = getRegisterTypeForCallingConv(CLI.RetTy->getContext(), CLI.CallConv, VT); @@ -9960,11 +9964,6 @@ Type *ArgTy = VT.getTypeForEVT(*DAG.getContext()); ISD::ArgFlagsTy Flags; - // Certain targets (such as MIPS), may have a different ABI alignment - // for a type depending on the context. Give the target a chance to - // specify the alignment it wants. - const Align OriginalAlignment( - TLI->getABIAlignmentForCallingConv(ArgTy, DL)); if (Arg.getType()->isPointerTy()) { Flags.setPointer(); @@ -10017,6 +10016,14 @@ Flags.setByVal(); } + // Certain targets (such as MIPS), may have a different ABI alignment + // for a type depending on the context. Give the target a chance to + // specify the alignment it wants. + const Align OriginalAlignment( + TLI->getABIAlignmentForCallingConv(ArgTy, DL)); + Flags.setOrigAlign(OriginalAlignment); + + Align MemAlign; Type *ArgMemTy = nullptr; if (Flags.isByVal() || Flags.isInAlloca() || Flags.isPreallocated() || Flags.isByRef()) { @@ -10028,24 +10035,27 @@ // For in-memory arguments, 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. - MaybeAlign MemAlign = Arg.getParamAlign(); - if (!MemAlign) + if (auto ParamAlign = Arg.getParamStackAlign()) + MemAlign = *ParamAlign; + else if ((ParamAlign = Arg.getParamAlign())) + MemAlign = *ParamAlign; + else MemAlign = Align(TLI->getByValTypeAlignment(ArgMemTy, DL)); - - if (Flags.isByRef()) { + if (Flags.isByRef()) Flags.setByRefSize(MemSize); - Flags.setByRefAlign(*MemAlign); - } else { + else Flags.setByValSize(MemSize); - Flags.setByValAlign(*MemAlign); - } + } else if (auto ParamAlign = Arg.getParamStackAlign()) { + MemAlign = *ParamAlign; + } else { + MemAlign = OriginalAlignment; } + Flags.setMemAlign(MemAlign); if (Arg.hasAttribute(Attribute::Nest)) Flags.setNest(); if (NeedsRegBlock) Flags.setInConsecutiveRegs(); - Flags.setOrigAlign(OriginalAlignment); if (ArgCopyElisionCandidates.count(&Arg)) Flags.setCopyElisionCandidate(); if (Arg.hasAttribute(Attribute::Returned)) diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -115,10 +115,13 @@ IsReturned = Call->paramHasAttr(ArgIdx, Attribute::Returned); IsSwiftSelf = Call->paramHasAttr(ArgIdx, Attribute::SwiftSelf); IsSwiftError = Call->paramHasAttr(ArgIdx, Attribute::SwiftError); - Alignment = Call->getParamAlign(ArgIdx); + Alignment = Call->getParamStackAlign(ArgIdx); ByValType = nullptr; - if (IsByVal) + if (IsByVal) { ByValType = Call->getParamByValType(ArgIdx); + if (!Alignment) + Alignment = Call->getParamAlign(ArgIdx); + } PreallocatedType = nullptr; if (IsPreallocated) PreallocatedType = Call->getParamPreallocatedType(ArgIdx); diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1583,6 +1583,10 @@ return getAttributes(ArgNo + FirstArgIndex).getAlignment(); } +MaybeAlign AttributeList::getParamStackAlignment(unsigned ArgNo) const { + return getAttributes(ArgNo + FirstArgIndex).getStackAlignment(); +} + Type *AttributeList::getParamByValType(unsigned Index) const { return getAttributes(Index+FirstArgIndex).getByValType(); } diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -200,6 +200,10 @@ return getParent()->getParamAlign(getArgNo()); } +MaybeAlign Argument::getParamStackAlign() const { + return getParent()->getParamStackAlign(getArgNo()); +} + Type *Argument::getParamByValType() const { assert(getType()->isPointerTy() && "Only pointers have byval types"); return getParent()->getParamByValType(getArgNo()); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1648,7 +1648,6 @@ case Attribute::NoImplicitFloat: case Attribute::Naked: case Attribute::InlineHint: - case Attribute::StackAlignment: case Attribute::UWTable: case Attribute::VScaleRange: case Attribute::NonLazyBind: @@ -1691,7 +1690,7 @@ static bool isFuncOrArgAttr(Attribute::AttrKind Kind) { return Kind == Attribute::ReadOnly || Kind == Attribute::WriteOnly || Kind == Attribute::ReadNone || Kind == Attribute::NoFree || - Kind == Attribute::Preallocated; + Kind == Attribute::Preallocated || Kind == Attribute::StackAlignment; } void Verifier::verifyAttributeTypes(AttributeSet Attrs, bool IsFunction, @@ -3313,7 +3312,7 @@ static const Attribute::AttrKind ABIAttrs[] = { Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError, - Attribute::Preallocated, Attribute::ByRef}; + Attribute::Preallocated, Attribute::ByRef, Attribute::StackAlignment}; AttrBuilder Copy; for (auto AK : ABIAttrs) { if (Attrs.hasParamAttribute(I, AK)) diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp b/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp @@ -88,13 +88,8 @@ } unsigned Size = LocVT.getSizeInBits() / 8; - const Align StackAlign = - State.getMachineFunction().getDataLayout().getStackAlignment(); - const Align OrigAlign = ArgFlags.getNonZeroOrigAlign(); - const Align Alignment = std::min(OrigAlign, StackAlign); - for (auto &It : PendingMembers) { - It.convertToMem(State.AllocateStack(Size, std::max(Alignment, SlotAlign))); + It.convertToMem(State.AllocateStack(Size, SlotAlign)); State.addLoc(It); SlotAlign = Align(1); } @@ -197,7 +192,12 @@ State.AllocateReg(Reg); } - const Align SlotAlign = Subtarget.isTargetDarwin() ? Align(1) : Align(8); + const Align StackAlign = + State.getMachineFunction().getDataLayout().getStackAlignment(); + const Align MemAlign = ArgFlags.getNonZeroMemAlign(); + Align SlotAlign = std::min(MemAlign, StackAlign); + if (!Subtarget.isTargetDarwin()) + SlotAlign = std::max(SlotAlign, Align(8)); return finishStackBlock(PendingMembers, LocVT, ArgFlags, State, SlotAlign); } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -550,6 +550,8 @@ ; CHECK: declare void @f.param.dereferenceable(i8* dereferenceable(4)) declare void @f.param.dereferenceable_or_null(i8* dereferenceable_or_null(4)) ; CHECK: declare void @f.param.dereferenceable_or_null(i8* dereferenceable_or_null(4)) +declare void @f.param.stack_align([2 x double] alignstack(16)) +; CHECK: declare void @f.param.stack_align([2 x double] alignstack(16)) ; Functions -- unnamed_addr and local_unnamed_addr declare void @f.unnamed_addr() unnamed_addr diff --git a/llvm/test/CodeGen/AArch64/arm64-abi-hfa-args.ll b/llvm/test/CodeGen/AArch64/arm64-abi-hfa-args.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64-abi-hfa-args.ll @@ -0,0 +1,33 @@ +; RUN: llc < %s -mtriple=arm64-none-eabi | FileCheck %s + +; Over-aligned HFA argument placed on register - one element per register +define double @test_hfa_align_arg_reg([2 x double] alignstack(16) %h.coerce) local_unnamed_addr #0 { +entry: +; CHECK-LABEL: test_hfa_align_arg_reg: +; CHECK-NOT: mov +; CHECK-NOT: ld +; CHECK: ret + %h.coerce.fca.0.extract = extractvalue [2 x double] %h.coerce, 0 + ret double %h.coerce.fca.0.extract +} + +; Call with over-aligned HFA argument placed on register - one element per register +define double @test_hfa_align_call_reg() local_unnamed_addr #0 { +entry: +; CHECK-LABEL: test_hfa_align_call_reg: +; CHECK-DAG: fmov d0, #1.00000000 +; CHECK-DAG: fmov d1, #2.00000000 +; CHECK: bl test_hfa_align_arg_reg + %call = call double @test_hfa_align_arg_reg([2 x double] alignstack(16) [double 1.000000e+00, double 2.000000e+00]) + ret double %call +} + +; Over-aligned HFA argument placed on stack - stack round up to alignment +define double @test_hfa_align_arg_stack(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %f, [2 x double] alignstack(16) %h.coerce) local_unnamed_addr #0 { +entry: +; CHECK-LABEL: test_hfa_align_arg_stack: +; CHECK: ldr d0, [sp, #16] +; CHECK-NEXT: ret + %h.coerce.fca.0.extract = extractvalue [2 x double] %h.coerce, 0 + ret double %h.coerce.fca.0.extract +}