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