// // Copyright (c) 2017 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. // // WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped // variables there to make the AST compatible with HLSL output. // // switch (init) // { // case 0: // float f; // default: // f = 1.0; // } // // becomes // // { // float f; // switch (init) // { // case 0: // default: // f = 1.0; // } // } #include "compiler/translator/WrapSwitchStatementsInBlocks.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermTraverse.h" namespace sh { namespace { class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser { public: WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false), mDidWrap(false) { } bool visitSwitch(Visit visit, TIntermSwitch *node) override; bool didWrap() const { return mDidWrap; } private: bool mDidWrap; }; bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node) { std::vector declarations; TIntermSequence *statementList = node->getStatementList()->getSequence(); for (TIntermNode *statement : *statementList) { TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode(); if (asDeclaration) { declarations.push_back(asDeclaration); } } if (declarations.empty()) { // We don't need to wrap the switch if it doesn't contain declarations as its direct // descendants. return true; } TIntermBlock *wrapperBlock = new TIntermBlock(); for (TIntermDeclaration *declaration : declarations) { // SeparateDeclarations should have already been run. ASSERT(declaration->getSequence()->size() == 1); TIntermDeclaration *declarationInBlock = new TIntermDeclaration(); TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode(); if (declaratorAsSymbol) { // This is a simple declaration like: "float f;" // Remove the declaration from inside the switch and put it in the wrapping block. TIntermSequence emptyReplacement; mMultiReplacements.push_back(NodeReplaceWithMultipleEntry( node->getStatementList(), declaration, emptyReplacement)); declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy()); } else { // This is an init declaration like: "float f = 0.0;" // Change the init declaration inside the switch into an assignment and put a plain // declaration in the wrapping block. TIntermBinary *declaratorAsBinary = declaration->getSequence()->at(0)->getAsBinaryNode(); ASSERT(declaratorAsBinary); TIntermBinary *initAssignment = new TIntermBinary( EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight()); queueReplacementWithParent(node->getStatementList(), declaration, initAssignment, OriginalNode::IS_DROPPED); declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy()); } wrapperBlock->appendStatement(declarationInBlock); } wrapperBlock->appendStatement(node); queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD); mDidWrap = true; // Should be fine to process multiple switch statements, even nesting ones in the same // traversal. return true; } } // anonymous namespace // Wrap switch statements in the AST into blocks when needed. bool WrapSwitchStatementsInBlocks(TIntermBlock *root) { WrapSwitchStatementsInBlocksTraverser traverser; root->traverse(&traverser); traverser.updateTree(); return traverser.didWrap(); } } // namespace sh