blob: c671e8f4406ff235457a61f0204840be80297067 [file] [log] [blame]
alan-bakerfec0a472018-11-08 18:09:40 -05001// Copyright 2018 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "clang/AST/RecordLayout.h"
16#include "clang/AST/RecursiveASTVisitor.h"
17#include "clang/Basic/TargetInfo.h"
18#include "clang/CodeGen/CodeGenAction.h"
19#include "clang/Frontend/CompilerInstance.h"
20#include "clang/Frontend/FrontendPluginRegistry.h"
21#include "clang/Frontend/TextDiagnosticPrinter.h"
22
Marco Antognini535998c2020-09-16 18:48:51 +010023#include "llvm/Support/Debug.h"
24
alan-bakerfec0a472018-11-08 18:09:40 -050025#include "clspv/Option.h"
26
27#include "FrontendPlugin.h"
28
Kévin Petit0fc88042019-04-09 23:25:02 +010029#include <unordered_set>
30
alan-bakerfec0a472018-11-08 18:09:40 -050031using namespace clang;
32
33namespace {
alan-baker9b0ec3c2020-04-06 14:45:34 -040034
35static uint32_t kClusteredCount = 0;
36
alan-bakerfec0a472018-11-08 18:09:40 -050037struct ExtraValidationConsumer final : public ASTConsumer {
38private:
39 CompilerInstance &Instance;
40 llvm::StringRef InFile;
41
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040042 enum Layout { UBO, SSBO };
alan-baker9bb09792019-03-25 11:25:13 -040043
alan-bakerfec0a472018-11-08 18:09:40 -050044 enum CustomDiagnosticType {
Marco Antognini535998c2020-09-16 18:48:51 +010045 CustomDiagnosticVectorsMoreThan4Elements,
46 CustomDiagnosticUnsupportedKernelParameter,
47 CustomDiagnosticVoidPointer,
48 CustomDiagnosticUnalignedScalar,
49 CustomDiagnosticUnalignedVec2,
50 CustomDiagnosticUnalignedVec4,
51 CustomDiagnosticUBOUnalignedArray,
52 CustomDiagnosticUBOUnalignedStruct,
53 CustomDiagnosticSmallStraddle,
54 CustomDiagnosticLargeStraddle,
55 CustomDiagnosticUnalignedStructMember,
56 CustomDiagnosticUBORestrictedSize,
57 CustomDiagnosticUBORestrictedStruct,
58 CustomDiagnosticUBOArrayStride,
59 CustomDiagnosticLocationInfo,
60 CustomDiagnosticSSBOUnalignedArray,
61 CustomDiagnosticSSBOUnalignedStruct,
62 CustomDiagnosticOverloadedKernel,
63 CustomDiagnosticStructContainsPointer,
64 CustomDiagnosticRecursiveStruct,
65 CustomDiagnosticPushConstantSizeExceeded,
66 CustomDiagnosticPushConstantContainsArray,
67 CustomDiagnosticUnsupported16BitStorage,
68 CustomDiagnosticUnsupported8BitStorage,
alan-bakerfec0a472018-11-08 18:09:40 -050069 CustomDiagnosticTotal
70 };
71 std::vector<unsigned> CustomDiagnosticsIDMap;
72
alan-baker7efcaaa2020-05-06 19:33:27 -040073 clspv::Option::StorageClass ConvertToStorageClass(clang::LangAS aspace) {
74 switch (aspace) {
75 case LangAS::opencl_constant:
76 if (clspv::Option::ConstantArgsInUniformBuffer()) {
77 return clspv::Option::StorageClass::kUBO;
78 } else {
79 return clspv::Option::StorageClass::kSSBO;
80 }
81 case LangAS::opencl_global:
82 default:
83 return clspv::Option::StorageClass::kSSBO;
84 }
85 }
86
87 bool ContainsSizedType(QualType QT, uint32_t width) {
88 auto canonical = QT.getCanonicalType();
89 if (auto *BT = dyn_cast<BuiltinType>(canonical)) {
90 switch (BT->getKind()) {
91 case BuiltinType::UShort:
92 case BuiltinType::Short:
93 case BuiltinType::Half:
94 case BuiltinType::Float16:
95 return width == 16;
96 case BuiltinType::UChar:
97 case BuiltinType::Char_U:
98 case BuiltinType::SChar:
99 case BuiltinType::Char_S:
100 return width == 8;
101 default:
102 return false;
103 }
104 } else if (auto *PT = dyn_cast<PointerType>(canonical)) {
105 return ContainsSizedType(PT->getPointeeType(), width);
106 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
107 return ContainsSizedType(AT->getElementType(), width);
108 } else if (auto *VT = dyn_cast<VectorType>(canonical)) {
109 return ContainsSizedType(VT->getElementType(), width);
110 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
111 for (auto field_decl : RT->getDecl()->fields()) {
112 if (ContainsSizedType(field_decl->getType(), width))
113 return true;
114 }
115 }
116
117 return false;
118 }
119
alan-baker990e9b92019-06-07 11:26:39 -0400120 bool ContainsPointerType(QualType QT) {
121 auto canonical = QT.getCanonicalType();
122 if (canonical->isPointerType()) {
123 return true;
124 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
125 return ContainsPointerType(AT->getElementType());
126 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
127 for (auto field_decl : RT->getDecl()->fields()) {
128 if (ContainsPointerType(field_decl->getType()))
129 return true;
130 }
131 }
132
133 return false;
134 }
135
alan-baker9b0ec3c2020-04-06 14:45:34 -0400136 bool ContainsArrayType(QualType QT) {
137 auto canonical = QT.getCanonicalType();
138 if (auto *PT = dyn_cast<PointerType>(canonical)) {
139 return ContainsArrayType(PT->getPointeeType());
alan-baker4a757f62020-04-22 08:17:49 -0400140 } else if (isa<ArrayType>(canonical)) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400141 return true;
142 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
143 for (auto field_decl : RT->getDecl()->fields()) {
144 if (ContainsArrayType(field_decl->getType()))
145 return true;
146 }
147 }
148
149 return false;
150 }
151
alan-baker28361f72020-01-07 16:35:25 -0500152 bool IsRecursiveType(QualType QT, llvm::DenseSet<const Type *> *seen) {
153 auto canonical = QT.getCanonicalType();
154 if (canonical->isRecordType() &&
155 !seen->insert(canonical.getTypePtr()).second) {
156 return true;
157 }
158
159 if (auto *PT = dyn_cast<PointerType>(canonical)) {
alan-baker4a757f62020-04-22 08:17:49 -0400160 return IsRecursiveType(PT->getPointeeType(), seen);
alan-baker28361f72020-01-07 16:35:25 -0500161 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
162 return IsRecursiveType(AT->getElementType(), seen);
163 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
164 for (auto field_decl : RT->getDecl()->fields()) {
165 if (IsRecursiveType(field_decl->getType(), seen))
166 return true;
167 }
168 }
169
170 seen->erase(canonical.getTypePtr());
171 return false;
172 }
173
Marco Antognini535998c2020-09-16 18:48:51 +0100174 bool IsSupportedType(QualType QT, SourceRange SR, bool IsKernelParameter) {
alan-bakerfec0a472018-11-08 18:09:40 -0500175 auto *Ty = QT.getTypePtr();
176
177 // First check if we have a pointer type.
178 if (Ty->isPointerType()) {
179 const Type *pointeeTy = Ty->getPointeeType().getTypePtr();
180 if (pointeeTy && pointeeTy->isVoidType()) {
181 // We don't support void pointers.
182 Instance.getDiagnostics().Report(
183 SR.getBegin(), CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer]);
184 return false;
185 }
186 // Otherwise check recursively.
Marco Antognini535998c2020-09-16 18:48:51 +0100187 return IsSupportedType(Ty->getPointeeType(), SR, IsKernelParameter);
alan-bakerfec0a472018-11-08 18:09:40 -0500188 }
189
190 const auto &canonicalType = QT.getCanonicalType();
191 if (auto *VT = llvm::dyn_cast<ExtVectorType>(canonicalType)) {
Marco Antognini535998c2020-09-16 18:48:51 +0100192 // We don't support vectors with more than 4 elements under all
193 // circumstances.
alan-bakerfec0a472018-11-08 18:09:40 -0500194 if (4 < VT->getNumElements()) {
Marco Antognini535998c2020-09-16 18:48:51 +0100195 if (clspv::Option::LongVectorSupport()) {
196 if (IsKernelParameter) {
197 Report(CustomDiagnosticUnsupportedKernelParameter, SR, SR);
198 return false;
199 }
200 } else {
201 Report(CustomDiagnosticVectorsMoreThan4Elements, SR, SR);
202 return false;
203 }
alan-bakerfec0a472018-11-08 18:09:40 -0500204 }
Marco Antognini535998c2020-09-16 18:48:51 +0100205
206 return true;
207 }
208
209 if (auto *RT = llvm::dyn_cast<RecordType>(canonicalType)) {
alan-baker28361f72020-01-07 16:35:25 -0500210 // Do not allow recursive struct definitions.
211 llvm::DenseSet<const Type *> seen;
212 if (IsRecursiveType(canonicalType, &seen)) {
alan-baker990e9b92019-06-07 11:26:39 -0400213 Instance.getDiagnostics().Report(
214 SR.getBegin(),
alan-baker28361f72020-01-07 16:35:25 -0500215 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct]);
alan-baker990e9b92019-06-07 11:26:39 -0400216 return false;
217 }
Marco Antognini535998c2020-09-16 18:48:51 +0100218
219 // To avoid infinite recursion, first verify that the record is not
220 // recursive and then that its fields are supported.
221 for (auto *field_decl : RT->getDecl()->fields()) {
222 if (!IsSupportedType(field_decl->getType(), SR, IsKernelParameter)) {
223 return false;
224 }
225 }
226
227 return true;
alan-bakerfec0a472018-11-08 18:09:40 -0500228 }
229
Marco Antognini535998c2020-09-16 18:48:51 +0100230 if (auto *AT = llvm::dyn_cast<ArrayType>(canonicalType)) {
231 return IsSupportedType(AT->getElementType(), SR, IsKernelParameter);
232 }
233
234 // For function prototypes, recurse on return type and parameter types.
235 if (auto *FT = llvm::dyn_cast<FunctionProtoType>(canonicalType)) {
236 IsKernelParameter =
237 IsKernelParameter || (FT->getCallConv() == CC_OpenCLKernel);
238 for (auto param : FT->getParamTypes()) {
239 if (!IsSupportedType(param, SR, IsKernelParameter)) {
240 return false;
241 }
242 }
243
244 if (!IsSupportedType(FT->getReturnType(), SR, IsKernelParameter)) {
245 return false;
246 }
247
248 return true;
249 }
250
251 if (QT->isBuiltinType()) {
252 return true;
253 }
254
alan-baker4986eff2020-10-29 13:38:00 -0400255 if (QT->isAtomicType()) {
256 return true;
257 }
258
Marco Antognini535998c2020-09-16 18:48:51 +0100259#ifndef NDEBUG
260 llvm::dbgs() << "IsSupportedType lacks support for QualType: "
261 << QT.getAsString() << '\n';
262#endif
263 llvm_unreachable("Type not covered by IsSupportedType.");
alan-bakerfec0a472018-11-08 18:09:40 -0500264 }
265
alan-baker3d9e2012019-01-11 14:55:30 -0500266 // Report a diagnostic using |diag|. If |arg_range| and |specific_range|
267 // differ, also issue a note with the specific location of the error.
268 void Report(const CustomDiagnosticType &diag, SourceRange arg_range,
269 SourceRange specific_range) {
270 Instance.getDiagnostics().Report(arg_range.getBegin(),
271 CustomDiagnosticsIDMap[diag]);
272 if (arg_range != specific_range) {
273 Instance.getDiagnostics().Report(
274 specific_range.getBegin(),
275 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo]);
276 }
277 }
278
alan-baker9bb09792019-03-25 11:25:13 -0400279 // Returns the alignment of |QT| to satisfy |layout|'s rules.
280 uint64_t GetAlignment(const QualType QT, const Layout &layout,
281 const ASTContext &context) const {
alan-bakerfec0a472018-11-08 18:09:40 -0500282 const auto canonical = QT.getCanonicalType();
283 uint64_t alignment = context.getTypeAlignInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400284 if (layout == UBO &&
285 (canonical->isRecordType() || canonical->isArrayType())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500286 return llvm::alignTo(alignment, 16);
287 }
288 return alignment;
289 }
290
291 // Returns true if |QT| is a valid layout for a Uniform buffer. Refer to
292 // 14.5.4 in the Vulkan specification.
alan-baker9bb09792019-03-25 11:25:13 -0400293 bool IsSupportedLayout(QualType QT, uint64_t offset, const Layout &layout,
294 ASTContext &context, SourceRange arg_range,
295 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500296 const auto canonical = QT.getCanonicalType();
297 if (canonical->isScalarType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400298 if (!IsSupportedScalarLayout(canonical, offset, layout, context,
299 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500300 return false;
301 } else if (canonical->isExtVectorType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400302 if (!IsSupportedVectorLayout(canonical, offset, layout, context,
303 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500304 return false;
305 } else if (canonical->isArrayType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400306 if (!IsSupportedArrayLayout(canonical, offset, layout, context, arg_range,
307 specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500308 return false;
309 } else if (canonical->isRecordType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400310 if (!IsSupportedRecordLayout(canonical, offset, layout, context,
311 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500312 return false;
313 }
314
315 // TODO(alan-baker): Find a way to avoid this restriction.
316 // Don't allow padding. This prevents structs like:
317 // struct {
318 // int x[2];
319 // int y __attribute((aligned(16)));
320 // };
321 //
322 // This would map in LLVM to { [2 x i32], [8 x i8], i32, [12 xi8] }.
323 // There is no easy way to manipulate the padding after the array to
324 // satisfy the standard Uniform buffer layout rules in this case. The usual
325 // trick is replacing the i8 arrays with an i32 element, but the i32 would
326 // still be laid out too close to the array.
327 const auto type_size = context.getTypeSizeInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400328 const auto type_align = GetAlignment(canonical, layout, context);
329 if (layout == UBO && (type_size % type_align != 0)) {
alan-baker3d9e2012019-01-11 14:55:30 -0500330 Report(CustomDiagnosticUBORestrictedSize, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500331 return false;
332 }
333
334 return true;
335 }
336
alan-baker9bb09792019-03-25 11:25:13 -0400337 bool IsSupportedScalarLayout(QualType QT, uint64_t offset,
338 const Layout & /*layout*/, ASTContext &context,
339 SourceRange arg_range,
340 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500341 // A scalar type of size N has a base alignment on N.
342 const unsigned type_size = context.getTypeSizeInChars(QT).getQuantity();
343 if (offset % type_size != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400344 Report(CustomDiagnosticUnalignedScalar, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500345 return false;
346 }
347
348 return true;
349 }
350
alan-baker9bb09792019-03-25 11:25:13 -0400351 bool IsSupportedVectorLayout(QualType QT, uint64_t offset,
352 const Layout &layout, ASTContext &context,
353 SourceRange arg_range,
354 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500355 // 2-component vectors have a base alignment of 2 * (size of element).
356 // 3- and 4-component vectors hae a base alignment of 4 * (size of
357 // element).
358 const auto *VT = llvm::cast<VectorType>(QT);
359 const auto ele_size =
360 context.getTypeSizeInChars(VT->getElementType()).getQuantity();
361 if (VT->getNumElements() == 2) {
362 if (offset % (ele_size * 2) != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400363 Report(CustomDiagnosticUnalignedVec2, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500364 return false;
365 }
366 } else if (offset % (ele_size * 4) != 0) {
367 // Other vector sizes cause errors elsewhere.
alan-baker9bb09792019-03-25 11:25:13 -0400368 Report(CustomDiagnosticUnalignedVec4, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500369 return false;
370 }
371
372 // Straddling rules:
373 // * If total vector size is less than 16 bytes, the offset must place the
374 // entire vector within the same 16 bytes.
375 // * If total vector size is greater than 16 bytes, the offset must be a
376 // multiple of 16.
377 const auto size = context.getTypeSizeInChars(QT).getQuantity();
378 if (size <= 16 && (offset / 16 != (offset + size - 1) / 16)) {
alan-baker9bb09792019-03-25 11:25:13 -0400379 Report(CustomDiagnosticSmallStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500380 return false;
381 } else if (size > 16 && (offset % 16 != 0)) {
alan-baker9bb09792019-03-25 11:25:13 -0400382 Report(CustomDiagnosticLargeStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500383 return false;
384 }
385
alan-baker9bb09792019-03-25 11:25:13 -0400386 return IsSupportedLayout(VT->getElementType(), offset, layout, context,
387 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500388 }
389
alan-baker9bb09792019-03-25 11:25:13 -0400390 bool IsSupportedArrayLayout(QualType QT, uint64_t offset,
391 const Layout &layout, ASTContext &context,
392 SourceRange arg_range,
393 SourceRange specific_range) {
394 // An array has a base alignment of is element type.
395 // If the layout is UBO, the alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500396 const auto *AT = llvm::cast<ArrayType>(QT);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400397 const auto element_align =
398 GetAlignment(AT->getElementType(), layout, context);
alan-baker9bb09792019-03-25 11:25:13 -0400399 const auto type_align =
400 layout == UBO ? llvm::alignTo(element_align, 16) : element_align;
alan-bakerfec0a472018-11-08 18:09:40 -0500401 if (offset % type_align != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400402 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedArray
403 : CustomDiagnosticSSBOUnalignedArray;
404 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500405 return false;
406 }
alan-baker9bb09792019-03-25 11:25:13 -0400407 if (layout == UBO && !clspv::Option::RelaxedUniformBufferLayout()) {
alan-baker3d9e2012019-01-11 14:55:30 -0500408 // The ArrayStride must be a multiple of the base alignment of the array
409 // (i.e. a multiple of 16). This means that the element size must be
410 // restricted to be the base alignment of the array.
411 const auto element_size =
412 context.getTypeSizeInChars(AT->getElementType()).getQuantity();
413 if (element_size % type_align != 0) {
414 Report(CustomDiagnosticUBOArrayStride, arg_range, specific_range);
415 return false;
416 }
417 }
alan-bakerfec0a472018-11-08 18:09:40 -0500418
alan-baker9bb09792019-03-25 11:25:13 -0400419 return IsSupportedLayout(AT->getElementType(), offset, layout, context,
420 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500421 }
422
alan-baker9bb09792019-03-25 11:25:13 -0400423 bool IsSupportedRecordLayout(QualType QT, uint64_t offset,
424 const Layout &layout, ASTContext &context,
425 SourceRange arg_range,
426 SourceRange specific_range) {
427 // A structure has a base alignment of its largest member. For UBO layouts,
428 // alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500429 const auto *RT = llvm::cast<RecordType>(QT);
alan-baker9bb09792019-03-25 11:25:13 -0400430 auto type_alignment = GetAlignment(QT, layout, context);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400431 if (layout == UBO)
432 llvm::alignTo(type_alignment, 16);
alan-bakerfec0a472018-11-08 18:09:40 -0500433 if (offset % type_alignment != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400434 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedStruct
435 : CustomDiagnosticSSBOUnalignedStruct;
436 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500437 return false;
438 }
439
alan-baker9bb09792019-03-25 11:25:13 -0400440 const auto &record_layout = context.getASTRecordLayout(RT->getDecl());
alan-bakerfec0a472018-11-08 18:09:40 -0500441 const FieldDecl *prev = nullptr;
442 for (auto field_decl : RT->getDecl()->fields()) {
443 const auto field_type = field_decl->getType();
alan-bakerfec0a472018-11-08 18:09:40 -0500444 const unsigned field_no = field_decl->getFieldIndex();
445 const uint64_t field_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400446 record_layout.getFieldOffset(field_no) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500447
448 // Rules must be checked recursively.
alan-baker9bb09792019-03-25 11:25:13 -0400449 if (!IsSupportedLayout(field_type, field_offset + offset, layout, context,
450 arg_range, field_decl->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500451 return false;
452 }
453
454 if (prev) {
455 const auto prev_canonical = prev->getType().getCanonicalType();
456 const uint64_t prev_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400457 record_layout.getFieldOffset(field_no - 1) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500458 const auto prev_size =
459 context.getTypeSizeInChars(prev_canonical).getQuantity();
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400460 const auto prev_alignment =
461 GetAlignment(prev_canonical, layout, context);
alan-bakerfec0a472018-11-08 18:09:40 -0500462 const auto next_available =
463 prev_offset + llvm::alignTo(prev_size, prev_alignment);
464 if (prev_canonical->isArrayType() || prev_canonical->isRecordType()) {
465 // The next element after an array or struct must be placed on or
466 // after the next multiple of the alignment of that array or
467 // struct.
alan-baker9bb09792019-03-25 11:25:13 -0400468 // For UBO layouts, both arrays and structs must be aligned to a
469 // multiple of 16 bytes.
470 const uint64_t final_align = layout == UBO
471 ? llvm::alignTo(next_available, 16)
472 : next_available;
473 if (final_align > field_offset) {
474 Report(CustomDiagnosticUnalignedStructMember, arg_range,
alan-baker3d9e2012019-01-11 14:55:30 -0500475 field_decl->getSourceRange());
alan-bakerfec0a472018-11-08 18:09:40 -0500476 return false;
477 }
478 }
479 }
480
481 prev = field_decl;
482 }
483
484 return true;
485 }
486
487 // This will be used to check the inside of function bodies.
488 class DeclVisitor : public RecursiveASTVisitor<DeclVisitor> {
489 private:
490 ExtraValidationConsumer &consumer;
491
492 public:
493 explicit DeclVisitor(ExtraValidationConsumer &VC) : consumer(VC) {}
494
495 // Visits a declaration. Emits a diagnostic and returns false if the
496 // declaration represents an unsupported vector value or vector type.
497 // Otherwise returns true.
Marco Antognini535998c2020-09-16 18:48:51 +0100498 //
499 // Looking at the Decl class hierarchy, it seems ValueDecl and TypeDecl
500 // are the only two that might represent an unsupported vector type.
501 bool VisitValueDecl(ValueDecl *VD) {
502 return consumer.IsSupportedType(VD->getType(), VD->getSourceRange(),
503 false);
504 }
505 bool VisitValueDecl(TypeDecl *TD) {
506 QualType DefinedType = TD->getASTContext().getTypeDeclType(TD);
507 return consumer.IsSupportedType(DefinedType, TD->getSourceRange(), false);
alan-bakerfec0a472018-11-08 18:09:40 -0500508 }
509 };
510
511 DeclVisitor Visitor;
Kévin Petit0fc88042019-04-09 23:25:02 +0100512 std::unordered_set<std::string> Kernels;
alan-bakerfec0a472018-11-08 18:09:40 -0500513
514public:
515 explicit ExtraValidationConsumer(CompilerInstance &Instance,
516 llvm::StringRef InFile)
517 : Instance(Instance), InFile(InFile),
518 CustomDiagnosticsIDMap(CustomDiagnosticTotal), Visitor(*this) {
519 auto &DE = Instance.getDiagnostics();
520
521 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements] =
522 DE.getCustomDiagID(
523 DiagnosticsEngine::Error,
524 "vectors with more than 4 elements are not supported");
Marco Antognini535998c2020-09-16 18:48:51 +0100525 CustomDiagnosticsIDMap[CustomDiagnosticUnsupportedKernelParameter] =
526 DE.getCustomDiagID(DiagnosticsEngine::Error,
527 "vectors with more than 4 elements are not "
528 "supported as kernel parameters");
alan-bakerfec0a472018-11-08 18:09:40 -0500529 CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer] = DE.getCustomDiagID(
530 DiagnosticsEngine::Error, "pointer-to-void is not supported");
alan-baker9bb09792019-03-25 11:25:13 -0400531 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedScalar] =
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400532 DE.getCustomDiagID(DiagnosticsEngine::Error,
533 "scalar elements must be aligned to their size");
alan-baker9bb09792019-03-25 11:25:13 -0400534 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec2] = DE.getCustomDiagID(
535 DiagnosticsEngine::Error,
536 "two-component vectors must be aligned to 2 times their element size");
537 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec4] =
alan-bakerfec0a472018-11-08 18:09:40 -0500538 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400539 "three- and four-component vectors must be aligned "
540 "to 4 times their element size");
alan-bakerfec0a472018-11-08 18:09:40 -0500541 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedArray] =
542 DE.getCustomDiagID(DiagnosticsEngine::Error,
543 "in an UBO, arrays must be aligned to their element "
544 "alignment, rounded up to a multiple of 16 bytes");
545 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedStruct] =
546 DE.getCustomDiagID(
547 DiagnosticsEngine::Error,
548 "in an UBO, structs must be aligned to their "
549 "largest element alignment, rounded up to a multiple of "
550 "16 bytes");
alan-baker9bb09792019-03-25 11:25:13 -0400551 CustomDiagnosticsIDMap[CustomDiagnosticSmallStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500552 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400553 "vectors with a total size less than or equal to 16 "
554 "bytes must be placed entirely within a 16 byte "
555 "aligned region");
556 CustomDiagnosticsIDMap[CustomDiagnosticLargeStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500557 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400558 "vectors with a total size greater than 16 bytes "
559 "must aligned to 16 bytes");
560 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedStructMember] =
alan-bakerfec0a472018-11-08 18:09:40 -0500561 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400562 "a structure member must not be placed between the "
563 "end of a structure or array and the next multiple "
564 "of the base alignment of that structure or array");
alan-bakerfec0a472018-11-08 18:09:40 -0500565 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedSize] =
566 DE.getCustomDiagID(DiagnosticsEngine::Error,
567 "clspv restriction: UBO element size must be a "
568 "multiple of that element's alignment");
569 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedStruct] =
570 DE.getCustomDiagID(
571 DiagnosticsEngine::Error,
572 "clspv restriction: UBO structures may not have implicit padding");
alan-baker3d9e2012019-01-11 14:55:30 -0500573 CustomDiagnosticsIDMap[CustomDiagnosticUBOArrayStride] = DE.getCustomDiagID(
574 DiagnosticsEngine::Error,
575 "clspv restriction: to satisfy UBO ArrayStride restrictions, element "
576 "size must be a multiple of array alignment");
577 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo] =
578 DE.getCustomDiagID(DiagnosticsEngine::Note, "here");
alan-baker9bb09792019-03-25 11:25:13 -0400579 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedArray] =
580 DE.getCustomDiagID(
581 DiagnosticsEngine::Error,
582 "in a SSBO, arrays must be aligned to their element alignment");
583 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedStruct] =
584 DE.getCustomDiagID(DiagnosticsEngine::Error,
585 "in a SSBO, structs must be aligned to their "
586 "largest element alignment");
Kévin Petit0fc88042019-04-09 23:25:02 +0100587 CustomDiagnosticsIDMap[CustomDiagnosticOverloadedKernel] =
588 DE.getCustomDiagID(DiagnosticsEngine::Error,
589 "kernel functions can't be overloaded");
alan-baker990e9b92019-06-07 11:26:39 -0400590 CustomDiagnosticsIDMap[CustomDiagnosticStructContainsPointer] =
591 DE.getCustomDiagID(DiagnosticsEngine::Error,
592 "structures may not contain pointers");
alan-baker28361f72020-01-07 16:35:25 -0500593 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct] =
594 DE.getCustomDiagID(DiagnosticsEngine::Error,
595 "recursive structures are not supported");
alan-baker9b0ec3c2020-04-06 14:45:34 -0400596 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantSizeExceeded] =
597 DE.getCustomDiagID(DiagnosticsEngine::Error,
598 "max push constant size exceeded");
599 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantContainsArray] =
600 DE.getCustomDiagID(
601 DiagnosticsEngine::Error,
602 "arrays are not supported in push constants currently");
alan-baker7efcaaa2020-05-06 19:33:27 -0400603 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported16BitStorage] =
604 DE.getCustomDiagID(DiagnosticsEngine::Error,
605 "16-bit storage is not supported for "
606 "%select{SSBOs|UBOs|push constants}0");
607 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported8BitStorage] =
608 DE.getCustomDiagID(DiagnosticsEngine::Error,
609 "8-bit storage is not supported for "
610 "%select{SSBOs|UBOs|push constants}0");
alan-bakerfec0a472018-11-08 18:09:40 -0500611 }
612
613 virtual bool HandleTopLevelDecl(DeclGroupRef DG) override {
614 for (auto *D : DG) {
615 if (auto *FD = llvm::dyn_cast<FunctionDecl>(D)) {
616 // If the function has a body it means we are not an OpenCL builtin
617 // function.
618 if (FD->hasBody()) {
619 if (!IsSupportedType(FD->getReturnType(),
Marco Antognini535998c2020-09-16 18:48:51 +0100620 FD->getReturnTypeSourceRange(), false)) {
alan-bakerfec0a472018-11-08 18:09:40 -0500621 return false;
622 }
623
624 bool is_opencl_kernel = false;
625 if (FD->hasAttrs()) {
626 for (auto *attr : FD->attrs()) {
627 if (attr->getKind() == attr::Kind::OpenCLKernel) {
628 is_opencl_kernel = true;
629 }
630 }
631 }
632
Kévin Petit0fc88042019-04-09 23:25:02 +0100633 if (is_opencl_kernel) {
alan-baker21574d32020-01-29 16:00:31 -0500634 if (Kernels.count(FD->getName().str()) != 0) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100635 auto srcRange = FD->getSourceRange();
636 Report(CustomDiagnosticOverloadedKernel, srcRange, srcRange);
637 } else {
alan-baker21574d32020-01-29 16:00:31 -0500638 Kernels.insert(FD->getName().str());
Kévin Petit0fc88042019-04-09 23:25:02 +0100639 }
640 }
641
alan-baker9b0ec3c2020-04-06 14:45:34 -0400642 RecordDecl *clustered_args = nullptr;
643 if (is_opencl_kernel && clspv::Option::PodArgsInPushConstants()) {
644 clustered_args = FD->getASTContext().buildImplicitRecord(
645 "__clspv.clustered_args." + std::to_string(kClusteredCount++));
646 clustered_args->startDefinition();
647 }
alan-bakerfec0a472018-11-08 18:09:40 -0500648 for (auto *P : FD->parameters()) {
649 auto type = P->getType();
Marco Antognini535998c2020-09-16 18:48:51 +0100650 if (!IsSupportedType(P->getOriginalType(), P->getSourceRange(),
651 is_opencl_kernel)) {
alan-bakerfec0a472018-11-08 18:09:40 -0500652 return false;
653 }
654
alan-baker9bb09792019-03-25 11:25:13 -0400655 if (is_opencl_kernel && type->isPointerType() &&
656 ((type->getPointeeType().getAddressSpace() ==
657 LangAS::opencl_constant) ||
658 (type->getPointeeType().getAddressSpace() ==
659 LangAS::opencl_global))) {
alan-baker3d9e2012019-01-11 14:55:30 -0500660 // The argument will be generated as an array within a block.
661 // Generate an array type to check the validity for the generated
662 // case.
alan-baker9bb09792019-03-25 11:25:13 -0400663 Layout layout = SSBO;
664 if (clspv::Option::ConstantArgsInUniformBuffer() &&
665 !clspv::Option::Std430UniformBufferLayout() &&
666 type->getPointeeType().getAddressSpace() ==
667 LangAS::opencl_constant) {
668 layout = UBO;
669 }
alan-baker3d9e2012019-01-11 14:55:30 -0500670 auto array_type = FD->getASTContext().getIncompleteArrayType(
671 type->getPointeeType(), clang::ArrayType::Normal, 0);
alan-baker9bb09792019-03-25 11:25:13 -0400672 if (!IsSupportedLayout(array_type, 0, layout, FD->getASTContext(),
673 P->getSourceRange(),
674 P->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500675 return false;
676 }
677 }
alan-baker038e9242019-04-19 22:14:41 -0400678
alan-baker7efcaaa2020-05-06 19:33:27 -0400679 // Check if storage capabilities are supported.
680 if (is_opencl_kernel) {
681 bool contains_16bit =
682 ContainsSizedType(type.getCanonicalType(), 16);
683 bool contains_8bit =
684 ContainsSizedType(type.getCanonicalType(), 8);
685 auto sc = clspv::Option::StorageClass::kSSBO;
686 if (type->isPointerType()) {
687 sc = ConvertToStorageClass(
688 type->getPointeeType().getAddressSpace());
689 } else if (clspv::Option::PodArgsInUniformBuffer()) {
690 sc = clspv::Option::StorageClass::kUBO;
691 } else if (clspv::Option::PodArgsInPushConstants()) {
692 sc = clspv::Option::StorageClass::kPushConstant;
693 }
alan-bakerfb7e2be2020-05-25 08:45:39 -0400694
695 if (type->isPointerType() ||
696 sc != clspv::Option::StorageClass::kSSBO ||
697 !clspv::Option::ClusterPodKernelArgs()) {
698 // For clustered pod args, assume we can fall back on
699 // type-mangling.
700 if (contains_16bit &&
701 !clspv::Option::Supports16BitStorageClass(sc)) {
702 Instance.getDiagnostics().Report(
703 P->getSourceRange().getBegin(),
704 CustomDiagnosticsIDMap
705 [CustomDiagnosticUnsupported16BitStorage])
706 << static_cast<int>(sc);
707 }
708 if (contains_8bit &&
709 !clspv::Option::Supports8BitStorageClass(sc)) {
710 Instance.getDiagnostics().Report(
711 P->getSourceRange().getBegin(),
712 CustomDiagnosticsIDMap
713 [CustomDiagnosticUnsupported8BitStorage])
714 << static_cast<int>(sc);
715 }
alan-baker7efcaaa2020-05-06 19:33:27 -0400716 }
717 }
718
alan-baker28361f72020-01-07 16:35:25 -0500719 if (is_opencl_kernel && type->isPointerType()) {
720 auto pointee_type = type->getPointeeType().getCanonicalType();
721 if (ContainsPointerType(pointee_type)) {
722 Instance.getDiagnostics().Report(
723 P->getSourceRange().getBegin(),
724 CustomDiagnosticsIDMap
725 [CustomDiagnosticStructContainsPointer]);
726 return false;
727 }
728 }
729
alan-baker038e9242019-04-19 22:14:41 -0400730 if (is_opencl_kernel && !type->isPointerType()) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400731 if (clspv::Option::PodArgsInPushConstants()) {
732 // Don't allow arrays in push constants currently.
733 if (ContainsArrayType(type)) {
734 Report(CustomDiagnosticPushConstantContainsArray,
735 P->getSourceRange(), P->getSourceRange());
736 return false;
737 }
738 FieldDecl *field_decl = FieldDecl::Create(
739 FD->getASTContext(),
740 Decl::castToDeclContext(clustered_args),
741 P->getSourceRange().getBegin(),
742 P->getSourceRange().getEnd(), P->getIdentifier(),
743 P->getType(), nullptr, nullptr, false, ICIS_NoInit);
744 field_decl->setAccess(AS_public);
745 clustered_args->addDecl(field_decl);
746 } else {
747 Layout layout = SSBO;
748 if (clspv::Option::PodArgsInUniformBuffer() &&
749 !clspv::Option::Std430UniformBufferLayout())
750 layout = UBO;
alan-baker038e9242019-04-19 22:14:41 -0400751
alan-baker9b0ec3c2020-04-06 14:45:34 -0400752 if (!IsSupportedLayout(type, 0, layout, FD->getASTContext(),
753 P->getSourceRange(),
754 P->getSourceRange())) {
755 return false;
756 }
757 }
758 }
759 }
760
761 if (clustered_args) {
762 clustered_args->completeDefinition();
763 if (!clustered_args->field_empty()) {
764 auto record_type =
765 FD->getASTContext().getRecordType(clustered_args);
766 if (!IsSupportedLayout(record_type, 0, SSBO, FD->getASTContext(),
767 FD->getSourceRange(),
768 FD->getSourceRange())) {
769 return false;
770 }
771
772 if (FD->getASTContext()
773 .getTypeSizeInChars(record_type)
774 .getQuantity() > clspv::Option::MaxPushConstantsSize()) {
775 Report(CustomDiagnosticPushConstantSizeExceeded,
776 FD->getSourceRange(), FD->getSourceRange());
alan-baker038e9242019-04-19 22:14:41 -0400777 return false;
778 }
779 }
alan-bakerfec0a472018-11-08 18:09:40 -0500780 }
781
782 // Check for unsupported vector types.
783 Visitor.TraverseDecl(FD);
784 }
785 }
786 }
787
788 return true;
789 }
790};
791} // namespace
792
793namespace clspv {
794std::unique_ptr<ASTConsumer>
795ExtraValidationASTAction::CreateASTConsumer(CompilerInstance &CI,
796 llvm::StringRef InFile) {
797 return std::unique_ptr<ASTConsumer>(new ExtraValidationConsumer(CI, InFile));
798}
799} // namespace clspv