Bug Summary

File:root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp
Warning:line 422, column 9
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name RewriteStructSamplers.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +sse2 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D ANGLE_PLATFORM_EXPORT= -D __NDK_FPABI__= -D ANGLE_SKIP_DXGI_1_2_CHECK -D ANGLE_ENABLE_KEYEDMUTEX -D ANGLE_TRANSLATOR_ESSL_ONLY -D ANGLE_DISABLE_POOL_ALLOC -D ANGLE_ENABLE_APPLE_WORKAROUNDS -D ANGLE_ENABLE_ESSL -D ANGLE_ENABLE_GLSL -D ANGLE_ENABLE_HLSL -D ANGLE_ENABLE_SHARE_CONTEXT_LOCK=1 -D ANGLE_VMA_VERSION=2003000 -D CR_CLANG_REVISION="llvmorg-16-init-6578-g0d30e92f-2" -D DYNAMIC_ANNOTATIONS_ENABLED=0 -D NOMINMAX -D UNICODE -D WINVER=0x0A00 -D _ATL_NO_OPENGL -D _CRT_NONSTDC_NO_WARNINGS -D _CRT_RAND_S -D _CRT_SECURE_NO_DEPRECATE -D _HAS_EXCEPTIONS=0 -D _SCL_SECURE_NO_DEPRECATE -D _SECURE_ATL -D _UNICODE -D _WINSOCK_DEPRECATED_NO_WARNINGS -I /root/firefox-clang/gfx/angle/targets/translator -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -I /root/firefox-clang/gfx/angle/checkout/include -I /root/firefox-clang/gfx/angle/checkout/src -I /root/firefox-clang/gfx/angle/checkout/src/common/third_party/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-final-dtor-non-final-class -Wno-implicit-const-int-float-conversion -Wno-range-loop-construct -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp
1//
2// Copyright 2018 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// RewriteStructSamplers: Extract samplers from structs.
7//
8
9#include "compiler/translator/tree_ops/RewriteStructSamplers.h"
10
11#include "compiler/translator/ImmutableStringBuilder.h"
12#include "compiler/translator/SymbolTable.h"
13#include "compiler/translator/tree_util/IntermNode_util.h"
14#include "compiler/translator/tree_util/IntermTraverse.h"
15
16namespace sh
17{
18namespace
19{
20
21// Used to map one structure type to another (one where the samplers are removed).
22struct StructureData
23{
24 // The structure this was replaced with. If nullptr, it means the structure is removed (because
25 // it had all samplers).
26 const TStructure *modified;
27 // Indexed by the field index of original structure, to get the field index of the modified
28 // structure. For example:
29 //
30 // struct Original
31 // {
32 // sampler2D s1;
33 // vec4 f1;
34 // sampler2D s2;
35 // sampler2D s3;
36 // vec4 f2;
37 // };
38 //
39 // struct Modified
40 // {
41 // vec4 f1;
42 // vec4 f2;
43 // };
44 //
45 // fieldMap:
46 // 0 -> Invalid
47 // 1 -> 0
48 // 2 -> Invalid
49 // 3 -> Invalid
50 // 4 -> 1
51 //
52 TVector<int> fieldMap;
53};
54
55using StructureMap = angle::HashMap<const TStructure *, StructureData>;
56using StructureUniformMap = angle::HashMap<const TVariable *, const TVariable *>;
57using ExtractedSamplerMap = angle::HashMap<std::string, const TVariable *>;
58
59TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
60 TCompiler *compiler,
61 TIntermBinary *node,
62 const StructureMap &structureMap,
63 const StructureUniformMap &structureUniformMap,
64 const ExtractedSamplerMap &extractedSamplers);
65
66TIntermTyped *RewriteExpressionVisitBinaryHelper(TCompiler *compiler,
67 TIntermBinary *node,
68 const StructureMap &structureMap,
69 const StructureUniformMap &structureUniformMap,
70 const ExtractedSamplerMap &extractedSamplers)
71{
72 // Only interested in EOpIndexDirectStruct binary nodes.
73 if (node->getOp() != EOpIndexDirectStruct)
74 {
75 return nullptr;
76 }
77
78 const TStructure *structure = node->getLeft()->getType().getStruct();
79 ASSERT(structure)(structure ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 79, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 79 << "): " << "structure"
))
;
80
81 // If the result of the index is not a sampler and the struct is not replaced, there's nothing
82 // to do.
83 if (!node->getType().isSampler() && structureMap.find(structure) == structureMap.end())
84 {
85 return nullptr;
86 }
87
88 // Otherwise, replace the whole expression such that:
89 //
90 // - if sampler, it's indexed with whatever indices the parent structs were indexed with,
91 // - otherwise, the chain of field selections is rewritten by modifying the base uniform so all
92 // the intermediate nodes would have the correct type (and therefore fields).
93 ASSERT(structureMap.find(structure) != structureMap.end())(structureMap.find(structure) != structureMap.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 93, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 93 << "): " << "structureMap.find(structure) != structureMap.end()"
))
;
94
95 return RewriteModifiedStructFieldSelectionExpression(compiler, node, structureMap,
96 structureUniformMap, extractedSamplers);
97}
98
99// Given an expression, this traverser calculates a new expression where sampler-in-structs are
100// replaced with their extracted ones, and field indices are adjusted for the rest of the fields.
101// In particular, this is run on the right node of EOpIndexIndirect binary nodes, so that the
102// expression in the index gets a chance to go through this transformation.
103class RewriteExpressionTraverser final : public TIntermTraverser
104{
105 public:
106 explicit RewriteExpressionTraverser(TCompiler *compiler,
107 const StructureMap &structureMap,
108 const StructureUniformMap &structureUniformMap,
109 const ExtractedSamplerMap &extractedSamplers)
110 : TIntermTraverser(true, false, false),
111 mCompiler(compiler),
112 mStructureMap(structureMap),
113 mStructureUniformMap(structureUniformMap),
114 mExtractedSamplers(extractedSamplers)
115 {}
116
117 bool visitBinary(Visit visit, TIntermBinary *node) override
118 {
119 TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
120 mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
121
122 if (rewritten == nullptr)
123 {
124 return true;
125 }
126
127 queueReplacement(rewritten, OriginalNode::IS_DROPPED);
128
129 // Don't iterate as the expression is rewritten.
130 return false;
131 }
132
133 void visitSymbol(TIntermSymbol *node) override
134 {
135 // It's impossible to reach here with a symbol that needs replacement.
136 // MonomorphizeUnsupportedFunctions makes sure that whole structs containing
137 // samplers are not passed to functions, so any instance of the struct uniform is
138 // necessarily indexed right away. visitBinary should have already taken care of it.
139 ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end())(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap
.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 139, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 139 << "): " << "mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()"
))
;
140 }
141
142 private:
143 TCompiler *mCompiler;
144
145 // See RewriteStructSamplersTraverser.
146 const StructureMap &mStructureMap;
147 const StructureUniformMap &mStructureUniformMap;
148 const ExtractedSamplerMap &mExtractedSamplers;
149};
150
151// Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because
152// it cannot be a sampler itself or of a struct type.
153void RewriteIndexExpression(TCompiler *compiler,
154 TIntermTyped *expression,
155 const StructureMap &structureMap,
156 const StructureUniformMap &structureUniformMap,
157 const ExtractedSamplerMap &extractedSamplers)
158{
159 RewriteExpressionTraverser traverser(compiler, structureMap, structureUniformMap,
160 extractedSamplers);
161 expression->traverse(&traverser);
162 bool valid = traverser.updateTree(compiler, expression);
163 ASSERT(valid)(valid ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 163, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 163 << "): " << "valid"))
;
164}
165
166// Given an expression such as the following:
167//
168// EOpIndexDirectStruct (sampler)
169// / \
170// EOpIndex* field index
171// / \
172// EOpIndexDirectStruct index 2
173// / \
174// EOpIndex* field index
175// / \
176// EOpIndexDirectStruct index 1
177// / \
178// Uniform Struct field index
179//
180// produces:
181//
182// EOpIndex*
183// / \
184// EOpIndex* index 2
185// / \
186// sampler index 1
187//
188// Alternatively, if the expression is as such:
189//
190// EOpIndexDirectStruct
191// / \
192// (modified struct type) EOpIndex* field index
193// / \
194// EOpIndexDirectStruct index 2
195// / \
196// EOpIndex* field index
197// / \
198// EOpIndexDirectStruct index 1
199// / \
200// Uniform Struct field index
201//
202// produces:
203//
204// EOpIndexDirectStruct
205// / \
206// EOpIndex* mapped field index
207// / \
208// EOpIndexDirectStruct index 2
209// / \
210// EOpIndex* mapped field index
211// / \
212// EOpIndexDirectStruct index 1
213// / \
214// Uniform Struct mapped field index
215//
216TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
217 TCompiler *compiler,
218 TIntermBinary *node,
219 const StructureMap &structureMap,
220 const StructureUniformMap &structureUniformMap,
221 const ExtractedSamplerMap &extractedSamplers)
222{
223 ASSERT(node->getOp() == EOpIndexDirectStruct)(node->getOp() == EOpIndexDirectStruct ? static_cast<void
>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage(::gl
::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify
() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 223, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 223 << "): " << "node->getOp() == EOpIndexDirectStruct"
))
;
224
225 const bool isSampler = node->getType().isSampler();
226
227 TIntermSymbol *baseUniform = nullptr;
228 std::string samplerName;
229
230 TVector<TIntermBinary *> indexNodeStack;
231
232 // Iterate once and build the name of the sampler.
233 TIntermBinary *iter = node;
234 while (baseUniform == nullptr)
235 {
236 indexNodeStack.push_back(iter);
237 baseUniform = iter->getLeft()->getAsSymbolNode();
238
239 if (isSampler)
240 {
241 if (iter->getOp() == EOpIndexDirectStruct)
242 {
243 // When indexed into a struct, get the field name instead and construct the sampler
244 // name.
245 samplerName.insert(0, iter->getIndexStructFieldName().data());
246 samplerName.insert(0, "_");
247 }
248
249 if (baseUniform)
250 {
251 // If left is a symbol, we have reached the end of the chain. Use the struct name
252 // to finish building the name of the sampler.
253 samplerName.insert(0, baseUniform->variable().name().data());
254 }
255 }
256
257 iter = iter->getLeft()->getAsBinaryNode();
258 }
259
260 TIntermTyped *rewritten = nullptr;
261
262 if (isSampler)
263 {
264 ASSERT(extractedSamplers.find(samplerName) != extractedSamplers.end())(extractedSamplers.find(samplerName) != extractedSamplers.end
() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 264, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 264 << "): " << "extractedSamplers.find(samplerName) != extractedSamplers.end()"
))
;
265 rewritten = new TIntermSymbol(extractedSamplers.at(samplerName));
266 }
267 else
268 {
269 const TVariable *baseUniformVar = &baseUniform->variable();
270 ASSERT(structureUniformMap.find(baseUniformVar) != structureUniformMap.end())(structureUniformMap.find(baseUniformVar) != structureUniformMap
.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 270, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 270 << "): " << "structureUniformMap.find(baseUniformVar) != structureUniformMap.end()"
))
;
271 rewritten = new TIntermSymbol(structureUniformMap.at(baseUniformVar));
272 }
273
274 // Iterate again and build the expression from bottom up.
275 for (auto it = indexNodeStack.rbegin(); it != indexNodeStack.rend(); ++it)
276 {
277 TIntermBinary *indexNode = *it;
278
279 switch (indexNode->getOp())
280 {
281 case EOpIndexDirectStruct:
282 if (!isSampler)
283 {
284 // Remap the field.
285 const TStructure *structure = indexNode->getLeft()->getType().getStruct();
286 ASSERT(structureMap.find(structure) != structureMap.end())(structureMap.find(structure) != structureMap.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 286, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 286 << "): " << "structureMap.find(structure) != structureMap.end()"
))
;
287
288 TIntermConstantUnion *asConstantUnion =
289 indexNode->getRight()->getAsConstantUnion();
290 ASSERT(asConstantUnion)(asConstantUnion ? static_cast<void>(0) : (!((::gl::priv
::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast
<void>(0) : ::gl::priv::LogMessageVoidify() & (::gl
::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 290, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 290 << "): " << "asConstantUnion"
))
;
291
292 const int fieldIndex = asConstantUnion->getIConst(0);
293 ASSERT(fieldIndex <(fieldIndex < static_cast<int>(structureMap.at(structure
).fieldMap.size()) ? static_cast<void>(0) : (!((::gl::priv
::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast
<void>(0) : ::gl::priv::LogMessageVoidify() & (::gl
::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 294, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 294 << "): " << "fieldIndex < static_cast<int>(structureMap.at(structure).fieldMap.size())"
))
294 static_cast<int>(structureMap.at(structure).fieldMap.size()))(fieldIndex < static_cast<int>(structureMap.at(structure
).fieldMap.size()) ? static_cast<void>(0) : (!((::gl::priv
::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast
<void>(0) : ::gl::priv::LogMessageVoidify() & (::gl
::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 294, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 294 << "): " << "fieldIndex < static_cast<int>(structureMap.at(structure).fieldMap.size())"
))
;
295
296 const int mappedFieldIndex = structureMap.at(structure).fieldMap[fieldIndex];
297
298 rewritten = new TIntermBinary(EOpIndexDirectStruct, rewritten,
299 CreateIndexNode(mappedFieldIndex));
300 }
301 break;
302
303 case EOpIndexDirect:
304 rewritten = new TIntermBinary(EOpIndexDirect, rewritten, indexNode->getRight());
305 break;
306
307 case EOpIndexIndirect:
308 {
309 // Run RewriteExpressionTraverser on the right node. It may itself be an expression
310 // with a sampler inside that needs to be rewritten, or simply use a field of a
311 // struct that's remapped.
312 TIntermTyped *indexExpression = indexNode->getRight();
313 RewriteIndexExpression(compiler, indexExpression, structureMap, structureUniformMap,
314 extractedSamplers);
315 rewritten = new TIntermBinary(EOpIndexIndirect, rewritten, indexExpression);
316 break;
317 }
318
319 default:
320 UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL
))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify
() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 320, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: "
<< __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 320 << ")"; } while (0)
;
321 break;
322 }
323 }
324
325 return rewritten;
326}
327
328class RewriteStructSamplersTraverser final : public TIntermTraverser
329{
330 public:
331 explicit RewriteStructSamplersTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
332 : TIntermTraverser(true, false, false, symbolTable),
333 mCompiler(compiler),
334 mRemovedUniformsCount(0)
335 {}
336
337 int removedUniformsCount() const { return mRemovedUniformsCount; }
338
339 // Each struct sampler declaration is stripped of its samplers. New uniforms are added for each
340 // stripped struct sampler.
341 bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
342 {
343 if (!mInGlobalScope)
1
Assuming field 'mInGlobalScope' is true
2
Taking false branch
344 {
345 return true;
346 }
347
348 const TIntermSequence &sequence = *(decl->getSequence());
349 TIntermTyped *declarator = sequence.front()->getAsTyped();
350 const TType &type = declarator->getType();
351
352 if (!type.isStructureContainingSamplers())
3
Value assigned to field 'mStructure'
4
Assuming the condition is false
5
Taking false branch
353 {
354 return false;
355 }
356
357 TIntermSequence newSequence;
358
359 if (type.isStructSpecifier())
6
Assuming the condition is true
7
Taking true branch
360 {
361 // If this is just a struct definition (not a uniform variable declaration of a
362 // struct type), just remove the samplers. They are not instantiated yet.
363 const TStructure *structure = type.getStruct();
8
'structure' initialized here
364 ASSERT(structure && mStructureMap.find(structure) == mStructureMap.end())(structure && mStructureMap.find(structure) == mStructureMap
.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 364, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 364 << "): " << "structure && mStructureMap.find(structure) == mStructureMap.end()"
))
;
9
Assuming 'structure' is null
10
Assuming the condition is true
11
'?' condition is true
365
366 stripStructSpecifierSamplers(structure, &newSequence);
12
Passing null pointer value via 1st parameter 'structure'
13
Calling 'RewriteStructSamplersTraverser::stripStructSpecifierSamplers'
367 }
368 else
369 {
370 const TStructure *structure = type.getStruct();
371
372 // If the structure is defined at the same time, create the mapping to the stripped
373 // version first.
374 if (mStructureMap.find(structure) == mStructureMap.end())
375 {
376 stripStructSpecifierSamplers(structure, &newSequence);
377 }
378
379 // Then, extract the samplers from the struct and create global-scope variables instead.
380 TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
381 ASSERT(asSymbol)(asSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 381, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 381 << "): " << "asSymbol"
))
;
382 const TVariable &variable = asSymbol->variable();
383 ASSERT(variable.symbolType() != SymbolType::Empty)(variable.symbolType() != SymbolType::Empty ? static_cast<
void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage(
::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv::
LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 383, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 383 << "): " << "variable.symbolType() != SymbolType::Empty"
))
;
384
385 extractStructSamplerUniforms(variable, structure, &newSequence);
386 }
387
388 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl,
389 std::move(newSequence));
390
391 return false;
392 }
393
394 // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
395 bool visitBinary(Visit visit, TIntermBinary *node) override
396 {
397 TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
398 mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
399
400 if (rewritten == nullptr)
401 {
402 return true;
403 }
404
405 queueReplacement(rewritten, OriginalNode::IS_DROPPED);
406
407 // Don't iterate as the expression is rewritten.
408 return false;
409 }
410
411 // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
412 void visitSymbol(TIntermSymbol *node) override
413 {
414 ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end())(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap
.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 414, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 414 << "): " << "mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()"
))
;
415 }
416
417 private:
418 // Removes all samplers from a struct specifier.
419 void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
420 {
421 TFieldList *newFieldList = new TFieldList;
422 ASSERT(structure->containsSamplers())(structure->containsSamplers() ? static_cast<void>(0
) : (!((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL
))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify
() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 422, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 422 << "): " << "structure->containsSamplers()"
))
;
14
Called C++ object pointer is null
423
424 // Add this struct to the struct map
425 ASSERT(mStructureMap.find(structure) == mStructureMap.end())(mStructureMap.find(structure) == mStructureMap.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 425, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 425 << "): " << "mStructureMap.find(structure) == mStructureMap.end()"
))
;
426 StructureData *modifiedData = &mStructureMap[structure];
427
428 modifiedData->modified = nullptr;
429 modifiedData->fieldMap.resize(structure->fields().size(), std::numeric_limits<int>::max());
430
431 for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex)
432 {
433 const TField *field = structure->fields()[fieldIndex];
434 const TType &fieldType = *field->type();
435
436 // If the field is a sampler, or a struct that's entirely removed, skip it.
437 if (!fieldType.isSampler() && !isRemovedStructType(fieldType))
438 {
439 TType *newType = nullptr;
440
441 // Otherwise, if it's a struct that's replaced, create a new field of the replaced
442 // type.
443 if (fieldType.isStructureContainingSamplers())
444 {
445 const TStructure *fieldStruct = fieldType.getStruct();
446 ASSERT(mStructureMap.find(fieldStruct) != mStructureMap.end())(mStructureMap.find(fieldStruct) != mStructureMap.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 446, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 446 << "): " << "mStructureMap.find(fieldStruct) != mStructureMap.end()"
))
;
447
448 const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified;
449 ASSERT(modifiedStruct)(modifiedStruct ? static_cast<void>(0) : (!((::gl::priv
::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast
<void>(0) : ::gl::priv::LogMessageVoidify() & (::gl
::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 449, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 449 << "): " << "modifiedStruct"
))
;
450
451 newType = new TType(modifiedStruct, true);
452 if (fieldType.isArray())
453 {
454 newType->makeArrays(fieldType.getArraySizes());
455 }
456 }
457 else
458 {
459 // If not, duplicate the field as is.
460 newType = new TType(fieldType);
461 }
462
463 // Record the mapping of the field indices, so future EOpIndexDirectStruct's into
464 // this struct can be fixed up.
465 modifiedData->fieldMap[fieldIndex] = static_cast<int>(newFieldList->size());
466
467 TField *newField =
468 new TField(newType, field->name(), field->line(), field->symbolType());
469 newFieldList->push_back(newField);
470 }
471 }
472
473 // Prune empty structs.
474 if (newFieldList->empty())
475 {
476 return;
477 }
478
479 // Declare a new struct with the same name and the new fields.
480 modifiedData->modified =
481 new TStructure(mSymbolTable, structure->name(), newFieldList, structure->symbolType());
482 TType *newStructType = new TType(modifiedData->modified, true);
483 TVariable *newStructVar =
484 new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty);
485 TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar);
486
487 TIntermDeclaration *structDecl = new TIntermDeclaration;
488 structDecl->appendDeclarator(newStructRef);
489
490 newSequence->push_back(structDecl);
491 }
492
493 // Returns true if the type is a struct that was removed because we extracted all the members.
494 bool isRemovedStructType(const TType &type) const
495 {
496 const TStructure *structure = type.getStruct();
497 if (structure == nullptr)
498 {
499 // Not a struct
500 return false;
501 }
502
503 // A struct is removed if it is in the map, but doesn't have a replacement struct.
504 auto iter = mStructureMap.find(structure);
505 return iter != mStructureMap.end() && iter->second.modified == nullptr;
506 }
507
508 // Removes samplers from struct uniforms. For each sampler removed also adds a new globally
509 // defined sampler uniform.
510 void extractStructSamplerUniforms(const TVariable &variable,
511 const TStructure *structure,
512 TIntermSequence *newSequence)
513 {
514 ASSERT(structure->containsSamplers())(structure->containsSamplers() ? static_cast<void>(0
) : (!((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL
))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify
() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 514, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 514 << "): " << "structure->containsSamplers()"
))
;
515 ASSERT(mStructureMap.find(structure) != mStructureMap.end())(mStructureMap.find(structure) != mStructureMap.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 515, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 515 << "): " << "mStructureMap.find(structure) != mStructureMap.end()"
))
;
516
517 const TType &type = variable.getType();
518 enterArray(type);
519
520 for (const TField *field : structure->fields())
521 {
522 extractFieldSamplers(variable.name().data(), field, newSequence);
523 }
524
525 // If there's a replacement structure (because there are non-sampler fields in the struct),
526 // add a declaration with that type.
527 const TStructure *modified = mStructureMap[structure].modified;
528 if (modified != nullptr)
529 {
530 TType *newType = new TType(modified, false);
531 if (type.isArray())
532 {
533 newType->makeArrays(type.getArraySizes());
534 }
535 newType->setQualifier(EvqUniform);
536 const TVariable *newVariable =
537 new TVariable(mSymbolTable, variable.name(), newType, variable.symbolType());
538
539 TIntermDeclaration *newDecl = new TIntermDeclaration();
540 newDecl->appendDeclarator(new TIntermSymbol(newVariable));
541
542 newSequence->push_back(newDecl);
543
544 ASSERT(mStructureUniformMap.find(&variable) == mStructureUniformMap.end())(mStructureUniformMap.find(&variable) == mStructureUniformMap
.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 544, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 544 << "): " << "mStructureUniformMap.find(&variable) == mStructureUniformMap.end()"
))
;
545 mStructureUniformMap[&variable] = newVariable;
546 }
547 else
548 {
549 mRemovedUniformsCount++;
550 }
551
552 exitArray(type);
553 }
554
555 // Extracts samplers from a field of a struct. Works with nested structs and arrays.
556 void extractFieldSamplers(const std::string &prefix,
557 const TField *field,
558 TIntermSequence *newSequence)
559 {
560 const TType &fieldType = *field->type();
561 if (fieldType.isSampler() || fieldType.isStructureContainingSamplers())
562 {
563 std::string newPrefix = prefix + "_" + field->name().data();
564
565 if (fieldType.isSampler())
566 {
567 extractSampler(newPrefix, fieldType, newSequence);
568 }
569 else
570 {
571 enterArray(fieldType);
572 const TStructure *structure = fieldType.getStruct();
573 for (const TField *nestedField : structure->fields())
574 {
575 extractFieldSamplers(newPrefix, nestedField, newSequence);
576 }
577 exitArray(fieldType);
578 }
579 }
580 }
581
582 void GenerateArraySizesFromStack(TVector<unsigned int> *sizesOut)
583 {
584 sizesOut->reserve(mArraySizeStack.size());
585
586 for (auto it = mArraySizeStack.rbegin(); it != mArraySizeStack.rend(); ++it)
587 {
588 sizesOut->push_back(*it);
589 }
590 }
591
592 // Extracts a sampler from a struct. Declares the new extracted sampler.
593 void extractSampler(const std::string &newName,
594 const TType &fieldType,
595 TIntermSequence *newSequence)
596 {
597 ASSERT(fieldType.isSampler())(fieldType.isSampler() ? static_cast<void>(0) : (!((::gl
::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast
<void>(0) : ::gl::priv::LogMessageVoidify() & (::gl
::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 597, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 597 << "): " << "fieldType.isSampler()"
))
;
598
599 TType *newType = new TType(fieldType);
600
601 // Add array dimensions accumulated so far due to struct arrays. Note that to support
602 // nested arrays, mArraySizeStack has the outermost size in the front. |makeArrays| thus
603 // expects this in reverse order.
604 TVector<unsigned int> parentArraySizes;
605 GenerateArraySizesFromStack(&parentArraySizes);
606 newType->makeArrays(parentArraySizes);
607
608 ImmutableStringBuilder nameBuilder(newName.size() + 1);
609 nameBuilder << newName;
610
611 newType->setQualifier(EvqUniform);
612 TVariable *newVariable =
613 new TVariable(mSymbolTable, nameBuilder, newType, SymbolType::AngleInternal);
614 TIntermSymbol *newSymbol = new TIntermSymbol(newVariable);
615
616 TIntermDeclaration *samplerDecl = new TIntermDeclaration;
617 samplerDecl->appendDeclarator(newSymbol);
618
619 newSequence->push_back(samplerDecl);
620
621 // TODO: Use a temp name instead of generating a name as currently done. There is no
622 // guarantee that these generated names cannot clash. Create a mapping from the previous
623 // name to the name assigned to the temp variable so ShaderVariable::mappedName can be
624 // updated post-transformation. http://anglebug.com/4301
625 ASSERT(mExtractedSamplers.find(newName) == mExtractedSamplers.end())(mExtractedSamplers.find(newName) == mExtractedSamplers.end()
? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 625, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 625 << "): " << "mExtractedSamplers.find(newName) == mExtractedSamplers.end()"
))
;
626 mExtractedSamplers[newName] = newVariable;
627 }
628
629 void enterArray(const TType &arrayType)
630 {
631 const TSpan<const unsigned int> &arraySizes = arrayType.getArraySizes();
632 for (auto it = arraySizes.rbegin(); it != arraySizes.rend(); ++it)
633 {
634 unsigned int arraySize = *it;
635 mArraySizeStack.push_back(arraySize);
636 }
637 }
638
639 void exitArray(const TType &arrayType)
640 {
641 mArraySizeStack.resize(mArraySizeStack.size() - arrayType.getNumArraySizes());
642 }
643
644 TCompiler *mCompiler;
645 int mRemovedUniformsCount;
646
647 // Map structures with samplers to ones that have their samplers removed.
648 StructureMap mStructureMap;
649
650 // Map uniform variables of structure type that are replaced with another variable.
651 StructureUniformMap mStructureUniformMap;
652
653 // Map a constructed sampler name to its variable. Used to replace an expression that uses this
654 // sampler with the extracted one.
655 ExtractedSamplerMap mExtractedSamplers;
656
657 // A stack of array sizes. Used to figure out the array dimensions of the extracted sampler,
658 // for example when it's nested in an array of structs in an array of structs.
659 TVector<unsigned int> mArraySizeStack;
660};
661} // anonymous namespace
662
663bool RewriteStructSamplers(TCompiler *compiler,
664 TIntermBlock *root,
665 TSymbolTable *symbolTable,
666 int *removedUniformsCountOut)
667{
668 RewriteStructSamplersTraverser traverser(compiler, symbolTable);
669 root->traverse(&traverser);
670 *removedUniformsCountOut = traverser.removedUniformsCount();
671 return traverser.updateTree(compiler, root);
672}
673} // namespace sh