blob: 2758d070fbda58e09918a1876b3d0dfb64843d05 [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-bakerfec0a472018-11-08 18:09:40 -050064 CustomDiagnosticTotal
65 };
66 std::vector<unsigned> CustomDiagnosticsIDMap;
67
alan-baker990e9b92019-06-07 11:26:39 -040068 bool ContainsPointerType(QualType QT) {
69 auto canonical = QT.getCanonicalType();
70 if (canonical->isPointerType()) {
71 return true;
72 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
73 return ContainsPointerType(AT->getElementType());
74 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
75 for (auto field_decl : RT->getDecl()->fields()) {
76 if (ContainsPointerType(field_decl->getType()))
77 return true;
78 }
79 }
80
81 return false;
82 }
83
alan-baker9b0ec3c2020-04-06 14:45:34 -040084 bool ContainsArrayType(QualType QT) {
85 auto canonical = QT.getCanonicalType();
86 if (auto *PT = dyn_cast<PointerType>(canonical)) {
87 return ContainsArrayType(PT->getPointeeType());
alan-baker4a757f62020-04-22 08:17:49 -040088 } else if (isa<ArrayType>(canonical)) {
alan-baker9b0ec3c2020-04-06 14:45:34 -040089 return true;
90 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
91 for (auto field_decl : RT->getDecl()->fields()) {
92 if (ContainsArrayType(field_decl->getType()))
93 return true;
94 }
95 }
96
97 return false;
98 }
99
alan-baker28361f72020-01-07 16:35:25 -0500100 bool IsRecursiveType(QualType QT, llvm::DenseSet<const Type *> *seen) {
101 auto canonical = QT.getCanonicalType();
102 if (canonical->isRecordType() &&
103 !seen->insert(canonical.getTypePtr()).second) {
104 return true;
105 }
106
107 if (auto *PT = dyn_cast<PointerType>(canonical)) {
alan-baker4a757f62020-04-22 08:17:49 -0400108 return IsRecursiveType(PT->getPointeeType(), seen);
alan-baker28361f72020-01-07 16:35:25 -0500109 } else if (auto *AT = dyn_cast<ArrayType>(canonical)) {
110 return IsRecursiveType(AT->getElementType(), seen);
111 } else if (auto *RT = dyn_cast<RecordType>(canonical)) {
112 for (auto field_decl : RT->getDecl()->fields()) {
113 if (IsRecursiveType(field_decl->getType(), seen))
114 return true;
115 }
116 }
117
118 seen->erase(canonical.getTypePtr());
119 return false;
120 }
121
alan-bakerfec0a472018-11-08 18:09:40 -0500122 bool IsSupportedType(QualType QT, SourceRange SR) {
123 auto *Ty = QT.getTypePtr();
124
125 // First check if we have a pointer type.
126 if (Ty->isPointerType()) {
127 const Type *pointeeTy = Ty->getPointeeType().getTypePtr();
128 if (pointeeTy && pointeeTy->isVoidType()) {
129 // We don't support void pointers.
130 Instance.getDiagnostics().Report(
131 SR.getBegin(), CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer]);
132 return false;
133 }
134 // Otherwise check recursively.
135 return IsSupportedType(Ty->getPointeeType(), SR);
136 }
137
138 const auto &canonicalType = QT.getCanonicalType();
139 if (auto *VT = llvm::dyn_cast<ExtVectorType>(canonicalType)) {
140 // We don't support vectors with more than 4 elements.
141 if (4 < VT->getNumElements()) {
142 Instance.getDiagnostics().Report(
143 SR.getBegin(),
144 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements]);
145 return false;
146 }
alan-baker990e9b92019-06-07 11:26:39 -0400147 } else if (canonicalType->isRecordType()) {
alan-baker28361f72020-01-07 16:35:25 -0500148 // Do not allow recursive struct definitions.
149 llvm::DenseSet<const Type *> seen;
150 if (IsRecursiveType(canonicalType, &seen)) {
alan-baker990e9b92019-06-07 11:26:39 -0400151 Instance.getDiagnostics().Report(
152 SR.getBegin(),
alan-baker28361f72020-01-07 16:35:25 -0500153 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct]);
alan-baker990e9b92019-06-07 11:26:39 -0400154 return false;
155 }
alan-bakerfec0a472018-11-08 18:09:40 -0500156 }
157
158 return true;
159 }
160
alan-baker3d9e2012019-01-11 14:55:30 -0500161 // Report a diagnostic using |diag|. If |arg_range| and |specific_range|
162 // differ, also issue a note with the specific location of the error.
163 void Report(const CustomDiagnosticType &diag, SourceRange arg_range,
164 SourceRange specific_range) {
165 Instance.getDiagnostics().Report(arg_range.getBegin(),
166 CustomDiagnosticsIDMap[diag]);
167 if (arg_range != specific_range) {
168 Instance.getDiagnostics().Report(
169 specific_range.getBegin(),
170 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo]);
171 }
172 }
173
alan-baker9bb09792019-03-25 11:25:13 -0400174 // Returns the alignment of |QT| to satisfy |layout|'s rules.
175 uint64_t GetAlignment(const QualType QT, const Layout &layout,
176 const ASTContext &context) const {
alan-bakerfec0a472018-11-08 18:09:40 -0500177 const auto canonical = QT.getCanonicalType();
178 uint64_t alignment = context.getTypeAlignInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400179 if (layout == UBO &&
180 (canonical->isRecordType() || canonical->isArrayType())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500181 return llvm::alignTo(alignment, 16);
182 }
183 return alignment;
184 }
185
186 // Returns true if |QT| is a valid layout for a Uniform buffer. Refer to
187 // 14.5.4 in the Vulkan specification.
alan-baker9bb09792019-03-25 11:25:13 -0400188 bool IsSupportedLayout(QualType QT, uint64_t offset, const Layout &layout,
189 ASTContext &context, SourceRange arg_range,
190 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500191 const auto canonical = QT.getCanonicalType();
192 if (canonical->isScalarType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400193 if (!IsSupportedScalarLayout(canonical, offset, layout, context,
194 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500195 return false;
196 } else if (canonical->isExtVectorType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400197 if (!IsSupportedVectorLayout(canonical, offset, layout, context,
198 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500199 return false;
200 } else if (canonical->isArrayType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400201 if (!IsSupportedArrayLayout(canonical, offset, layout, context, arg_range,
202 specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500203 return false;
204 } else if (canonical->isRecordType()) {
alan-baker9bb09792019-03-25 11:25:13 -0400205 if (!IsSupportedRecordLayout(canonical, offset, layout, context,
206 arg_range, specific_range))
alan-bakerfec0a472018-11-08 18:09:40 -0500207 return false;
208 }
209
210 // TODO(alan-baker): Find a way to avoid this restriction.
211 // Don't allow padding. This prevents structs like:
212 // struct {
213 // int x[2];
214 // int y __attribute((aligned(16)));
215 // };
216 //
217 // This would map in LLVM to { [2 x i32], [8 x i8], i32, [12 xi8] }.
218 // There is no easy way to manipulate the padding after the array to
219 // satisfy the standard Uniform buffer layout rules in this case. The usual
220 // trick is replacing the i8 arrays with an i32 element, but the i32 would
221 // still be laid out too close to the array.
222 const auto type_size = context.getTypeSizeInChars(canonical).getQuantity();
alan-baker9bb09792019-03-25 11:25:13 -0400223 const auto type_align = GetAlignment(canonical, layout, context);
224 if (layout == UBO && (type_size % type_align != 0)) {
alan-baker3d9e2012019-01-11 14:55:30 -0500225 Report(CustomDiagnosticUBORestrictedSize, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500226 return false;
227 }
228
229 return true;
230 }
231
alan-baker9bb09792019-03-25 11:25:13 -0400232 bool IsSupportedScalarLayout(QualType QT, uint64_t offset,
233 const Layout & /*layout*/, ASTContext &context,
234 SourceRange arg_range,
235 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500236 // A scalar type of size N has a base alignment on N.
237 const unsigned type_size = context.getTypeSizeInChars(QT).getQuantity();
238 if (offset % type_size != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400239 Report(CustomDiagnosticUnalignedScalar, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500240 return false;
241 }
242
243 return true;
244 }
245
alan-baker9bb09792019-03-25 11:25:13 -0400246 bool IsSupportedVectorLayout(QualType QT, uint64_t offset,
247 const Layout &layout, ASTContext &context,
248 SourceRange arg_range,
249 SourceRange specific_range) {
alan-bakerfec0a472018-11-08 18:09:40 -0500250 // 2-component vectors have a base alignment of 2 * (size of element).
251 // 3- and 4-component vectors hae a base alignment of 4 * (size of
252 // element).
253 const auto *VT = llvm::cast<VectorType>(QT);
254 const auto ele_size =
255 context.getTypeSizeInChars(VT->getElementType()).getQuantity();
256 if (VT->getNumElements() == 2) {
257 if (offset % (ele_size * 2) != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400258 Report(CustomDiagnosticUnalignedVec2, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500259 return false;
260 }
261 } else if (offset % (ele_size * 4) != 0) {
262 // Other vector sizes cause errors elsewhere.
alan-baker9bb09792019-03-25 11:25:13 -0400263 Report(CustomDiagnosticUnalignedVec4, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500264 return false;
265 }
266
267 // Straddling rules:
268 // * If total vector size is less than 16 bytes, the offset must place the
269 // entire vector within the same 16 bytes.
270 // * If total vector size is greater than 16 bytes, the offset must be a
271 // multiple of 16.
272 const auto size = context.getTypeSizeInChars(QT).getQuantity();
273 if (size <= 16 && (offset / 16 != (offset + size - 1) / 16)) {
alan-baker9bb09792019-03-25 11:25:13 -0400274 Report(CustomDiagnosticSmallStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500275 return false;
276 } else if (size > 16 && (offset % 16 != 0)) {
alan-baker9bb09792019-03-25 11:25:13 -0400277 Report(CustomDiagnosticLargeStraddle, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500278 return false;
279 }
280
alan-baker9bb09792019-03-25 11:25:13 -0400281 return IsSupportedLayout(VT->getElementType(), offset, layout, context,
282 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500283 }
284
alan-baker9bb09792019-03-25 11:25:13 -0400285 bool IsSupportedArrayLayout(QualType QT, uint64_t offset,
286 const Layout &layout, ASTContext &context,
287 SourceRange arg_range,
288 SourceRange specific_range) {
289 // An array has a base alignment of is element type.
290 // If the layout is UBO, the alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500291 const auto *AT = llvm::cast<ArrayType>(QT);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400292 const auto element_align =
293 GetAlignment(AT->getElementType(), layout, context);
alan-baker9bb09792019-03-25 11:25:13 -0400294 const auto type_align =
295 layout == UBO ? llvm::alignTo(element_align, 16) : element_align;
alan-bakerfec0a472018-11-08 18:09:40 -0500296 if (offset % type_align != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400297 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedArray
298 : CustomDiagnosticSSBOUnalignedArray;
299 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500300 return false;
301 }
alan-baker9bb09792019-03-25 11:25:13 -0400302 if (layout == UBO && !clspv::Option::RelaxedUniformBufferLayout()) {
alan-baker3d9e2012019-01-11 14:55:30 -0500303 // The ArrayStride must be a multiple of the base alignment of the array
304 // (i.e. a multiple of 16). This means that the element size must be
305 // restricted to be the base alignment of the array.
306 const auto element_size =
307 context.getTypeSizeInChars(AT->getElementType()).getQuantity();
308 if (element_size % type_align != 0) {
309 Report(CustomDiagnosticUBOArrayStride, arg_range, specific_range);
310 return false;
311 }
312 }
alan-bakerfec0a472018-11-08 18:09:40 -0500313
alan-baker9bb09792019-03-25 11:25:13 -0400314 return IsSupportedLayout(AT->getElementType(), offset, layout, context,
315 arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500316 }
317
alan-baker9bb09792019-03-25 11:25:13 -0400318 bool IsSupportedRecordLayout(QualType QT, uint64_t offset,
319 const Layout &layout, ASTContext &context,
320 SourceRange arg_range,
321 SourceRange specific_range) {
322 // A structure has a base alignment of its largest member. For UBO layouts,
323 // alignment is rounded up to a multiple of 16.
alan-bakerfec0a472018-11-08 18:09:40 -0500324 const auto *RT = llvm::cast<RecordType>(QT);
alan-baker9bb09792019-03-25 11:25:13 -0400325 auto type_alignment = GetAlignment(QT, layout, context);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400326 if (layout == UBO)
327 llvm::alignTo(type_alignment, 16);
alan-bakerfec0a472018-11-08 18:09:40 -0500328 if (offset % type_alignment != 0) {
alan-baker9bb09792019-03-25 11:25:13 -0400329 auto diag_id = layout == UBO ? CustomDiagnosticUBOUnalignedStruct
330 : CustomDiagnosticSSBOUnalignedStruct;
331 Report(diag_id, arg_range, specific_range);
alan-bakerfec0a472018-11-08 18:09:40 -0500332 return false;
333 }
334
alan-baker9bb09792019-03-25 11:25:13 -0400335 const auto &record_layout = context.getASTRecordLayout(RT->getDecl());
alan-bakerfec0a472018-11-08 18:09:40 -0500336 const FieldDecl *prev = nullptr;
337 for (auto field_decl : RT->getDecl()->fields()) {
338 const auto field_type = field_decl->getType();
alan-bakerfec0a472018-11-08 18:09:40 -0500339 const unsigned field_no = field_decl->getFieldIndex();
340 const uint64_t field_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400341 record_layout.getFieldOffset(field_no) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500342
343 // Rules must be checked recursively.
alan-baker9bb09792019-03-25 11:25:13 -0400344 if (!IsSupportedLayout(field_type, field_offset + offset, layout, context,
345 arg_range, field_decl->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500346 return false;
347 }
348
349 if (prev) {
350 const auto prev_canonical = prev->getType().getCanonicalType();
351 const uint64_t prev_offset =
alan-baker9bb09792019-03-25 11:25:13 -0400352 record_layout.getFieldOffset(field_no - 1) / context.getCharWidth();
alan-bakerfec0a472018-11-08 18:09:40 -0500353 const auto prev_size =
354 context.getTypeSizeInChars(prev_canonical).getQuantity();
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400355 const auto prev_alignment =
356 GetAlignment(prev_canonical, layout, context);
alan-bakerfec0a472018-11-08 18:09:40 -0500357 const auto next_available =
358 prev_offset + llvm::alignTo(prev_size, prev_alignment);
359 if (prev_canonical->isArrayType() || prev_canonical->isRecordType()) {
360 // The next element after an array or struct must be placed on or
361 // after the next multiple of the alignment of that array or
362 // struct.
alan-baker9bb09792019-03-25 11:25:13 -0400363 // For UBO layouts, both arrays and structs must be aligned to a
364 // multiple of 16 bytes.
365 const uint64_t final_align = layout == UBO
366 ? llvm::alignTo(next_available, 16)
367 : next_available;
368 if (final_align > field_offset) {
369 Report(CustomDiagnosticUnalignedStructMember, arg_range,
alan-baker3d9e2012019-01-11 14:55:30 -0500370 field_decl->getSourceRange());
alan-bakerfec0a472018-11-08 18:09:40 -0500371 return false;
372 }
373 }
374 }
375
376 prev = field_decl;
377 }
378
379 return true;
380 }
381
382 // This will be used to check the inside of function bodies.
383 class DeclVisitor : public RecursiveASTVisitor<DeclVisitor> {
384 private:
385 ExtraValidationConsumer &consumer;
386
387 public:
388 explicit DeclVisitor(ExtraValidationConsumer &VC) : consumer(VC) {}
389
390 // Visits a declaration. Emits a diagnostic and returns false if the
391 // declaration represents an unsupported vector value or vector type.
392 // Otherwise returns true.
393 bool VisitDecl(Decl *D) {
394 // Looking at the Decl class hierarchy, it seems ValueDecl and TypeDecl
395 // are the only two that might represent an unsupported vector type.
396 if (auto *VD = dyn_cast<ValueDecl>(D)) {
397 return consumer.IsSupportedType(VD->getType(), D->getSourceRange());
398 } else if (auto *TD = dyn_cast<TypeDecl>(D)) {
399 QualType DefinedType = TD->getASTContext().getTypeDeclType(TD);
400 return consumer.IsSupportedType(DefinedType, TD->getSourceRange());
401 }
402 return true;
403 }
404 };
405
406 DeclVisitor Visitor;
Kévin Petit0fc88042019-04-09 23:25:02 +0100407 std::unordered_set<std::string> Kernels;
alan-bakerfec0a472018-11-08 18:09:40 -0500408
409public:
410 explicit ExtraValidationConsumer(CompilerInstance &Instance,
411 llvm::StringRef InFile)
412 : Instance(Instance), InFile(InFile),
413 CustomDiagnosticsIDMap(CustomDiagnosticTotal), Visitor(*this) {
414 auto &DE = Instance.getDiagnostics();
415
416 CustomDiagnosticsIDMap[CustomDiagnosticVectorsMoreThan4Elements] =
417 DE.getCustomDiagID(
418 DiagnosticsEngine::Error,
419 "vectors with more than 4 elements are not supported");
420 CustomDiagnosticsIDMap[CustomDiagnosticVoidPointer] = DE.getCustomDiagID(
421 DiagnosticsEngine::Error, "pointer-to-void is not supported");
alan-baker9bb09792019-03-25 11:25:13 -0400422 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedScalar] =
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400423 DE.getCustomDiagID(DiagnosticsEngine::Error,
424 "scalar elements must be aligned to their size");
alan-baker9bb09792019-03-25 11:25:13 -0400425 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec2] = DE.getCustomDiagID(
426 DiagnosticsEngine::Error,
427 "two-component vectors must be aligned to 2 times their element size");
428 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedVec4] =
alan-bakerfec0a472018-11-08 18:09:40 -0500429 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400430 "three- and four-component vectors must be aligned "
431 "to 4 times their element size");
alan-bakerfec0a472018-11-08 18:09:40 -0500432 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedArray] =
433 DE.getCustomDiagID(DiagnosticsEngine::Error,
434 "in an UBO, arrays must be aligned to their element "
435 "alignment, rounded up to a multiple of 16 bytes");
436 CustomDiagnosticsIDMap[CustomDiagnosticUBOUnalignedStruct] =
437 DE.getCustomDiagID(
438 DiagnosticsEngine::Error,
439 "in an UBO, structs must be aligned to their "
440 "largest element alignment, rounded up to a multiple of "
441 "16 bytes");
alan-baker9bb09792019-03-25 11:25:13 -0400442 CustomDiagnosticsIDMap[CustomDiagnosticSmallStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500443 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400444 "vectors with a total size less than or equal to 16 "
445 "bytes must be placed entirely within a 16 byte "
446 "aligned region");
447 CustomDiagnosticsIDMap[CustomDiagnosticLargeStraddle] =
alan-bakerfec0a472018-11-08 18:09:40 -0500448 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400449 "vectors with a total size greater than 16 bytes "
450 "must aligned to 16 bytes");
451 CustomDiagnosticsIDMap[CustomDiagnosticUnalignedStructMember] =
alan-bakerfec0a472018-11-08 18:09:40 -0500452 DE.getCustomDiagID(DiagnosticsEngine::Error,
alan-baker9bb09792019-03-25 11:25:13 -0400453 "a structure member must not be placed between the "
454 "end of a structure or array and the next multiple "
455 "of the base alignment of that structure or array");
alan-bakerfec0a472018-11-08 18:09:40 -0500456 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedSize] =
457 DE.getCustomDiagID(DiagnosticsEngine::Error,
458 "clspv restriction: UBO element size must be a "
459 "multiple of that element's alignment");
460 CustomDiagnosticsIDMap[CustomDiagnosticUBORestrictedStruct] =
461 DE.getCustomDiagID(
462 DiagnosticsEngine::Error,
463 "clspv restriction: UBO structures may not have implicit padding");
alan-baker3d9e2012019-01-11 14:55:30 -0500464 CustomDiagnosticsIDMap[CustomDiagnosticUBOArrayStride] = DE.getCustomDiagID(
465 DiagnosticsEngine::Error,
466 "clspv restriction: to satisfy UBO ArrayStride restrictions, element "
467 "size must be a multiple of array alignment");
468 CustomDiagnosticsIDMap[CustomDiagnosticLocationInfo] =
469 DE.getCustomDiagID(DiagnosticsEngine::Note, "here");
alan-baker9bb09792019-03-25 11:25:13 -0400470 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedArray] =
471 DE.getCustomDiagID(
472 DiagnosticsEngine::Error,
473 "in a SSBO, arrays must be aligned to their element alignment");
474 CustomDiagnosticsIDMap[CustomDiagnosticSSBOUnalignedStruct] =
475 DE.getCustomDiagID(DiagnosticsEngine::Error,
476 "in a SSBO, structs must be aligned to their "
477 "largest element alignment");
Kévin Petit0fc88042019-04-09 23:25:02 +0100478 CustomDiagnosticsIDMap[CustomDiagnosticOverloadedKernel] =
479 DE.getCustomDiagID(DiagnosticsEngine::Error,
480 "kernel functions can't be overloaded");
alan-baker990e9b92019-06-07 11:26:39 -0400481 CustomDiagnosticsIDMap[CustomDiagnosticStructContainsPointer] =
482 DE.getCustomDiagID(DiagnosticsEngine::Error,
483 "structures may not contain pointers");
alan-baker28361f72020-01-07 16:35:25 -0500484 CustomDiagnosticsIDMap[CustomDiagnosticRecursiveStruct] =
485 DE.getCustomDiagID(DiagnosticsEngine::Error,
486 "recursive structures are not supported");
alan-baker9b0ec3c2020-04-06 14:45:34 -0400487 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantSizeExceeded] =
488 DE.getCustomDiagID(DiagnosticsEngine::Error,
489 "max push constant size exceeded");
490 CustomDiagnosticsIDMap[CustomDiagnosticPushConstantContainsArray] =
491 DE.getCustomDiagID(
492 DiagnosticsEngine::Error,
493 "arrays are not supported in push constants currently");
alan-bakerfec0a472018-11-08 18:09:40 -0500494 }
495
496 virtual bool HandleTopLevelDecl(DeclGroupRef DG) override {
497 for (auto *D : DG) {
498 if (auto *FD = llvm::dyn_cast<FunctionDecl>(D)) {
499 // If the function has a body it means we are not an OpenCL builtin
500 // function.
501 if (FD->hasBody()) {
502 if (!IsSupportedType(FD->getReturnType(),
503 FD->getReturnTypeSourceRange())) {
504 return false;
505 }
506
507 bool is_opencl_kernel = false;
508 if (FD->hasAttrs()) {
509 for (auto *attr : FD->attrs()) {
510 if (attr->getKind() == attr::Kind::OpenCLKernel) {
511 is_opencl_kernel = true;
512 }
513 }
514 }
515
Kévin Petit0fc88042019-04-09 23:25:02 +0100516 if (is_opencl_kernel) {
alan-baker21574d32020-01-29 16:00:31 -0500517 if (Kernels.count(FD->getName().str()) != 0) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100518 auto srcRange = FD->getSourceRange();
519 Report(CustomDiagnosticOverloadedKernel, srcRange, srcRange);
520 } else {
alan-baker21574d32020-01-29 16:00:31 -0500521 Kernels.insert(FD->getName().str());
Kévin Petit0fc88042019-04-09 23:25:02 +0100522 }
523 }
524
alan-baker9b0ec3c2020-04-06 14:45:34 -0400525 RecordDecl *clustered_args = nullptr;
526 if (is_opencl_kernel && clspv::Option::PodArgsInPushConstants()) {
527 clustered_args = FD->getASTContext().buildImplicitRecord(
528 "__clspv.clustered_args." + std::to_string(kClusteredCount++));
529 clustered_args->startDefinition();
530 }
alan-bakerfec0a472018-11-08 18:09:40 -0500531 for (auto *P : FD->parameters()) {
532 auto type = P->getType();
533 if (!IsSupportedType(P->getOriginalType(), P->getSourceRange())) {
534 return false;
535 }
536
alan-baker9bb09792019-03-25 11:25:13 -0400537 if (is_opencl_kernel && type->isPointerType() &&
538 ((type->getPointeeType().getAddressSpace() ==
539 LangAS::opencl_constant) ||
540 (type->getPointeeType().getAddressSpace() ==
541 LangAS::opencl_global))) {
alan-baker3d9e2012019-01-11 14:55:30 -0500542 // The argument will be generated as an array within a block.
543 // Generate an array type to check the validity for the generated
544 // case.
alan-baker9bb09792019-03-25 11:25:13 -0400545 Layout layout = SSBO;
546 if (clspv::Option::ConstantArgsInUniformBuffer() &&
547 !clspv::Option::Std430UniformBufferLayout() &&
548 type->getPointeeType().getAddressSpace() ==
549 LangAS::opencl_constant) {
550 layout = UBO;
551 }
alan-baker3d9e2012019-01-11 14:55:30 -0500552 auto array_type = FD->getASTContext().getIncompleteArrayType(
553 type->getPointeeType(), clang::ArrayType::Normal, 0);
alan-baker9bb09792019-03-25 11:25:13 -0400554 if (!IsSupportedLayout(array_type, 0, layout, FD->getASTContext(),
555 P->getSourceRange(),
556 P->getSourceRange())) {
alan-bakerfec0a472018-11-08 18:09:40 -0500557 return false;
558 }
559 }
alan-baker038e9242019-04-19 22:14:41 -0400560
alan-baker28361f72020-01-07 16:35:25 -0500561 if (is_opencl_kernel && type->isPointerType()) {
562 auto pointee_type = type->getPointeeType().getCanonicalType();
563 if (ContainsPointerType(pointee_type)) {
564 Instance.getDiagnostics().Report(
565 P->getSourceRange().getBegin(),
566 CustomDiagnosticsIDMap
567 [CustomDiagnosticStructContainsPointer]);
568 return false;
569 }
570 }
571
alan-baker038e9242019-04-19 22:14:41 -0400572 if (is_opencl_kernel && !type->isPointerType()) {
alan-baker9b0ec3c2020-04-06 14:45:34 -0400573 if (clspv::Option::PodArgsInPushConstants()) {
574 // Don't allow arrays in push constants currently.
575 if (ContainsArrayType(type)) {
576 Report(CustomDiagnosticPushConstantContainsArray,
577 P->getSourceRange(), P->getSourceRange());
578 return false;
579 }
580 FieldDecl *field_decl = FieldDecl::Create(
581 FD->getASTContext(),
582 Decl::castToDeclContext(clustered_args),
583 P->getSourceRange().getBegin(),
584 P->getSourceRange().getEnd(), P->getIdentifier(),
585 P->getType(), nullptr, nullptr, false, ICIS_NoInit);
586 field_decl->setAccess(AS_public);
587 clustered_args->addDecl(field_decl);
588 } else {
589 Layout layout = SSBO;
590 if (clspv::Option::PodArgsInUniformBuffer() &&
591 !clspv::Option::Std430UniformBufferLayout())
592 layout = UBO;
alan-baker038e9242019-04-19 22:14:41 -0400593
alan-baker9b0ec3c2020-04-06 14:45:34 -0400594 if (!IsSupportedLayout(type, 0, layout, FD->getASTContext(),
595 P->getSourceRange(),
596 P->getSourceRange())) {
597 return false;
598 }
599 }
600 }
601 }
602
603 if (clustered_args) {
604 clustered_args->completeDefinition();
605 if (!clustered_args->field_empty()) {
606 auto record_type =
607 FD->getASTContext().getRecordType(clustered_args);
608 if (!IsSupportedLayout(record_type, 0, SSBO, FD->getASTContext(),
609 FD->getSourceRange(),
610 FD->getSourceRange())) {
611 return false;
612 }
613
614 if (FD->getASTContext()
615 .getTypeSizeInChars(record_type)
616 .getQuantity() > clspv::Option::MaxPushConstantsSize()) {
617 Report(CustomDiagnosticPushConstantSizeExceeded,
618 FD->getSourceRange(), FD->getSourceRange());
alan-baker038e9242019-04-19 22:14:41 -0400619 return false;
620 }
621 }
alan-bakerfec0a472018-11-08 18:09:40 -0500622 }
623
624 // Check for unsupported vector types.
625 Visitor.TraverseDecl(FD);
626 }
627 }
628 }
629
630 return true;
631 }
632};
633} // namespace
634
635namespace clspv {
636std::unique_ptr<ASTConsumer>
637ExtraValidationASTAction::CreateASTConsumer(CompilerInstance &CI,
638 llvm::StringRef InFile) {
639 return std::unique_ptr<ASTConsumer>(new ExtraValidationConsumer(CI, InFile));
640}
641} // namespace clspv