Hash :
11724133
Author :
Date :
2024-08-06T12:02:54
Metal: Separate vars with normal types Separate compound expressions separates expressions as temporary values. Previously creating a temporary variable would copy the entire type. Qualifiers and interface block info but shouldn't ever be applied to temporary variables. This would lead to assertions during MSL output. Fix by copying the type and unsetting qualifiers and interface block info. Bug: angleproject:357622691 Change-Id: Id868cd3eaabe3710121d6c9a565304e282ddb69f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5762605 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Auto-Submit: Kimmo Kinnunen <kkinnunen@apple.com> Reviewed-by: Kenneth Russell <kbr@chromium.org> Commit-Queue: Kimmo Kinnunen <kkinnunen@apple.com>
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
//
// 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/Compiler.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, 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
size_t paramCount = func->getParamCount();
for (size_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(),
EvqTemporary),
mFunctionBody});
}
}
}
return true;
}
// Perform replacement of vars for any deferred replacements that were identified
[[nodiscard]] bool executeReplacements(TCompiler *compiler)
{
for (DeferredReplacementBlock &replace : mReplacements)
{
if (!ReplaceVariable(compiler, replace.functionBody, replace.originalVariable,
replace.replacementVariable))
{
return false;
}
}
mReplacements.clear();
return true;
}
private:
std::unordered_set<std::string> mParameterNames;
TIntermBlock *mFunctionBody;
std::vector<DeferredReplacementBlock> mReplacements;
};
} // anonymous namespace
// Replaces every occurrence of a variable with another variable.
[[nodiscard]] bool ReplaceShadowingVariables(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
{
ReplaceShadowingVariablesTraverser traverser(symbolTable);
root->traverse(&traverser);
if (!traverser.executeReplacements(compiler))
{
return false;
}
return traverser.updateTree(compiler, root);
}
} // namespace sh