1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "compiler/translator/ValidateAST.h"
8
9 #include "common/utilities.h"
10 #include "compiler/translator/Diagnostics.h"
11 #include "compiler/translator/ImmutableStringBuilder.h"
12 #include "compiler/translator/Name.h"
13 #include "compiler/translator/Symbol.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 #include "compiler/translator/tree_util/SpecializationConstant.h"
16 #include "compiler/translator/util.h"
17
18 namespace sh
19 {
20
21 namespace
22 {
23
24 class ValidateAST : public TIntermTraverser
25 {
26 public:
27 static bool validate(TIntermNode *root,
28 TDiagnostics *diagnostics,
29 const ValidateASTOptions &options);
30
31 void visitSymbol(TIntermSymbol *node) override;
32 void visitConstantUnion(TIntermConstantUnion *node) override;
33 bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
34 bool visitBinary(Visit visit, TIntermBinary *node) override;
35 bool visitUnary(Visit visit, TIntermUnary *node) override;
36 bool visitTernary(Visit visit, TIntermTernary *node) override;
37 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
38 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
39 bool visitCase(Visit visit, TIntermCase *node) override;
40 void visitFunctionPrototype(TIntermFunctionPrototype *node) override;
41 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
42 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
43 bool visitBlock(Visit visit, TIntermBlock *node) override;
44 bool visitGlobalQualifierDeclaration(Visit visit,
45 TIntermGlobalQualifierDeclaration *node) override;
46 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
47 bool visitLoop(Visit visit, TIntermLoop *node) override;
48 bool visitBranch(Visit visit, TIntermBranch *node) override;
49 void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override;
50
51 private:
52 ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options);
53
54 // Visit as a generic node
55 void visitNode(Visit visit, TIntermNode *node);
56 // Visit a structure or interface block, and recursively visit its fields of structure type.
57 void visitStructOrInterfaceBlockDeclaration(const TType &type, const TSourceLoc &location);
58 void visitStructUsage(const TType &type, const TSourceLoc &location);
59 // Visit a unary or aggregate node and validate its built-in op against its built-in function.
60 void visitBuiltInFunction(TIntermOperator *op, const TFunction *function);
61 // Visit an aggregate node and validate its function call is to one that's already defined.
62 void visitFunctionCall(TIntermAggregate *node);
63 // Visit a binary node and validate its type against its operands.
64 void validateExpressionTypeBinary(TIntermBinary *node);
65 // Visit a switch node and validate its selector type is integer.
66 void validateExpressionTypeSwitch(TIntermSwitch *node);
67 // Visit a symbol node and validate it's declared previously.
68 void visitVariableNeedingDeclaration(TIntermSymbol *node);
69 // Visit a built-in symbol node and validate it's consistently used across the tree.
70 void visitBuiltInVariable(TIntermSymbol *node);
71
72 void scope(Visit visit);
73 bool isVariableDeclared(const TVariable *variable);
74 bool variableNeedsDeclaration(const TVariable *variable);
75 const TFieldListCollection *getStructOrInterfaceBlock(const TType &type, Name *typeNameOut);
76
77 void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count);
78
79 bool validateInternal();
80 bool isInDeclaration() const;
81
82 ValidateASTOptions mOptions;
83 TDiagnostics *mDiagnostics;
84
85 // For validateSingleParent:
86 std::map<TIntermNode *, TIntermNode *> mParent;
87 bool mSingleParentFailed = false;
88
89 // For validateVariableReferences:
90 std::vector<std::set<const TVariable *>> mDeclaredVariables;
91 std::set<const TInterfaceBlock *> mNamelessInterfaceBlocks;
92 std::map<ImmutableString, const TVariable *> mReferencedBuiltIns;
93 bool mVariableReferencesFailed = false;
94
95 // For validateOps:
96 bool mOpsFailed = false;
97
98 // For validateBuiltInOps:
99 bool mBuiltInOpsFailed = false;
100
101 // For validateFunctionCall:
102 std::set<const TFunction *> mDeclaredFunctions;
103 bool mFunctionCallFailed = false;
104
105 // For validateNoRawFunctionCalls:
106 bool mNoRawFunctionCallsFailed = false;
107
108 // For validateNullNodes:
109 bool mNullNodesFailed = false;
110
111 // For validateQualifiers:
112 bool mQualifiersFailed = false;
113
114 // For validatePrecision:
115 bool mPrecisionFailed = false;
116
117 // For validateStructUsage:
118 std::vector<std::map<Name, const TFieldListCollection *>> mStructsAndBlocksByName;
119 std::set<const TFunction *> mStructUsageProcessedFunctions;
120 bool mStructUsageFailed = false;
121
122 // For validateExpressionTypes:
123 bool mExpressionTypesFailed = false;
124
125 // For validateMultiDeclarations:
126 bool mMultiDeclarationsFailed = false;
127
128 // For validateNoSwizzleOfSwizzle:
129 bool mNoSwizzleOfSwizzleFailed = false;
130
131 // For validateNoQualifiersOnConstructors:
132 bool mNoQualifiersOnConstructorsFailed = false;
133
134 // For validateNoStatementsAfterBranch:
135 bool mIsBranchVisitedInBlock = false;
136 bool mNoStatementsAfterBranchFailed = false;
137
138 bool mVariableNamingFailed = false;
139 };
140
IsSameType(const TType & a,const TType & b)141 bool IsSameType(const TType &a, const TType &b)
142 {
143 return a.getBasicType() == b.getBasicType() && a.getNominalSize() == b.getNominalSize() &&
144 a.getSecondarySize() == b.getSecondarySize() && a.getArraySizes() == b.getArraySizes() &&
145 a.getStruct() == b.getStruct() &&
146 (!a.isInterfaceBlock() || a.getInterfaceBlock() == b.getInterfaceBlock());
147 }
148
IsUnaryOp(TOperator op)149 bool IsUnaryOp(TOperator op)
150 {
151 switch (op)
152 {
153 case EOpNegative:
154 case EOpPositive:
155 case EOpLogicalNot:
156 case EOpBitwiseNot:
157 case EOpPostIncrement:
158 case EOpPostDecrement:
159 case EOpPreIncrement:
160 case EOpPreDecrement:
161 case EOpArrayLength:
162 return true;
163 default:
164 return false;
165 }
166 }
167
IsBinaryOp(TOperator op)168 bool IsBinaryOp(TOperator op)
169 {
170 switch (op)
171 {
172 case EOpAdd:
173 case EOpSub:
174 case EOpMul:
175 case EOpDiv:
176 case EOpIMod:
177 case EOpEqual:
178 case EOpNotEqual:
179 case EOpLessThan:
180 case EOpGreaterThan:
181 case EOpLessThanEqual:
182 case EOpGreaterThanEqual:
183 case EOpComma:
184 case EOpVectorTimesScalar:
185 case EOpVectorTimesMatrix:
186 case EOpMatrixTimesVector:
187 case EOpMatrixTimesScalar:
188 case EOpMatrixTimesMatrix:
189 case EOpLogicalOr:
190 case EOpLogicalXor:
191 case EOpLogicalAnd:
192 case EOpBitShiftLeft:
193 case EOpBitShiftRight:
194 case EOpBitwiseAnd:
195 case EOpBitwiseXor:
196 case EOpBitwiseOr:
197 case EOpIndexDirect:
198 case EOpIndexIndirect:
199 case EOpIndexDirectStruct:
200 case EOpIndexDirectInterfaceBlock:
201 case EOpAssign:
202 case EOpInitialize:
203 case EOpAddAssign:
204 case EOpSubAssign:
205 case EOpMulAssign:
206 case EOpVectorTimesMatrixAssign:
207 case EOpVectorTimesScalarAssign:
208 case EOpMatrixTimesScalarAssign:
209 case EOpMatrixTimesMatrixAssign:
210 case EOpDivAssign:
211 case EOpIModAssign:
212 case EOpBitShiftLeftAssign:
213 case EOpBitShiftRightAssign:
214 case EOpBitwiseAndAssign:
215 case EOpBitwiseXorAssign:
216 case EOpBitwiseOrAssign:
217 return true;
218 default:
219 return false;
220 }
221 }
222
IsBranchOp(TOperator op)223 bool IsBranchOp(TOperator op)
224 {
225 switch (op)
226 {
227 case EOpKill:
228 case EOpReturn:
229 case EOpBreak:
230 case EOpContinue:
231 return true;
232 default:
233 return false;
234 }
235 }
236
validate(TIntermNode * root,TDiagnostics * diagnostics,const ValidateASTOptions & options)237 bool ValidateAST::validate(TIntermNode *root,
238 TDiagnostics *diagnostics,
239 const ValidateASTOptions &options)
240 {
241 ValidateAST validate(root, diagnostics, options);
242 root->traverse(&validate);
243 return validate.validateInternal();
244 }
245
ValidateAST(TIntermNode * root,TDiagnostics * diagnostics,const ValidateASTOptions & options)246 ValidateAST::ValidateAST(TIntermNode *root,
247 TDiagnostics *diagnostics,
248 const ValidateASTOptions &options)
249 : TIntermTraverser(true, false, true, nullptr), mOptions(options), mDiagnostics(diagnostics)
250 {
251 bool isTreeRoot = root->getAsBlock() && root->getAsBlock()->isTreeRoot();
252
253 // Some validations are not applicable unless run on the entire tree.
254 if (!isTreeRoot)
255 {
256 mOptions.validateVariableReferences = false;
257 mOptions.validateFunctionCall = false;
258 mOptions.validateStructUsage = false;
259 }
260
261 if (mOptions.validateSingleParent)
262 {
263 mParent[root] = nullptr;
264 }
265 }
266
visitNode(Visit visit,TIntermNode * node)267 void ValidateAST::visitNode(Visit visit, TIntermNode *node)
268 {
269 if (visit == PreVisit && mOptions.validateSingleParent)
270 {
271 size_t childCount = node->getChildCount();
272 for (size_t i = 0; i < childCount; ++i)
273 {
274 TIntermNode *child = node->getChildNode(i);
275 if (mParent.find(child) != mParent.end())
276 {
277 // If child is visited twice but through the same parent, the problem is in one of
278 // the ancestors.
279 if (mParent[child] != node)
280 {
281 mDiagnostics->error(node->getLine(), "Found child with two parents",
282 "<validateSingleParent>");
283 mSingleParentFailed = true;
284 }
285 }
286
287 mParent[child] = node;
288 }
289 }
290
291 if (visit == PreVisit && mOptions.validateNoStatementsAfterBranch)
292 {
293 // If a branch has already been visited in this block, there should be no statements that
294 // follow. Only expected node visit should be PostVisit of the block.
295 if (mIsBranchVisitedInBlock)
296 {
297 mDiagnostics->error(node->getLine(), "Found dead code after branch",
298 "<validateNoStatementsAfterBranch>");
299 mNoStatementsAfterBranchFailed = true;
300 }
301 }
302 }
303
visitStructOrInterfaceBlockDeclaration(const TType & type,const TSourceLoc & location)304 void ValidateAST::visitStructOrInterfaceBlockDeclaration(const TType &type,
305 const TSourceLoc &location)
306 {
307 if (type.getStruct() == nullptr && type.getInterfaceBlock() == nullptr)
308 {
309 return;
310 }
311
312 // Make sure the structure or interface block is not doubly defined.
313 Name typeName;
314 const TFieldListCollection *namedStructOrBlock = getStructOrInterfaceBlock(type, &typeName);
315
316 // Recurse the fields of the structure or interface block and check members of structure type.
317 // This is done before visiting the struct itself, because if the fields refer to a struct with
318 // the same name, they would be referencing the struct declared in an outer scope.
319 {
320 // Note that structOrBlock was previously only set for named structures, so make sure
321 // nameless structs are also recursed.
322 const TFieldListCollection *structOrBlock = namedStructOrBlock;
323 if (structOrBlock == nullptr)
324 {
325 structOrBlock = type.getStruct();
326 }
327 ASSERT(structOrBlock != nullptr);
328
329 for (const TField *field : structOrBlock->fields())
330 {
331 visitStructUsage(*field->type(), field->line());
332 }
333 }
334
335 if (namedStructOrBlock)
336 {
337 ASSERT(!typeName.empty());
338 // Structures are not allowed to be doubly defined
339 if (type.getStruct() == nullptr)
340 {
341 // Allow interfaces to be doubly-defined.
342 ImmutableString rawName = typeName.rawName();
343
344 if (IsShaderIn(type.getQualifier()))
345 {
346 rawName = BuildConcatenatedImmutableString(rawName, "<input>");
347 }
348 else if (IsShaderOut(type.getQualifier()))
349 {
350 rawName = BuildConcatenatedImmutableString(rawName, "<output>");
351 }
352 else if (IsStorageBuffer(type.getQualifier()))
353 {
354 rawName = BuildConcatenatedImmutableString(rawName, "<buffer>");
355 }
356 else if (type.getQualifier() == EvqUniform)
357 {
358 rawName = BuildConcatenatedImmutableString(rawName, "<uniform>");
359 }
360 typeName = Name(rawName, typeName.symbolType());
361 }
362
363 if (mStructsAndBlocksByName.back().find(typeName) != mStructsAndBlocksByName.back().end())
364 {
365 mDiagnostics->error(location,
366 "Found redeclaration of struct or interface block with the same "
367 "name in the same scope <validateStructUsage>",
368 typeName.rawName().data());
369 mStructUsageFailed = true;
370 }
371 else
372 {
373 // First encounter.
374 mStructsAndBlocksByName.back()[typeName] = namedStructOrBlock;
375 }
376 }
377 }
378
visitStructUsage(const TType & type,const TSourceLoc & location)379 void ValidateAST::visitStructUsage(const TType &type, const TSourceLoc &location)
380 {
381 if (type.getStruct() == nullptr)
382 {
383 return;
384 }
385
386 // Make sure the structure being referenced has the same pointer as the closest (in scope)
387 // definition.
388 const TStructure *structure = type.getStruct();
389 const Name typeName(*structure);
390
391 bool foundDeclaration = false;
392 for (size_t scopeIndex = mStructsAndBlocksByName.size(); scopeIndex > 0; --scopeIndex)
393 {
394 const std::map<Name, const TFieldListCollection *> &scopeDecls =
395 mStructsAndBlocksByName[scopeIndex - 1];
396
397 auto iter = scopeDecls.find(typeName);
398 if (iter != scopeDecls.end())
399 {
400 foundDeclaration = true;
401
402 if (iter->second != structure)
403 {
404 mDiagnostics->error(location,
405 "Found reference to struct or interface block with doubly "
406 "created type <validateStructUsage>",
407 typeName.rawName().data());
408 mStructUsageFailed = true;
409 }
410
411 break;
412 }
413 }
414
415 if (!foundDeclaration)
416 {
417 mDiagnostics->error(location,
418 "Found reference to struct or interface block with no declaration "
419 "<validateStructUsage>",
420 typeName.rawName().data());
421 mStructUsageFailed = true;
422 }
423 }
424
visitBuiltInFunction(TIntermOperator * node,const TFunction * function)425 void ValidateAST::visitBuiltInFunction(TIntermOperator *node, const TFunction *function)
426 {
427 const TOperator op = node->getOp();
428 if (!BuiltInGroup::IsBuiltIn(op))
429 {
430 return;
431 }
432
433 ImmutableString opValue = BuildConcatenatedImmutableString("op: ", op);
434 if (function == nullptr)
435 {
436 mDiagnostics->error(node->getLine(),
437 "Found node calling built-in without a reference to the built-in "
438 "function <validateBuiltInOps>",
439 opValue.data());
440 mVariableReferencesFailed = true;
441 }
442 else if (function->getBuiltInOp() != op)
443 {
444 mDiagnostics->error(node->getLine(),
445 "Found node calling built-in with a reference to a different function "
446 "<validateBuiltInOps>",
447 opValue.data());
448 mVariableReferencesFailed = true;
449 }
450 }
451
visitFunctionCall(TIntermAggregate * node)452 void ValidateAST::visitFunctionCall(TIntermAggregate *node)
453 {
454 if (node->getOp() != EOpCallFunctionInAST)
455 {
456 return;
457 }
458
459 const TFunction *function = node->getFunction();
460
461 if (function == nullptr)
462 {
463 mDiagnostics->error(node->getLine(),
464 "Found node calling function without a reference to it",
465 "<validateFunctionCall>");
466 mFunctionCallFailed = true;
467 }
468 else if (mDeclaredFunctions.find(function) == mDeclaredFunctions.end())
469 {
470 mDiagnostics->error(node->getLine(),
471 "Found node calling previously undeclared function "
472 "<validateFunctionCall>",
473 function->name().data());
474 mFunctionCallFailed = true;
475 }
476 }
477
validateExpressionTypeBinary(TIntermBinary * node)478 void ValidateAST::validateExpressionTypeBinary(TIntermBinary *node)
479 {
480 switch (node->getOp())
481 {
482 case EOpIndexDirect:
483 case EOpIndexIndirect:
484 {
485 TType expectedType(node->getLeft()->getType());
486 if (!expectedType.isArray())
487 {
488 // TODO: Validate matrix column selection and vector component selection.
489 // http://anglebug.com/42261441
490 break;
491 }
492
493 expectedType.toArrayElementType();
494
495 if (!IsSameType(node->getType(), expectedType))
496 {
497 const TSymbol *symbol = expectedType.getStruct();
498 if (symbol == nullptr)
499 {
500 symbol = expectedType.getInterfaceBlock();
501 }
502 const char *name = nullptr;
503 if (symbol)
504 {
505 name = symbol->name().data();
506 }
507 else if (expectedType.isScalar())
508 {
509 name = "<scalar array>";
510 }
511 else if (expectedType.isVector())
512 {
513 name = "<vector array>";
514 }
515 else
516 {
517 ASSERT(expectedType.isMatrix());
518 name = "<matrix array>";
519 }
520
521 mDiagnostics->error(
522 node->getLine(),
523 "Found index node with type that is inconsistent with the array being indexed "
524 "<validateExpressionTypes>",
525 name);
526 mExpressionTypesFailed = true;
527 }
528 }
529 break;
530 default:
531 // TODO: Validate other expressions. http://anglebug.com/42261441
532 break;
533 }
534
535 switch (node->getOp())
536 {
537 case EOpIndexDirect:
538 case EOpIndexDirectStruct:
539 case EOpIndexDirectInterfaceBlock:
540 if (node->getRight()->getAsConstantUnion() == nullptr)
541 {
542 mDiagnostics->error(node->getLine(),
543 "Found direct index node with a non-constant index",
544 "<validateExpressionTypes>");
545 mExpressionTypesFailed = true;
546 }
547 break;
548 default:
549 break;
550 }
551 }
552
validateExpressionTypeSwitch(TIntermSwitch * node)553 void ValidateAST::validateExpressionTypeSwitch(TIntermSwitch *node)
554 {
555 const TType &selectorType = node->getInit()->getType();
556
557 if (selectorType.getBasicType() != EbtYuvCscStandardEXT &&
558 selectorType.getBasicType() != EbtInt && selectorType.getBasicType() != EbtUInt)
559 {
560 mDiagnostics->error(node->getLine(), "Found switch selector expression that is not integer",
561 "<validateExpressionTypes>");
562 mExpressionTypesFailed = true;
563 }
564 else if (!selectorType.isScalar())
565 {
566 mDiagnostics->error(node->getLine(), "Found switch selector expression that is not scalar",
567 "<validateExpressionTypes>");
568 mExpressionTypesFailed = true;
569 }
570 }
571
visitVariableNeedingDeclaration(TIntermSymbol * node)572 void ValidateAST::visitVariableNeedingDeclaration(TIntermSymbol *node)
573 {
574 const TVariable *variable = &node->variable();
575 const TType &type = node->getType();
576
577 // If it's a reference to a field of a nameless interface block, match it by index and name.
578 if (type.getInterfaceBlock() && !type.isInterfaceBlock())
579 {
580 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
581 const TFieldList &fieldList = interfaceBlock->fields();
582 const size_t fieldIndex = type.getInterfaceBlockFieldIndex();
583
584 if (mNamelessInterfaceBlocks.count(interfaceBlock) == 0)
585 {
586 mDiagnostics->error(node->getLine(),
587 "Found reference to undeclared or inconsistenly transformed "
588 "nameless interface block <validateVariableReferences>",
589 node->getName().data());
590 mVariableReferencesFailed = true;
591 }
592 else if (fieldIndex >= fieldList.size() || node->getName() != fieldList[fieldIndex]->name())
593 {
594 mDiagnostics->error(node->getLine(),
595 "Found reference to inconsistenly transformed nameless "
596 "interface block field <validateVariableReferences>",
597 node->getName().data());
598 mVariableReferencesFailed = true;
599 }
600 return;
601 }
602
603 const bool isStructDeclaration =
604 type.isStructSpecifier() && variable->symbolType() == SymbolType::Empty;
605
606 if (!isStructDeclaration && !isVariableDeclared(variable))
607 {
608 mDiagnostics->error(node->getLine(),
609 "Found reference to undeclared or inconsistently transformed "
610 "variable <validateVariableReferences>",
611 node->getName().data());
612 mVariableReferencesFailed = true;
613 }
614 }
615
visitBuiltInVariable(TIntermSymbol * node)616 void ValidateAST::visitBuiltInVariable(TIntermSymbol *node)
617 {
618 const TVariable *variable = &node->variable();
619 ImmutableString name = variable->name();
620
621 if (mOptions.validateVariableReferences)
622 {
623 auto iter = mReferencedBuiltIns.find(name);
624 if (iter == mReferencedBuiltIns.end())
625 {
626 mReferencedBuiltIns[name] = variable;
627 return;
628 }
629
630 if (variable != iter->second)
631 {
632 mDiagnostics->error(
633 node->getLine(),
634 "Found inconsistent references to built-in variable <validateVariableReferences>",
635 name.data());
636 mVariableReferencesFailed = true;
637 }
638 }
639
640 if (mOptions.validateQualifiers)
641 {
642 TQualifier qualifier = variable->getType().getQualifier();
643
644 if ((name == "gl_ClipDistance" && qualifier != EvqClipDistance) ||
645 (name == "gl_CullDistance" && qualifier != EvqCullDistance) ||
646 (name == "gl_FragDepth" && qualifier != EvqFragDepth) ||
647 (name == "gl_LastFragData" && qualifier != EvqLastFragData) ||
648 (name == "gl_LastFragColorARM" && qualifier != EvqLastFragColor) ||
649 (name == "gl_LastFragDepthARM" && qualifier != EvqLastFragDepth) ||
650 (name == "gl_LastFragStencilARM" && qualifier != EvqLastFragStencil))
651 {
652 mDiagnostics->error(
653 node->getLine(),
654 "Incorrect qualifier applied to redeclared built-in <validateQualifiers>",
655 name.data());
656 mQualifiersFailed = true;
657 }
658 }
659 }
660
scope(Visit visit)661 void ValidateAST::scope(Visit visit)
662 {
663 if (mOptions.validateVariableReferences)
664 {
665 if (visit == PreVisit)
666 {
667 mDeclaredVariables.push_back({});
668 }
669 else if (visit == PostVisit)
670 {
671 mDeclaredVariables.pop_back();
672 }
673 }
674
675 if (mOptions.validateStructUsage)
676 {
677 if (visit == PreVisit)
678 {
679 mStructsAndBlocksByName.push_back({});
680 }
681 else if (visit == PostVisit)
682 {
683 mStructsAndBlocksByName.pop_back();
684 }
685 }
686 }
687
isVariableDeclared(const TVariable * variable)688 bool ValidateAST::isVariableDeclared(const TVariable *variable)
689 {
690 ASSERT(mOptions.validateVariableReferences);
691
692 for (const std::set<const TVariable *> &scopeVariables : mDeclaredVariables)
693 {
694 if (scopeVariables.count(variable) > 0)
695 {
696 return true;
697 }
698 }
699
700 return false;
701 }
702
variableNeedsDeclaration(const TVariable * variable)703 bool ValidateAST::variableNeedsDeclaration(const TVariable *variable)
704 {
705 // Don't expect declaration for built-in variables.
706 if (gl::IsBuiltInName(variable->name().data()))
707 {
708 return false;
709 }
710
711 // Additionally, don't expect declaration for Vulkan specialization constants if not enabled.
712 // The declaration of these variables is deferred.
713 if (variable->getType().getQualifier() == EvqSpecConst)
714 {
715 return mOptions.validateSpecConstReferences;
716 }
717
718 return true;
719 }
720
getStructOrInterfaceBlock(const TType & type,Name * typeNameOut)721 const TFieldListCollection *ValidateAST::getStructOrInterfaceBlock(const TType &type,
722 Name *typeNameOut)
723 {
724 const TStructure *structure = type.getStruct();
725 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
726
727 ASSERT(structure != nullptr || interfaceBlock != nullptr);
728
729 // Make sure the structure or interface block is not doubly defined.
730 const TFieldListCollection *structOrBlock = nullptr;
731 if (structure != nullptr && structure->symbolType() != SymbolType::Empty)
732 {
733 structOrBlock = structure;
734 *typeNameOut = Name(*structure);
735 }
736 else if (interfaceBlock != nullptr)
737 {
738 structOrBlock = interfaceBlock;
739 *typeNameOut = Name(*interfaceBlock);
740 }
741
742 return structOrBlock;
743 }
744
expectNonNullChildren(Visit visit,TIntermNode * node,size_t least_count)745 void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count)
746 {
747 if (visit == PreVisit && mOptions.validateNullNodes)
748 {
749 size_t childCount = node->getChildCount();
750 if (childCount < least_count)
751 {
752 mDiagnostics->error(node->getLine(), "Too few children", "<validateNullNodes>");
753 mNullNodesFailed = true;
754 }
755
756 for (size_t i = 0; i < childCount; ++i)
757 {
758 if (node->getChildNode(i) == nullptr)
759 {
760 mDiagnostics->error(node->getLine(), "Found nullptr child", "<validateNullNodes>");
761 mNullNodesFailed = true;
762 }
763 }
764 }
765 }
766
visitSymbol(TIntermSymbol * node)767 void ValidateAST::visitSymbol(TIntermSymbol *node)
768 {
769 visitNode(PreVisit, node);
770
771 const TVariable *variable = &node->variable();
772
773 if (mOptions.validateVariableReferences)
774 {
775 if (variableNeedsDeclaration(variable))
776 {
777 visitVariableNeedingDeclaration(node);
778 }
779 }
780 if (variable->symbolType() == SymbolType::Empty)
781 {
782 if (!isInDeclaration())
783 {
784 mDiagnostics->error(node->getLine(), "Found symbol with empty name", "");
785 mVariableNamingFailed = true;
786 }
787 }
788 const bool isBuiltIn = gl::IsBuiltInName(variable->name().data());
789 if (isBuiltIn)
790 {
791 visitBuiltInVariable(node);
792 }
793
794 if (mOptions.validatePrecision)
795 {
796 if (!isBuiltIn && IsPrecisionApplicableToType(node->getBasicType()) &&
797 node->getType().getPrecision() == EbpUndefined)
798 {
799 // Note that some built-ins don't have a precision.
800 mDiagnostics->error(node->getLine(),
801 "Found symbol with undefined precision <validatePrecision>",
802 variable->name().data());
803 mPrecisionFailed = true;
804 }
805 }
806 }
807
visitConstantUnion(TIntermConstantUnion * node)808 void ValidateAST::visitConstantUnion(TIntermConstantUnion *node)
809 {
810 visitNode(PreVisit, node);
811 }
812
visitSwizzle(Visit visit,TIntermSwizzle * node)813 bool ValidateAST::visitSwizzle(Visit visit, TIntermSwizzle *node)
814 {
815 visitNode(visit, node);
816
817 if (mOptions.validateNoSwizzleOfSwizzle)
818 {
819 if (node->getOperand()->getAsSwizzleNode() != nullptr)
820 {
821 mDiagnostics->error(node->getLine(), "Found swizzle applied to swizzle",
822 "<validateNoSwizzleOfSwizzle>");
823 mNoSwizzleOfSwizzleFailed = true;
824 }
825 }
826
827 return true;
828 }
829
visitBinary(Visit visit,TIntermBinary * node)830 bool ValidateAST::visitBinary(Visit visit, TIntermBinary *node)
831 {
832 visitNode(visit, node);
833
834 if (visit == PreVisit && mOptions.validateOps)
835 {
836 const bool hasParent = getParentNode() != nullptr;
837 const bool isInDeclaration =
838 hasParent && getParentNode()->getAsDeclarationNode() != nullptr;
839 const TOperator op = node->getOp();
840 if (!BuiltInGroup::IsBuiltIn(op) && !IsBinaryOp(op))
841 {
842 mDiagnostics->error(node->getLine(),
843 "Found binary node with non-binary op <validateOps>",
844 GetOperatorString(op));
845 mOpsFailed = true;
846 }
847 else if (op == EOpInitialize && hasParent && !isInDeclaration)
848 {
849 mDiagnostics->error(node->getLine(),
850 "Found EOpInitialize node outside declaration <validateOps>",
851 GetOperatorString(op));
852 mOpsFailed = true;
853 }
854 else if (op == EOpAssign && hasParent && isInDeclaration)
855 {
856 mDiagnostics->error(node->getLine(),
857 "Found EOpAssign node inside declaration <validateOps>",
858 GetOperatorString(op));
859 mOpsFailed = true;
860 }
861 }
862 if (mOptions.validateExpressionTypes && visit == PreVisit)
863 {
864 validateExpressionTypeBinary(node);
865 }
866
867 return true;
868 }
869
visitUnary(Visit visit,TIntermUnary * node)870 bool ValidateAST::visitUnary(Visit visit, TIntermUnary *node)
871 {
872 visitNode(visit, node);
873
874 if (visit == PreVisit && mOptions.validateOps)
875 {
876 const TOperator op = node->getOp();
877 if (!BuiltInGroup::IsBuiltIn(op) && !IsUnaryOp(op))
878 {
879 mDiagnostics->error(node->getLine(), "Found unary node with non-unary op <validateOps>",
880 GetOperatorString(op));
881 mOpsFailed = true;
882 }
883 }
884 if (visit == PreVisit && mOptions.validateBuiltInOps)
885 {
886 visitBuiltInFunction(node, node->getFunction());
887 }
888
889 return true;
890 }
891
visitTernary(Visit visit,TIntermTernary * node)892 bool ValidateAST::visitTernary(Visit visit, TIntermTernary *node)
893 {
894 visitNode(visit, node);
895 return true;
896 }
897
visitIfElse(Visit visit,TIntermIfElse * node)898 bool ValidateAST::visitIfElse(Visit visit, TIntermIfElse *node)
899 {
900 visitNode(visit, node);
901 return true;
902 }
903
visitSwitch(Visit visit,TIntermSwitch * node)904 bool ValidateAST::visitSwitch(Visit visit, TIntermSwitch *node)
905 {
906 visitNode(visit, node);
907
908 if (mOptions.validateExpressionTypes && visit == PreVisit)
909 {
910 validateExpressionTypeSwitch(node);
911 }
912
913 return true;
914 }
915
visitCase(Visit visit,TIntermCase * node)916 bool ValidateAST::visitCase(Visit visit, TIntermCase *node)
917 {
918 // Case is allowed to come after a branch, and for dead-code-elimination purposes acts as if a
919 // new block is started.
920 mIsBranchVisitedInBlock = false;
921
922 visitNode(visit, node);
923
924 return true;
925 }
926
visitFunctionPrototype(TIntermFunctionPrototype * node)927 void ValidateAST::visitFunctionPrototype(TIntermFunctionPrototype *node)
928 {
929 visitNode(PreVisit, node);
930
931 if (mOptions.validateFunctionCall)
932 {
933 const TFunction *function = node->getFunction();
934 mDeclaredFunctions.insert(function);
935 }
936
937 const TFunction *function = node->getFunction();
938 const TType &returnType = function->getReturnType();
939 if (mOptions.validatePrecision && IsPrecisionApplicableToType(returnType.getBasicType()) &&
940 returnType.getPrecision() == EbpUndefined)
941 {
942 mDiagnostics->error(
943 node->getLine(),
944 "Found function with undefined precision on return value <validatePrecision>",
945 function->name().data());
946 mPrecisionFailed = true;
947 }
948
949 if (mOptions.validateStructUsage)
950 {
951 bool needsProcessing =
952 mStructUsageProcessedFunctions.find(function) == mStructUsageProcessedFunctions.end();
953 if (needsProcessing && returnType.isStructSpecifier())
954 {
955 visitStructOrInterfaceBlockDeclaration(returnType, node->getLine());
956 mStructUsageProcessedFunctions.insert(function);
957 }
958 else
959 {
960 visitStructUsage(returnType, node->getLine());
961 }
962 }
963
964 for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
965 {
966 const TVariable *param = function->getParam(paramIndex);
967 const TType ¶mType = param->getType();
968
969 if (mOptions.validateStructUsage)
970 {
971 visitStructUsage(paramType, node->getLine());
972 }
973
974 if (mOptions.validateQualifiers)
975 {
976 TQualifier qualifier = paramType.getQualifier();
977 if (qualifier != EvqParamIn && qualifier != EvqParamOut && qualifier != EvqParamInOut &&
978 qualifier != EvqParamConst)
979 {
980 mDiagnostics->error(node->getLine(),
981 "Found function prototype with an invalid qualifier "
982 "<validateQualifiers>",
983 param->name().data());
984 mQualifiersFailed = true;
985 }
986
987 if (IsOpaqueType(paramType.getBasicType()) && qualifier != EvqParamIn)
988 {
989 mDiagnostics->error(
990 node->getLine(),
991 "Found function prototype with an invalid qualifier on opaque parameter "
992 "<validateQualifiers>",
993 param->name().data());
994 mQualifiersFailed = true;
995 }
996 }
997
998 if (mOptions.validatePrecision && IsPrecisionApplicableToType(paramType.getBasicType()) &&
999 paramType.getPrecision() == EbpUndefined)
1000 {
1001 mDiagnostics->error(
1002 node->getLine(),
1003 "Found function parameter with undefined precision <validatePrecision>",
1004 param->name().data());
1005 mPrecisionFailed = true;
1006 }
1007 }
1008 }
1009
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)1010 bool ValidateAST::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
1011 {
1012 visitNode(visit, node);
1013
1014 if (mOptions.validateVariableReferences && visit == PreVisit)
1015 {
1016 const TFunction *function = node->getFunction();
1017
1018 size_t paramCount = function->getParamCount();
1019 for (size_t paramIndex = 0; paramIndex < paramCount; ++paramIndex)
1020 {
1021 const TVariable *variable = function->getParam(paramIndex);
1022
1023 if (isVariableDeclared(variable))
1024 {
1025 mDiagnostics->error(node->getLine(),
1026 "Found two declarations of the same function argument "
1027 "<validateVariableReferences>",
1028 variable->name().data());
1029 mVariableReferencesFailed = true;
1030 break;
1031 }
1032
1033 mDeclaredVariables.back().insert(variable);
1034 }
1035 }
1036
1037 return true;
1038 }
1039
visitAggregate(Visit visit,TIntermAggregate * node)1040 bool ValidateAST::visitAggregate(Visit visit, TIntermAggregate *node)
1041 {
1042 visitNode(visit, node);
1043 expectNonNullChildren(visit, node, 0);
1044
1045 if (visit == PreVisit && mOptions.validateBuiltInOps)
1046 {
1047 visitBuiltInFunction(node, node->getFunction());
1048 }
1049
1050 if (visit == PreVisit && mOptions.validateFunctionCall)
1051 {
1052 visitFunctionCall(node);
1053 }
1054
1055 if (visit == PreVisit && mOptions.validateNoRawFunctionCalls)
1056 {
1057 if (node->getOp() == EOpCallInternalRawFunction)
1058 {
1059 mDiagnostics->error(node->getLine(),
1060 "Found node calling a raw function (deprecated) "
1061 "<validateNoRawFunctionCalls>",
1062 node->getFunction()->name().data());
1063 mNoRawFunctionCallsFailed = true;
1064 }
1065 }
1066
1067 if (visit == PreVisit && mOptions.validateNoQualifiersOnConstructors)
1068 {
1069 if (node->getOp() == EOpConstruct)
1070 {
1071 if (node->getType().isInvariant())
1072 {
1073 mDiagnostics->error(node->getLine(), "Found constructor node with invariant type",
1074 "<validateNoQualifiersOnConstructors>");
1075 mNoQualifiersOnConstructorsFailed = true;
1076 }
1077 if (node->getType().isPrecise())
1078 {
1079 mDiagnostics->error(node->getLine(), "Found constructor node with precise type",
1080 "<validateNoQualifiersOnConstructors>");
1081 mNoQualifiersOnConstructorsFailed = true;
1082 }
1083 if (node->getType().isInterpolant())
1084 {
1085 mDiagnostics->error(node->getLine(), "Found constructor node with interpolant type",
1086 "<validateNoQualifiersOnConstructors>");
1087 mNoQualifiersOnConstructorsFailed = true;
1088 }
1089 if (!node->getType().getMemoryQualifier().isEmpty())
1090 {
1091 mDiagnostics->error(node->getLine(),
1092 "Found constructor node whose type has a memory qualifier",
1093 "<validateNoQualifiersOnConstructors>");
1094 mNoQualifiersOnConstructorsFailed = true;
1095 }
1096 if (node->getType().getInterfaceBlock() != nullptr)
1097 {
1098 mDiagnostics->error(
1099 node->getLine(),
1100 "Found constructor node whose type references an interface block",
1101 "<validateNoQualifiersOnConstructors>");
1102 mNoQualifiersOnConstructorsFailed = true;
1103 }
1104 if (!node->getType().getLayoutQualifier().isEmpty())
1105 {
1106 mDiagnostics->error(node->getLine(),
1107 "Found constructor node whose type has a layout qualifier",
1108 "<validateNoQualifiersOnConstructors>");
1109 mNoQualifiersOnConstructorsFailed = true;
1110 }
1111 }
1112 }
1113
1114 return true;
1115 }
1116
visitBlock(Visit visit,TIntermBlock * node)1117 bool ValidateAST::visitBlock(Visit visit, TIntermBlock *node)
1118 {
1119 visitNode(visit, node);
1120 scope(visit);
1121 expectNonNullChildren(visit, node, 0);
1122
1123 if (visit == PostVisit)
1124 {
1125 // If the parent is a block and mIsBranchVisitedInBlock is set, this is a nested block
1126 // without any condition (like if, loop or switch), so the rest of the parent block is also
1127 // dead code. Otherwise the parent block can contain code after this.
1128 if (getParentNode() == nullptr || getParentNode()->getAsBlock() == nullptr)
1129 {
1130 mIsBranchVisitedInBlock = false;
1131 }
1132 }
1133
1134 return true;
1135 }
1136
visitGlobalQualifierDeclaration(Visit visit,TIntermGlobalQualifierDeclaration * node)1137 bool ValidateAST::visitGlobalQualifierDeclaration(Visit visit,
1138 TIntermGlobalQualifierDeclaration *node)
1139 {
1140 visitNode(visit, node);
1141
1142 const TVariable *variable = &node->getSymbol()->variable();
1143
1144 if (mOptions.validateVariableReferences && variableNeedsDeclaration(variable))
1145 {
1146 if (!isVariableDeclared(variable))
1147 {
1148 mDiagnostics->error(node->getLine(),
1149 "Found reference to undeclared or inconsistently transformed "
1150 "variable <validateVariableReferences>",
1151 variable->name().data());
1152 mVariableReferencesFailed = true;
1153 }
1154 }
1155 return true;
1156 }
1157
visitDeclaration(Visit visit,TIntermDeclaration * node)1158 bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node)
1159 {
1160 visitNode(visit, node);
1161 expectNonNullChildren(visit, node, 0);
1162
1163 const TIntermSequence &sequence = *(node->getSequence());
1164
1165 if (mOptions.validateMultiDeclarations && sequence.size() > 1)
1166 {
1167 TIntermSymbol *symbol = sequence[1]->getAsSymbolNode();
1168 if (symbol == nullptr)
1169 {
1170 TIntermBinary *init = sequence[1]->getAsBinaryNode();
1171 ASSERT(init && init->getOp() == EOpInitialize);
1172 symbol = init->getLeft()->getAsSymbolNode();
1173 }
1174 ASSERT(symbol);
1175
1176 mDiagnostics->error(node->getLine(),
1177 "Found multiple declarations where SeparateDeclarations should have "
1178 "separated them <validateMultiDeclarations>",
1179 symbol->variable().name().data());
1180 mMultiDeclarationsFailed = true;
1181 }
1182
1183 if (visit == PreVisit)
1184 {
1185 bool validateStructUsage = mOptions.validateStructUsage;
1186
1187 for (TIntermNode *instance : sequence)
1188 {
1189 TIntermSymbol *symbol = instance->getAsSymbolNode();
1190 if (symbol == nullptr)
1191 {
1192 TIntermBinary *init = instance->getAsBinaryNode();
1193 ASSERT(init && init->getOp() == EOpInitialize);
1194 symbol = init->getLeft()->getAsSymbolNode();
1195 }
1196 ASSERT(symbol);
1197
1198 const TVariable *variable = &symbol->variable();
1199 const TType &type = variable->getType();
1200
1201 if (mOptions.validateVariableReferences)
1202 {
1203 if (isVariableDeclared(variable))
1204 {
1205 mDiagnostics->error(
1206 node->getLine(),
1207 "Found two declarations of the same variable <validateVariableReferences>",
1208 variable->name().data());
1209 mVariableReferencesFailed = true;
1210 break;
1211 }
1212
1213 mDeclaredVariables.back().insert(variable);
1214
1215 const TInterfaceBlock *interfaceBlock = variable->getType().getInterfaceBlock();
1216
1217 if (variable->symbolType() == SymbolType::Empty && interfaceBlock != nullptr)
1218 {
1219 // Nameless interface blocks can only be declared at the top level. Their
1220 // fields are matched by field index, and then verified to match by name.
1221 // Conflict in names should have already generated a compile error.
1222 ASSERT(mDeclaredVariables.size() == 1);
1223 ASSERT(mNamelessInterfaceBlocks.count(interfaceBlock) == 0);
1224
1225 mNamelessInterfaceBlocks.insert(interfaceBlock);
1226 }
1227 }
1228
1229 if (validateStructUsage)
1230 {
1231 // Only declare and/or validate the struct once.
1232 validateStructUsage = false;
1233
1234 if (type.isStructSpecifier() || type.isInterfaceBlock())
1235 {
1236 visitStructOrInterfaceBlockDeclaration(type, node->getLine());
1237 }
1238 else
1239 {
1240 visitStructUsage(type, node->getLine());
1241 }
1242 }
1243
1244 if (gl::IsBuiltInName(variable->name().data()))
1245 {
1246 visitBuiltInVariable(symbol);
1247 }
1248
1249 if (mOptions.validatePrecision && (type.isStructSpecifier() || type.isInterfaceBlock()))
1250 {
1251 const TFieldListCollection *structOrBlock = type.getStruct();
1252 if (structOrBlock == nullptr)
1253 {
1254 structOrBlock = type.getInterfaceBlock();
1255 }
1256
1257 for (const TField *field : structOrBlock->fields())
1258 {
1259 const TType *fieldType = field->type();
1260 if (IsPrecisionApplicableToType(fieldType->getBasicType()) &&
1261 fieldType->getPrecision() == EbpUndefined)
1262 {
1263 mDiagnostics->error(
1264 node->getLine(),
1265 "Found block field with undefined precision <validatePrecision>",
1266 field->name().data());
1267 mPrecisionFailed = true;
1268 }
1269 }
1270 }
1271 }
1272 }
1273
1274 return true;
1275 }
1276
visitLoop(Visit visit,TIntermLoop * node)1277 bool ValidateAST::visitLoop(Visit visit, TIntermLoop *node)
1278 {
1279 visitNode(visit, node);
1280 return true;
1281 }
1282
visitBranch(Visit visit,TIntermBranch * node)1283 bool ValidateAST::visitBranch(Visit visit, TIntermBranch *node)
1284 {
1285 visitNode(visit, node);
1286
1287 if (visit == PreVisit && mOptions.validateOps)
1288 {
1289 const TOperator op = node->getFlowOp();
1290 if (!IsBranchOp(op))
1291 {
1292 mDiagnostics->error(node->getLine(),
1293 "Found branch node with non-branch op <validateOps>",
1294 GetOperatorString(op));
1295 mOpsFailed = true;
1296 }
1297 }
1298 if (visit == PostVisit)
1299 {
1300 mIsBranchVisitedInBlock = true;
1301 }
1302
1303 return true;
1304 }
1305
visitPreprocessorDirective(TIntermPreprocessorDirective * node)1306 void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node)
1307 {
1308 visitNode(PreVisit, node);
1309 }
1310
validateInternal()1311 bool ValidateAST::validateInternal()
1312 {
1313 return !mSingleParentFailed && !mVariableReferencesFailed && !mOpsFailed &&
1314 !mBuiltInOpsFailed && !mFunctionCallFailed && !mNoRawFunctionCallsFailed &&
1315 !mNullNodesFailed && !mQualifiersFailed && !mPrecisionFailed && !mStructUsageFailed &&
1316 !mExpressionTypesFailed && !mMultiDeclarationsFailed && !mNoSwizzleOfSwizzleFailed &&
1317 !mNoQualifiersOnConstructorsFailed && !mNoStatementsAfterBranchFailed &&
1318 !mVariableNamingFailed;
1319 }
1320
isInDeclaration() const1321 bool ValidateAST::isInDeclaration() const
1322 {
1323 auto *parent = getParentNode();
1324 return parent != nullptr && parent->getAsDeclarationNode() != nullptr;
1325 }
1326
1327 } // anonymous namespace
1328
ValidateAST(TIntermNode * root,TDiagnostics * diagnostics,const ValidateASTOptions & options)1329 bool ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options)
1330 {
1331 // ValidateAST is called after transformations, so if |validateNoMoreTransformations| is set,
1332 // it's immediately an error.
1333 if (options.validateNoMoreTransformations)
1334 {
1335 diagnostics->error(kNoSourceLoc, "Unexpected transformation after AST post-processing",
1336 "<validateNoMoreTransformations>");
1337 return false;
1338 }
1339
1340 return ValidateAST::validate(root, diagnostics, options);
1341 }
1342
1343 } // namespace sh
1344