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