blob: 9a7cbb9195ab89f463ae2fcdf8fd5d80c3ad3443 [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
23#include "clspv/Option.h"
24
25#include "FrontendPlugin.h"
26
Kévin Petit0fc88042019-04-09 23:25:02 +010027#include <unordered_set>
28
alan-bakerfec0a472018-11-08 18:09:40 -050029using namespace clang;
30
31namespace {
alan-baker9b0ec3c2020-04-06 14:45:34 -040032
33static uint32_t kClusteredCount = 0;
34
alan-bakerfec0a472018-11-08 18:09:40 -050035struct ExtraValidationConsumer final : public ASTConsumer {
36private:
37 CompilerInstance &Instance;
38 llvm::StringRef InFile;
39
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040040 enum Layout { UBO, SSBO };
alan-baker9bb09792019-03-25 11:25:13 -040041
alan-bakerfec0a472018-11-08 18:09:40 -050042 enum CustomDiagnosticType {
43 CustomDiagnosticVectorsMoreThan4Elements = 0,
44 CustomDiagnosticVoidPointer = 1,
alan-baker9bb09792019-03-25 11:25:13 -040045 CustomDiagnosticUnalignedScalar = 2,
46 CustomDiagnosticUnalignedVec2 = 3,
47 CustomDiagnosticUnalignedVec4 = 4,
alan-bakerfec0a472018-11-08 18:09:40 -050048 CustomDiagnosticUBOUnalignedArray = 5,
49 CustomDiagnosticUBOUnalignedStruct = 6,
alan-baker9bb09792019-03-25 11:25:13 -040050 CustomDiagnosticSmallStraddle = 7,
51 CustomDiagnosticLargeStraddle = 8,
52 CustomDiagnosticUnalignedStructMember = 9,
alan-bakerfec0a472018-11-08 18:09:40 -050053 CustomDiagnosticUBORestrictedSize = 10,
54 CustomDiagnosticUBORestrictedStruct = 11,
alan-baker3d9e2012019-01-11 14:55:30 -050055 CustomDiagnosticUBOArrayStride = 12,
56 CustomDiagnosticLocationInfo = 13,
alan-baker9bb09792019-03-25 11:25:13 -040057 CustomDiagnosticSSBOUnalignedArray = 14,
58 CustomDiagnosticSSBOUnalignedStruct = 15,
Kévin Petit0fc88042019-04-09 23:25:02 +010059 CustomDiagnosticOverloadedKernel = 16,
alan-baker990e9b92019-06-07 11:26:39 -040060 CustomDiagnosticStructContainsPointer = 17,
alan-baker28361f72020-01-07 16:35:25 -050061 CustomDiagnosticRecursiveStruct = 18,
alan-baker9b0ec3c2020-04-06 14:45:34 -040062 CustomDiagnosticPushConstantSizeExceeded = 19,
63 CustomDiagnosticPushConstantContainsArray = 20,
alan-baker7efcaaa2020-05-06 19:33:27 -040064 CustomDiagnosticUnsupported16BitStorage = 21,
65 CustomDiagnosticUnsupported8BitStorage = 22,
alan-bakerfec0a472018-11-08 18:09:40 -050066 CustomDiagnosticTotal
67 };
68 std::vector<unsigned> CustomDiagnosticsIDMap;
69
alan-baker7efcaaa2020-05-06 19:33:27 -040070 clspv::Option::StorageClass ConvertToStorageClass(clang::LangAS aspace) {
71 switch (aspace) {
72 case LangAS::opencl_constant:
73 if (clspv::Option::ConstantArgsInUniformBuffer()) {
74 return clspv::Option::StorageClass::kUBO;
75 } else {
76 return clspv::Option::StorageClass::kSSBO;
77 }
78 case LangAS::opencl_global:
79 default:
80 return clspv::Option::StorageClass::kSSBO;
81 }
82 }
83
84 bool ContainsSizedType(QualType QT, uint32_t width) {
85 auto canonical = QT.getCanonicalType();
86 if (auto *BT = dyn_cast<BuiltinType>(canonical)) {
87 switch (BT->getKind()) {
88 case BuiltinType::UShort:
89 case BuiltinType::Short:
90 case BuiltinType::Half:
91 case BuiltinType::Float16:
92 return width == 16;
93 case BuiltinType::UChar:
94 case BuiltinType::Char_U:
95 case BuiltinType::SChar:
96 case BuiltinType::Char_S:
97 return width == 8;
98 default:
99 return false;
100 }
101 } else if (auto *PT = dyn_cast<PointerType>(canonical)) {
102 return ContainsSizedType(PT->getPointeeType(), width);
103 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
104 return ContainsSizedType(AT->getElementType(), width);
105 } else if (auto *VT = dyn_cast<VectorType>(canonical)) {
106 return ContainsSizedType(VT->getElementType(), width);
107 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
108 for (auto field_decl : RT->getDecl()->fields()) {
109 if (ContainsSizedType(field_decl->getType(), width))
110 return true;
111 }
112 }
113
114 return false;
115 }
116
alan-baker990e9b92019-06-07 11:26:39 -0400117 bool ContainsPointerType(QualType QT) {
118 auto canonical = QT.getCanonicalType();
119 if (canonical->isPointerType()) {
120 return true;
121 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
122 return ContainsPointerType(AT->getElementType());
123 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
124 for (auto field_decl : RT->getDecl()->fields()) {
125 if (ContainsPointerType(field_decl->getType()))
126 return true;
127 }
128 }
129
130 return false;
131 }
132
alan-baker9b0ec3c2020-04-06 14:45:34 -0400133 bool ContainsArrayType(QualType QT) {
134 auto canonical = QT.getCanonicalType();
135 if (auto *PT = dyn_cast<PointerType>(canonical)) {
136 return ContainsArrayType(PT->getPointeeType());
alan-baker4a757f62020-04-22 08:17:49 -0400137 } else if (isa<ArrayType>(canonical)) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400138 return true;
139 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
140 for (auto field_decl : RT->getDecl()->fields()) {
141 if (ContainsArrayType(field_decl->getType()))
142 return true;
143 }
144 }
145
146 return false;
147 }
148
alan-baker28361f72020-01-07 16:35:25 -0500149 bool IsRecursiveType(QualType QT, llvm::DenseSet<const Type *> *seen) {
150 auto canonical = QT.getCanonicalType();
151 if (canonical->isRecordType() &&
152 !seen->insert(canonical.getTypePtr()).second) {
153 return true;
154 }
155
156 if (auto *PT = dyn_cast<PointerType>(canonical)) {
alan-baker4a757f62020-04-22 08:17:49 -0400157 return IsRecursiveType(PT->getPointeeType(), seen);
alan-baker28361f72020-01-07 16:35:25 -0500158 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
159 return IsRecursiveType(AT->getElementType(), seen);
160 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
161 for (auto field_decl : RT->getDecl()->fields()) {
162 if (IsRecursiveType(field_decl->getType(), seen))
163 return true;
164 }
165 }
166
167 seen->erase(canonical.getTypePtr());
168 return false;
169 }
170
alan-bakerfec0a472018-11-08 18:09:40 -0500171 bool IsSupportedType(QualType QT, SourceRange SR) {
172 auto *Ty = QT.getTypePtr();
173
174 // First check if we have a pointer type.
175 if (Ty->isPointerType()) {
176 const Type *pointeeTy = Ty->getPointeeType().getTypePtr();
177 if (pointeeTy && pointeeTy->isVoidType()) {
178 // We don't support void pointers.
179 Instance.getDiagnostics().Report(
180 SR.getBegin(), CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer]);
181 return false;
182 }
183 // Otherwise check recursively.
184 return IsSupportedType(Ty->getPointeeType(), SR);
185 }
186
187 const auto &canonicalType = QT.getCanonicalType();
188 if (auto *VT = llvm::dyn_cast<ExtVectorType>(canonicalType)) {
189 // We don't support vectors with more than 4 elements.
190 if (4 < VT->getNumElements()) {
191 Instance.getDiagnostics().Report(
192 SR.getBegin(),
193 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements]);
194 return false;
195 }
alan-baker990e9b92019-06-07 11:26:39 -0400196 } else if (canonicalType->isRecordType()) {
alan-baker28361f72020-01-07 16:35:25 -0500197 // Do not allow recursive struct definitions.
198 llvm::DenseSet<const Type *> seen;
199 if (IsRecursiveType(canonicalType, &seen)) {
alan-baker990e9b92019-06-07 11:26:39 -0400200 Instance.getDiagnostics().Report(
201 SR.getBegin(),
alan-baker28361f72020-01-07 16:35:25 -0500202 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct]);
alan-baker990e9b92019-06-07 11:26:39 -0400203 return false;
204 }
alan-bakerfec0a472018-11-08 18:09:40 -0500205 }
206
207 return true;
208 }
209
alan-baker3d9e2012019-01-11 14:55:30 -0500210 // Report a diagnostic using |diag|. If |arg_range| and |specific_range|
211 // differ, also issue a note with the specific location of the error.
212 void Report(const CustomDiagnosticType &diag, SourceRange arg_range,
213 SourceRange specific_range) {
214 Instance.getDiagnostics().Report(arg_range.getBegin(),
215 CustomDiagnosticsIDMap[diag]);
216 if (arg_range != specific_range) {
217 Instance.getDiagnostics().Report(
218 specific_range.getBegin(),
219 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo]);
220 }
221 }
222
alan-baker9bb09792019-03-25 11:25:13 -0400223 // Returns the alignment of |QT| to satisfy |layout|'s rules.
224 uint64_t GetAlignment(const QualType QT, const Layout &layout,
225 const ASTContext &context) const {
alan-bakerfec0a472018-11-08 18:09:40 -0500226 const auto canonical = QT.getCanonicalType();
227 uint64_t alignment = context.getTypeAlignInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400228 if (layout == UBO &&
229 (canonical->isRecordType() || canonical->isArrayType())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500230 return llvm::alignTo(alignment, 16);
231 }
232 return alignment;
233 }
234
235 // Returns true if |QT| is a valid layout for a Uniform buffer. Refer to
236 // 14.5.4 in the Vulkan specification.
alan-baker9bb09792019-03-25 11:25:13 -0400237 bool IsSupportedLayout(QualType QT, uint64_t offset, const Layout &layout,
238 ASTContext &context, SourceRange arg_range,
239 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500240 const auto canonical = QT.getCanonicalType();
241 if (canonical->isScalarType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400242 if (!IsSupportedScalarLayout(canonical, offset, layout, context,
243 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500244 return false;
245 } else if (canonical->isExtVectorType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400246 if (!IsSupportedVectorLayout(canonical, offset, layout, context,
247 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500248 return false;
249 } else if (canonical->isArrayType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400250 if (!IsSupportedArrayLayout(canonical, offset, layout, context, arg_range,
251 specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500252 return false;
253 } else if (canonical->isRecordType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400254 if (!IsSupportedRecordLayout(canonical, offset, layout, context,
255 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500256 return false;
257 }
258
259 // TODO(alan-baker): Find a way to avoid this restriction.
260 // Don't allow padding. This prevents structs like:
261 // struct {
262 // int x[2];
263 // int y __attribute((aligned(16)));
264 // };
265 //
266 // This would map in LLVM to { [2 x i32], [8 x i8], i32, [12 xi8] }.
267 // There is no easy way to manipulate the padding after the array to
268 // satisfy the standard Uniform buffer layout rules in this case. The usual
269 // trick is replacing the i8 arrays with an i32 element, but the i32 would
270 // still be laid out too close to the array.
271 const auto type_size = context.getTypeSizeInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400272 const auto type_align = GetAlignment(canonical, layout, context);
273 if (layout == UBO && (type_size % type_align != 0)) {
alan-baker3d9e2012019-01-11 14:55:30 -0500274 Report(CustomDiagnosticUBORestrictedSize, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500275 return false;
276 }
277
278 return true;
279 }
280
alan-baker9bb09792019-03-25 11:25:13 -0400281 bool IsSupportedScalarLayout(QualType QT, uint64_t offset,
282 const Layout & /*layout*/, ASTContext &context,
283 SourceRange arg_range,
284 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500285 // A scalar type of size N has a base alignment on N.
286 const unsigned type_size = context.getTypeSizeInChars(QT).getQuantity();
287 if (offset % type_size != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400288 Report(CustomDiagnosticUnalignedScalar, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500289 return false;
290 }
291
292 return true;
293 }
294
alan-baker9bb09792019-03-25 11:25:13 -0400295 bool IsSupportedVectorLayout(QualType QT, uint64_t offset,
296 const Layout &layout, ASTContext &context,
297 SourceRange arg_range,
298 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500299 // 2-component vectors have a base alignment of 2 * (size of element).
300 // 3- and 4-component vectors hae a base alignment of 4 * (size of
301 // element).
302 const auto *VT = llvm::cast<VectorType>(QT);
303 const auto ele_size =
304 context.getTypeSizeInChars(VT->getElementType()).getQuantity();
305 if (VT->getNumElements() == 2) {
306 if (offset % (ele_size * 2) != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400307 Report(CustomDiagnosticUnalignedVec2, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500308 return false;
309 }
310 } else if (offset % (ele_size * 4) != 0) {
311 // Other vector sizes cause errors elsewhere.
alan-baker9bb09792019-03-25 11:25:13 -0400312 Report(CustomDiagnosticUnalignedVec4, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500313 return false;
314 }
315
316 // Straddling rules:
317 // * If total vector size is less than 16 bytes, the offset must place the
318 // entire vector within the same 16 bytes.
319 // * If total vector size is greater than 16 bytes, the offset must be a
320 // multiple of 16.
321 const auto size = context.getTypeSizeInChars(QT).getQuantity();
322 if (size <= 16 && (offset / 16 != (offset + size - 1) / 16)) {
alan-baker9bb09792019-03-25 11:25:13 -0400323 Report(CustomDiagnosticSmallStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500324 return false;
325 } else if (size > 16 && (offset % 16 != 0)) {
alan-baker9bb09792019-03-25 11:25:13 -0400326 Report(CustomDiagnosticLargeStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500327 return false;
328 }
329
alan-baker9bb09792019-03-25 11:25:13 -0400330 return IsSupportedLayout(VT->getElementType(), offset, layout, context,
331 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500332 }
333
alan-baker9bb09792019-03-25 11:25:13 -0400334 bool IsSupportedArrayLayout(QualType QT, uint64_t offset,
335 const Layout &layout, ASTContext &context,
336 SourceRange arg_range,
337 SourceRange specific_range) {
338 // An array has a base alignment of is element type.
339 // If the layout is UBO, the alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500340 const auto *AT = llvm::cast<ArrayType>(QT);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400341 const auto element_align =
342 GetAlignment(AT->getElementType(), layout, context);
alan-baker9bb09792019-03-25 11:25:13 -0400343 const auto type_align =
344 layout == UBO ? llvm::alignTo(element_align, 16) : element_align;
alan-bakerfec0a472018-11-08 18:09:40 -0500345 if (offset % type_align != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400346 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedArray
347 : CustomDiagnosticSSBOUnalignedArray;
348 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500349 return false;
350 }
alan-baker9bb09792019-03-25 11:25:13 -0400351 if (layout == UBO && !clspv::Option::RelaxedUniformBufferLayout()) {
alan-baker3d9e2012019-01-11 14:55:30 -0500352 // The ArrayStride must be a multiple of the base alignment of the array
353 // (i.e. a multiple of 16). This means that the element size must be
354 // restricted to be the base alignment of the array.
355 const auto element_size =
356 context.getTypeSizeInChars(AT->getElementType()).getQuantity();
357 if (element_size % type_align != 0) {
358 Report(CustomDiagnosticUBOArrayStride, arg_range, specific_range);
359 return false;
360 }
361 }
alan-bakerfec0a472018-11-08 18:09:40 -0500362
alan-baker9bb09792019-03-25 11:25:13 -0400363 return IsSupportedLayout(AT->getElementType(), offset, layout, context,
364 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500365 }
366
alan-baker9bb09792019-03-25 11:25:13 -0400367 bool IsSupportedRecordLayout(QualType QT, uint64_t offset,
368 const Layout &layout, ASTContext &context,
369 SourceRange arg_range,
370 SourceRange specific_range) {
371 // A structure has a base alignment of its largest member. For UBO layouts,
372 // alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500373 const auto *RT = llvm::cast<RecordType>(QT);
alan-baker9bb09792019-03-25 11:25:13 -0400374 auto type_alignment = GetAlignment(QT, layout, context);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400375 if (layout == UBO)
376 llvm::alignTo(type_alignment, 16);
alan-bakerfec0a472018-11-08 18:09:40 -0500377 if (offset % type_alignment != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400378 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedStruct
379 : CustomDiagnosticSSBOUnalignedStruct;
380 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500381 return false;
382 }
383
alan-baker9bb09792019-03-25 11:25:13 -0400384 const auto &record_layout = context.getASTRecordLayout(RT->getDecl());
alan-bakerfec0a472018-11-08 18:09:40 -0500385 const FieldDecl *prev = nullptr;
386 for (auto field_decl : RT->getDecl()->fields()) {
387 const auto field_type = field_decl->getType();
alan-bakerfec0a472018-11-08 18:09:40 -0500388 const unsigned field_no = field_decl->getFieldIndex();
389 const uint64_t field_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400390 record_layout.getFieldOffset(field_no) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500391
392 // Rules must be checked recursively.
alan-baker9bb09792019-03-25 11:25:13 -0400393 if (!IsSupportedLayout(field_type, field_offset + offset, layout, context,
394 arg_range, field_decl->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500395 return false;
396 }
397
398 if (prev) {
399 const auto prev_canonical = prev->getType().getCanonicalType();
400 const uint64_t prev_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400401 record_layout.getFieldOffset(field_no - 1) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500402 const auto prev_size =
403 context.getTypeSizeInChars(prev_canonical).getQuantity();
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400404 const auto prev_alignment =
405 GetAlignment(prev_canonical, layout, context);
alan-bakerfec0a472018-11-08 18:09:40 -0500406 const auto next_available =
407 prev_offset + llvm::alignTo(prev_size, prev_alignment);
408 if (prev_canonical->isArrayType() || prev_canonical->isRecordType()) {
409 // The next element after an array or struct must be placed on or
410 // after the next multiple of the alignment of that array or
411 // struct.
alan-baker9bb09792019-03-25 11:25:13 -0400412 // For UBO layouts, both arrays and structs must be aligned to a
413 // multiple of 16 bytes.
414 const uint64_t final_align = layout == UBO
415 ? llvm::alignTo(next_available, 16)
416 : next_available;
417 if (final_align > field_offset) {
418 Report(CustomDiagnosticUnalignedStructMember, arg_range,
alan-baker3d9e2012019-01-11 14:55:30 -0500419 field_decl->getSourceRange());
alan-bakerfec0a472018-11-08 18:09:40 -0500420 return false;
421 }
422 }
423 }
424
425 prev = field_decl;
426 }
427
428 return true;
429 }
430
431 // This will be used to check the inside of function bodies.
432 class DeclVisitor : public RecursiveASTVisitor<DeclVisitor> {
433 private:
434 ExtraValidationConsumer &consumer;
435
436 public:
437 explicit DeclVisitor(ExtraValidationConsumer &VC) : consumer(VC) {}
438
439 // Visits a declaration. Emits a diagnostic and returns false if the
440 // declaration represents an unsupported vector value or vector type.
441 // Otherwise returns true.
442 bool VisitDecl(Decl *D) {
443 // Looking at the Decl class hierarchy, it seems ValueDecl and TypeDecl
444 // are the only two that might represent an unsupported vector type.
445 if (auto *VD = dyn_cast<ValueDecl>(D)) {
446 return consumer.IsSupportedType(VD->getType(), D->getSourceRange());
447 } else if (auto *TD = dyn_cast<TypeDecl>(D)) {
448 QualType DefinedType = TD->getASTContext().getTypeDeclType(TD);
449 return consumer.IsSupportedType(DefinedType, TD->getSourceRange());
450 }
451 return true;
452 }
453 };
454
455 DeclVisitor Visitor;
Kévin Petit0fc88042019-04-09 23:25:02 +0100456 std::unordered_set<std::string> Kernels;
alan-bakerfec0a472018-11-08 18:09:40 -0500457
458public:
459 explicit ExtraValidationConsumer(CompilerInstance &Instance,
460 llvm::StringRef InFile)
461 : Instance(Instance), InFile(InFile),
462 CustomDiagnosticsIDMap(CustomDiagnosticTotal), Visitor(*this) {
463 auto &DE = Instance.getDiagnostics();
464
465 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements] =
466 DE.getCustomDiagID(
467 DiagnosticsEngine::Error,
468 "vectors with more than 4 elements are not supported");
469 CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer] = DE.getCustomDiagID(
470 DiagnosticsEngine::Error, "pointer-to-void is not supported");
alan-baker9bb09792019-03-25 11:25:13 -0400471 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedScalar] =
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400472 DE.getCustomDiagID(DiagnosticsEngine::Error,
473 "scalar elements must be aligned to their size");
alan-baker9bb09792019-03-25 11:25:13 -0400474 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec2] = DE.getCustomDiagID(
475 DiagnosticsEngine::Error,
476 "two-component vectors must be aligned to 2 times their element size");
477 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec4] =
alan-bakerfec0a472018-11-08 18:09:40 -0500478 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400479 "three- and four-component vectors must be aligned "
480 "to 4 times their element size");
alan-bakerfec0a472018-11-08 18:09:40 -0500481 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedArray] =
482 DE.getCustomDiagID(DiagnosticsEngine::Error,
483 "in an UBO, arrays must be aligned to their element "
484 "alignment, rounded up to a multiple of 16 bytes");
485 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedStruct] =
486 DE.getCustomDiagID(
487 DiagnosticsEngine::Error,
488 "in an UBO, structs must be aligned to their "
489 "largest element alignment, rounded up to a multiple of "
490 "16 bytes");
alan-baker9bb09792019-03-25 11:25:13 -0400491 CustomDiagnosticsIDMap[CustomDiagnosticSmallStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500492 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400493 "vectors with a total size less than or equal to 16 "
494 "bytes must be placed entirely within a 16 byte "
495 "aligned region");
496 CustomDiagnosticsIDMap[CustomDiagnosticLargeStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500497 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400498 "vectors with a total size greater than 16 bytes "
499 "must aligned to 16 bytes");
500 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedStructMember] =
alan-bakerfec0a472018-11-08 18:09:40 -0500501 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400502 "a structure member must not be placed between the "
503 "end of a structure or array and the next multiple "
504 "of the base alignment of that structure or array");
alan-bakerfec0a472018-11-08 18:09:40 -0500505 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedSize] =
506 DE.getCustomDiagID(DiagnosticsEngine::Error,
507 "clspv restriction: UBO element size must be a "
508 "multiple of that element's alignment");
509 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedStruct] =
510 DE.getCustomDiagID(
511 DiagnosticsEngine::Error,
512 "clspv restriction: UBO structures may not have implicit padding");
alan-baker3d9e2012019-01-11 14:55:30 -0500513 CustomDiagnosticsIDMap[CustomDiagnosticUBOArrayStride] = DE.getCustomDiagID(
514 DiagnosticsEngine::Error,
515 "clspv restriction: to satisfy UBO ArrayStride restrictions, element "
516 "size must be a multiple of array alignment");
517 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo] =
518 DE.getCustomDiagID(DiagnosticsEngine::Note, "here");
alan-baker9bb09792019-03-25 11:25:13 -0400519 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedArray] =
520 DE.getCustomDiagID(
521 DiagnosticsEngine::Error,
522 "in a SSBO, arrays must be aligned to their element alignment");
523 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedStruct] =
524 DE.getCustomDiagID(DiagnosticsEngine::Error,
525 "in a SSBO, structs must be aligned to their "
526 "largest element alignment");
Kévin Petit0fc88042019-04-09 23:25:02 +0100527 CustomDiagnosticsIDMap[CustomDiagnosticOverloadedKernel] =
528 DE.getCustomDiagID(DiagnosticsEngine::Error,
529 "kernel functions can't be overloaded");
alan-baker990e9b92019-06-07 11:26:39 -0400530 CustomDiagnosticsIDMap[CustomDiagnosticStructContainsPointer] =
531 DE.getCustomDiagID(DiagnosticsEngine::Error,
532 "structures may not contain pointers");
alan-baker28361f72020-01-07 16:35:25 -0500533 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct] =
534 DE.getCustomDiagID(DiagnosticsEngine::Error,
535 "recursive structures are not supported");
alan-baker9b0ec3c2020-04-06 14:45:34 -0400536 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantSizeExceeded] =
537 DE.getCustomDiagID(DiagnosticsEngine::Error,
538 "max push constant size exceeded");
539 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantContainsArray] =
540 DE.getCustomDiagID(
541 DiagnosticsEngine::Error,
542 "arrays are not supported in push constants currently");
alan-baker7efcaaa2020-05-06 19:33:27 -0400543 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported16BitStorage] =
544 DE.getCustomDiagID(DiagnosticsEngine::Error,
545 "16-bit storage is not supported for "
546 "%select{SSBOs|UBOs|push constants}0");
547 CustomDiagnosticsIDMap[CustomDiagnosticUnsupported8BitStorage] =
548 DE.getCustomDiagID(DiagnosticsEngine::Error,
549 "8-bit storage is not supported for "
550 "%select{SSBOs|UBOs|push constants}0");
alan-bakerfec0a472018-11-08 18:09:40 -0500551 }
552
553 virtual bool HandleTopLevelDecl(DeclGroupRef DG) override {
554 for (auto *D : DG) {
555 if (auto *FD = llvm::dyn_cast<FunctionDecl>(D)) {
556 // If the function has a body it means we are not an OpenCL builtin
557 // function.
558 if (FD->hasBody()) {
559 if (!IsSupportedType(FD->getReturnType(),
560 FD->getReturnTypeSourceRange())) {
561 return false;
562 }
563
564 bool is_opencl_kernel = false;
565 if (FD->hasAttrs()) {
566 for (auto *attr : FD->attrs()) {
567 if (attr->getKind() == attr::Kind::OpenCLKernel) {
568 is_opencl_kernel = true;
569 }
570 }
571 }
572
Kévin Petit0fc88042019-04-09 23:25:02 +0100573 if (is_opencl_kernel) {
alan-baker21574d32020-01-29 16:00:31 -0500574 if (Kernels.count(FD->getName().str()) != 0) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100575 auto srcRange = FD->getSourceRange();
576 Report(CustomDiagnosticOverloadedKernel, srcRange, srcRange);
577 } else {
alan-baker21574d32020-01-29 16:00:31 -0500578 Kernels.insert(FD->getName().str());
Kévin Petit0fc88042019-04-09 23:25:02 +0100579 }
580 }
581
alan-baker9b0ec3c2020-04-06 14:45:34 -0400582 RecordDecl *clustered_args = nullptr;
583 if (is_opencl_kernel && clspv::Option::PodArgsInPushConstants()) {
584 clustered_args = FD->getASTContext().buildImplicitRecord(
585 "__clspv.clustered_args." + std::to_string(kClusteredCount++));
586 clustered_args->startDefinition();
587 }
alan-bakerfec0a472018-11-08 18:09:40 -0500588 for (auto *P : FD->parameters()) {
589 auto type = P->getType();
590 if (!IsSupportedType(P->getOriginalType(), P->getSourceRange())) {
591 return false;
592 }
593
alan-baker9bb09792019-03-25 11:25:13 -0400594 if (is_opencl_kernel && type->isPointerType() &&
595 ((type->getPointeeType().getAddressSpace() ==
596 LangAS::opencl_constant) ||
597 (type->getPointeeType().getAddressSpace() ==
598 LangAS::opencl_global))) {
alan-baker3d9e2012019-01-11 14:55:30 -0500599 // The argument will be generated as an array within a block.
600 // Generate an array type to check the validity for the generated
601 // case.
alan-baker9bb09792019-03-25 11:25:13 -0400602 Layout layout = SSBO;
603 if (clspv::Option::ConstantArgsInUniformBuffer() &&
604 !clspv::Option::Std430UniformBufferLayout() &&
605 type->getPointeeType().getAddressSpace() ==
606 LangAS::opencl_constant) {
607 layout = UBO;
608 }
alan-baker3d9e2012019-01-11 14:55:30 -0500609 auto array_type = FD->getASTContext().getIncompleteArrayType(
610 type->getPointeeType(), clang::ArrayType::Normal, 0);
alan-baker9bb09792019-03-25 11:25:13 -0400611 if (!IsSupportedLayout(array_type, 0, layout, FD->getASTContext(),
612 P->getSourceRange(),
613 P->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500614 return false;
615 }
616 }
alan-baker038e9242019-04-19 22:14:41 -0400617
alan-baker7efcaaa2020-05-06 19:33:27 -0400618 // Check if storage capabilities are supported.
619 if (is_opencl_kernel) {
620 bool contains_16bit =
621 ContainsSizedType(type.getCanonicalType(), 16);
622 bool contains_8bit =
623 ContainsSizedType(type.getCanonicalType(), 8);
624 auto sc = clspv::Option::StorageClass::kSSBO;
625 if (type->isPointerType()) {
626 sc = ConvertToStorageClass(
627 type->getPointeeType().getAddressSpace());
628 } else if (clspv::Option::PodArgsInUniformBuffer()) {
629 sc = clspv::Option::StorageClass::kUBO;
630 } else if (clspv::Option::PodArgsInPushConstants()) {
631 sc = clspv::Option::StorageClass::kPushConstant;
632 }
633 if (contains_16bit &&
634 !clspv::Option::Supports16BitStorageClass(sc)) {
635 Instance.getDiagnostics().Report(
636 P->getSourceRange().getBegin(),
637 CustomDiagnosticsIDMap
638 [CustomDiagnosticUnsupported16BitStorage])
639 << static_cast<int>(sc);
640 }
641 if (contains_8bit &&
642 !clspv::Option::Supports8BitStorageClass(sc)) {
643 Instance.getDiagnostics().Report(
644 P->getSourceRange().getBegin(),
645 CustomDiagnosticsIDMap
646 [CustomDiagnosticUnsupported8BitStorage])
647 << static_cast<int>(sc);
648 }
649 }
650
alan-baker28361f72020-01-07 16:35:25 -0500651 if (is_opencl_kernel && type->isPointerType()) {
652 auto pointee_type = type->getPointeeType().getCanonicalType();
653 if (ContainsPointerType(pointee_type)) {
654 Instance.getDiagnostics().Report(
655 P->getSourceRange().getBegin(),
656 CustomDiagnosticsIDMap
657 [CustomDiagnosticStructContainsPointer]);
658 return false;
659 }
660 }
661
alan-baker038e9242019-04-19 22:14:41 -0400662 if (is_opencl_kernel && !type->isPointerType()) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400663 if (clspv::Option::PodArgsInPushConstants()) {
664 // Don't allow arrays in push constants currently.
665 if (ContainsArrayType(type)) {
666 Report(CustomDiagnosticPushConstantContainsArray,
667 P->getSourceRange(), P->getSourceRange());
668 return false;
669 }
670 FieldDecl *field_decl = FieldDecl::Create(
671 FD->getASTContext(),
672 Decl::castToDeclContext(clustered_args),
673 P->getSourceRange().getBegin(),
674 P->getSourceRange().getEnd(), P->getIdentifier(),
675 P->getType(), nullptr, nullptr, false, ICIS_NoInit);
676 field_decl->setAccess(AS_public);
677 clustered_args->addDecl(field_decl);
678 } else {
679 Layout layout = SSBO;
680 if (clspv::Option::PodArgsInUniformBuffer() &&
681 !clspv::Option::Std430UniformBufferLayout())
682 layout = UBO;
alan-baker038e9242019-04-19 22:14:41 -0400683
alan-baker9b0ec3c2020-04-06 14:45:34 -0400684 if (!IsSupportedLayout(type, 0, layout, FD->getASTContext(),
685 P->getSourceRange(),
686 P->getSourceRange())) {
687 return false;
688 }
689 }
690 }
691 }
692
693 if (clustered_args) {
694 clustered_args->completeDefinition();
695 if (!clustered_args->field_empty()) {
696 auto record_type =
697 FD->getASTContext().getRecordType(clustered_args);
698 if (!IsSupportedLayout(record_type, 0, SSBO, FD->getASTContext(),
699 FD->getSourceRange(),
700 FD->getSourceRange())) {
701 return false;
702 }
703
704 if (FD->getASTContext()
705 .getTypeSizeInChars(record_type)
706 .getQuantity() > clspv::Option::MaxPushConstantsSize()) {
707 Report(CustomDiagnosticPushConstantSizeExceeded,
708 FD->getSourceRange(), FD->getSourceRange());
alan-baker038e9242019-04-19 22:14:41 -0400709 return false;
710 }
711 }
alan-bakerfec0a472018-11-08 18:09:40 -0500712 }
713
714 // Check for unsupported vector types.
715 Visitor.TraverseDecl(FD);
716 }
717 }
718 }
719
720 return true;
721 }
722};
723} // namespace
724
725namespace clspv {
726std::unique_ptr<ASTConsumer>
727ExtraValidationASTAction::CreateASTConsumer(CompilerInstance &CI,
728 llvm::StringRef InFile) {
729 return std::unique_ptr<ASTConsumer>(new ExtraValidationConsumer(CI, InFile));
730}
731} // namespace clspv