Hash :
38ff3c70
Author :
Date :
2019-05-15T10:07:09
Vulkan:Allow same-named var in nested scope ESSL 1.00 spec allows for variable with same name to override outer variable inside of a nested scope. This change adds new scope to symbol table inside of a function defintion, but after function parameters for ESSL 1.00 shaders (but not webGL). This prevents an error while parsing. This also includes some new code in translator to rename any vars that are redefined between the function body and the function parameters. This prevents an error later on when the translated shader is then parsed as a desktop GLSL version. Bug: angleproject:3287 Change-Id: I3f025805cf8d65bf912283bb15e6dad6e5e9b967 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1601553 Commit-Queue: Tobin Ehlis <tobine@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@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
//
// Copyright 2019 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.
//
// ReplaceShadowingVariables.cpp: Replace all references to any variable in the AST that is
// a redefinition of a variable in a nested scope. This is a useful for ESSL 1.00 shaders
// where the spec section "4.2.3. Redeclaring Variables" states "However, a nested scope can
// override an outer scope's declaration of a particular variable name." This is changed in
// later spec versions, such as ESSL 3.20 spec which states "If [a variable] is declared as
// a parameter in a function definition, it is scoped until the end of that function
// definition. A function's parameter declarations and body together form a single scope."
//
// So this class is useful when translating from ESSL 1.00 shaders, where function body var
// redefinition is allowed, to later shader versions where it's not allowed.
//
#include "compiler/translator/tree_util/ReplaceShadowingVariables.h"
#include "compiler/translator/tree_util/ReplaceVariable.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/Symbol.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include <unordered_set>
namespace sh
{
namespace
{
// Custom struct to queue up any replacements until after AST traversal
struct DeferredReplacementBlock
{
const TVariable *originalVariable; // variable to be replaced
TVariable *replacementVariable; // variable to replace originalVar with
TIntermBlock *functionBody; // function body where replacement occurs
};
class ReplaceShadowingVariablesTraverser : public TIntermTraverser
{
public:
ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable)
: TIntermTraverser(true, true, true),
mSymbolTable(symbolTable),
mParameterNames{},
mFunctionBody(nullptr)
{}
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
{
// In pre-visit of function, record params
if (visit == PreVisit)
{
ASSERT(mParameterNames.size() == 0);
const TFunction *func = node->getFunctionPrototype()->getFunction();
// Grab all of the parameter names from the function prototype
uint32_t paramCount = func->getParamCount();
for (uint32_t i = 0; i < paramCount; ++i)
{
mParameterNames.emplace(std::string(func->getParam(i)->name().data()));
}
if (mParameterNames.size() > 0)
mFunctionBody = node->getBody();
}
else if (visit == PostVisit)
{
// Clear data saved from function definition
mParameterNames.clear();
mFunctionBody = nullptr;
}
return true;
}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
if (visit == PreVisit && mParameterNames.size() != 0)
{
TIntermSequence *decls = node->getSequence();
for (auto &declVector : *decls)
{
// no init case
TIntermSymbol *symNode = declVector->getAsSymbolNode();
if (symNode == nullptr)
{
// init case
TIntermBinary *binaryNode = declVector->getAsBinaryNode();
ASSERT(binaryNode->getOp() == EOpInitialize);
symNode = binaryNode->getLeft()->getAsSymbolNode();
}
ASSERT(symNode != nullptr);
std::string varName = std::string(symNode->variable().name().data());
if (mParameterNames.count(varName) > 0)
{
// We found a redefined var so queue replacement
mReplacements.emplace_back(DeferredReplacementBlock{
&symNode->variable(),
CreateTempVariable(mSymbolTable, &symNode->variable().getType()),
mFunctionBody});
}
}
}
return true;
}
// Perform replacement of vars for any deferred replacements that were identified
void executeReplacements()
{
for (DeferredReplacementBlock &replace : mReplacements)
{
ReplaceVariable(replace.functionBody, replace.originalVariable,
replace.replacementVariable);
}
mReplacements.clear();
}
private:
TSymbolTable *mSymbolTable;
std::unordered_set<std::string> mParameterNames;
TIntermBlock *mFunctionBody;
std::vector<DeferredReplacementBlock> mReplacements;
};
} // anonymous namespace
// Replaces every occurrence of a variable with another variable.
void ReplaceShadowingVariables(TIntermBlock *root, TSymbolTable *symbolTable)
{
ReplaceShadowingVariablesTraverser traverser(symbolTable);
root->traverse(&traverser);
traverser.executeReplacements();
traverser.updateTree();
}
} // namespace sh