/src/llvm-project/clang/lib/CodeGen/ABIInfoImpl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ABIInfoImpl.cpp ----------------------------------------------------===// |
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 "ABIInfoImpl.h" |
10 | | |
11 | | using namespace clang; |
12 | | using namespace clang::CodeGen; |
13 | | |
14 | | // Pin the vtable to this file. |
15 | 0 | DefaultABIInfo::~DefaultABIInfo() = default; |
16 | | |
17 | 0 | ABIArgInfo DefaultABIInfo::classifyArgumentType(QualType Ty) const { |
18 | 0 | Ty = useFirstFieldIfTransparentUnion(Ty); |
19 | |
|
20 | 0 | if (isAggregateTypeForABI(Ty)) { |
21 | | // Records with non-trivial destructors/copy-constructors should not be |
22 | | // passed by value. |
23 | 0 | if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) |
24 | 0 | return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory); |
25 | | |
26 | 0 | return getNaturalAlignIndirect(Ty); |
27 | 0 | } |
28 | | |
29 | | // Treat an enum type as its underlying type. |
30 | 0 | if (const EnumType *EnumTy = Ty->getAs<EnumType>()) |
31 | 0 | Ty = EnumTy->getDecl()->getIntegerType(); |
32 | |
|
33 | 0 | ASTContext &Context = getContext(); |
34 | 0 | if (const auto *EIT = Ty->getAs<BitIntType>()) |
35 | 0 | if (EIT->getNumBits() > |
36 | 0 | Context.getTypeSize(Context.getTargetInfo().hasInt128Type() |
37 | 0 | ? Context.Int128Ty |
38 | 0 | : Context.LongLongTy)) |
39 | 0 | return getNaturalAlignIndirect(Ty); |
40 | | |
41 | 0 | return (isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty) |
42 | 0 | : ABIArgInfo::getDirect()); |
43 | 0 | } |
44 | | |
45 | 0 | ABIArgInfo DefaultABIInfo::classifyReturnType(QualType RetTy) const { |
46 | 0 | if (RetTy->isVoidType()) |
47 | 0 | return ABIArgInfo::getIgnore(); |
48 | | |
49 | 0 | if (isAggregateTypeForABI(RetTy)) |
50 | 0 | return getNaturalAlignIndirect(RetTy); |
51 | | |
52 | | // Treat an enum type as its underlying type. |
53 | 0 | if (const EnumType *EnumTy = RetTy->getAs<EnumType>()) |
54 | 0 | RetTy = EnumTy->getDecl()->getIntegerType(); |
55 | |
|
56 | 0 | if (const auto *EIT = RetTy->getAs<BitIntType>()) |
57 | 0 | if (EIT->getNumBits() > |
58 | 0 | getContext().getTypeSize(getContext().getTargetInfo().hasInt128Type() |
59 | 0 | ? getContext().Int128Ty |
60 | 0 | : getContext().LongLongTy)) |
61 | 0 | return getNaturalAlignIndirect(RetTy); |
62 | | |
63 | 0 | return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy) |
64 | 0 | : ABIArgInfo::getDirect()); |
65 | 0 | } |
66 | | |
67 | 0 | void DefaultABIInfo::computeInfo(CGFunctionInfo &FI) const { |
68 | 0 | if (!getCXXABI().classifyReturnType(FI)) |
69 | 0 | FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); |
70 | 0 | for (auto &I : FI.arguments()) |
71 | 0 | I.info = classifyArgumentType(I.type); |
72 | 0 | } |
73 | | |
74 | | Address DefaultABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
75 | 0 | QualType Ty) const { |
76 | 0 | return EmitVAArgInstr(CGF, VAListAddr, Ty, classifyArgumentType(Ty)); |
77 | 0 | } |
78 | | |
79 | | ABIArgInfo CodeGen::coerceToIntArray(QualType Ty, ASTContext &Context, |
80 | 0 | llvm::LLVMContext &LLVMContext) { |
81 | | // Alignment and Size are measured in bits. |
82 | 0 | const uint64_t Size = Context.getTypeSize(Ty); |
83 | 0 | const uint64_t Alignment = Context.getTypeAlign(Ty); |
84 | 0 | llvm::Type *IntType = llvm::Type::getIntNTy(LLVMContext, Alignment); |
85 | 0 | const uint64_t NumElements = (Size + Alignment - 1) / Alignment; |
86 | 0 | return ABIArgInfo::getDirect(llvm::ArrayType::get(IntType, NumElements)); |
87 | 0 | } |
88 | | |
89 | | void CodeGen::AssignToArrayRange(CodeGen::CGBuilderTy &Builder, |
90 | | llvm::Value *Array, llvm::Value *Value, |
91 | 0 | unsigned FirstIndex, unsigned LastIndex) { |
92 | | // Alternatively, we could emit this as a loop in the source. |
93 | 0 | for (unsigned I = FirstIndex; I <= LastIndex; ++I) { |
94 | 0 | llvm::Value *Cell = |
95 | 0 | Builder.CreateConstInBoundsGEP1_32(Builder.getInt8Ty(), Array, I); |
96 | 0 | Builder.CreateAlignedStore(Value, Cell, CharUnits::One()); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | bool CodeGen::isAggregateTypeForABI(QualType T) { |
101 | 0 | return !CodeGenFunction::hasScalarEvaluationKind(T) || |
102 | 0 | T->isMemberFunctionPointerType(); |
103 | 0 | } |
104 | | |
105 | 0 | llvm::Type *CodeGen::getVAListElementType(CodeGenFunction &CGF) { |
106 | 0 | return CGF.ConvertTypeForMem( |
107 | 0 | CGF.getContext().getBuiltinVaListType()->getPointeeType()); |
108 | 0 | } |
109 | | |
110 | | CGCXXABI::RecordArgABI CodeGen::getRecordArgABI(const RecordType *RT, |
111 | 0 | CGCXXABI &CXXABI) { |
112 | 0 | const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); |
113 | 0 | if (!RD) { |
114 | 0 | if (!RT->getDecl()->canPassInRegisters()) |
115 | 0 | return CGCXXABI::RAA_Indirect; |
116 | 0 | return CGCXXABI::RAA_Default; |
117 | 0 | } |
118 | 0 | return CXXABI.getRecordArgABI(RD); |
119 | 0 | } |
120 | | |
121 | 0 | CGCXXABI::RecordArgABI CodeGen::getRecordArgABI(QualType T, CGCXXABI &CXXABI) { |
122 | 0 | const RecordType *RT = T->getAs<RecordType>(); |
123 | 0 | if (!RT) |
124 | 0 | return CGCXXABI::RAA_Default; |
125 | 0 | return getRecordArgABI(RT, CXXABI); |
126 | 0 | } |
127 | | |
128 | | bool CodeGen::classifyReturnType(const CGCXXABI &CXXABI, CGFunctionInfo &FI, |
129 | 0 | const ABIInfo &Info) { |
130 | 0 | QualType Ty = FI.getReturnType(); |
131 | |
|
132 | 0 | if (const auto *RT = Ty->getAs<RecordType>()) |
133 | 0 | if (!isa<CXXRecordDecl>(RT->getDecl()) && |
134 | 0 | !RT->getDecl()->canPassInRegisters()) { |
135 | 0 | FI.getReturnInfo() = Info.getNaturalAlignIndirect(Ty); |
136 | 0 | return true; |
137 | 0 | } |
138 | | |
139 | 0 | return CXXABI.classifyReturnType(FI); |
140 | 0 | } |
141 | | |
142 | 0 | QualType CodeGen::useFirstFieldIfTransparentUnion(QualType Ty) { |
143 | 0 | if (const RecordType *UT = Ty->getAsUnionType()) { |
144 | 0 | const RecordDecl *UD = UT->getDecl(); |
145 | 0 | if (UD->hasAttr<TransparentUnionAttr>()) { |
146 | 0 | assert(!UD->field_empty() && "sema created an empty transparent union"); |
147 | 0 | return UD->field_begin()->getType(); |
148 | 0 | } |
149 | 0 | } |
150 | 0 | return Ty; |
151 | 0 | } |
152 | | |
153 | | llvm::Value *CodeGen::emitRoundPointerUpToAlignment(CodeGenFunction &CGF, |
154 | | llvm::Value *Ptr, |
155 | 0 | CharUnits Align) { |
156 | | // OverflowArgArea = (OverflowArgArea + Align - 1) & -Align; |
157 | 0 | llvm::Value *RoundUp = CGF.Builder.CreateConstInBoundsGEP1_32( |
158 | 0 | CGF.Builder.getInt8Ty(), Ptr, Align.getQuantity() - 1); |
159 | 0 | return CGF.Builder.CreateIntrinsic( |
160 | 0 | llvm::Intrinsic::ptrmask, {CGF.AllocaInt8PtrTy, CGF.IntPtrTy}, |
161 | 0 | {RoundUp, llvm::ConstantInt::get(CGF.IntPtrTy, -Align.getQuantity())}, |
162 | 0 | nullptr, Ptr->getName() + ".aligned"); |
163 | 0 | } |
164 | | |
165 | | Address |
166 | | CodeGen::emitVoidPtrDirectVAArg(CodeGenFunction &CGF, Address VAListAddr, |
167 | | llvm::Type *DirectTy, CharUnits DirectSize, |
168 | | CharUnits DirectAlign, CharUnits SlotSize, |
169 | 0 | bool AllowHigherAlign, bool ForceRightAdjust) { |
170 | | // Cast the element type to i8* if necessary. Some platforms define |
171 | | // va_list as a struct containing an i8* instead of just an i8*. |
172 | 0 | if (VAListAddr.getElementType() != CGF.Int8PtrTy) |
173 | 0 | VAListAddr = VAListAddr.withElementType(CGF.Int8PtrTy); |
174 | |
|
175 | 0 | llvm::Value *Ptr = CGF.Builder.CreateLoad(VAListAddr, "argp.cur"); |
176 | | |
177 | | // If the CC aligns values higher than the slot size, do so if needed. |
178 | 0 | Address Addr = Address::invalid(); |
179 | 0 | if (AllowHigherAlign && DirectAlign > SlotSize) { |
180 | 0 | Addr = Address(emitRoundPointerUpToAlignment(CGF, Ptr, DirectAlign), |
181 | 0 | CGF.Int8Ty, DirectAlign); |
182 | 0 | } else { |
183 | 0 | Addr = Address(Ptr, CGF.Int8Ty, SlotSize); |
184 | 0 | } |
185 | | |
186 | | // Advance the pointer past the argument, then store that back. |
187 | 0 | CharUnits FullDirectSize = DirectSize.alignTo(SlotSize); |
188 | 0 | Address NextPtr = |
189 | 0 | CGF.Builder.CreateConstInBoundsByteGEP(Addr, FullDirectSize, "argp.next"); |
190 | 0 | CGF.Builder.CreateStore(NextPtr.getPointer(), VAListAddr); |
191 | | |
192 | | // If the argument is smaller than a slot, and this is a big-endian |
193 | | // target, the argument will be right-adjusted in its slot. |
194 | 0 | if (DirectSize < SlotSize && CGF.CGM.getDataLayout().isBigEndian() && |
195 | 0 | (!DirectTy->isStructTy() || ForceRightAdjust)) { |
196 | 0 | Addr = CGF.Builder.CreateConstInBoundsByteGEP(Addr, SlotSize - DirectSize); |
197 | 0 | } |
198 | |
|
199 | 0 | return Addr.withElementType(DirectTy); |
200 | 0 | } |
201 | | |
202 | | Address CodeGen::emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr, |
203 | | QualType ValueTy, bool IsIndirect, |
204 | | TypeInfoChars ValueInfo, |
205 | | CharUnits SlotSizeAndAlign, |
206 | | bool AllowHigherAlign, |
207 | 0 | bool ForceRightAdjust) { |
208 | | // The size and alignment of the value that was passed directly. |
209 | 0 | CharUnits DirectSize, DirectAlign; |
210 | 0 | if (IsIndirect) { |
211 | 0 | DirectSize = CGF.getPointerSize(); |
212 | 0 | DirectAlign = CGF.getPointerAlign(); |
213 | 0 | } else { |
214 | 0 | DirectSize = ValueInfo.Width; |
215 | 0 | DirectAlign = ValueInfo.Align; |
216 | 0 | } |
217 | | |
218 | | // Cast the address we've calculated to the right type. |
219 | 0 | llvm::Type *DirectTy = CGF.ConvertTypeForMem(ValueTy), *ElementTy = DirectTy; |
220 | 0 | if (IsIndirect) { |
221 | 0 | unsigned AllocaAS = CGF.CGM.getDataLayout().getAllocaAddrSpace(); |
222 | 0 | DirectTy = llvm::PointerType::get(CGF.getLLVMContext(), AllocaAS); |
223 | 0 | } |
224 | |
|
225 | 0 | Address Addr = emitVoidPtrDirectVAArg(CGF, VAListAddr, DirectTy, DirectSize, |
226 | 0 | DirectAlign, SlotSizeAndAlign, |
227 | 0 | AllowHigherAlign, ForceRightAdjust); |
228 | |
|
229 | 0 | if (IsIndirect) { |
230 | 0 | Addr = Address(CGF.Builder.CreateLoad(Addr), ElementTy, ValueInfo.Align); |
231 | 0 | } |
232 | |
|
233 | 0 | return Addr; |
234 | 0 | } |
235 | | |
236 | | Address CodeGen::emitMergePHI(CodeGenFunction &CGF, Address Addr1, |
237 | | llvm::BasicBlock *Block1, Address Addr2, |
238 | | llvm::BasicBlock *Block2, |
239 | 0 | const llvm::Twine &Name) { |
240 | 0 | assert(Addr1.getType() == Addr2.getType()); |
241 | 0 | llvm::PHINode *PHI = CGF.Builder.CreatePHI(Addr1.getType(), 2, Name); |
242 | 0 | PHI->addIncoming(Addr1.getPointer(), Block1); |
243 | 0 | PHI->addIncoming(Addr2.getPointer(), Block2); |
244 | 0 | CharUnits Align = std::min(Addr1.getAlignment(), Addr2.getAlignment()); |
245 | 0 | return Address(PHI, Addr1.getElementType(), Align); |
246 | 0 | } |
247 | | |
248 | | bool CodeGen::isEmptyField(ASTContext &Context, const FieldDecl *FD, |
249 | 0 | bool AllowArrays, bool AsIfNoUniqueAddr) { |
250 | 0 | if (FD->isUnnamedBitfield()) |
251 | 0 | return true; |
252 | | |
253 | 0 | QualType FT = FD->getType(); |
254 | | |
255 | | // Constant arrays of empty records count as empty, strip them off. |
256 | | // Constant arrays of zero length always count as empty. |
257 | 0 | bool WasArray = false; |
258 | 0 | if (AllowArrays) |
259 | 0 | while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT)) { |
260 | 0 | if (AT->getSize() == 0) |
261 | 0 | return true; |
262 | 0 | FT = AT->getElementType(); |
263 | | // The [[no_unique_address]] special case below does not apply to |
264 | | // arrays of C++ empty records, so we need to remember this fact. |
265 | 0 | WasArray = true; |
266 | 0 | } |
267 | | |
268 | 0 | const RecordType *RT = FT->getAs<RecordType>(); |
269 | 0 | if (!RT) |
270 | 0 | return false; |
271 | | |
272 | | // C++ record fields are never empty, at least in the Itanium ABI. |
273 | | // |
274 | | // FIXME: We should use a predicate for whether this behavior is true in the |
275 | | // current ABI. |
276 | | // |
277 | | // The exception to the above rule are fields marked with the |
278 | | // [[no_unique_address]] attribute (since C++20). Those do count as empty |
279 | | // according to the Itanium ABI. The exception applies only to records, |
280 | | // not arrays of records, so we must also check whether we stripped off an |
281 | | // array type above. |
282 | 0 | if (isa<CXXRecordDecl>(RT->getDecl()) && |
283 | 0 | (WasArray || (!AsIfNoUniqueAddr && !FD->hasAttr<NoUniqueAddressAttr>()))) |
284 | 0 | return false; |
285 | | |
286 | 0 | return isEmptyRecord(Context, FT, AllowArrays, AsIfNoUniqueAddr); |
287 | 0 | } |
288 | | |
289 | | bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays, |
290 | 0 | bool AsIfNoUniqueAddr) { |
291 | 0 | const RecordType *RT = T->getAs<RecordType>(); |
292 | 0 | if (!RT) |
293 | 0 | return false; |
294 | 0 | const RecordDecl *RD = RT->getDecl(); |
295 | 0 | if (RD->hasFlexibleArrayMember()) |
296 | 0 | return false; |
297 | | |
298 | | // If this is a C++ record, check the bases first. |
299 | 0 | if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) |
300 | 0 | for (const auto &I : CXXRD->bases()) |
301 | 0 | if (!isEmptyRecord(Context, I.getType(), true, AsIfNoUniqueAddr)) |
302 | 0 | return false; |
303 | | |
304 | 0 | for (const auto *I : RD->fields()) |
305 | 0 | if (!isEmptyField(Context, I, AllowArrays, AsIfNoUniqueAddr)) |
306 | 0 | return false; |
307 | 0 | return true; |
308 | 0 | } |
309 | | |
310 | 0 | const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) { |
311 | 0 | const RecordType *RT = T->getAs<RecordType>(); |
312 | 0 | if (!RT) |
313 | 0 | return nullptr; |
314 | | |
315 | 0 | const RecordDecl *RD = RT->getDecl(); |
316 | 0 | if (RD->hasFlexibleArrayMember()) |
317 | 0 | return nullptr; |
318 | | |
319 | 0 | const Type *Found = nullptr; |
320 | | |
321 | | // If this is a C++ record, check the bases first. |
322 | 0 | if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { |
323 | 0 | for (const auto &I : CXXRD->bases()) { |
324 | | // Ignore empty records. |
325 | 0 | if (isEmptyRecord(Context, I.getType(), true)) |
326 | 0 | continue; |
327 | | |
328 | | // If we already found an element then this isn't a single-element struct. |
329 | 0 | if (Found) |
330 | 0 | return nullptr; |
331 | | |
332 | | // If this is non-empty and not a single element struct, the composite |
333 | | // cannot be a single element struct. |
334 | 0 | Found = isSingleElementStruct(I.getType(), Context); |
335 | 0 | if (!Found) |
336 | 0 | return nullptr; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | // Check for single element. |
341 | 0 | for (const auto *FD : RD->fields()) { |
342 | 0 | QualType FT = FD->getType(); |
343 | | |
344 | | // Ignore empty fields. |
345 | 0 | if (isEmptyField(Context, FD, true)) |
346 | 0 | continue; |
347 | | |
348 | | // If we already found an element then this isn't a single-element |
349 | | // struct. |
350 | 0 | if (Found) |
351 | 0 | return nullptr; |
352 | | |
353 | | // Treat single element arrays as the element. |
354 | 0 | while (const ConstantArrayType *AT = Context.getAsConstantArrayType(FT)) { |
355 | 0 | if (AT->getSize().getZExtValue() != 1) |
356 | 0 | break; |
357 | 0 | FT = AT->getElementType(); |
358 | 0 | } |
359 | |
|
360 | 0 | if (!isAggregateTypeForABI(FT)) { |
361 | 0 | Found = FT.getTypePtr(); |
362 | 0 | } else { |
363 | 0 | Found = isSingleElementStruct(FT, Context); |
364 | 0 | if (!Found) |
365 | 0 | return nullptr; |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | // We don't consider a struct a single-element struct if it has |
370 | | // padding beyond the element type. |
371 | 0 | if (Found && Context.getTypeSize(Found) != Context.getTypeSize(T)) |
372 | 0 | return nullptr; |
373 | | |
374 | 0 | return Found; |
375 | 0 | } |
376 | | |
377 | | Address CodeGen::EmitVAArgInstr(CodeGenFunction &CGF, Address VAListAddr, |
378 | 0 | QualType Ty, const ABIArgInfo &AI) { |
379 | | // This default implementation defers to the llvm backend's va_arg |
380 | | // instruction. It can handle only passing arguments directly |
381 | | // (typically only handled in the backend for primitive types), or |
382 | | // aggregates passed indirectly by pointer (NOTE: if the "byval" |
383 | | // flag has ABI impact in the callee, this implementation cannot |
384 | | // work.) |
385 | | |
386 | | // Only a few cases are covered here at the moment -- those needed |
387 | | // by the default abi. |
388 | 0 | llvm::Value *Val; |
389 | |
|
390 | 0 | if (AI.isIndirect()) { |
391 | 0 | assert(!AI.getPaddingType() && |
392 | 0 | "Unexpected PaddingType seen in arginfo in generic VAArg emitter!"); |
393 | 0 | assert( |
394 | 0 | !AI.getIndirectRealign() && |
395 | 0 | "Unexpected IndirectRealign seen in arginfo in generic VAArg emitter!"); |
396 | | |
397 | 0 | auto TyInfo = CGF.getContext().getTypeInfoInChars(Ty); |
398 | 0 | CharUnits TyAlignForABI = TyInfo.Align; |
399 | |
|
400 | 0 | llvm::Type *ElementTy = CGF.ConvertTypeForMem(Ty); |
401 | 0 | llvm::Type *BaseTy = llvm::PointerType::getUnqual(ElementTy); |
402 | 0 | llvm::Value *Addr = |
403 | 0 | CGF.Builder.CreateVAArg(VAListAddr.getPointer(), BaseTy); |
404 | 0 | return Address(Addr, ElementTy, TyAlignForABI); |
405 | 0 | } else { |
406 | 0 | assert((AI.isDirect() || AI.isExtend()) && |
407 | 0 | "Unexpected ArgInfo Kind in generic VAArg emitter!"); |
408 | | |
409 | 0 | assert(!AI.getInReg() && |
410 | 0 | "Unexpected InReg seen in arginfo in generic VAArg emitter!"); |
411 | 0 | assert(!AI.getPaddingType() && |
412 | 0 | "Unexpected PaddingType seen in arginfo in generic VAArg emitter!"); |
413 | 0 | assert(!AI.getDirectOffset() && |
414 | 0 | "Unexpected DirectOffset seen in arginfo in generic VAArg emitter!"); |
415 | 0 | assert(!AI.getCoerceToType() && |
416 | 0 | "Unexpected CoerceToType seen in arginfo in generic VAArg emitter!"); |
417 | | |
418 | 0 | Address Temp = CGF.CreateMemTemp(Ty, "varet"); |
419 | 0 | Val = CGF.Builder.CreateVAArg(VAListAddr.getPointer(), |
420 | 0 | CGF.ConvertTypeForMem(Ty)); |
421 | 0 | CGF.Builder.CreateStore(Val, Temp); |
422 | 0 | return Temp; |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | 0 | bool CodeGen::isSIMDVectorType(ASTContext &Context, QualType Ty) { |
427 | 0 | return Ty->getAs<VectorType>() && Context.getTypeSize(Ty) == 128; |
428 | 0 | } |
429 | | |
430 | 0 | bool CodeGen::isRecordWithSIMDVectorType(ASTContext &Context, QualType Ty) { |
431 | 0 | const RecordType *RT = Ty->getAs<RecordType>(); |
432 | 0 | if (!RT) |
433 | 0 | return false; |
434 | 0 | const RecordDecl *RD = RT->getDecl(); |
435 | | |
436 | | // If this is a C++ record, check the bases first. |
437 | 0 | if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) |
438 | 0 | for (const auto &I : CXXRD->bases()) |
439 | 0 | if (!isRecordWithSIMDVectorType(Context, I.getType())) |
440 | 0 | return false; |
441 | | |
442 | 0 | for (const auto *i : RD->fields()) { |
443 | 0 | QualType FT = i->getType(); |
444 | |
|
445 | 0 | if (isSIMDVectorType(Context, FT)) |
446 | 0 | return true; |
447 | | |
448 | 0 | if (isRecordWithSIMDVectorType(Context, FT)) |
449 | 0 | return true; |
450 | 0 | } |
451 | | |
452 | 0 | return false; |
453 | 0 | } |