1//===- AArch64.cpp - AArch64 ABI Implementation ---------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/ABI/FunctionInfo.h"
10#include "llvm/ABI/TargetInfo.h"
11#include "llvm/ABI/Types.h"
12#include "llvm/Support/Casting.h"
13#include "llvm/Support/ErrorHandling.h"
14#include "llvm/Support/MathExtras.h"
15#include "llvm/Support/WithColor.h"
16#include <algorithm>
17#include <cstdint>
18
19namespace llvm {
20namespace abi {
21
22class AArch64TargetInfo : public TargetInfo {
23public:
24 AArch64TargetInfo(TypeBuilder &TB, const AArch64ABIOptions &Opts)
25 : TargetInfo(TB), Opts(Opts) {}
26
27 const ABICompatInfo &getABICompatInfo() const override {
28 return Opts.CompatInfo;
29 }
30
31 void computeInfo(FunctionInfo &FI) const override {
32 if (!maybeCommonClassifyReturnType(FI))
33 FI.getReturnInfo() =
34 classifyReturnType(RetTy: FI.getReturnType(), IsVariadicFn: FI.isVariadic());
35
36 unsigned ArgNo = 0;
37 unsigned NSRN = 0, NPRN = 0;
38 for (auto &I : FI.arguments()) {
39 const bool IsNamedArg =
40 !FI.isVariadic() || ArgNo < FI.getNumRequiredArgs();
41 ++ArgNo;
42 I.Info = classifyArgumentType(Ty: I.ABIType, IsVariadicFn: FI.isVariadic(), IsNamedArg,
43 CallingConvention: FI.getCallingConvention(), NSRN, NPRN);
44 }
45 }
46
47private:
48 AArch64ABIOptions Opts;
49
50 ArgInfo classifyReturnType(const Type *RetTy, bool IsVariadicFn) const;
51 ArgInfo classifyArgumentType(const Type *Ty, bool IsVariadicFn,
52 bool IsNamedArg, unsigned CallingConvention,
53 unsigned &NSRN, unsigned &NPRN) const;
54
55 bool isDarwinPCS() const { return Opts.Kind == AArch64ABIKind::DarwinPCS; }
56 bool isSoftFloat() const { return Opts.Kind == AArch64ABIKind::AAPCSSoft; }
57 bool passAsAggregateType(const Type *Ty) const;
58
59 bool isHomogeneousAggregateBaseType(const Type *Ty) const override;
60 bool isHomogeneousAggregateSmallEnough(const Type *Base,
61 uint64_t Members) const override;
62 bool isZeroLengthBitfieldPermittedInHomogeneousAggregate() const override;
63 bool isPermittedToBeHomogeneousAggregate(const RecordType *RT) const override;
64};
65
66std::unique_ptr<TargetInfo>
67createAArch64TargetInfo(TypeBuilder &TB, const AArch64ABIOptions &Opts) {
68 return std::make_unique<AArch64TargetInfo>(args&: TB, args: Opts);
69}
70
71static void reportNYI(StringRef Feature) {
72 WithColor::warning()
73 << Feature
74 << " is not yet implemented for AArch64 in the LLVM ABI library.\n";
75}
76
77ArgInfo AArch64TargetInfo::classifyReturnType(const Type *RetTy,
78 bool IsVariadicFn) const {
79 if (RetTy->isVoid())
80 return ArgInfo::getIgnore();
81
82 if (RetTy->isVector()) {
83 reportNYI(Feature: "Vector return type handling");
84 return ArgInfo::getIgnore();
85 }
86
87 if (!passAsAggregateType(Ty: RetTy)) {
88 if (const auto *IntTy = dyn_cast<IntegerType>(Val: RetTy)) {
89 if (IntTy->isBitInt())
90 if (RetTy->getSizeInBits().getFixedValue() > 128)
91 return getNaturalAlignIndirect(Ty: RetTy);
92
93 if (isPromotableInteger(IT: IntTy) && isDarwinPCS())
94 return ArgInfo::getExtend(T: IntTy);
95 }
96
97 // Everything not handled above is returned directly.
98 return ArgInfo::getDirect();
99 }
100
101 // TODO: Handle empty records and zero-size non-SVE types.
102
103 const Type *Base = nullptr;
104 uint64_t Members = 0;
105 if (isHomogeneousAggregate(Ty: RetTy, Base, Members) &&
106 !(Opts.IsILP32 && IsVariadicFn)) {
107 // Homogeneous Floating-point Aggregates (HFAs) are returned directly.
108 return ArgInfo::getDirect();
109 }
110
111 reportNYI(Feature: "Aggregate return type handling");
112 return ArgInfo::getIgnore();
113}
114
115ArgInfo AArch64TargetInfo::classifyArgumentType(
116 const Type *Ty, bool IsVariadicFn, bool IsNamedArg,
117 unsigned CallingConvention, unsigned &NSRN, unsigned &NPRN) const {
118 Ty = useFirstFieldIfTransparentUnion(Ty);
119
120 if (Ty->isVector()) {
121 reportNYI(Feature: "Vector argument type handling");
122 return ArgInfo::getIgnore();
123 }
124
125 if (!passAsAggregateType(Ty)) {
126 if (const auto *IntTy = dyn_cast<IntegerType>(Val: Ty)) {
127 if (IntTy->isBitInt())
128 if (Ty->getSizeInBits().getFixedValue() > 128)
129 return getNaturalAlignIndirect(Ty, /*ByVal=*/false);
130
131 if (isPromotableInteger(IT: IntTy) && isDarwinPCS())
132 return ArgInfo::getExtend(T: IntTy);
133 }
134
135 // TODO: Legal vector types will update NSRN or NPRN.
136
137 if (Ty->isFloat())
138 NSRN = std::min(a: NSRN + 1, b: 8u);
139
140 // Everything not handled above is returned directly.
141 return ArgInfo::getDirect();
142 }
143
144 // Structures with either a non-trivial destructor or a non-trivial
145 // copy constructor are always indirect.
146 if (auto RecordRAA = getRecordArgABI(Ty)) {
147 return getNaturalAlignIndirect(Ty, ByVal: RecordRAA ==
148 RecordArgABI::RAA_DirectInMemory);
149 }
150
151 TypeSize TySize = Ty->getSizeInBits();
152 uint64_t Size = TySize.isFixed() ? TySize.getFixedValue() : 0;
153 const auto *RT = dyn_cast<RecordType>(Val: Ty);
154 if (!Ty->isSVESizelessType() && ((RT && RT->isEmpty()) || Size == 0)) {
155 reportNYI(Feature: "Empty record argument handling");
156 return ArgInfo::getIgnore();
157 }
158
159 // Homogeneous Floating-point Aggregates (HFAs) need to be expanded.
160 const Type *Base = nullptr;
161 uint64_t Members = 0;
162 bool IsWin64 = Opts.Kind == AArch64ABIKind::Win64 ||
163 CallingConvention == llvm::CallingConv::Win64;
164 bool IsWinVariadic = IsWin64 && IsVariadicFn;
165 // In variadic functions on Windows, all composite types are treated alike,
166 // no special handling of HFAs/HVAs.
167 if (!IsWinVariadic && isHomogeneousAggregate(Ty, Base, Members)) {
168 NSRN = std::min(a: NSRN + Members, b: uint64_t(8));
169 uint64_t BaseAllocSizeInBits = Base->getTypeAllocSize().getFixedValue() * 8;
170 const Type *CoerceTy =
171 TB.getArrayType(ElementType: Base, NumElements: Members, SizeInBits: Members * BaseAllocSizeInBits);
172 if (Opts.Kind != AArch64ABIKind::AAPCS)
173 return ArgInfo::getDirect(T: CoerceTy);
174
175 // For HFAs/HVAs, cap the argument alignment to 16, otherwise
176 // set it to 8 according to the AAPCS64 document.
177 unsigned TyAlign = Ty->getUnadjustedAlignment().value();
178 TyAlign = (TyAlign >= 16) ? 16 : 8;
179 return ArgInfo::getDirect(T: CoerceTy, /*Offset=*/0, Align: llvm::Align(TyAlign));
180 }
181
182 reportNYI(Feature: "Aggregate argument type handling");
183 return ArgInfo::getIgnore();
184}
185
186bool AArch64TargetInfo::passAsAggregateType(const Type *Ty) const {
187 // TODO: Handle SVE types. For now, they don't get through the type mapper.
188 return isAggregateTypeForABI(Ty);
189}
190
191bool AArch64TargetInfo::isHomogeneousAggregateBaseType(const Type *Ty) const {
192 // Soft-float ABI: no types are homogeneous aggregates.
193 if (isSoftFloat())
194 return false;
195
196 // Homogeneous aggregates for AAPCS64 must have base types of a floating
197 // point type or a short-vector type.
198 if (Ty->isFloat())
199 return true;
200
201 if (const auto *VT = dyn_cast<VectorType>(Val: Ty)) {
202 if (VT->isScalable() || VT->isSVEData() || VT->isSVEPredicate())
203 return false;
204
205 // Clang's getTypeSize for non-power-of-2 vectors rounds the width up to
206 // the next power-of-2 alignment (e.g. 3 x float is 96 bits of payload but
207 // 128 bits of ABI size), so those vectors are short-vector HVA bases.
208 uint64_t VecSize =
209 bit_ceil(Value: std::max<uint64_t>(a: 8, b: VT->getSizeInBits().getFixedValue()));
210 if (VecSize == 64 || VecSize == 128)
211 return true;
212 }
213 return false;
214}
215
216bool AArch64TargetInfo::isHomogeneousAggregateSmallEnough(
217 const Type * /*Base*/, uint64_t Members) const {
218 return Members <= 4;
219}
220
221bool AArch64TargetInfo::isZeroLengthBitfieldPermittedInHomogeneousAggregate()
222 const {
223 // AAPCS64 applies homogeneity to the output of the data layout decision, so
224 // zero-length bitfields do not affect homogeneity.
225 return true;
226}
227
228bool AArch64TargetInfo::isPermittedToBeHomogeneousAggregate(
229 const RecordType *RT) const {
230 if (Opts.IsMicrosoftCXXABI && RT->isCXXRecord()) {
231 // This won't always return false, but we don't have enough information to
232 // perform the full check correctly yet.
233 reportNYI(Feature: "MicrosoftCXXABI homogeneous record classification");
234 return false;
235 }
236
237 return true;
238}
239
240} // namespace abi
241} // namespace llvm
242