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 "compiler/translator/Diagnostics.h"
10#include "compiler/translator/tree_util/IntermTraverse.h"
11
12namespace sh
13{
14
15namespace
16{
17
18class ValidateAST : public TIntermTraverser
19{
20 public:
21 static bool validate(TIntermNode *root,
22 TDiagnostics *diagnostics,
23 const ValidateASTOptions &options);
24
25 void visitSymbol(TIntermSymbol *node) override;
26 void visitConstantUnion(TIntermConstantUnion *node) override;
27 bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
28 bool visitBinary(Visit visit, TIntermBinary *node) override;
29 bool visitUnary(Visit visit, TIntermUnary *node) override;
30 bool visitTernary(Visit visit, TIntermTernary *node) override;
31 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
32 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
33 bool visitCase(Visit visit, TIntermCase *node) override;
34 void visitFunctionPrototype(TIntermFunctionPrototype *node) override;
35 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
36 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
37 bool visitBlock(Visit visit, TIntermBlock *node) override;
38 bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
39 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
40 bool visitLoop(Visit visit, TIntermLoop *node) override;
41 bool visitBranch(Visit visit, TIntermBranch *node) override;
42 void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override;
43
44 private:
45 ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options);
46
47 // Visit as a generic node
48 void visitNode(Visit visit, TIntermNode *node);
49
50 void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count);
51
52 bool validateInternal();
53
54 ValidateASTOptions mOptions;
55 TDiagnostics *mDiagnostics;
56
57 // For validateSingleParent:
58 std::map<TIntermNode *, TIntermNode *> mParent;
59 bool mSingleParentFailed = false;
60
61 // For validateNullNodes
62 bool mNullNodesFailed = false;
63};
64
65bool ValidateAST::validate(TIntermNode *root,
66 TDiagnostics *diagnostics,
67 const ValidateASTOptions &options)
68{
69 ValidateAST validate(root, diagnostics, options);
70 root->traverse(&validate);
71 return validate.validateInternal();
72}
73
74ValidateAST::ValidateAST(TIntermNode *root,
75 TDiagnostics *diagnostics,
76 const ValidateASTOptions &options)
77 : TIntermTraverser(true, false, true, nullptr), mOptions(options), mDiagnostics(diagnostics)
78{
79 if (mOptions.validateSingleParent)
80 {
81 mParent[root] = nullptr;
82 }
83}
84
85void ValidateAST::visitNode(Visit visit, TIntermNode *node)
86{
87 if (visit == PreVisit && mOptions.validateSingleParent)
88 {
89 size_t childCount = node->getChildCount();
90 for (size_t i = 0; i < childCount; ++i)
91 {
92 TIntermNode *child = node->getChildNode(i);
93 if (mParent.find(child) != mParent.end())
94 {
95 // If child is visited twice but through the same parent, the problem is in one of
96 // the ancestors.
97 if (mParent[child] != node)
98 {
99 mDiagnostics->error(node->getLine(), "Found child with two parents",
100 "<validateSingleParent>");
101 mSingleParentFailed = true;
102 }
103 }
104
105 mParent[child] = node;
106 }
107 }
108}
109
110void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count)
111{
112 if (visit == PreVisit && mOptions.validateNullNodes)
113 {
114 size_t childCount = node->getChildCount();
115 if (childCount < least_count)
116 {
117 mDiagnostics->error(node->getLine(), "Too few children", "<validateNullNodes>");
118 mNullNodesFailed = true;
119 }
120
121 for (size_t i = 0; i < childCount; ++i)
122 {
123 if (node->getChildNode(i) == nullptr)
124 {
125 mDiagnostics->error(node->getLine(), "Found nullptr child", "<validateNullNodes>");
126 mNullNodesFailed = true;
127 }
128 }
129 }
130}
131
132void ValidateAST::visitSymbol(TIntermSymbol *node)
133{
134 visitNode(PreVisit, node);
135}
136
137void ValidateAST::visitConstantUnion(TIntermConstantUnion *node)
138{
139 visitNode(PreVisit, node);
140}
141
142bool ValidateAST::visitSwizzle(Visit visit, TIntermSwizzle *node)
143{
144 visitNode(visit, node);
145 return true;
146}
147
148bool ValidateAST::visitBinary(Visit visit, TIntermBinary *node)
149{
150 visitNode(visit, node);
151 return true;
152}
153
154bool ValidateAST::visitUnary(Visit visit, TIntermUnary *node)
155{
156 visitNode(visit, node);
157 return true;
158}
159
160bool ValidateAST::visitTernary(Visit visit, TIntermTernary *node)
161{
162 visitNode(visit, node);
163 return true;
164}
165
166bool ValidateAST::visitIfElse(Visit visit, TIntermIfElse *node)
167{
168 visitNode(visit, node);
169 return true;
170}
171
172bool ValidateAST::visitSwitch(Visit visit, TIntermSwitch *node)
173{
174 visitNode(visit, node);
175 return true;
176}
177
178bool ValidateAST::visitCase(Visit visit, TIntermCase *node)
179{
180 visitNode(visit, node);
181 return true;
182}
183
184void ValidateAST::visitFunctionPrototype(TIntermFunctionPrototype *node)
185{
186 visitNode(PreVisit, node);
187}
188
189bool ValidateAST::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
190{
191 visitNode(visit, node);
192 return true;
193}
194
195bool ValidateAST::visitAggregate(Visit visit, TIntermAggregate *node)
196{
197 visitNode(visit, node);
198 expectNonNullChildren(visit, node, 0);
199 return true;
200}
201
202bool ValidateAST::visitBlock(Visit visit, TIntermBlock *node)
203{
204 visitNode(visit, node);
205 expectNonNullChildren(visit, node, 0);
206 return true;
207}
208
209bool ValidateAST::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
210{
211 visitNode(visit, node);
212 return true;
213}
214
215bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node)
216{
217 visitNode(visit, node);
218 expectNonNullChildren(visit, node, 0);
219 return true;
220}
221
222bool ValidateAST::visitLoop(Visit visit, TIntermLoop *node)
223{
224 visitNode(visit, node);
225 return true;
226}
227
228bool ValidateAST::visitBranch(Visit visit, TIntermBranch *node)
229{
230 visitNode(visit, node);
231 return true;
232}
233
234void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node)
235{
236 visitNode(PreVisit, node);
237}
238
239bool ValidateAST::validateInternal()
240{
241 return !mSingleParentFailed && !mNullNodesFailed;
242}
243
244} // anonymous namespace
245
246bool ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options)
247{
248 return ValidateAST::validate(root, diagnostics, options);
249}
250
251} // namespace sh
252