Bug Summary

File:root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp
Warning:line 375, 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 -O2 -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 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/angle/translator_gn -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/angle/translator_gn -resource-dir /usr/lib/llvm-23/lib/clang/23 -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 -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D ANGLE_DISABLE_POOL_ALLOC -D ANGLE_ENABLE_CONTEXT_MUTEX=1 -U ANGLE_ENABLE_DEBUG_ANNOTATIONS -D ANGLE_ENABLE_ESSL -D ANGLE_ENABLE_GLSL -D ANGLE_ENABLE_KEYEDMUTEX -D ANGLE_ENABLE_SHARE_CONTEXT_LOCK=1 -D ANGLE_PLATFORM_EXPORT= -D ANGLE_SKIP_DXGI_1_2_CHECK -D ANGLE_DEBUG_LAYERS_ENABLED -D DYNAMIC_ANNOTATIONS_ENABLED=1 -D ANGLE_IS_LINUX -D ANGLE_USE_VULKAN_DISPLAY -D ANGLE_VULKAN_DISPLAY_MODE_HEADLESS -D USE_AURA=1 -D USE_GLIB=1 -D USE_OZONE=1 -D USE_UDEV -D _FILE_OFFSET_BITS=64 -D _LARGEFILE64_SOURCE -D _LARGEFILE_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D _DEBUG -D ANGLE_USE_X11 -D _GNU_SOURCE -I /root/firefox-clang/third_party/angle/translator_gn -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/angle/translator_gn -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/angle/include -I /root/firefox-clang/third_party/angle/src -I /root/firefox-clang/third_party/angle/src/common/base -I /root/firefox-clang/third_party/angle/src/common/third_party/xxhash -I /root/firefox-clang/tools/profiler/public -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -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-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-character-conversion -std=gnu++20 -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 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-10-17-231001-351276-1 -x c++ /root/firefox-clang/third_party/angle/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 "GLSLANG/ShaderVars.h"
12#include "common/hash_containers.h"
13#include "common/span.h"
14#include "compiler/translator/Compiler.h"
15#include "compiler/translator/ImmutableString.h"
16#include "compiler/translator/ImmutableStringBuilder.h"
17#include "compiler/translator/SymbolTable.h"
18#include "compiler/translator/tree_util/IntermNode_util.h"
19#include "compiler/translator/tree_util/IntermTraverse.h"
20
21namespace sh
22{
23const char kExtractedSamplerNamePrefix[] = "extractedSampler";
24
25namespace
26{
27// Used to map one structure type to another (one where the samplers are removed).
28struct StructureData
29{
30 // The structure this was replaced with. If nullptr, it means the structure is removed (because
31 // it had all samplers).
32 //
33 // ParseContext reorders the samplers to the end of the struct, so the EOpIndexDirectStruct
34 // expressions that select non-sampler members don't have to change when they are moved out of
35 // the struct.
36 const TStructure *modified;
37};
38
39using StructureMap = angle::HashMap<const TStructure *, StructureData>;
40using StructureUniformMap = angle::HashMap<const TVariable *, const TVariable *>;
41using ExtractedSamplerMap = angle::HashMap<std::string, const TVariable *>;
42
43TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
44 TCompiler *compiler,
45 TIntermBinary *node,
46 const StructureMap &structureMap,
47 const StructureUniformMap &structureUniformMap,
48 const ExtractedSamplerMap &extractedSamplers);
49
50TIntermTyped *RewriteExpressionVisitBinaryHelper(TCompiler *compiler,
51 TIntermBinary *node,
52 const StructureMap &structureMap,
53 const StructureUniformMap &structureUniformMap,
54 const ExtractedSamplerMap &extractedSamplers)
55{
56 // Only interested in EOpIndex* binary nodes.
57 switch (node->getOp())
58 {
59 case EOpIndexDirectInterfaceBlock:
60 case EOpIndexIndirect:
61 case EOpIndexDirect:
62 case EOpIndexDirectStruct:
63 break;
64 default:
65 return nullptr;
66 }
67
68 const TStructure *structure = node->getLeft()->getType().getStruct();
69 if (structure == nullptr)
70 {
71 return nullptr;
72 }
73
74 // If the result of the index is not a sampler and the struct is not replaced, there's nothing
75 // to do.
76 if (!node->getType().isSampler() && structureMap.find(structure) == structureMap.end())
77 {
78 return nullptr;
79 }
80
81 // Otherwise, replace the whole expression such that:
82 //
83 // - if sampler, it's indexed with whatever indices the parent structs were indexed with,
84 // - otherwise, the chain of field selections is rewritten by modifying the base uniform so all
85 // the intermediate nodes would have the correct type (and therefore fields).
86 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 86, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 86 << "): " << "structureMap.find(structure) != structureMap.end()"
))
;
87
88 return RewriteModifiedStructFieldSelectionExpression(compiler, node, structureMap,
89 structureUniformMap, extractedSamplers);
90}
91
92// Given an expression, this traverser calculates a new expression where sampler-in-structs are
93// replaced with their extracted ones, and field indices are adjusted for the rest of the fields.
94// In particular, this is run on the right node of EOpIndexIndirect binary nodes, so that the
95// expression in the index gets a chance to go through this transformation.
96class RewriteExpressionTraverser final : public TIntermTraverser
97{
98 public:
99 explicit RewriteExpressionTraverser(TCompiler *compiler,
100 const StructureMap &structureMap,
101 const StructureUniformMap &structureUniformMap,
102 const ExtractedSamplerMap &extractedSamplers)
103 : TIntermTraverser(true, false, false),
104 mCompiler(compiler),
105 mStructureMap(structureMap),
106 mStructureUniformMap(structureUniformMap),
107 mExtractedSamplers(extractedSamplers)
108 {}
109
110 bool visitBinary(Visit visit, TIntermBinary *node) override
111 {
112 TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
113 mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
114
115 if (rewritten == nullptr)
116 {
117 return true;
118 }
119
120 queueReplacement(rewritten, OriginalNode::IS_DROPPED);
121
122 // Don't iterate as the expression is rewritten.
123 return false;
124 }
125
126 void visitSymbol(TIntermSymbol *node) override
127 {
128 // It's impossible to reach here with a symbol that needs replacement.
129 // MonomorphizeUnsupportedFunctions makes sure that whole structs containing
130 // samplers are not passed to functions, so any instance of the struct uniform is
131 // necessarily indexed right away. visitBinary should have already taken care of it.
132 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 132, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 132 << "): " << "mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()"
))
;
133 }
134
135 private:
136 TCompiler *mCompiler;
137
138 // See RewriteStructSamplersTraverser.
139 const StructureMap &mStructureMap;
140 const StructureUniformMap &mStructureUniformMap;
141 const ExtractedSamplerMap &mExtractedSamplers;
142};
143
144// Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because
145// it cannot be a sampler itself or of a struct type.
146void RewriteIndexExpression(TCompiler *compiler,
147 TIntermTyped *expression,
148 const StructureMap &structureMap,
149 const StructureUniformMap &structureUniformMap,
150 const ExtractedSamplerMap &extractedSamplers)
151{
152 RewriteExpressionTraverser traverser(compiler, structureMap, structureUniformMap,
153 extractedSamplers);
154 expression->traverse(&traverser);
155 bool valid = traverser.updateTree(compiler, expression);
156 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 156, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 156 << "): " << "valid"))
;
157}
158
159// Given an expression such as the following:
160//
161// EOpIndexDirectStruct (sampler)
162// / \
163// EOpIndex* field index
164// / \
165// EOpIndexDirectStruct index 2
166// / \
167// EOpIndex* field index
168// / \
169// EOpIndexDirectStruct index 1
170// / \
171// Uniform Struct field index
172//
173// produces:
174//
175// EOpIndex*
176// / \
177// EOpIndex* index 2
178// / \
179// sampler index 1
180//
181// If the expression is not a sampler, it only replaces the struct with the modified one, while
182// still processing the EOpIndexIndirect expressions (which may contain more structs to map).
183TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
184 TCompiler *compiler,
185 TIntermBinary *node,
186 const StructureMap &structureMap,
187 const StructureUniformMap &structureUniformMap,
188 const ExtractedSamplerMap &extractedSamplers)
189{
190 const bool isSampler = node->getType().isSampler();
191
192 TIntermSymbol *baseUniform = nullptr;
193 std::string samplerPath;
194
195 TVector<TIntermBinary *> indexNodeStack;
196
197 // Iterate once and build the name of the sampler.
198 TIntermBinary *iter = node;
199 while (baseUniform == nullptr)
200 {
201 indexNodeStack.push_back(iter);
202 baseUniform = iter->getLeft()->getAsSymbolNode();
203
204 if (isSampler)
205 {
206 if (iter->getOp() == EOpIndexDirectStruct)
207 {
208 // When indexed into a struct, get the field name instead and construct the sampler
209 // name.
210 samplerPath.insert(0, iter->getIndexStructFieldName().data());
211 samplerPath.insert(0, ".");
212 }
213
214 if (baseUniform)
215 {
216 // If left is a symbol, we have reached the end of the chain. Use the struct name
217 // to finish building the name of the sampler.
218 samplerPath.insert(0, baseUniform->variable().name().data());
219 }
220 }
221
222 iter = iter->getLeft()->getAsBinaryNode();
223 }
224
225 TIntermTyped *rewritten = nullptr;
226
227 if (isSampler)
228 {
229 ASSERT(extractedSamplers.find(samplerPath) != extractedSamplers.end())(extractedSamplers.find(samplerPath) != extractedSamplers.end
() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 229, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 229 << "): " << "extractedSamplers.find(samplerPath) != extractedSamplers.end()"
))
;
230 rewritten = new TIntermSymbol(extractedSamplers.at(samplerPath));
231 }
232 else
233 {
234 const TVariable *baseUniformVar = &baseUniform->variable();
235 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 235, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 235 << "): " << "structureUniformMap.find(baseUniformVar) != structureUniformMap.end()"
))
;
236 rewritten = new TIntermSymbol(structureUniformMap.at(baseUniformVar));
237 }
238
239 // Iterate again and build the expression from bottom up.
240 for (auto it = indexNodeStack.rbegin(); it != indexNodeStack.rend(); ++it)
241 {
242 TIntermBinary *indexNode = *it;
243
244 switch (indexNode->getOp())
245 {
246 case EOpIndexDirectStruct:
247 if (!isSampler)
248 {
249 rewritten =
250 new TIntermBinary(EOpIndexDirectStruct, rewritten, indexNode->getRight());
251 }
252 break;
253
254 case EOpIndexDirect:
255 rewritten = new TIntermBinary(EOpIndexDirect, rewritten, indexNode->getRight());
256 break;
257
258 case EOpIndexIndirect:
259 {
260 // Run RewriteExpressionTraverser on the right node. It may itself be an expression
261 // with a sampler inside that needs to be rewritten, or simply use a field of a
262 // struct that's remapped.
263 TIntermTyped *indexExpression = indexNode->getRight();
264 RewriteIndexExpression(compiler, indexExpression, structureMap, structureUniformMap,
265 extractedSamplers);
266 rewritten = new TIntermBinary(EOpIndexIndirect, rewritten, indexExpression);
267 break;
268 }
269
270 default:
271 UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL
))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify
() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 271, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: "
<< __FUNCTION__ << "(" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 271 << ")"; } while (0)
;
272 break;
273 }
274 }
275
276 return rewritten;
277}
278
279class RewriteStructSamplersTraverser final : public TIntermTraverser
280{
281 public:
282 explicit RewriteStructSamplersTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
283 : TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler)
284 {}
285
286
287 // Each struct sampler declaration is stripped of its samplers. New uniforms are added for each
288 // stripped struct sampler.
289 bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
290 {
291 if (!mInGlobalScope)
1
Assuming field 'mInGlobalScope' is true
2
Taking false branch
292 {
293 return true;
294 }
295
296 const TIntermSequence &sequence = *(decl->getSequence());
297 TIntermTyped *declarator = sequence.front()->getAsTyped();
298 const TType &type = declarator->getType();
299
300 if (!type.isStructureContainingSamplers())
3
Value assigned to field 'mStructure'
4
Assuming the condition is false
5
Taking false branch
301 {
302 return false;
303 }
304
305 TIntermSequence newSequence;
306
307 if (type.isStructSpecifier())
6
Assuming the condition is true
7
Taking true branch
308 {
309 // If this is just a struct definition (not a uniform variable declaration of a
310 // struct type), just remove the samplers. They are not instantiated yet.
311 const TStructure *structure = type.getStruct();
8
'structure' initialized here
312 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 312, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 312 << "): " << "structure && mStructureMap.find(structure) == mStructureMap.end()"
))
;
9
Assuming 'structure' is null
10
Assuming the condition is true
11
'?' condition is true
313
314 stripStructSpecifierSamplers(structure, &newSequence);
12
Passing null pointer value via 1st parameter 'structure'
13
Calling 'RewriteStructSamplersTraverser::stripStructSpecifierSamplers'
315 }
316 else
317 {
318 const TStructure *structure = type.getStruct();
319
320 // If the structure is defined at the same time, create the mapping to the stripped
321 // version first.
322 if (mStructureMap.find(structure) == mStructureMap.end())
323 {
324 stripStructSpecifierSamplers(structure, &newSequence);
325 }
326
327 // Then, extract the samplers from the struct and create global-scope variables instead.
328 TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
329 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 329, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 329 << "): " << "asSymbol"
))
;
330 const TVariable &variable = asSymbol->variable();
331 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 331, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 331 << "): " << "variable.symbolType() != SymbolType::Empty"
))
;
332
333 extractStructSamplerUniforms(variable, structure, &newSequence);
334 }
335
336 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl,
337 std::move(newSequence));
338
339 return false;
340 }
341
342 // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
343 bool visitBinary(Visit visit, TIntermBinary *node) override
344 {
345 TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
346 mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
347
348 if (rewritten == nullptr)
349 {
350 return true;
351 }
352
353 queueReplacement(rewritten, OriginalNode::IS_DROPPED);
354
355 // Don't iterate as the expression is rewritten.
356 return false;
357 }
358
359 // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
360 void visitSymbol(TIntermSymbol *node) override
361 {
362 auto replacement = mStructureUniformMap.find(&node->variable());
363 if (replacement != mStructureUniformMap.end())
364 {
365 // This is a reference to the whole struct, just replace it with its replacement.
366 queueReplacement(new TIntermSymbol(replacement->second), OriginalNode::IS_DROPPED);
367 }
368 }
369
370 private:
371 // Removes all samplers from a struct specifier.
372 void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
373 {
374 TFieldList *newFieldList = new TFieldList;
375 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 375, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 375 << "): " << "structure->containsSamplers()"
))
;
14
Called C++ object pointer is null
376
377 // Add this struct to the struct map
378 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 378, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 378 << "): " << "mStructureMap.find(structure) == mStructureMap.end()"
))
;
379 StructureData *modifiedData = &mStructureMap[structure];
380
381 modifiedData->modified = nullptr;
382
383 for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex)
384 {
385 const TField *field = structure->fields()[fieldIndex];
386 const TType &fieldType = *field->type();
387
388 // If the field is a sampler, or a struct that's entirely removed, skip it.
389 if (!fieldType.isSampler() && !isRemovedStructType(fieldType))
390 {
391 TType *newType = nullptr;
392
393 // Otherwise, if it's a struct that's replaced, create a new field of the replaced
394 // type.
395 if (fieldType.isStructureContainingSamplers())
396 {
397 const TStructure *fieldStruct = fieldType.getStruct();
398 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 398, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 398 << "): " << "mStructureMap.find(fieldStruct) != mStructureMap.end()"
))
;
399
400 const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified;
401 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 401, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 401 << "): " << "modifiedStruct"
))
;
402
403 newType = new TType(modifiedStruct, false);
404 if (fieldType.isArray())
405 {
406 newType->makeArrays(fieldType.getArraySizes());
407 }
408 }
409 else
410 {
411 // If not, duplicate the field as is.
412 newType = new TType(fieldType);
413 }
414
415 TField *newField =
416 new TField(newType, field->name(), field->line(), field->symbolType());
417 newFieldList->push_back(newField);
418 }
419 }
420
421 // Prune empty structs.
422 if (newFieldList->empty())
423 {
424 return;
425 }
426
427 // Declare a new struct with the same name and the new fields.
428 modifiedData->modified =
429 new TStructure(mSymbolTable,
430 structure->symbolType() == SymbolType::Empty ? kEmptyImmutableString
431 : structure->name(),
432 newFieldList, structure->symbolType());
433 TType *newStructType = new TType(modifiedData->modified, true);
434 TVariable *newStructVar =
435 new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty);
436 TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar);
437
438 TIntermDeclaration *structDecl = new TIntermDeclaration;
439 structDecl->appendDeclarator(newStructRef);
440
441 newSequence->push_back(structDecl);
442 }
443
444 // Returns true if the type is a struct that was removed because we extracted all the members.
445 bool isRemovedStructType(const TType &type) const
446 {
447 const TStructure *structure = type.getStruct();
448 if (structure == nullptr)
449 {
450 // Not a struct
451 return false;
452 }
453
454 // A struct is removed if it is in the map, but doesn't have a replacement struct.
455 auto iter = mStructureMap.find(structure);
456 return iter != mStructureMap.end() && iter->second.modified == nullptr;
457 }
458
459 // Removes samplers from struct uniforms. For each sampler removed also adds a new globally
460 // defined sampler uniform.
461 void extractStructSamplerUniforms(const TVariable &variable,
462 const TStructure *structure,
463 TIntermSequence *newSequence)
464 {
465 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 465, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 465 << "): " << "structure->containsSamplers()"
))
;
466 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 466, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 466 << "): " << "mStructureMap.find(structure) != mStructureMap.end()"
))
;
467
468 const TType &type = variable.getType();
469 enterArray(type);
470
471 for (const TField *field : structure->fields())
472 {
473 extractFieldSamplers(variable.name().data(), field, newSequence);
474 }
475
476 // If there's a replacement structure (because there are non-sampler fields in the struct),
477 // add a declaration with that type.
478 const TStructure *modified = mStructureMap[structure].modified;
479 if (modified != nullptr)
480 {
481 TType *newType = new TType(modified, false);
482 if (type.isArray())
483 {
484 newType->makeArrays(type.getArraySizes());
485 }
486 newType->setQualifier(EvqUniform);
487 const TVariable *newVariable =
488 new TVariable(mSymbolTable, variable.name(), newType, variable.symbolType());
489
490 TIntermDeclaration *newDecl = new TIntermDeclaration();
491 newDecl->appendDeclarator(new TIntermSymbol(newVariable));
492
493 newSequence->push_back(newDecl);
494
495 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 495, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 495 << "): " << "mStructureUniformMap.find(&variable) == mStructureUniformMap.end()"
))
;
496 mStructureUniformMap[&variable] = newVariable;
497 }
498
499 exitArray(type);
500 }
501
502 // Extracts samplers from a field of a struct. Works with nested structs and arrays.
503 void extractFieldSamplers(const std::string &prefix,
504 const TField *field,
505 TIntermSequence *newSequence)
506 {
507 const TType &fieldType = *field->type();
508 if (fieldType.isSampler() || fieldType.isStructureContainingSamplers())
509 {
510 std::string newPrefix = prefix + "." + field->name().data();
511
512 if (fieldType.isSampler())
513 {
514 extractSampler(newPrefix, fieldType, newSequence);
515 }
516 else
517 {
518 enterArray(fieldType);
519 const TStructure *structure = fieldType.getStruct();
520 for (const TField *nestedField : structure->fields())
521 {
522 extractFieldSamplers(newPrefix, nestedField, newSequence);
523 }
524 exitArray(fieldType);
525 }
526 }
527 }
528
529 void GenerateArraySizesFromStack(TVector<unsigned int> *sizesOut)
530 {
531 sizesOut->reserve(mArraySizeStack.size());
532
533 for (auto it = mArraySizeStack.rbegin(); it != mArraySizeStack.rend(); ++it)
534 {
535 sizesOut->push_back(*it);
536 }
537 }
538
539 // Extracts a sampler from a struct. Declares the new extracted sampler.
540 void extractSampler(const std::string &path,
541 const TType &fieldType,
542 TIntermSequence *newSequence)
543 {
544 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/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 544, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 544 << "): " << "fieldType.isSampler()"
))
;
545
546 TType *newType = new TType(fieldType);
547
548 // Add array dimensions accumulated so far due to struct arrays. Note that to support
549 // nested arrays, mArraySizeStack has the outermost size in the front. |makeArrays| thus
550 // expects this in reverse order.
551 TVector<unsigned int> parentArraySizes;
552 GenerateArraySizesFromStack(&parentArraySizes);
553 newType->makeArrays(parentArraySizes);
554
555 std::ostringstream newNameBuilder;
556 newNameBuilder << kExtractedSamplerNamePrefix << "_" << mExtractedSamplers.size();
557 const std::string newName = newNameBuilder.str();
558
559 ImmutableStringBuilder nameBuilder(newName.size() + 1);
560 nameBuilder << newName;
561
562 newType->setQualifier(EvqUniform);
563 TVariable *newVariable =
564 new TVariable(mSymbolTable, nameBuilder, newType, SymbolType::AngleInternal);
565 TIntermSymbol *newSymbol = new TIntermSymbol(newVariable);
566
567 TIntermDeclaration *samplerDecl = new TIntermDeclaration;
568 samplerDecl->appendDeclarator(newSymbol);
569
570 newSequence->push_back(samplerDecl);
571
572 ASSERT(mExtractedSamplers.find(path) == mExtractedSamplers.end())(mExtractedSamplers.find(path) == mExtractedSamplers.end() ? static_cast
<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage
(::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv
::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
, __FUNCTION__, 572, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in "
<< __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp"
<< ":" << 572 << "): " << "mExtractedSamplers.find(path) == mExtractedSamplers.end()"
))
;
573 mExtractedSamplers[path] = newVariable;
574 }
575
576 void enterArray(const TType &arrayType)
577 {
578 const angle::Span<const unsigned int> &arraySizes = arrayType.getArraySizes();
579 for (auto it = arraySizes.rbegin(); it != arraySizes.rend(); ++it)
580 {
581 unsigned int arraySize = *it;
582 mArraySizeStack.push_back(arraySize);
583 }
584 }
585
586 void exitArray(const TType &arrayType)
587 {
588 mArraySizeStack.resize(mArraySizeStack.size() - arrayType.getNumArraySizes());
589 }
590
591 TCompiler *mCompiler;
592
593 // Map structures with samplers to ones that have their samplers removed.
594 StructureMap mStructureMap;
595
596 // Map uniform variables of structure type that are replaced with another variable.
597 StructureUniformMap mStructureUniformMap;
598
599 // Map a constructed sampler name to its variable. Used to replace an expression that uses this
600 // sampler with the extracted one.
601 ExtractedSamplerMap mExtractedSamplers;
602
603 // A stack of array sizes. Used to figure out the array dimensions of the extracted sampler,
604 // for example when it's nested in an array of structs in an array of structs.
605 TVector<unsigned int> mArraySizeStack;
606};
607} // anonymous namespace
608
609bool RewriteStructSamplers(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
610{
611 RewriteStructSamplersTraverser traverser(compiler, symbolTable);
612 root->traverse(&traverser);
613 return traverser.updateTree(compiler, root);
614}
615} // namespace sh