blob: b18e80fe42eee9bd9cc6c4f26d9799b7b1d925df [file] [log] [blame]
Marco Antognini381943b2021-01-06 14:39:00 +00001// Copyright 2018-2021 The Clspv Authors. All rights reserved.
alan-bakerfec0a472018-11-08 18:09:40 -05002//
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,
Marco Antognini535998c2020-09-16 18:48:51 +010046 CustomDiagnosticVoidPointer,
47 CustomDiagnosticUnalignedScalar,
48 CustomDiagnosticUnalignedVec2,
49 CustomDiagnosticUnalignedVec4,
50 CustomDiagnosticUBOUnalignedArray,
51 CustomDiagnosticUBOUnalignedStruct,
52 CustomDiagnosticSmallStraddle,
53 CustomDiagnosticLargeStraddle,
54 CustomDiagnosticUnalignedStructMember,
55 CustomDiagnosticUBORestrictedSize,
56 CustomDiagnosticUBORestrictedStruct,
57 CustomDiagnosticUBOArrayStride,
58 CustomDiagnosticLocationInfo,
59 CustomDiagnosticSSBOUnalignedArray,
60 CustomDiagnosticSSBOUnalignedStruct,
61 CustomDiagnosticOverloadedKernel,
62 CustomDiagnosticStructContainsPointer,
63 CustomDiagnosticRecursiveStruct,
64 CustomDiagnosticPushConstantSizeExceeded,
65 CustomDiagnosticPushConstantContainsArray,
66 CustomDiagnosticUnsupported16BitStorage,
67 CustomDiagnosticUnsupported8BitStorage,
alan-bakerfec0a472018-11-08 18:09:40 -050068 CustomDiagnosticTotal
69 };
70 std::vector<unsigned> CustomDiagnosticsIDMap;
71
alan-baker7efcaaa2020-05-06 19:33:27 -040072 clspv::Option::StorageClass ConvertToStorageClass(clang::LangAS aspace) {
73 switch (aspace) {
74 case LangAS::opencl_constant:
75 if (clspv::Option::ConstantArgsInUniformBuffer()) {
76 return clspv::Option::StorageClass::kUBO;
77 } else {
78 return clspv::Option::StorageClass::kSSBO;
79 }
80 case LangAS::opencl_global:
81 default:
82 return clspv::Option::StorageClass::kSSBO;
83 }
84 }
85
86 bool ContainsSizedType(QualType QT, uint32_t width) {
87 auto canonical = QT.getCanonicalType();
88 if (auto *BT = dyn_cast<BuiltinType>(canonical)) {
89 switch (BT->getKind()) {
90 case BuiltinType::UShort:
91 case BuiltinType::Short:
92 case BuiltinType::Half:
93 case BuiltinType::Float16:
94 return width == 16;
95 case BuiltinType::UChar:
96 case BuiltinType::Char_U:
97 case BuiltinType::SChar:
98 case BuiltinType::Char_S:
99 return width == 8;
100 default:
101 return false;
102 }
103 } else if (auto *PT = dyn_cast<PointerType>(canonical)) {
104 return ContainsSizedType(PT->getPointeeType(), width);
105 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
106 return ContainsSizedType(AT->getElementType(), width);
107 } else if (auto *VT = dyn_cast<VectorType>(canonical)) {
108 return ContainsSizedType(VT->getElementType(), width);
109 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
110 for (auto field_decl : RT->getDecl()->fields()) {
111 if (ContainsSizedType(field_decl->getType(), width))
112 return true;
113 }
114 }
115
116 return false;
117 }
118
alan-baker990e9b92019-06-07 11:26:39 -0400119 bool ContainsPointerType(QualType QT) {
120 auto canonical = QT.getCanonicalType();
121 if (canonical->isPointerType()) {
122 return true;
123 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
124 return ContainsPointerType(AT->getElementType());
125 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
126 for (auto field_decl : RT->getDecl()->fields()) {
127 if (ContainsPointerType(field_decl->getType()))
128 return true;
129 }
130 }
131
132 return false;
133 }
134
alan-baker9b0ec3c2020-04-06 14:45:34 -0400135 bool ContainsArrayType(QualType QT) {
136 auto canonical = QT.getCanonicalType();
137 if (auto *PT = dyn_cast<PointerType>(canonical)) {
138 return ContainsArrayType(PT->getPointeeType());
alan-baker4a757f62020-04-22 08:17:49 -0400139 } else if (isa<ArrayType>(canonical)) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400140 return true;
141 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
142 for (auto field_decl : RT->getDecl()->fields()) {
143 if (ContainsArrayType(field_decl->getType()))
144 return true;
145 }
146 }
147
148 return false;
149 }
150
alan-baker28361f72020-01-07 16:35:25 -0500151 bool IsRecursiveType(QualType QT, llvm::DenseSet<const Type *> *seen) {
152 auto canonical = QT.getCanonicalType();
153 if (canonical->isRecordType() &&
154 !seen->insert(canonical.getTypePtr()).second) {
155 return true;
156 }
157
158 if (auto *PT = dyn_cast<PointerType>(canonical)) {
alan-baker4a757f62020-04-22 08:17:49 -0400159 return IsRecursiveType(PT->getPointeeType(), seen);
alan-baker28361f72020-01-07 16:35:25 -0500160 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
161 return IsRecursiveType(AT->getElementType(), seen);
162 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
163 for (auto field_decl : RT->getDecl()->fields()) {
164 if (IsRecursiveType(field_decl->getType(), seen))
165 return true;
166 }
167 }
168
169 seen->erase(canonical.getTypePtr());
170 return false;
171 }
172
Marco Antognini535998c2020-09-16 18:48:51 +0100173 bool IsSupportedType(QualType QT, SourceRange SR, bool IsKernelParameter) {
alan-bakerfec0a472018-11-08 18:09:40 -0500174 auto *Ty = QT.getTypePtr();
175
176 // First check if we have a pointer type.
177 if (Ty->isPointerType()) {
178 const Type *pointeeTy = Ty->getPointeeType().getTypePtr();
179 if (pointeeTy && pointeeTy->isVoidType()) {
180 // We don't support void pointers.
181 Instance.getDiagnostics().Report(
182 SR.getBegin(), CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer]);
183 return false;
184 }
185 // Otherwise check recursively.
Marco Antognini535998c2020-09-16 18:48:51 +0100186 return IsSupportedType(Ty->getPointeeType(), SR, IsKernelParameter);
alan-bakerfec0a472018-11-08 18:09:40 -0500187 }
188
189 const auto &canonicalType = QT.getCanonicalType();
190 if (auto *VT = llvm::dyn_cast<ExtVectorType>(canonicalType)) {
Marco Antognini535998c2020-09-16 18:48:51 +0100191 // We don't support vectors with more than 4 elements under all
192 // circumstances.
Marco Antognini381943b2021-01-06 14:39:00 +0000193 if (4 < VT->getNumElements() && !clspv::Option::LongVectorSupport()) {
194 Report(CustomDiagnosticVectorsMoreThan4Elements, SR, SR);
195 return false;
alan-bakerfec0a472018-11-08 18:09:40 -0500196 }
Marco Antognini535998c2020-09-16 18:48:51 +0100197
198 return true;
199 }
200
201 if (auto *RT = llvm::dyn_cast<RecordType>(canonicalType)) {
alan-baker28361f72020-01-07 16:35:25 -0500202 // Do not allow recursive struct definitions.
203 llvm::DenseSet<const Type *> seen;
204 if (IsRecursiveType(canonicalType, &seen)) {
alan-baker990e9b92019-06-07 11:26:39 -0400205 Instance.getDiagnostics().Report(
206 SR.getBegin(),
alan-baker28361f72020-01-07 16:35:25 -0500207 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct]);
alan-baker990e9b92019-06-07 11:26:39 -0400208 return false;
209 }
Marco Antognini535998c2020-09-16 18:48:51 +0100210
211 // To avoid infinite recursion, first verify that the record is not
212 // recursive and then that its fields are supported.
213 for (auto *field_decl : RT->getDecl()->fields()) {
214 if (!IsSupportedType(field_decl->getType(), SR, IsKernelParameter)) {
215 return false;
216 }
217 }
218
219 return true;
alan-bakerfec0a472018-11-08 18:09:40 -0500220 }
221
Marco Antognini535998c2020-09-16 18:48:51 +0100222 if (auto *AT = llvm::dyn_cast<ArrayType>(canonicalType)) {
223 return IsSupportedType(AT->getElementType(), SR, IsKernelParameter);
224 }
225
226 // For function prototypes, recurse on return type and parameter types.
227 if (auto *FT = llvm::dyn_cast<FunctionProtoType>(canonicalType)) {
228 IsKernelParameter =
229 IsKernelParameter || (FT->getCallConv() == CC_OpenCLKernel);
230 for (auto param : FT->getParamTypes()) {
231 if (!IsSupportedType(param, SR, IsKernelParameter)) {
232 return false;
233 }
234 }
235
236 if (!IsSupportedType(FT->getReturnType(), SR, IsKernelParameter)) {
237 return false;
238 }
239
240 return true;
241 }
242
243 if (QT->isBuiltinType()) {
244 return true;
245 }
246
alan-baker4986eff2020-10-29 13:38:00 -0400247 if (QT->isAtomicType()) {
248 return true;
249 }
250
Marco Antognini535998c2020-09-16 18:48:51 +0100251#ifndef NDEBUG
252 llvm::dbgs() << "IsSupportedType lacks support for QualType: "
253 << QT.getAsString() << '\n';
254#endif
255 llvm_unreachable("Type not covered by IsSupportedType.");
alan-bakerfec0a472018-11-08 18:09:40 -0500256 }
257
alan-baker3d9e2012019-01-11 14:55:30 -0500258 // Report a diagnostic using |diag|. If |arg_range| and |specific_range|
259 // differ, also issue a note with the specific location of the error.
260 void Report(const CustomDiagnosticType &diag, SourceRange arg_range,
261 SourceRange specific_range) {
262 Instance.getDiagnostics().Report(arg_range.getBegin(),
263 CustomDiagnosticsIDMap[diag]);
264 if (arg_range != specific_range) {
265 Instance.getDiagnostics().Report(
266 specific_range.getBegin(),
267 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo]);
268 }
269 }
270
alan-baker9bb09792019-03-25 11:25:13 -0400271 // Returns the alignment of |QT| to satisfy |layout|'s rules.
272 uint64_t GetAlignment(const QualType QT, const Layout &layout,
273 const ASTContext &context) const {
alan-bakerfec0a472018-11-08 18:09:40 -0500274 const auto canonical = QT.getCanonicalType();
275 uint64_t alignment = context.getTypeAlignInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400276 if (layout == UBO &&
277 (canonical->isRecordType() || canonical->isArrayType())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500278 return llvm::alignTo(alignment, 16);
279 }
280 return alignment;
281 }
282
283 // Returns true if |QT| is a valid layout for a Uniform buffer. Refer to
284 // 14.5.4 in the Vulkan specification.
alan-baker9bb09792019-03-25 11:25:13 -0400285 bool IsSupportedLayout(QualType QT, uint64_t offset, const Layout &layout,
286 ASTContext &context, SourceRange arg_range,
287 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500288 const auto canonical = QT.getCanonicalType();
289 if (canonical->isScalarType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400290 if (!IsSupportedScalarLayout(canonical, offset, layout, context,
291 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500292 return false;
293 } else if (canonical->isExtVectorType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400294 if (!IsSupportedVectorLayout(canonical, offset, layout, context,
295 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500296 return false;
297 } else if (canonical->isArrayType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400298 if (!IsSupportedArrayLayout(canonical, offset, layout, context, arg_range,
299 specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500300 return false;
301 } else if (canonical->isRecordType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400302 if (!IsSupportedRecordLayout(canonical, offset, layout, context,
303 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500304 return false;
305 }
306
307 // TODO(alan-baker): Find a way to avoid this restriction.
308 // Don't allow padding. This prevents structs like:
309 // struct {
310 // int x[2];
311 // int y __attribute((aligned(16)));
312 // };
313 //
314 // This would map in LLVM to { [2 x i32], [8 x i8], i32, [12 xi8] }.
315 // There is no easy way to manipulate the padding after the array to
316 // satisfy the standard Uniform buffer layout rules in this case. The usual
317 // trick is replacing the i8 arrays with an i32 element, but the i32 would
318 // still be laid out too close to the array.
319 const auto type_size = context.getTypeSizeInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400320 const auto type_align = GetAlignment(canonical, layout, context);
321 if (layout == UBO && (type_size % type_align != 0)) {
alan-baker3d9e2012019-01-11 14:55:30 -0500322 Report(CustomDiagnosticUBORestrictedSize, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500323 return false;
324 }
325
326 return true;
327 }
328
alan-baker9bb09792019-03-25 11:25:13 -0400329 bool IsSupportedScalarLayout(QualType QT, uint64_t offset,
330 const Layout & /*layout*/, ASTContext &context,
331 SourceRange arg_range,
332 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500333 // A scalar type of size N has a base alignment on N.
334 const unsigned type_size = context.getTypeSizeInChars(QT).getQuantity();
335 if (offset % type_size != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400336 Report(CustomDiagnosticUnalignedScalar, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500337 return false;
338 }
339
340 return true;
341 }
342
alan-baker9bb09792019-03-25 11:25:13 -0400343 bool IsSupportedVectorLayout(QualType QT, uint64_t offset,
344 const Layout &layout, ASTContext &context,
345 SourceRange arg_range,
346 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500347 // 2-component vectors have a base alignment of 2 * (size of element).
348 // 3- and 4-component vectors hae a base alignment of 4 * (size of
349 // element).
350 const auto *VT = llvm::cast<VectorType>(QT);
351 const auto ele_size =
352 context.getTypeSizeInChars(VT->getElementType()).getQuantity();
353 if (VT->getNumElements() == 2) {
354 if (offset % (ele_size * 2) != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400355 Report(CustomDiagnosticUnalignedVec2, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500356 return false;
357 }
358 } else if (offset % (ele_size * 4) != 0) {
359 // Other vector sizes cause errors elsewhere.
alan-baker9bb09792019-03-25 11:25:13 -0400360 Report(CustomDiagnosticUnalignedVec4, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500361 return false;
362 }
363
364 // Straddling rules:
365 // * If total vector size is less than 16 bytes, the offset must place the
366 // entire vector within the same 16 bytes.
367 // * If total vector size is greater than 16 bytes, the offset must be a
368 // multiple of 16.
369 const auto size = context.getTypeSizeInChars(QT).getQuantity();
370 if (size <= 16 && (offset / 16 != (offset + size - 1) / 16)) {
alan-baker9bb09792019-03-25 11:25:13 -0400371 Report(CustomDiagnosticSmallStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500372 return false;
373 } else if (size > 16 && (offset % 16 != 0)) {
alan-baker9bb09792019-03-25 11:25:13 -0400374 Report(CustomDiagnosticLargeStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500375 return false;
376 }
377
alan-baker9bb09792019-03-25 11:25:13 -0400378 return IsSupportedLayout(VT->getElementType(), offset, layout, context,
379 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500380 }
381
alan-baker9bb09792019-03-25 11:25:13 -0400382 bool IsSupportedArrayLayout(QualType QT, uint64_t offset,
383 const Layout &layout, ASTContext &context,
384 SourceRange arg_range,
385 SourceRange specific_range) {
386 // An array has a base alignment of is element type.
387 // If the layout is UBO, the alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500388 const auto *AT = llvm::cast<ArrayType>(QT);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400389 const auto element_align =
390 GetAlignment(AT->getElementType(), layout, context);
alan-baker9bb09792019-03-25 11:25:13 -0400391 const auto type_align =
392 layout == UBO ? llvm::alignTo(element_align, 16) : element_align;
alan-bakerfec0a472018-11-08 18:09:40 -0500393 if (offset % type_align != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400394 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedArray
395 : CustomDiagnosticSSBOUnalignedArray;
396 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500397 return false;
398 }
alan-baker9bb09792019-03-25 11:25:13 -0400399 if (layout == UBO && !clspv::Option::RelaxedUniformBufferLayout()) {
alan-baker3d9e2012019-01-11 14:55:30 -0500400 // The ArrayStride must be a multiple of the base alignment of the array
401 // (i.e. a multiple of 16). This means that the element size must be
402 // restricted to be the base alignment of the array.
403 const auto element_size =
404 context.getTypeSizeInChars(AT->getElementType()).getQuantity();
405 if (element_size % type_align != 0) {
406 Report(CustomDiagnosticUBOArrayStride, arg_range, specific_range);
407 return false;
408 }
409 }
alan-bakerfec0a472018-11-08 18:09:40 -0500410
alan-baker9bb09792019-03-25 11:25:13 -0400411 return IsSupportedLayout(AT->getElementType(), offset, layout, context,
412 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500413 }
414
alan-baker9bb09792019-03-25 11:25:13 -0400415 bool IsSupportedRecordLayout(QualType QT, uint64_t offset,
416 const Layout &layout, ASTContext &context,
417 SourceRange arg_range,
418 SourceRange specific_range) {
419 // A structure has a base alignment of its largest member. For UBO layouts,
420 // alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500421 const auto *RT = llvm::cast<RecordType>(QT);
alan-baker9bb09792019-03-25 11:25:13 -0400422 auto type_alignment = GetAlignment(QT, layout, context);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400423 if (layout == UBO)
424 llvm::alignTo(type_alignment, 16);
alan-bakerfec0a472018-11-08 18:09:40 -0500425 if (offset % type_alignment != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400426 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedStruct
427 : CustomDiagnosticSSBOUnalignedStruct;
428 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500429 return false;
430 }
431
alan-baker9bb09792019-03-25 11:25:13 -0400432 const auto &record_layout = context.getASTRecordLayout(RT->getDecl());
alan-bakerfec0a472018-11-08 18:09:40 -0500433 const FieldDecl *prev = nullptr;
434 for (auto field_decl : RT->getDecl()->fields()) {
435 const auto field_type = field_decl->getType();
alan-bakerfec0a472018-11-08 18:09:40 -0500436 const unsigned field_no = field_decl->getFieldIndex();
437 const uint64_t field_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400438 record_layout.getFieldOffset(field_no) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500439
440 // Rules must be checked recursively.
alan-baker9bb09792019-03-25 11:25:13 -0400441 if (!IsSupportedLayout(field_type, field_offset + offset, layout, context,
442 arg_range, field_decl->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500443 return false;
444 }
445
446 if (prev) {
447 const auto prev_canonical = prev->getType().getCanonicalType();
448 const uint64_t prev_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400449 record_layout.getFieldOffset(field_no - 1) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500450 const auto prev_size =
451 context.getTypeSizeInChars(prev_canonical).getQuantity();
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400452 const auto prev_alignment =
453 GetAlignment(prev_canonical, layout, context);
alan-bakerfec0a472018-11-08 18:09:40 -0500454 const auto next_available =
455 prev_offset + llvm::alignTo(prev_size, prev_alignment);
456 if (prev_canonical->isArrayType() || prev_canonical->isRecordType()) {
457 // The next element after an array or struct must be placed on or
458 // after the next multiple of the alignment of that array or
459 // struct.
alan-baker9bb09792019-03-25 11:25:13 -0400460 // For UBO layouts, both arrays and structs must be aligned to a
461 // multiple of 16 bytes.
462 const uint64_t final_align = layout == UBO
463 ? llvm::alignTo(next_available, 16)
464 : next_available;
465 if (final_align > field_offset) {
466 Report(CustomDiagnosticUnalignedStructMember, arg_range,
alan-baker3d9e2012019-01-11 14:55:30 -0500467 field_decl->getSourceRange());
alan-bakerfec0a472018-11-08 18:09:40 -0500468 return false;
469 }
470 }
471 }
472
473 prev = field_decl;
474 }
475
476 return true;
477 }
478
479 // This will be used to check the inside of function bodies.
480 class DeclVisitor : public RecursiveASTVisitor<DeclVisitor> {
481 private:
482 ExtraValidationConsumer &consumer;
483
484 public:
485 explicit DeclVisitor(ExtraValidationConsumer &VC) : consumer(VC) {}
486
487 // Visits a declaration. Emits a diagnostic and returns false if the
488 // declaration represents an unsupported vector value or vector type.
489 // Otherwise returns true.
Marco Antognini535998c2020-09-16 18:48:51 +0100490 //
491 // Looking at the Decl class hierarchy, it seems ValueDecl and TypeDecl
492 // are the only two that might represent an unsupported vector type.
493 bool VisitValueDecl(ValueDecl *VD) {
494 return consumer.IsSupportedType(VD->getType(), VD->getSourceRange(),
495 false);
496 }
497 bool VisitValueDecl(TypeDecl *TD) {
498 QualType DefinedType = TD->getASTContext().getTypeDeclType(TD);
499 return consumer.IsSupportedType(DefinedType, TD->getSourceRange(), false);
alan-bakerfec0a472018-11-08 18:09:40 -0500500 }
501 };
502
503 DeclVisitor Visitor;
Kévin Petit0fc88042019-04-09 23:25:02 +0100504 std::unordered_set<std::string> Kernels;
alan-bakerfec0a472018-11-08 18:09:40 -0500505
506public:
507 explicit ExtraValidationConsumer(CompilerInstance &Instance,
508 llvm::StringRef InFile)
509 : Instance(Instance), InFile(InFile),
510 CustomDiagnosticsIDMap(CustomDiagnosticTotal), Visitor(*this) {
511 auto &DE = Instance.getDiagnostics();
512
513 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements] =
514 DE.getCustomDiagID(
515 DiagnosticsEngine::Error,
516 "vectors with more than 4 elements are not supported");
517 CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer] = DE.getCustomDiagID(
518 DiagnosticsEngine::Error, "pointer-to-void is not supported");
alan-baker9bb09792019-03-25 11:25:13 -0400519 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedScalar] =
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400520 DE.getCustomDiagID(DiagnosticsEngine::Error,
521 "scalar elements must be aligned to their size");
alan-baker9bb09792019-03-25 11:25:13 -0400522 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec2] = DE.getCustomDiagID(
523 DiagnosticsEngine::Error,
524 "two-component vectors must be aligned to 2 times their element size");
525 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec4] =
alan-bakerfec0a472018-11-08 18:09:40 -0500526 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400527 "three- and four-component vectors must be aligned "
528 "to 4 times their element size");
alan-bakerfec0a472018-11-08 18:09:40 -0500529 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedArray] =
530 DE.getCustomDiagID(DiagnosticsEngine::Error,
531 "in an UBO, arrays must be aligned to their element "
532 "alignment, rounded up to a multiple of 16 bytes");
533 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedStruct] =
534 DE.getCustomDiagID(
535 DiagnosticsEngine::Error,
536 "in an UBO, structs must be aligned to their "
537 "largest element alignment, rounded up to a multiple of "
538 "16 bytes");
alan-baker9bb09792019-03-25 11:25:13 -0400539 CustomDiagnosticsIDMap[CustomDiagnosticSmallStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500540 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400541 "vectors with a total size less than or equal to 16 "
542 "bytes must be placed entirely within a 16 byte "
543 "aligned region");
544 CustomDiagnosticsIDMap[CustomDiagnosticLargeStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500545 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400546 "vectors with a total size greater than 16 bytes "
547 "must aligned to 16 bytes");
548 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedStructMember] =
alan-bakerfec0a472018-11-08 18:09:40 -0500549 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400550 "a structure member must not be placed between the "
551 "end of a structure or array and the next multiple "
552 "of the base alignment of that structure or array");
alan-bakerfec0a472018-11-08 18:09:40 -0500553 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedSize] =
554 DE.getCustomDiagID(DiagnosticsEngine::Error,
555 "clspv restriction: UBO element size must be a "
556 "multiple of that element's alignment");
557 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedStruct] =
558 DE.getCustomDiagID(
559 DiagnosticsEngine::Error,
560 "clspv restriction: UBO structures may not have implicit padding");
alan-baker3d9e2012019-01-11 14:55:30 -0500561 CustomDiagnosticsIDMap[CustomDiagnosticUBOArrayStride] = DE.getCustomDiagID(
562 DiagnosticsEngine::Error,
563 "clspv restriction: to satisfy UBO ArrayStride restrictions, element "
564 "size must be a multiple of array alignment");
565 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo] =
566 DE.getCustomDiagID(DiagnosticsEngine::Note, "here");
alan-baker9bb09792019-03-25 11:25:13 -0400567 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedArray] =
568 DE.getCustomDiagID(
569 DiagnosticsEngine::Error,
570 "in a SSBO, arrays must be aligned to their element alignment");
571 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedStruct] =
572 DE.getCustomDiagID(DiagnosticsEngine::Error,
573 "in a SSBO, structs must be aligned to their "
574 "largest element alignment");
Kévin Petit0fc88042019-04-09 23:25:02 +0100575 CustomDiagnosticsIDMap[CustomDiagnosticOverloadedKernel] =
576 DE.getCustomDiagID(DiagnosticsEngine::Error,
577 "kernel functions can't be overloaded");
alan-baker990e9b92019-06-07 11:26:39 -0400578 CustomDiagnosticsIDMap[CustomDiagnosticStructContainsPointer] =
579 DE.getCustomDiagID(DiagnosticsEngine::Error,
580 "structures may not contain pointers");
alan-baker28361f72020-01-07 16:35:25 -0500581 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct] =
582 DE.getCustomDiagID(DiagnosticsEngine::Error,
583 "recursive structures are not supported");
alan-baker9b0ec3c2020-04-06 14:45:34 -0400584 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantSizeExceeded] =
585 DE.getCustomDiagID(DiagnosticsEngine::Error,
586 "max push constant size exceeded");
587 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantContainsArray] =
588 DE.getCustomDiagID(
589 DiagnosticsEngine::Error,
590 "arrays are not supported in push constants currently");
alan-baker7efcaaa2020-05-06 19:33:27 -0400591 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported16BitStorage] =
592 DE.getCustomDiagID(DiagnosticsEngine::Error,
593 "16-bit storage is not supported for "
594 "%select{SSBOs|UBOs|push constants}0");
595 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported8BitStorage] =
596 DE.getCustomDiagID(DiagnosticsEngine::Error,
597 "8-bit storage is not supported for "
598 "%select{SSBOs|UBOs|push constants}0");
alan-bakerfec0a472018-11-08 18:09:40 -0500599 }
600
601 virtual bool HandleTopLevelDecl(DeclGroupRef DG) override {
602 for (auto *D : DG) {
603 if (auto *FD = llvm::dyn_cast<FunctionDecl>(D)) {
604 // If the function has a body it means we are not an OpenCL builtin
605 // function.
606 if (FD->hasBody()) {
607 if (!IsSupportedType(FD->getReturnType(),
Marco Antognini535998c2020-09-16 18:48:51 +0100608 FD->getReturnTypeSourceRange(), false)) {
alan-bakerfec0a472018-11-08 18:09:40 -0500609 return false;
610 }
611
612 bool is_opencl_kernel = false;
613 if (FD->hasAttrs()) {
614 for (auto *attr : FD->attrs()) {
615 if (attr->getKind() == attr::Kind::OpenCLKernel) {
616 is_opencl_kernel = true;
617 }
618 }
619 }
620
Kévin Petit0fc88042019-04-09 23:25:02 +0100621 if (is_opencl_kernel) {
alan-baker21574d32020-01-29 16:00:31 -0500622 if (Kernels.count(FD->getName().str()) != 0) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100623 auto srcRange = FD->getSourceRange();
624 Report(CustomDiagnosticOverloadedKernel, srcRange, srcRange);
625 } else {
alan-baker21574d32020-01-29 16:00:31 -0500626 Kernels.insert(FD->getName().str());
Kévin Petit0fc88042019-04-09 23:25:02 +0100627 }
628 }
629
alan-baker9b0ec3c2020-04-06 14:45:34 -0400630 RecordDecl *clustered_args = nullptr;
631 if (is_opencl_kernel && clspv::Option::PodArgsInPushConstants()) {
632 clustered_args = FD->getASTContext().buildImplicitRecord(
633 "__clspv.clustered_args." + std::to_string(kClusteredCount++));
634 clustered_args->startDefinition();
635 }
alan-bakerfec0a472018-11-08 18:09:40 -0500636 for (auto *P : FD->parameters()) {
637 auto type = P->getType();
Marco Antognini535998c2020-09-16 18:48:51 +0100638 if (!IsSupportedType(P->getOriginalType(), P->getSourceRange(),
639 is_opencl_kernel)) {
alan-bakerfec0a472018-11-08 18:09:40 -0500640 return false;
641 }
642
alan-baker9bb09792019-03-25 11:25:13 -0400643 if (is_opencl_kernel && type->isPointerType() &&
644 ((type->getPointeeType().getAddressSpace() ==
645 LangAS::opencl_constant) ||
646 (type->getPointeeType().getAddressSpace() ==
647 LangAS::opencl_global))) {
alan-baker3d9e2012019-01-11 14:55:30 -0500648 // The argument will be generated as an array within a block.
649 // Generate an array type to check the validity for the generated
650 // case.
alan-baker9bb09792019-03-25 11:25:13 -0400651 Layout layout = SSBO;
652 if (clspv::Option::ConstantArgsInUniformBuffer() &&
653 !clspv::Option::Std430UniformBufferLayout() &&
654 type->getPointeeType().getAddressSpace() ==
655 LangAS::opencl_constant) {
656 layout = UBO;
657 }
alan-baker3d9e2012019-01-11 14:55:30 -0500658 auto array_type = FD->getASTContext().getIncompleteArrayType(
659 type->getPointeeType(), clang::ArrayType::Normal, 0);
alan-baker9bb09792019-03-25 11:25:13 -0400660 if (!IsSupportedLayout(array_type, 0, layout, FD->getASTContext(),
661 P->getSourceRange(),
662 P->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500663 return false;
664 }
665 }
alan-baker038e9242019-04-19 22:14:41 -0400666
alan-baker7efcaaa2020-05-06 19:33:27 -0400667 // Check if storage capabilities are supported.
668 if (is_opencl_kernel) {
669 bool contains_16bit =
670 ContainsSizedType(type.getCanonicalType(), 16);
671 bool contains_8bit =
672 ContainsSizedType(type.getCanonicalType(), 8);
673 auto sc = clspv::Option::StorageClass::kSSBO;
674 if (type->isPointerType()) {
675 sc = ConvertToStorageClass(
676 type->getPointeeType().getAddressSpace());
677 } else if (clspv::Option::PodArgsInUniformBuffer()) {
678 sc = clspv::Option::StorageClass::kUBO;
679 } else if (clspv::Option::PodArgsInPushConstants()) {
680 sc = clspv::Option::StorageClass::kPushConstant;
681 }
alan-bakerfb7e2be2020-05-25 08:45:39 -0400682
683 if (type->isPointerType() ||
684 sc != clspv::Option::StorageClass::kSSBO ||
685 !clspv::Option::ClusterPodKernelArgs()) {
686 // For clustered pod args, assume we can fall back on
687 // type-mangling.
688 if (contains_16bit &&
689 !clspv::Option::Supports16BitStorageClass(sc)) {
690 Instance.getDiagnostics().Report(
691 P->getSourceRange().getBegin(),
692 CustomDiagnosticsIDMap
693 [CustomDiagnosticUnsupported16BitStorage])
694 << static_cast<int>(sc);
695 }
696 if (contains_8bit &&
697 !clspv::Option::Supports8BitStorageClass(sc)) {
698 Instance.getDiagnostics().Report(
699 P->getSourceRange().getBegin(),
700 CustomDiagnosticsIDMap
701 [CustomDiagnosticUnsupported8BitStorage])
702 << static_cast<int>(sc);
703 }
alan-baker7efcaaa2020-05-06 19:33:27 -0400704 }
705 }
706
alan-baker28361f72020-01-07 16:35:25 -0500707 if (is_opencl_kernel && type->isPointerType()) {
708 auto pointee_type = type->getPointeeType().getCanonicalType();
709 if (ContainsPointerType(pointee_type)) {
710 Instance.getDiagnostics().Report(
711 P->getSourceRange().getBegin(),
712 CustomDiagnosticsIDMap
713 [CustomDiagnosticStructContainsPointer]);
714 return false;
715 }
716 }
717
alan-baker038e9242019-04-19 22:14:41 -0400718 if (is_opencl_kernel && !type->isPointerType()) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400719 if (clspv::Option::PodArgsInPushConstants()) {
720 // Don't allow arrays in push constants currently.
721 if (ContainsArrayType(type)) {
722 Report(CustomDiagnosticPushConstantContainsArray,
723 P->getSourceRange(), P->getSourceRange());
724 return false;
725 }
726 FieldDecl *field_decl = FieldDecl::Create(
727 FD->getASTContext(),
728 Decl::castToDeclContext(clustered_args),
729 P->getSourceRange().getBegin(),
730 P->getSourceRange().getEnd(), P->getIdentifier(),
731 P->getType(), nullptr, nullptr, false, ICIS_NoInit);
732 field_decl->setAccess(AS_public);
733 clustered_args->addDecl(field_decl);
734 } else {
735 Layout layout = SSBO;
736 if (clspv::Option::PodArgsInUniformBuffer() &&
737 !clspv::Option::Std430UniformBufferLayout())
738 layout = UBO;
alan-baker038e9242019-04-19 22:14:41 -0400739
alan-baker9b0ec3c2020-04-06 14:45:34 -0400740 if (!IsSupportedLayout(type, 0, layout, FD->getASTContext(),
741 P->getSourceRange(),
742 P->getSourceRange())) {
743 return false;
744 }
745 }
746 }
747 }
748
749 if (clustered_args) {
750 clustered_args->completeDefinition();
751 if (!clustered_args->field_empty()) {
752 auto record_type =
753 FD->getASTContext().getRecordType(clustered_args);
754 if (!IsSupportedLayout(record_type, 0, SSBO, FD->getASTContext(),
755 FD->getSourceRange(),
756 FD->getSourceRange())) {
757 return false;
758 }
759
760 if (FD->getASTContext()
761 .getTypeSizeInChars(record_type)
762 .getQuantity() > clspv::Option::MaxPushConstantsSize()) {
763 Report(CustomDiagnosticPushConstantSizeExceeded,
764 FD->getSourceRange(), FD->getSourceRange());
alan-baker038e9242019-04-19 22:14:41 -0400765 return false;
766 }
767 }
alan-bakerfec0a472018-11-08 18:09:40 -0500768 }
769
770 // Check for unsupported vector types.
771 Visitor.TraverseDecl(FD);
772 }
773 }
774 }
775
776 return true;
777 }
778};
779} // namespace
780
781namespace clspv {
782std::unique_ptr<ASTConsumer>
783ExtraValidationASTAction::CreateASTConsumer(CompilerInstance &CI,
784 llvm::StringRef InFile) {
785 return std::unique_ptr<ASTConsumer>(new ExtraValidationConsumer(CI, InFile));
786}
787} // namespace clspv