| // | |
| // 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. | |
| // | |
| // Implementation of the function RewriteAtomicFunctionExpressions. | |
| // See the header for more details. | |
| #include "RewriteAtomicFunctionExpressions.h" | |
| #include "compiler/translator/tree_util/IntermNodePatternMatcher.h" | |
| #include "compiler/translator/tree_util/IntermNode_util.h" | |
| #include "compiler/translator/tree_util/IntermTraverse.h" | |
| #include "compiler/translator/util.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| // Traverser that simplifies all the atomic function expressions into the ones that can be directly | |
| // translated into HLSL. | |
| // | |
| // case 1 (only for atomicExchange and atomicCompSwap): | |
| // original: | |
| // atomicExchange(counter, newValue); | |
| // new: | |
| // tempValue = atomicExchange(counter, newValue); | |
| // | |
| // case 2 (atomic function, temporary variable required): | |
| // original: | |
| // value = atomicAdd(counter, 1) * otherValue; | |
| // someArray[atomicAdd(counter, 1)] = someOtherValue; | |
| // new: | |
| // value = ((tempValue = atomicAdd(counter, 1)), tempValue) * otherValue; | |
| // someArray[((tempValue = atomicAdd(counter, 1)), tempValue)] = someOtherValue; | |
| // | |
| // case 3 (atomic function used directly initialize a variable): | |
| // original: | |
| // int value = atomicAdd(counter, 1); | |
| // new: | |
| // tempValue = atomicAdd(counter, 1); | |
| // int value = tempValue; | |
| // | |
| class RewriteAtomicFunctionExpressionsTraverser : public TIntermTraverser | |
| { | |
| public: | |
| RewriteAtomicFunctionExpressionsTraverser(TSymbolTable *symbolTable, int shaderVersion); | |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override; | |
| bool visitBlock(Visit visit, TIntermBlock *node) override; | |
| private: | |
| static bool IsAtomicExchangeOrCompSwapNoReturnValue(TIntermAggregate *node, | |
| TIntermNode *parentNode); | |
| static bool IsAtomicFunctionInsideExpression(TIntermAggregate *node, TIntermNode *parentNode); | |
| void rewriteAtomicFunctionCallNode(TIntermAggregate *oldAtomicFunctionNode); | |
| const TVariable *getTempVariable(const TType *type); | |
| int mShaderVersion; | |
| TIntermSequence mTempVariables; | |
| }; | |
| RewriteAtomicFunctionExpressionsTraverser::RewriteAtomicFunctionExpressionsTraverser( | |
| TSymbolTable *symbolTable, | |
| int shaderVersion) | |
| : TIntermTraverser(false, false, true, symbolTable), mShaderVersion(shaderVersion) | |
| {} | |
| void RewriteAtomicFunctionExpressionsTraverser::rewriteAtomicFunctionCallNode( | |
| TIntermAggregate *oldAtomicFunctionNode) | |
| { | |
| ASSERT(oldAtomicFunctionNode); | |
| const TVariable *returnVariable = getTempVariable(&oldAtomicFunctionNode->getType()); | |
| TIntermBinary *rewrittenNode = new TIntermBinary( | |
| TOperator::EOpAssign, CreateTempSymbolNode(returnVariable), oldAtomicFunctionNode); | |
| auto *parentNode = getParentNode(); | |
| auto *parentBinary = parentNode->getAsBinaryNode(); | |
| if (parentBinary && parentBinary->getOp() == EOpInitialize) | |
| { | |
| insertStatementInParentBlock(rewrittenNode); | |
| queueReplacement(CreateTempSymbolNode(returnVariable), OriginalNode::IS_DROPPED); | |
| } | |
| else | |
| { | |
| // As all atomic function assignment will be converted to the last argument of an | |
| // interlocked function, if we need the return value, assignment needs to be wrapped with | |
| // the comma operator and the temporary variables. | |
| if (!parentNode->getAsBlock()) | |
| { | |
| rewrittenNode = TIntermBinary::CreateComma( | |
| rewrittenNode, new TIntermSymbol(returnVariable), mShaderVersion); | |
| } | |
| queueReplacement(rewrittenNode, OriginalNode::IS_DROPPED); | |
| } | |
| } | |
| const TVariable *RewriteAtomicFunctionExpressionsTraverser::getTempVariable(const TType *type) | |
| { | |
| TIntermDeclaration *variableDeclaration; | |
| TVariable *returnVariable = | |
| DeclareTempVariable(mSymbolTable, type, EvqTemporary, &variableDeclaration); | |
| mTempVariables.push_back(variableDeclaration); | |
| return returnVariable; | |
| } | |
| bool RewriteAtomicFunctionExpressionsTraverser::IsAtomicExchangeOrCompSwapNoReturnValue( | |
| TIntermAggregate *node, | |
| TIntermNode *parentNode) | |
| { | |
| ASSERT(node); | |
| return (node->getOp() == EOpAtomicExchange || node->getOp() == EOpAtomicCompSwap) && | |
| parentNode && parentNode->getAsBlock(); | |
| } | |
| bool RewriteAtomicFunctionExpressionsTraverser::IsAtomicFunctionInsideExpression( | |
| TIntermAggregate *node, | |
| TIntermNode *parentNode) | |
| { | |
| ASSERT(node); | |
| // We only need to handle atomic functions with a parent that it is not block nodes. If the | |
| // parent node is block, it means that the atomic function is not inside an expression. | |
| if (!IsAtomicFunction(node->getOp()) || parentNode->getAsBlock()) | |
| { | |
| return false; | |
| } | |
| auto *parentAsBinary = parentNode->getAsBinaryNode(); | |
| // Assignments are handled in OutputHLSL | |
| return !parentAsBinary || parentAsBinary->getOp() != EOpAssign; | |
| } | |
| bool RewriteAtomicFunctionExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node) | |
| { | |
| ASSERT(visit == PostVisit); | |
| // Skip atomic memory functions for SSBO. They will be processed in the OutputHLSL traverser. | |
| if (IsAtomicFunction(node->getOp()) && | |
| IsInShaderStorageBlock((*node->getSequence())[0]->getAsTyped())) | |
| { | |
| return false; | |
| } | |
| TIntermNode *parentNode = getParentNode(); | |
| if (IsAtomicExchangeOrCompSwapNoReturnValue(node, parentNode) || | |
| IsAtomicFunctionInsideExpression(node, parentNode)) | |
| { | |
| rewriteAtomicFunctionCallNode(node); | |
| } | |
| return true; | |
| } | |
| bool RewriteAtomicFunctionExpressionsTraverser::visitBlock(Visit visit, TIntermBlock *node) | |
| { | |
| ASSERT(visit == PostVisit); | |
| if (!mTempVariables.empty() && getParentNode()->getAsFunctionDefinition()) | |
| { | |
| insertStatementsInBlockAtPosition(node, 0, mTempVariables, TIntermSequence()); | |
| mTempVariables.clear(); | |
| } | |
| return true; | |
| } | |
| } // anonymous namespace | |
| bool RewriteAtomicFunctionExpressions(TCompiler *compiler, | |
| TIntermNode *root, | |
| TSymbolTable *symbolTable, | |
| int shaderVersion) | |
| { | |
| RewriteAtomicFunctionExpressionsTraverser traverser(symbolTable, shaderVersion); | |
| traverser.traverse(root); | |
| return traverser.updateTree(compiler, root); | |
| } | |
| } // namespace sh |