InitializeVariables.cpp (17704B)
1 // 2 // Copyright 2002 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 7 #include "compiler/translator/tree_ops/InitializeVariables.h" 8 9 #include "angle_gl.h" 10 #include "common/debug.h" 11 #include "compiler/translator/Compiler.h" 12 #include "compiler/translator/StaticType.h" 13 #include "compiler/translator/SymbolTable.h" 14 #include "compiler/translator/tree_util/FindMain.h" 15 #include "compiler/translator/tree_util/IntermNode_util.h" 16 #include "compiler/translator/tree_util/IntermTraverse.h" 17 #include "compiler/translator/util.h" 18 19 namespace sh 20 { 21 22 namespace 23 { 24 25 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, 26 bool canUseLoopsToInitialize, 27 bool highPrecisionSupported, 28 TIntermSequence *initSequenceOut, 29 TSymbolTable *symbolTable); 30 31 void AddStructZeroInitSequence(const TIntermTyped *initializedNode, 32 bool canUseLoopsToInitialize, 33 bool highPrecisionSupported, 34 TIntermSequence *initSequenceOut, 35 TSymbolTable *symbolTable); 36 37 TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode) 38 { 39 TIntermTyped *zero = CreateZeroNode(initializedNode->getType()); 40 return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero); 41 } 42 43 void AddZeroInitSequence(const TIntermTyped *initializedNode, 44 bool canUseLoopsToInitialize, 45 bool highPrecisionSupported, 46 TIntermSequence *initSequenceOut, 47 TSymbolTable *symbolTable) 48 { 49 if (initializedNode->isArray()) 50 { 51 AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported, 52 initSequenceOut, symbolTable); 53 } 54 else if (initializedNode->getType().isStructureContainingArrays() || 55 initializedNode->getType().isNamelessStruct()) 56 { 57 AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported, 58 initSequenceOut, symbolTable); 59 } 60 else if (initializedNode->getType().isInterfaceBlock()) 61 { 62 const TType &type = initializedNode->getType(); 63 const TInterfaceBlock &interfaceBlock = *type.getInterfaceBlock(); 64 const TFieldList &fieldList = interfaceBlock.fields(); 65 for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex) 66 { 67 const TField &field = *fieldList[fieldIndex]; 68 TIntermTyped *fieldIndexRef = CreateIndexNode(static_cast<int>(fieldIndex)); 69 TIntermTyped *fieldReference = 70 new TIntermBinary(TOperator::EOpIndexDirectInterfaceBlock, 71 initializedNode->deepCopy(), fieldIndexRef); 72 TIntermTyped *fieldZero = CreateZeroNode(*field.type()); 73 TIntermTyped *assignment = 74 new TIntermBinary(TOperator::EOpAssign, fieldReference, fieldZero); 75 initSequenceOut->push_back(assignment); 76 } 77 } 78 else 79 { 80 initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode)); 81 } 82 } 83 84 void AddStructZeroInitSequence(const TIntermTyped *initializedNode, 85 bool canUseLoopsToInitialize, 86 bool highPrecisionSupported, 87 TIntermSequence *initSequenceOut, 88 TSymbolTable *symbolTable) 89 { 90 ASSERT(initializedNode->getBasicType() == EbtStruct); 91 const TStructure *structType = initializedNode->getType().getStruct(); 92 for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i) 93 { 94 TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, 95 initializedNode->deepCopy(), CreateIndexNode(i)); 96 // Structs can't be defined inside structs, so the type of a struct field can't be a 97 // nameless struct. 98 ASSERT(!element->getType().isNamelessStruct()); 99 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported, 100 initSequenceOut, symbolTable); 101 } 102 } 103 104 void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode, 105 bool canUseLoopsToInitialize, 106 bool highPrecisionSupported, 107 TIntermSequence *initSequenceOut, 108 TSymbolTable *symbolTable) 109 { 110 for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i) 111 { 112 TIntermBinary *element = 113 new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i)); 114 AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported, 115 initSequenceOut, symbolTable); 116 } 117 } 118 119 void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode, 120 bool highPrecisionSupported, 121 TIntermSequence *initSequenceOut, 122 TSymbolTable *symbolTable) 123 { 124 ASSERT(initializedNode->isArray()); 125 const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>(); 126 const TType *highpIndexType = StaticType::Get<EbtInt, EbpHigh, EvqTemporary, 1, 1>(); 127 TVariable *indexVariable = 128 CreateTempVariable(symbolTable, highPrecisionSupported ? highpIndexType : mediumpIndexType); 129 130 TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable); 131 TIntermDeclaration *indexInit = 132 CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType())); 133 TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize()); 134 TIntermBinary *indexSmallerThanSize = 135 new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode); 136 TIntermUnary *indexIncrement = 137 new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr); 138 139 TIntermBlock *forLoopBody = new TIntermBlock(); 140 TIntermSequence *forLoopBodySeq = forLoopBody->getSequence(); 141 142 TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(), 143 indexSymbolNode->deepCopy()); 144 AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable); 145 146 TIntermLoop *forLoop = 147 new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody); 148 initSequenceOut->push_back(forLoop); 149 } 150 151 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, 152 bool canUseLoopsToInitialize, 153 bool highPrecisionSupported, 154 TIntermSequence *initSequenceOut, 155 TSymbolTable *symbolTable) 156 { 157 // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which 158 // doesn't have array assignment. We'll do this either with a for loop or just a list of 159 // statements assigning to each array index. Note that it is important to have the array init in 160 // the right order to workaround http://crbug.com/709317 161 bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u || 162 (initializedNode->getBasicType() != EbtStruct && 163 !initializedNode->getType().isArrayOfArrays() && 164 initializedNode->getOutermostArraySize() <= 3u); 165 if (initializedNode->getQualifier() == EvqFragData || 166 initializedNode->getQualifier() == EvqFragmentOut || isSmallArray || 167 !canUseLoopsToInitialize) 168 { 169 // Fragment outputs should not be indexed by non-constant indices. 170 // Also it doesn't make sense to use loops to initialize very small arrays. 171 AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize, 172 highPrecisionSupported, initSequenceOut, symbolTable); 173 } 174 else 175 { 176 AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut, 177 symbolTable); 178 } 179 } 180 181 void InsertInitCode(TCompiler *compiler, 182 TIntermSequence *mainBody, 183 const InitVariableList &variables, 184 TSymbolTable *symbolTable, 185 int shaderVersion, 186 const TExtensionBehavior &extensionBehavior, 187 bool canUseLoopsToInitialize, 188 bool highPrecisionSupported) 189 { 190 for (const ShaderVariable &var : variables) 191 { 192 // Note that tempVariableName will reference a short-lived char array here - that's fine 193 // since we're only using it to find symbols. 194 ImmutableString tempVariableName(var.name.c_str(), var.name.length()); 195 196 TIntermTyped *initializedSymbol = nullptr; 197 if (var.isBuiltIn() && !symbolTable->findUserDefined(tempVariableName)) 198 { 199 initializedSymbol = 200 ReferenceBuiltInVariable(tempVariableName, *symbolTable, shaderVersion); 201 if (initializedSymbol->getQualifier() == EvqFragData && 202 !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers)) 203 { 204 // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be 205 // written to. 206 // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up 207 // with a good way to do it. Right now "gl_FragData" in symbol table is initialized 208 // to have the array size of MaxDrawBuffers, and the initialization happens before 209 // the shader sets the extensions it is using. 210 initializedSymbol = 211 new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0)); 212 } 213 } 214 else 215 { 216 if (tempVariableName != "") 217 { 218 initializedSymbol = ReferenceGlobalVariable(tempVariableName, *symbolTable); 219 } 220 else 221 { 222 // Must be a nameless interface block. 223 ASSERT(var.structOrBlockName != ""); 224 const TSymbol *symbol = symbolTable->findGlobal(var.structOrBlockName); 225 ASSERT(symbol && symbol->isInterfaceBlock()); 226 const TInterfaceBlock *block = static_cast<const TInterfaceBlock *>(symbol); 227 228 for (const TField *field : block->fields()) 229 { 230 initializedSymbol = ReferenceGlobalVariable(field->name(), *symbolTable); 231 232 TIntermSequence initCode; 233 CreateInitCode(initializedSymbol, canUseLoopsToInitialize, 234 highPrecisionSupported, &initCode, symbolTable); 235 mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end()); 236 } 237 // Already inserted init code in this case 238 continue; 239 } 240 } 241 ASSERT(initializedSymbol != nullptr); 242 243 TIntermSequence initCode; 244 CreateInitCode(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported, 245 &initCode, symbolTable); 246 mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end()); 247 } 248 } 249 250 class InitializeLocalsTraverser : public TIntermTraverser 251 { 252 public: 253 InitializeLocalsTraverser(int shaderVersion, 254 TSymbolTable *symbolTable, 255 bool canUseLoopsToInitialize, 256 bool highPrecisionSupported) 257 : TIntermTraverser(true, false, false, symbolTable), 258 mShaderVersion(shaderVersion), 259 mCanUseLoopsToInitialize(canUseLoopsToInitialize), 260 mHighPrecisionSupported(highPrecisionSupported) 261 {} 262 263 protected: 264 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override 265 { 266 for (TIntermNode *declarator : *node->getSequence()) 267 { 268 if (!mInGlobalScope && !declarator->getAsBinaryNode()) 269 { 270 TIntermSymbol *symbol = declarator->getAsSymbolNode(); 271 ASSERT(symbol); 272 if (symbol->variable().symbolType() == SymbolType::Empty) 273 { 274 continue; 275 } 276 277 // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not 278 // support array constructors or assigning arrays. 279 bool arrayConstructorUnavailable = 280 (symbol->isArray() || symbol->getType().isStructureContainingArrays()) && 281 mShaderVersion == 100; 282 // Nameless struct constructors can't be referred to, so they also need to be 283 // initialized one element at a time. 284 // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we 285 // could use an initializer. It could at least reduce code size for very large 286 // arrays, but could hurt runtime performance. 287 if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct()) 288 { 289 // SimplifyLoopConditions should have been run so the parent node of this node 290 // should not be a loop. 291 ASSERT(getParentNode()->getAsLoopNode() == nullptr); 292 // SeparateDeclarations should have already been run, so we don't need to worry 293 // about further declarators in this declaration depending on the effects of 294 // this declarator. 295 ASSERT(node->getSequence()->size() == 1); 296 TIntermSequence initCode; 297 CreateInitCode(symbol, mCanUseLoopsToInitialize, mHighPrecisionSupported, 298 &initCode, mSymbolTable); 299 insertStatementsInParentBlock(TIntermSequence(), initCode); 300 } 301 else 302 { 303 TIntermBinary *init = 304 new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType())); 305 queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD); 306 } 307 } 308 } 309 return false; 310 } 311 312 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override 313 { 314 // Initialize output function arguments as well, the parameter passed in at call time may be 315 // clobbered if the function doesn't fully write to the argument. 316 317 TIntermSequence initCode; 318 319 const TFunction *function = node->getFunction(); 320 for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex) 321 { 322 const TVariable *paramVariable = function->getParam(paramIndex); 323 const TType ¶mType = paramVariable->getType(); 324 325 if (paramType.getQualifier() != EvqParamOut) 326 { 327 continue; 328 } 329 330 CreateInitCode(new TIntermSymbol(paramVariable), mCanUseLoopsToInitialize, 331 mHighPrecisionSupported, &initCode, mSymbolTable); 332 } 333 334 if (!initCode.empty()) 335 { 336 TIntermSequence *body = node->getBody()->getSequence(); 337 body->insert(body->begin(), initCode.begin(), initCode.end()); 338 } 339 340 return true; 341 } 342 343 private: 344 int mShaderVersion; 345 bool mCanUseLoopsToInitialize; 346 bool mHighPrecisionSupported; 347 }; 348 349 } // namespace 350 351 void CreateInitCode(const TIntermTyped *initializedSymbol, 352 bool canUseLoopsToInitialize, 353 bool highPrecisionSupported, 354 TIntermSequence *initCode, 355 TSymbolTable *symbolTable) 356 { 357 AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported, 358 initCode, symbolTable); 359 } 360 361 bool InitializeUninitializedLocals(TCompiler *compiler, 362 TIntermBlock *root, 363 int shaderVersion, 364 bool canUseLoopsToInitialize, 365 bool highPrecisionSupported, 366 TSymbolTable *symbolTable) 367 { 368 InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize, 369 highPrecisionSupported); 370 root->traverse(&traverser); 371 return traverser.updateTree(compiler, root); 372 } 373 374 bool InitializeVariables(TCompiler *compiler, 375 TIntermBlock *root, 376 const InitVariableList &vars, 377 TSymbolTable *symbolTable, 378 int shaderVersion, 379 const TExtensionBehavior &extensionBehavior, 380 bool canUseLoopsToInitialize, 381 bool highPrecisionSupported) 382 { 383 TIntermBlock *body = FindMainBody(root); 384 InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion, 385 extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported); 386 387 return compiler->validateAST(root); 388 } 389 390 } // namespace sh