Hash :
9733ceef
Author :
Date :
2017-05-11T19:14:35
Initialize uninitialized locals in GLSL output
Guarantee that local variables are initialized before they are used
in GLSL output. In HLSL output all variables were already being
initialized.
Locals are initialized using an AST transform. The local variable init
can only be run after some simplification of the AST, so that it is
able to handle complex cases like:
for (int i[2], j = i[0]; i[0] < 3; ++i[0]) {
}
If we're dealing with ESSL 1.00 which lacks array constructors, in
this kind of case the uninitialized array initialization code needs to
be hoisted out of the loop init statement, and the code also needs to
make sure that j's initializer is run after i is initialized.
Another complex case involves nameless structs. This can be an issue
also in ESSL 3.00 and above:
for (struct { float f; } s; s.f < 1.0; ++s.f) {
}
Since the struct doesn't have a name, its constructor can not be used.
We solve this by initializing the struct members individually,
similarly to how arrays are initialized in ESSL 1.00.
Initializing local variables is disabled on Mac and Android for now.
On Mac, invalid behavior was exposed in the WebGL 2.0 tests when
enabling it. On Android, the dEQP test runs failed for an unknown
reason. Bugs have been opened to resolve these issues later.
BUG=angleproject:1966
TEST=angle_end2end_tests, WebGL conformance tests
Change-Id: Ic06927f5b6cc9619bc82c647ee966605cd80bab2
Reviewed-on: https://chromium-review.googlesource.com/504728
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
//
// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/InitializeVariables.h"
#include "angle_gl.h"
#include "common/debug.h"
#include "compiler/translator/FindMain.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
bool IsNamelessStruct(const TIntermTyped *node)
{
return (node->getBasicType() == EbtStruct && node->getType().getStruct()->name() == "");
}
void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
TIntermSequence *initSequenceOut);
TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
{
TIntermTyped *zero = TIntermTyped::CreateZero(initializedNode->getType());
return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
}
void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
TIntermSequence *initSequenceOut)
{
ASSERT(initializedNode->getBasicType() == EbtStruct);
TStructure *structType = initializedNode->getType().getStruct();
for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
{
TIntermBinary *element = new TIntermBinary(
EOpIndexDirectStruct, initializedNode->deepCopy(), TIntermTyped::CreateIndexNode(i));
if (element->isArray())
{
AddArrayZeroInitSequence(element, initSequenceOut);
}
else if (element->getType().isStructureContainingArrays())
{
AddStructZeroInitSequence(element, initSequenceOut);
}
else
{
// Structs can't be defined inside structs, so the type of a struct field can't be a
// nameless struct.
ASSERT(!IsNamelessStruct(element));
initSequenceOut->push_back(CreateZeroInitAssignment(element));
}
}
}
void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, TIntermSequence *initSequenceOut)
{
ASSERT(initializedNode->isArray());
// Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
// doesn't have array assignment.
// Note that it is important to have the array init in the right order to workaround
// http://crbug.com/709317
for (unsigned int i = 0; i < initializedNode->getArraySize(); ++i)
{
TIntermBinary *element = new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(),
TIntermTyped::CreateIndexNode(i));
if (element->getType().isStructureContainingArrays())
{
AddStructZeroInitSequence(element, initSequenceOut);
}
else
{
initSequenceOut->push_back(CreateZeroInitAssignment(element));
}
}
}
void InsertInitCode(TIntermSequence *mainBody,
const InitVariableList &variables,
const TSymbolTable &symbolTable)
{
for (const auto &var : variables)
{
TString name = TString(var.name.c_str());
TIntermSymbol *initializedSymbol = nullptr;
if (var.isArray())
{
size_t pos = name.find_last_of('[');
if (pos != TString::npos)
{
name = name.substr(0, pos);
}
TType arrayType = sh::GetShaderVariableBasicType(var);
arrayType.setArraySize(var.elementCount());
initializedSymbol = new TIntermSymbol(0, name, arrayType);
}
else if (var.isStruct())
{
TVariable *structInfo = reinterpret_cast<TVariable *>(symbolTable.findGlobal(name));
ASSERT(structInfo);
initializedSymbol = new TIntermSymbol(0, name, structInfo->getType());
}
else
{
TType type = sh::GetShaderVariableBasicType(var);
initializedSymbol = new TIntermSymbol(0, name, type);
}
TIntermSequence *initCode = CreateInitCode(initializedSymbol);
mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
}
}
class InitializeLocalsTraverser : public TIntermTraverser
{
public:
InitializeLocalsTraverser(int shaderVersion)
: TIntermTraverser(true, false, false), mShaderVersion(shaderVersion)
{
}
protected:
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
for (TIntermNode *declarator : *node->getSequence())
{
if (!mInGlobalScope && !declarator->getAsBinaryNode())
{
TIntermSymbol *symbol = declarator->getAsSymbolNode();
ASSERT(symbol);
if (symbol->getSymbol() == "")
{
continue;
}
// Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
// support array constructors or assigning arrays.
bool arrayConstructorUnavailable =
(symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
mShaderVersion == 100;
// Nameless struct constructors can't be referred to, so they also need to be
// initialized one element at a time.
if (arrayConstructorUnavailable || IsNamelessStruct(symbol))
{
// SimplifyLoopConditions should have been run so the parent node of this node
// should not be a loop.
ASSERT(getParentNode()->getAsLoopNode() == nullptr);
// SeparateDeclarations should have already been run, so we don't need to worry
// about further declarators in this declaration depending on the effects of
// this declarator.
ASSERT(node->getSequence()->size() == 1);
insertStatementsInParentBlock(TIntermSequence(), *CreateInitCode(symbol));
}
else
{
TIntermBinary *init = new TIntermBinary(
EOpInitialize, symbol, TIntermTyped::CreateZero(symbol->getType()));
queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
}
}
}
return false;
}
private:
int mShaderVersion;
};
} // namespace anonymous
TIntermSequence *CreateInitCode(const TIntermSymbol *initializedSymbol)
{
TIntermSequence *initCode = new TIntermSequence();
if (initializedSymbol->isArray())
{
AddArrayZeroInitSequence(initializedSymbol, initCode);
}
else if (initializedSymbol->getType().isStructureContainingArrays() ||
IsNamelessStruct(initializedSymbol))
{
AddStructZeroInitSequence(initializedSymbol, initCode);
}
else
{
initCode->push_back(CreateZeroInitAssignment(initializedSymbol));
}
return initCode;
}
void InitializeUninitializedLocals(TIntermBlock *root, int shaderVersion)
{
InitializeLocalsTraverser traverser(shaderVersion);
root->traverse(&traverser);
traverser.updateTree();
}
void InitializeVariables(TIntermBlock *root,
const InitVariableList &vars,
const TSymbolTable &symbolTable)
{
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermBlock *body = main->getBody();
InsertInitCode(body->getSequence(), vars, symbolTable);
}
} // namespace sh