xref: /aosp_15_r20/external/angle/src/compiler/translator/ValidateAST.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 &paramType = 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