| // |
| // Copyright (c) 2002-2015 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. |
| // |
| // The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in |
| // function definitions, prototypes, and call sites. |
| |
| #include "compiler/translator/ArrayReturnValueToOutParameter.h" |
| |
| #include "compiler/translator/IntermNode.h" |
| |
| namespace |
| { |
| |
| void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to) |
| { |
| const TIntermSequence *fromSequence = from->getSequence(); |
| for (size_t ii = 0; ii < fromSequence->size(); ++ii) |
| { |
| to->getSequence()->push_back(fromSequence->at(ii)); |
| } |
| } |
| |
| TIntermSymbol *CreateReturnValueSymbol(const TType &type) |
| { |
| TIntermSymbol *node = new TIntermSymbol(0, "angle_return", type); |
| node->setInternal(true); |
| return node; |
| } |
| |
| TIntermSymbol *CreateReturnValueOutSymbol(const TType &type) |
| { |
| TType outType(type); |
| outType.setQualifier(EvqOut); |
| return CreateReturnValueSymbol(outType); |
| } |
| |
| TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermTyped *returnValueTarget) |
| { |
| TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall); |
| replacementCall->setType(TType(EbtVoid)); |
| replacementCall->setUserDefined(); |
| *replacementCall->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo(); |
| replacementCall->setLine(originalCall->getLine()); |
| TIntermSequence *replacementParameters = replacementCall->getSequence(); |
| TIntermSequence *originalParameters = originalCall->getSequence(); |
| for (auto ¶m : *originalParameters) |
| { |
| replacementParameters->push_back(param); |
| } |
| replacementParameters->push_back(returnValueTarget); |
| return replacementCall; |
| } |
| |
| class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser |
| { |
| public: |
| static void apply(TIntermNode *root, unsigned int *temporaryIndex); |
| private: |
| ArrayReturnValueToOutParameterTraverser(); |
| |
| bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override; |
| bool visitBranch(Visit visit, TIntermBranch *node) override; |
| bool visitBinary(Visit visit, TIntermBinary *node) override; |
| |
| bool mInFunctionWithArrayReturnValue; |
| }; |
| |
| void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, unsigned int *temporaryIndex) |
| { |
| ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam; |
| arrayReturnValueToOutParam.useTemporaryIndex(temporaryIndex); |
| root->traverse(&arrayReturnValueToOutParam); |
| arrayReturnValueToOutParam.updateTree(); |
| } |
| |
| ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser() |
| : TIntermTraverser(true, false, true), |
| mInFunctionWithArrayReturnValue(false) |
| { |
| } |
| |
| bool ArrayReturnValueToOutParameterTraverser::visitFunctionDefinition( |
| Visit visit, |
| TIntermFunctionDefinition *node) |
| { |
| if (node->isArray() && visit == PreVisit) |
| { |
| // Replace the parameters child node of the function definition with another node |
| // that has the out parameter added. |
| // Also set the function to return void. |
| |
| TIntermAggregate *params = node->getFunctionParameters(); |
| ASSERT(params != nullptr && params->getOp() == EOpParameters); |
| |
| TIntermAggregate *replacementParams = new TIntermAggregate; |
| replacementParams->setOp(EOpParameters); |
| CopyAggregateChildren(params, replacementParams); |
| replacementParams->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType())); |
| replacementParams->setLine(params->getLine()); |
| |
| queueReplacementWithParent(node, params, replacementParams, OriginalNode::IS_DROPPED); |
| |
| node->setType(TType(EbtVoid)); |
| |
| mInFunctionWithArrayReturnValue = true; |
| } |
| if (visit == PostVisit) |
| { |
| // This isn't conditional on node->isArray() since the type has already been changed on |
| // PreVisit. |
| mInFunctionWithArrayReturnValue = false; |
| } |
| return true; |
| } |
| |
| bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node) |
| { |
| if (visit == PreVisit) |
| { |
| if (node->isArray()) |
| { |
| if (node->getOp() == EOpPrototype) |
| { |
| // Replace the whole prototype node with another node that has the out parameter added. |
| TIntermAggregate *replacement = new TIntermAggregate; |
| replacement->setOp(EOpPrototype); |
| CopyAggregateChildren(node, replacement); |
| replacement->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType())); |
| replacement->setUserDefined(); |
| *replacement->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo(); |
| replacement->setLine(node->getLine()); |
| replacement->setType(TType(EbtVoid)); |
| |
| queueReplacement(node, replacement, OriginalNode::IS_DROPPED); |
| } |
| else if (node->getOp() == EOpFunctionCall) |
| { |
| // Handle call sites where the returned array is not assigned. |
| // Examples where f() is a function returning an array: |
| // 1. f(); |
| // 2. another_array == f(); |
| // 3. another_function(f()); |
| // 4. return f(); |
| // Cases 2 to 4 are already converted to simpler cases by SeparateExpressionsReturningArrays, so we |
| // only need to worry about the case where a function call returning an array forms an expression by |
| // itself. |
| TIntermBlock *parentBlock = getParentNode()->getAsBlock(); |
| if (parentBlock) |
| { |
| nextTemporaryIndex(); |
| TIntermSequence replacements; |
| replacements.push_back(createTempDeclaration(node->getType())); |
| TIntermSymbol *returnSymbol = createTempSymbol(node->getType()); |
| replacements.push_back(CreateReplacementCall(node, returnSymbol)); |
| mMultiReplacements.push_back( |
| NodeReplaceWithMultipleEntry(parentBlock, node, replacements)); |
| } |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node) |
| { |
| if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn) |
| { |
| // Instead of returning a value, assign to the out parameter and then return. |
| TIntermSequence replacements; |
| |
| TIntermTyped *expression = node->getExpression(); |
| ASSERT(expression != nullptr); |
| TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType()); |
| TIntermBinary *replacementAssignment = |
| new TIntermBinary(EOpAssign, returnValueSymbol, expression); |
| replacementAssignment->setLine(expression->getLine()); |
| replacements.push_back(replacementAssignment); |
| |
| TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr); |
| replacementBranch->setLine(node->getLine()); |
| replacements.push_back(replacementBranch); |
| |
| mMultiReplacements.push_back( |
| NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, replacements)); |
| } |
| return false; |
| } |
| |
| bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBinary *node) |
| { |
| if (node->getOp() == EOpAssign && node->getLeft()->isArray()) |
| { |
| TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); |
| if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && rightAgg->isUserDefined()) |
| { |
| TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft()); |
| queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED); |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex) |
| { |
| ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex); |
| } |