diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/PruneEmptyCases.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/tree_ops/PruneEmptyCases.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/PruneEmptyCases.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/PruneEmptyCases.cpp new file mode 100644 index 0000000000..276c8c98ed --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/PruneEmptyCases.cpp @@ -0,0 +1,127 @@ +// +// Copyright 2018 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. +// +// PruneEmptyCases.cpp: The PruneEmptyCases function prunes cases that are followed by nothing from +// the AST. + +#include "compiler/translator/tree_ops/PruneEmptyCases.h" + +#include "compiler/translator/Symbol.h" +#include "compiler/translator/tree_util/IntermTraverse.h" + +namespace sh +{ + +namespace +{ + +bool AreEmptyBlocks(const TIntermSequence *statements); + +bool IsEmptyBlock(TIntermNode *node) +{ + TIntermBlock *asBlock = node->getAsBlock(); + if (asBlock) + { + return AreEmptyBlocks(asBlock->getSequence()); + } + // Empty declarations should have already been pruned, otherwise they would need to be handled + // here. Note that declarations for struct types do contain a nameless child node. + ASSERT(node->getAsDeclarationNode() == nullptr || + !node->getAsDeclarationNode()->getSequence()->empty()); + // Pure literal statements should also already be pruned. + ASSERT(node->getAsConstantUnion() == nullptr); + return false; +} + +// Return true if all statements in "statements" consist only of empty blocks and no-op statements. +// Returns true also if there are no statements. +bool AreEmptyBlocks(const TIntermSequence *statements) +{ + for (size_t i = 0u; i < statements->size(); ++i) + { + if (!IsEmptyBlock(statements->at(i))) + { + return false; + } + } + return true; +} + +class PruneEmptyCasesTraverser : private TIntermTraverser +{ + public: + [[nodiscard]] static bool apply(TCompiler *compiler, TIntermBlock *root); + + private: + PruneEmptyCasesTraverser(); + bool visitSwitch(Visit visit, TIntermSwitch *node) override; +}; + +bool PruneEmptyCasesTraverser::apply(TCompiler *compiler, TIntermBlock *root) +{ + PruneEmptyCasesTraverser prune; + root->traverse(&prune); + return prune.updateTree(compiler, root); +} + +PruneEmptyCasesTraverser::PruneEmptyCasesTraverser() : TIntermTraverser(true, false, false) {} + +bool PruneEmptyCasesTraverser::visitSwitch(Visit visit, TIntermSwitch *node) +{ + // This may mutate the statementList, but that's okay, since traversal has not yet reached + // there. + TIntermBlock *statementList = node->getStatementList(); + TIntermSequence *statements = statementList->getSequence(); + + // Iterate block children in reverse order. Cases that are only followed by other cases or empty + // blocks are marked for pruning. + size_t i = statements->size(); + size_t lastNoOpInStatementList = i; + while (i > 0) + { + --i; + TIntermNode *statement = statements->at(i); + if (statement->getAsCaseNode() || IsEmptyBlock(statement)) + { + lastNoOpInStatementList = i; + } + else + { + break; + } + } + if (lastNoOpInStatementList == 0) + { + // Remove the entire switch statement, extracting the init expression if needed. + TIntermTyped *init = node->getInit(); + if (init->hasSideEffects()) + { + queueReplacement(init, OriginalNode::IS_DROPPED); + } + else + { + TIntermSequence emptyReplacement; + ASSERT(getParentNode()->getAsBlock()); + mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, + std::move(emptyReplacement)); + } + return false; + } + if (lastNoOpInStatementList < statements->size()) + { + statements->erase(statements->begin() + lastNoOpInStatementList, statements->end()); + } + + return true; +} + +} // namespace + +bool PruneEmptyCases(TCompiler *compiler, TIntermBlock *root) +{ + return PruneEmptyCasesTraverser::apply(compiler, root); +} + +} // namespace sh |