1 //
2 // Copyright 2017 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 // IntermNode_util.cpp: High-level utilities for creating AST nodes and node hierarchies. Mostly
7 // meant to be used in AST transforms.
8
9 #include "compiler/translator/tree_util/IntermNode_util.h"
10
11 #include "compiler/translator/FunctionLookup.h"
12 #include "compiler/translator/SymbolTable.h"
13
14 namespace sh
15 {
16
17 namespace
18 {
19
LookUpBuiltInFunction(const char * name,const TIntermSequence * arguments,const TSymbolTable & symbolTable,int shaderVersion)20 const TFunction *LookUpBuiltInFunction(const char *name,
21 const TIntermSequence *arguments,
22 const TSymbolTable &symbolTable,
23 int shaderVersion)
24 {
25 const ImmutableString &mangledName = TFunctionLookup::GetMangledName(name, *arguments);
26 const TSymbol *symbol = symbolTable.findBuiltIn(mangledName, shaderVersion);
27 if (symbol)
28 {
29 ASSERT(symbol->isFunction());
30 return static_cast<const TFunction *>(symbol);
31 }
32 return nullptr;
33 }
34
35 } // anonymous namespace
36
CreateInternalFunctionPrototypeNode(const TFunction & func)37 TIntermFunctionPrototype *CreateInternalFunctionPrototypeNode(const TFunction &func)
38 {
39 return new TIntermFunctionPrototype(&func);
40 }
41
CreateInternalFunctionDefinitionNode(const TFunction & func,TIntermBlock * functionBody)42 TIntermFunctionDefinition *CreateInternalFunctionDefinitionNode(const TFunction &func,
43 TIntermBlock *functionBody)
44 {
45 return new TIntermFunctionDefinition(new TIntermFunctionPrototype(&func), functionBody);
46 }
47
CreateZeroNode(const TType & type)48 TIntermTyped *CreateZeroNode(const TType &type)
49 {
50 TType constType(type);
51 constType.setQualifier(EvqConst);
52
53 // Make sure as a constructor, the type does not inherit qualifiers that are otherwise specified
54 // on interface blocks and varyings.
55 constType.setInvariant(false);
56 constType.setPrecise(false);
57 constType.setInterpolant(false);
58 constType.setMemoryQualifier(TMemoryQualifier::Create());
59 constType.setLayoutQualifier(TLayoutQualifier::Create());
60 constType.setInterfaceBlock(nullptr);
61
62 if (!type.isArray() && type.getBasicType() != EbtStruct)
63 {
64 size_t size = constType.getObjectSize();
65 TConstantUnion *u = new TConstantUnion[size];
66 for (size_t i = 0; i < size; ++i)
67 {
68 switch (type.getBasicType())
69 {
70 case EbtFloat:
71 u[i].setFConst(0.0f);
72 break;
73 case EbtInt:
74 u[i].setIConst(0);
75 break;
76 case EbtUInt:
77 u[i].setUConst(0u);
78 break;
79 case EbtBool:
80 u[i].setBConst(false);
81 break;
82 default:
83 // CreateZeroNode is called by ParseContext that keeps parsing even when an
84 // error occurs, so it is possible for CreateZeroNode to be called with
85 // non-basic types. This happens only on error condition but CreateZeroNode
86 // needs to return a value with the correct type to continue the type check.
87 // That's why we handle non-basic type by setting whatever value, we just need
88 // the type to be right.
89 u[i].setIConst(42);
90 break;
91 }
92 }
93
94 TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
95 return node;
96 }
97
98 TIntermSequence arguments;
99
100 if (type.isArray())
101 {
102 TType elementType(type);
103 elementType.toArrayElementType();
104
105 size_t arraySize = type.getOutermostArraySize();
106 for (size_t i = 0; i < arraySize; ++i)
107 {
108 arguments.push_back(CreateZeroNode(elementType));
109 }
110 }
111 else
112 {
113 ASSERT(type.getBasicType() == EbtStruct);
114
115 const TStructure *structure = type.getStruct();
116 for (const auto &field : structure->fields())
117 {
118 arguments.push_back(CreateZeroNode(*field->type()));
119 }
120 }
121
122 return TIntermAggregate::CreateConstructor(constType, &arguments);
123 }
124
CreateFloatNode(float value,TPrecision precision)125 TIntermConstantUnion *CreateFloatNode(float value, TPrecision precision)
126 {
127 TConstantUnion *u = new TConstantUnion[1];
128 u[0].setFConst(value);
129
130 TType type(EbtFloat, precision, EvqConst, 1);
131 return new TIntermConstantUnion(u, type);
132 }
133
CreateVecNode(const float values[],unsigned int vecSize,TPrecision precision)134 TIntermConstantUnion *CreateVecNode(const float values[],
135 unsigned int vecSize,
136 TPrecision precision)
137 {
138 TConstantUnion *u = new TConstantUnion[vecSize];
139 for (unsigned int channel = 0; channel < vecSize; ++channel)
140 {
141 u[channel].setFConst(values[channel]);
142 }
143
144 TType type(EbtFloat, precision, EvqConst, static_cast<uint8_t>(vecSize));
145 return new TIntermConstantUnion(u, type);
146 }
147
CreateUVecNode(const unsigned int values[],unsigned int vecSize,TPrecision precision)148 TIntermConstantUnion *CreateUVecNode(const unsigned int values[],
149 unsigned int vecSize,
150 TPrecision precision)
151 {
152 TConstantUnion *u = new TConstantUnion[vecSize];
153 for (unsigned int channel = 0; channel < vecSize; ++channel)
154 {
155 u[channel].setUConst(values[channel]);
156 }
157
158 TType type(EbtUInt, precision, EvqConst, static_cast<uint8_t>(vecSize));
159 return new TIntermConstantUnion(u, type);
160 }
161
CreateIndexNode(int index)162 TIntermConstantUnion *CreateIndexNode(int index)
163 {
164 TConstantUnion *u = new TConstantUnion[1];
165 u[0].setIConst(index);
166
167 TType type(EbtInt, EbpHigh, EvqConst, 1);
168 return new TIntermConstantUnion(u, type);
169 }
170
CreateUIntNode(unsigned int value)171 TIntermConstantUnion *CreateUIntNode(unsigned int value)
172 {
173 TConstantUnion *u = new TConstantUnion[1];
174 u[0].setUConst(value);
175
176 TType type(EbtUInt, EbpHigh, EvqConst, 1);
177 return new TIntermConstantUnion(u, type);
178 }
179
CreateBoolNode(bool value)180 TIntermConstantUnion *CreateBoolNode(bool value)
181 {
182 TConstantUnion *u = new TConstantUnion[1];
183 u[0].setBConst(value);
184
185 TType type(EbtBool, EbpUndefined, EvqConst, 1);
186 return new TIntermConstantUnion(u, type);
187 }
188
CreateTempVariable(TSymbolTable * symbolTable,const TType * type)189 TVariable *CreateTempVariable(TSymbolTable *symbolTable, const TType *type)
190 {
191 ASSERT(symbolTable != nullptr);
192 // TODO(oetuaho): Might be useful to sanitize layout qualifier etc. on the type of the created
193 // variable. This might need to be done in other places as well.
194 return new TVariable(symbolTable, kEmptyImmutableString, type, SymbolType::AngleInternal);
195 }
196
CreateTempVariable(TSymbolTable * symbolTable,const TType * type,TQualifier qualifier)197 TVariable *CreateTempVariable(TSymbolTable *symbolTable, const TType *type, TQualifier qualifier)
198 {
199 ASSERT(symbolTable != nullptr);
200 if (type->getQualifier() == qualifier)
201 {
202 return CreateTempVariable(symbolTable, type);
203 }
204 TType *typeWithQualifier = new TType(*type);
205 typeWithQualifier->setQualifier(qualifier);
206 return CreateTempVariable(symbolTable, typeWithQualifier);
207 }
208
CreateTempSymbolNode(const TVariable * tempVariable)209 TIntermSymbol *CreateTempSymbolNode(const TVariable *tempVariable)
210 {
211 ASSERT(tempVariable->symbolType() == SymbolType::AngleInternal);
212 ASSERT(tempVariable->getType().getQualifier() == EvqTemporary ||
213 tempVariable->getType().getQualifier() == EvqConst ||
214 tempVariable->getType().getQualifier() == EvqGlobal);
215 return new TIntermSymbol(tempVariable);
216 }
217
CreateTempDeclarationNode(const TVariable * tempVariable)218 TIntermDeclaration *CreateTempDeclarationNode(const TVariable *tempVariable)
219 {
220 TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
221 tempDeclaration->appendDeclarator(CreateTempSymbolNode(tempVariable));
222 return tempDeclaration;
223 }
224
CreateTempInitDeclarationNode(const TVariable * tempVariable,TIntermTyped * initializer)225 TIntermDeclaration *CreateTempInitDeclarationNode(const TVariable *tempVariable,
226 TIntermTyped *initializer)
227 {
228 ASSERT(initializer != nullptr);
229 TIntermSymbol *tempSymbol = CreateTempSymbolNode(tempVariable);
230 TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
231 TIntermBinary *tempInit = new TIntermBinary(EOpInitialize, tempSymbol, initializer);
232 tempDeclaration->appendDeclarator(tempInit);
233 return tempDeclaration;
234 }
235
CreateTempAssignmentNode(const TVariable * tempVariable,TIntermTyped * rightNode)236 TIntermBinary *CreateTempAssignmentNode(const TVariable *tempVariable, TIntermTyped *rightNode)
237 {
238 ASSERT(rightNode != nullptr);
239 TIntermSymbol *tempSymbol = CreateTempSymbolNode(tempVariable);
240 return new TIntermBinary(EOpAssign, tempSymbol, rightNode);
241 }
242
DeclareTempVariable(TSymbolTable * symbolTable,const TType * type,TQualifier qualifier,TIntermDeclaration ** declarationOut)243 TVariable *DeclareTempVariable(TSymbolTable *symbolTable,
244 const TType *type,
245 TQualifier qualifier,
246 TIntermDeclaration **declarationOut)
247 {
248 TVariable *variable = CreateTempVariable(symbolTable, type, qualifier);
249 *declarationOut = CreateTempDeclarationNode(variable);
250 return variable;
251 }
252
DeclareTempVariable(TSymbolTable * symbolTable,TIntermTyped * initializer,TQualifier qualifier,TIntermDeclaration ** declarationOut)253 TVariable *DeclareTempVariable(TSymbolTable *symbolTable,
254 TIntermTyped *initializer,
255 TQualifier qualifier,
256 TIntermDeclaration **declarationOut)
257 {
258 TVariable *variable =
259 CreateTempVariable(symbolTable, new TType(initializer->getType()), qualifier);
260 *declarationOut = CreateTempInitDeclarationNode(variable, initializer);
261 return variable;
262 }
263
DeclareStructure(TIntermBlock * root,TSymbolTable * symbolTable,TFieldList * fieldList,TQualifier qualifier,const TMemoryQualifier & memoryQualifier,uint32_t arraySize,const ImmutableString & structTypeName,const ImmutableString * structInstanceName)264 std::pair<const TVariable *, const TVariable *> DeclareStructure(
265 TIntermBlock *root,
266 TSymbolTable *symbolTable,
267 TFieldList *fieldList,
268 TQualifier qualifier,
269 const TMemoryQualifier &memoryQualifier,
270 uint32_t arraySize,
271 const ImmutableString &structTypeName,
272 const ImmutableString *structInstanceName)
273 {
274 TStructure *structure =
275 new TStructure(symbolTable, structTypeName, fieldList, SymbolType::AngleInternal);
276
277 auto makeStructureType = [&](bool isStructSpecifier) {
278 TType *structureType = new TType(structure, isStructSpecifier);
279 structureType->setQualifier(qualifier);
280 structureType->setMemoryQualifier(memoryQualifier);
281 if (arraySize > 0)
282 {
283 structureType->makeArray(arraySize);
284 }
285 return structureType;
286 };
287
288 TIntermSequence insertSequence;
289
290 TVariable *typeVar = new TVariable(symbolTable, kEmptyImmutableString, makeStructureType(true),
291 SymbolType::Empty);
292 insertSequence.push_back(new TIntermDeclaration{typeVar});
293
294 TVariable *instanceVar = nullptr;
295 if (structInstanceName)
296 {
297 instanceVar = new TVariable(symbolTable, *structInstanceName, makeStructureType(false),
298 SymbolType::AngleInternal);
299 insertSequence.push_back(new TIntermDeclaration{instanceVar});
300 }
301
302 size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
303 root->insertChildNodes(firstFunctionIndex, insertSequence);
304
305 return {typeVar, instanceVar};
306 }
307
DeclareInterfaceBlock(TIntermBlock * root,TSymbolTable * symbolTable,TFieldList * fieldList,TQualifier qualifier,const TLayoutQualifier & layoutQualifier,const TMemoryQualifier & memoryQualifier,uint32_t arraySize,const ImmutableString & blockTypeName,const ImmutableString & blockVariableName)308 const TVariable *DeclareInterfaceBlock(TIntermBlock *root,
309 TSymbolTable *symbolTable,
310 TFieldList *fieldList,
311 TQualifier qualifier,
312 const TLayoutQualifier &layoutQualifier,
313 const TMemoryQualifier &memoryQualifier,
314 uint32_t arraySize,
315 const ImmutableString &blockTypeName,
316 const ImmutableString &blockVariableName)
317 {
318 // Define an interface block.
319 TInterfaceBlock *interfaceBlock = new TInterfaceBlock(
320 symbolTable, blockTypeName, fieldList, layoutQualifier, SymbolType::AngleInternal);
321
322 // Turn the inteface block into a declaration.
323 TType *interfaceBlockType = new TType(interfaceBlock, qualifier, layoutQualifier);
324 interfaceBlockType->setMemoryQualifier(memoryQualifier);
325 if (arraySize > 0)
326 {
327 interfaceBlockType->makeArray(arraySize);
328 }
329
330 TIntermDeclaration *interfaceBlockDecl = new TIntermDeclaration;
331 TVariable *interfaceBlockVar =
332 new TVariable(symbolTable, blockVariableName, interfaceBlockType,
333 blockVariableName.empty() ? SymbolType::Empty : SymbolType::AngleInternal);
334 TIntermSymbol *interfaceBlockDeclarator = new TIntermSymbol(interfaceBlockVar);
335 interfaceBlockDecl->appendDeclarator(interfaceBlockDeclarator);
336
337 // Insert the declarations before the first function.
338 TIntermSequence insertSequence;
339 insertSequence.push_back(interfaceBlockDecl);
340
341 size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
342 root->insertChildNodes(firstFunctionIndex, insertSequence);
343
344 return interfaceBlockVar;
345 }
346
EnsureBlock(TIntermNode * node)347 TIntermBlock *EnsureBlock(TIntermNode *node)
348 {
349 if (node == nullptr)
350 return nullptr;
351 TIntermBlock *blockNode = node->getAsBlock();
352 if (blockNode != nullptr)
353 {
354 return blockNode;
355 }
356 blockNode = new TIntermBlock();
357 blockNode->setLine(node->getLine());
358 blockNode->appendStatement(node);
359 return blockNode;
360 }
361
EnsureLoopBodyBlock(TIntermNode * node)362 TIntermBlock *EnsureLoopBodyBlock(TIntermNode *node)
363 {
364 if (node == nullptr)
365 {
366 return new TIntermBlock();
367 }
368 return EnsureBlock(node);
369 }
370
ReferenceGlobalVariable(const ImmutableString & name,const TSymbolTable & symbolTable)371 TIntermSymbol *ReferenceGlobalVariable(const ImmutableString &name, const TSymbolTable &symbolTable)
372 {
373 const TSymbol *symbol = symbolTable.findGlobal(name);
374 ASSERT(symbol && symbol->isVariable());
375 return new TIntermSymbol(static_cast<const TVariable *>(symbol));
376 }
377
ReferenceBuiltInVariable(const ImmutableString & name,const TSymbolTable & symbolTable,int shaderVersion)378 TIntermSymbol *ReferenceBuiltInVariable(const ImmutableString &name,
379 const TSymbolTable &symbolTable,
380 int shaderVersion)
381 {
382 const TVariable *var =
383 static_cast<const TVariable *>(symbolTable.findBuiltIn(name, shaderVersion));
384 ASSERT(var);
385 return new TIntermSymbol(var);
386 }
387
CreateBuiltInFunctionCallNode(const char * name,TIntermSequence * arguments,const TSymbolTable & symbolTable,int shaderVersion)388 TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
389 TIntermSequence *arguments,
390 const TSymbolTable &symbolTable,
391 int shaderVersion)
392 {
393 const TFunction *fn = LookUpBuiltInFunction(name, arguments, symbolTable, shaderVersion);
394 ASSERT(fn);
395 TOperator op = fn->getBuiltInOp();
396 if (BuiltInGroup::IsMath(op) && arguments->size() == 1)
397 {
398 return new TIntermUnary(op, arguments->at(0)->getAsTyped(), fn);
399 }
400 return TIntermAggregate::CreateBuiltInFunctionCall(*fn, arguments);
401 }
402
CreateBuiltInFunctionCallNode(const char * name,const std::initializer_list<TIntermNode * > & arguments,const TSymbolTable & symbolTable,int shaderVersion)403 TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
404 const std::initializer_list<TIntermNode *> &arguments,
405 const TSymbolTable &symbolTable,
406 int shaderVersion)
407 {
408 TIntermSequence argSequence(arguments);
409 return CreateBuiltInFunctionCallNode(name, &argSequence, symbolTable, shaderVersion);
410 }
411
CreateBuiltInUnaryFunctionCallNode(const char * name,TIntermTyped * argument,const TSymbolTable & symbolTable,int shaderVersion)412 TIntermTyped *CreateBuiltInUnaryFunctionCallNode(const char *name,
413 TIntermTyped *argument,
414 const TSymbolTable &symbolTable,
415 int shaderVersion)
416 {
417 return CreateBuiltInFunctionCallNode(name, {argument}, symbolTable, shaderVersion);
418 }
419
GetESSLOrGLSLVersion(ShShaderSpec spec,int esslVersion,int glslVersion)420 int GetESSLOrGLSLVersion(ShShaderSpec spec, int esslVersion, int glslVersion)
421 {
422 return IsDesktopGLSpec(spec) ? glslVersion : esslVersion;
423 }
424
425 // Returns true if a block ends in a branch (break, continue, return, etc). This is only correct
426 // after PruneNoOps, because it expects empty blocks after a branch to have been already pruned,
427 // i.e. a block can only end in a branch if its last statement is a branch or is a block ending in
428 // branch.
EndsInBranch(TIntermBlock * block)429 bool EndsInBranch(TIntermBlock *block)
430 {
431 while (block != nullptr)
432 {
433 // Get the last statement of the block.
434 TIntermSequence &statements = *block->getSequence();
435 if (statements.empty())
436 {
437 return false;
438 }
439
440 TIntermNode *lastStatement = statements.back();
441
442 // If it's a branch itself, we have the answer.
443 if (lastStatement->getAsBranchNode())
444 {
445 return true;
446 }
447
448 // Otherwise, see if it's a block that ends in a branch
449 block = lastStatement->getAsBlock();
450 }
451
452 return false;
453 }
454
455 } // namespace sh
456