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