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