blob: d636126d2740e982db4c7c820a9b78cc8fa5dd6f [file] [log] [blame]
//
// Copyright 2002 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/tree_ops/SeparateDeclarations.h"
#include "common/hash_containers.h"
#include "compiler/translator/IntermRebuild.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
class Separator final : private TIntermRebuild
{
public:
Separator(TCompiler &compiler, bool separateCompoundStructDeclarations)
: TIntermRebuild(compiler, true, true),
mSeparateCompoundStructDeclarations(separateCompoundStructDeclarations)
{}
using TIntermRebuild::rebuildRoot;
private:
void recordModifiedStructVariables(TIntermDeclaration &node)
{
ASSERT(!mNewStructure); // No nested struct declarations.
TIntermSequence &sequence = *node.getSequence();
if (sequence.size() <= 1 && !mSeparateCompoundStructDeclarations)
{
return;
}
TIntermTyped *declarator = sequence.at(0)->getAsTyped();
const TType &declaratorType = declarator->getType();
const TStructure *structure = declaratorType.getStruct();
// Rewrite variable declarations that specify structs AND variable(s) at the same
// time.
if (!structure || !declaratorType.isStructSpecifier())
{
return;
}
if (mSeparateCompoundStructDeclarations && sequence.size() == 1)
{
if (TIntermSymbol *symbol = declarator->getAsSymbolNode(); symbol != nullptr)
{
if (symbol->variable().symbolType() == SymbolType::Empty)
{
return;
}
}
}
// By default, struct specifier changes for all variables except the first one.
uint32_t index = 1;
if (structure->symbolType() == SymbolType::Empty)
{
TStructure *newStructure =
new TStructure(&mSymbolTable, kEmptyImmutableString, &structure->fields(),
SymbolType::AngleInternal);
newStructure->setAtGlobalScope(structure->atGlobalScope());
structure = newStructure;
// Adding name causes the struct type to change, so also the first variable variable
// needs rewriting.
index = 0;
}
if (mSeparateCompoundStructDeclarations)
{
mNewStructure = structure;
// Separating struct and variable declaration causes the variable type to change
// from specifying to not-specifying, so also the first variable needs rewriting.
index = 0;
}
for (; index < sequence.size(); ++index)
{
Declaration decl = ViewDeclaration(node, index);
const TVariable &var = decl.symbol.variable();
const TType &varType = var.getType();
const bool newTypeIsSpecifier = index == 0 && !mSeparateCompoundStructDeclarations;
TType *newType = new TType(structure, newTypeIsSpecifier);
newType->setQualifier(varType.getQualifier());
newType->makeArrays(varType.getArraySizes());
TVariable *newVar = new TVariable(&mSymbolTable, var.name(), newType, var.symbolType());
mStructVariables.insert(std::make_pair(&var, newVar));
}
}
PreResult visitDeclarationPre(TIntermDeclaration &node) override
{
recordModifiedStructVariables(node);
return node;
}
PostResult visitDeclarationPost(TIntermDeclaration &node) override
{
TIntermSequence &sequence = *node.getSequence();
if (sequence.size() <= 1 && !mNewStructure)
{
return node;
}
std::vector<TIntermNode *> replacements;
if (mNewStructure)
{
TType *newType = new TType(mNewStructure, true);
if (mNewStructure->atGlobalScope())
{
newType->setQualifier(EvqGlobal);
}
TVariable *structVar =
new TVariable(&mSymbolTable, kEmptyImmutableString, newType, SymbolType::Empty);
TIntermDeclaration *replacement = new TIntermDeclaration({structVar});
replacement->setLine(node.getLine());
replacements.push_back(replacement);
mNewStructure = nullptr;
}
for (uint32_t index = 0; index < sequence.size(); ++index)
{
TIntermTyped *declarator = sequence.at(index)->getAsTyped();
TIntermDeclaration *replacement = new TIntermDeclaration({declarator});
replacement->setLine(declarator->getLine());
replacements.push_back(replacement);
}
return PostResult::Multi(std::move(replacements));
}
PreResult visitSymbolPre(TIntermSymbol &symbolNode) override
{
auto it = mStructVariables.find(&symbolNode.variable());
if (it == mStructVariables.end())
{
return symbolNode;
}
return *new TIntermSymbol(it->second);
}
PreResult visitFunctionPrototypePre(TIntermFunctionPrototype &node) override
{
const TFunction *function = node.getFunction();
auto it = mFunctionsToReplace.find(function);
if (it != mFunctionsToReplace.end())
{
TIntermFunctionPrototype *newFuncProto = new TIntermFunctionPrototype(it->second);
return newFuncProto;
}
else if (node.getType().isStructSpecifier())
{
const TType &oldType = node.getType();
const TStructure *structure = oldType.getStruct();
// Name unnamed inline structs
if (structure->symbolType() == SymbolType::Empty)
{
TStructure *newStructure =
new TStructure(&mSymbolTable, kEmptyImmutableString, &structure->fields(),
SymbolType::AngleInternal);
newStructure->setAtGlobalScope(structure->atGlobalScope());
structure = newStructure;
}
TType *newType = new TType(structure, true);
if (structure->atGlobalScope())
{
newType->setQualifier(EvqGlobal);
}
TVariable *structVar =
new TVariable(&mSymbolTable, ImmutableString(""), newType, SymbolType::Empty);
TType *returnType = new TType(structure, false);
if (oldType.isArray())
{
returnType->makeArrays(oldType.getArraySizes());
}
returnType->setQualifier(oldType.getQualifier());
const TFunction *oldFunc = function;
ASSERT(oldFunc->symbolType() == SymbolType::UserDefined);
const TFunction *newFunc = cloneFunctionAndChangeReturnType(oldFunc, returnType);
mFunctionsToReplace[oldFunc] = newFunc;
if (getParentNode()->getAsFunctionDefinition() != nullptr)
{
mNewFunctionReturnStructDeclaration = new TIntermDeclaration({structVar});
return new TIntermFunctionPrototype(newFunc);
}
return PreResult::Multi(
{new TIntermDeclaration({structVar}), new TIntermFunctionPrototype(newFunc)});
}
return node;
}
PostResult visitFunctionDefinitionPost(TIntermFunctionDefinition &node) override
{
if (mNewFunctionReturnStructDeclaration)
{
return PostResult::Multi(
{std::exchange(mNewFunctionReturnStructDeclaration, nullptr), &node});
}
return node;
}
PreResult visitAggregatePre(TIntermAggregate &node) override
{
const TFunction *function = node.getFunction();
auto it = mFunctionsToReplace.find(function);
if (it != mFunctionsToReplace.end())
{
TIntermAggregate *replacementNode =
TIntermAggregate::CreateFunctionCall(*it->second, node.getSequence());
return PreResult(replacementNode, VisitBits::Children);
}
return node;
}
private:
const TFunction *cloneFunctionAndChangeReturnType(const TFunction *oldFunc,
const TType *newReturnType)
{
ASSERT(oldFunc->symbolType() == SymbolType::UserDefined);
TFunction *newFunc = new TFunction(&mSymbolTable, oldFunc->name(), oldFunc->symbolType(),
newReturnType, oldFunc->isKnownToNotHaveSideEffects());
if (oldFunc->isDefined())
{
newFunc->setDefined();
}
if (oldFunc->hasPrototypeDeclaration())
{
newFunc->setHasPrototypeDeclaration();
}
const size_t paramCount = oldFunc->getParamCount();
for (size_t i = 0; i < paramCount; ++i)
{
const TVariable *var = oldFunc->getParam(i);
newFunc->addParameter(var);
}
return newFunc;
}
angle::HashMap<const TFunction *, const TFunction *> mFunctionsToReplace;
// New structure separated from function declaration.
TIntermDeclaration *mNewFunctionReturnStructDeclaration = nullptr;
// New structure from compound declaration.
const TStructure *mNewStructure = nullptr;
// Old struct variable to new struct variable mapping.
angle::HashMap<const TVariable *, TVariable *> mStructVariables;
const bool mSeparateCompoundStructDeclarations;
};
} // namespace
bool SeparateDeclarations(TCompiler &compiler,
TIntermBlock &root,
bool separateCompoundStructDeclarations)
{
Separator separator(compiler, separateCompoundStructDeclarations);
return separator.rebuildRoot(root);
}
} // namespace sh