Line data Source code
1 : //
2 : // Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
3 : // Use of this source code is governed by a BSD-style license that can be
4 : // found in the LICENSE file.
5 : //
6 : // The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in
7 : // function definitions, prototypes, and call sites.
8 :
9 : #include "compiler/translator/ArrayReturnValueToOutParameter.h"
10 :
11 : #include "compiler/translator/IntermNode.h"
12 :
13 : namespace sh
14 : {
15 :
16 : namespace
17 : {
18 :
19 0 : void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to)
20 : {
21 0 : const TIntermSequence *fromSequence = from->getSequence();
22 0 : for (size_t ii = 0; ii < fromSequence->size(); ++ii)
23 : {
24 0 : to->getSequence()->push_back(fromSequence->at(ii));
25 : }
26 0 : }
27 :
28 0 : TIntermSymbol *CreateReturnValueSymbol(const TType &type)
29 : {
30 0 : TIntermSymbol *node = new TIntermSymbol(0, "angle_return", type);
31 0 : node->setInternal(true);
32 0 : return node;
33 : }
34 :
35 0 : TIntermSymbol *CreateReturnValueOutSymbol(const TType &type)
36 : {
37 0 : TType outType(type);
38 0 : outType.setQualifier(EvqOut);
39 0 : return CreateReturnValueSymbol(outType);
40 : }
41 :
42 0 : TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermTyped *returnValueTarget)
43 : {
44 0 : TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall);
45 0 : replacementCall->setType(TType(EbtVoid));
46 0 : replacementCall->setUserDefined();
47 0 : *replacementCall->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo();
48 0 : replacementCall->setLine(originalCall->getLine());
49 0 : TIntermSequence *replacementParameters = replacementCall->getSequence();
50 0 : TIntermSequence *originalParameters = originalCall->getSequence();
51 0 : for (auto ¶m : *originalParameters)
52 : {
53 0 : replacementParameters->push_back(param);
54 : }
55 0 : replacementParameters->push_back(returnValueTarget);
56 0 : return replacementCall;
57 : }
58 :
59 0 : class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser
60 : {
61 : public:
62 : static void apply(TIntermNode *root, unsigned int *temporaryIndex);
63 : private:
64 : ArrayReturnValueToOutParameterTraverser();
65 :
66 : bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
67 : bool visitAggregate(Visit visit, TIntermAggregate *node) override;
68 : bool visitBranch(Visit visit, TIntermBranch *node) override;
69 : bool visitBinary(Visit visit, TIntermBinary *node) override;
70 :
71 : bool mInFunctionWithArrayReturnValue;
72 : };
73 :
74 0 : void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, unsigned int *temporaryIndex)
75 : {
76 0 : ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam;
77 0 : arrayReturnValueToOutParam.useTemporaryIndex(temporaryIndex);
78 0 : root->traverse(&arrayReturnValueToOutParam);
79 0 : arrayReturnValueToOutParam.updateTree();
80 0 : }
81 :
82 0 : ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser()
83 : : TIntermTraverser(true, false, true),
84 0 : mInFunctionWithArrayReturnValue(false)
85 : {
86 0 : }
87 :
88 0 : bool ArrayReturnValueToOutParameterTraverser::visitFunctionDefinition(
89 : Visit visit,
90 : TIntermFunctionDefinition *node)
91 : {
92 0 : if (node->isArray() && visit == PreVisit)
93 : {
94 : // Replace the parameters child node of the function definition with another node
95 : // that has the out parameter added.
96 : // Also set the function to return void.
97 :
98 0 : TIntermAggregate *params = node->getFunctionParameters();
99 0 : ASSERT(params != nullptr && params->getOp() == EOpParameters);
100 :
101 0 : TIntermAggregate *replacementParams = new TIntermAggregate;
102 0 : replacementParams->setOp(EOpParameters);
103 0 : CopyAggregateChildren(params, replacementParams);
104 0 : replacementParams->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
105 0 : replacementParams->setLine(params->getLine());
106 :
107 0 : queueReplacementWithParent(node, params, replacementParams, OriginalNode::IS_DROPPED);
108 :
109 0 : node->setType(TType(EbtVoid));
110 :
111 0 : mInFunctionWithArrayReturnValue = true;
112 : }
113 0 : if (visit == PostVisit)
114 : {
115 : // This isn't conditional on node->isArray() since the type has already been changed on
116 : // PreVisit.
117 0 : mInFunctionWithArrayReturnValue = false;
118 : }
119 0 : return true;
120 : }
121 :
122 0 : bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
123 : {
124 0 : if (visit == PreVisit)
125 : {
126 0 : if (node->isArray())
127 : {
128 0 : if (node->getOp() == EOpPrototype)
129 : {
130 : // Replace the whole prototype node with another node that has the out parameter added.
131 0 : TIntermAggregate *replacement = new TIntermAggregate;
132 0 : replacement->setOp(EOpPrototype);
133 0 : CopyAggregateChildren(node, replacement);
134 0 : replacement->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
135 0 : replacement->setUserDefined();
136 0 : *replacement->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
137 0 : replacement->setLine(node->getLine());
138 0 : replacement->setType(TType(EbtVoid));
139 :
140 0 : queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
141 : }
142 0 : else if (node->getOp() == EOpFunctionCall)
143 : {
144 : // Handle call sites where the returned array is not assigned.
145 : // Examples where f() is a function returning an array:
146 : // 1. f();
147 : // 2. another_array == f();
148 : // 3. another_function(f());
149 : // 4. return f();
150 : // Cases 2 to 4 are already converted to simpler cases by SeparateExpressionsReturningArrays, so we
151 : // only need to worry about the case where a function call returning an array forms an expression by
152 : // itself.
153 0 : TIntermBlock *parentBlock = getParentNode()->getAsBlock();
154 0 : if (parentBlock)
155 : {
156 0 : nextTemporaryIndex();
157 0 : TIntermSequence replacements;
158 0 : replacements.push_back(createTempDeclaration(node->getType()));
159 0 : TIntermSymbol *returnSymbol = createTempSymbol(node->getType());
160 0 : replacements.push_back(CreateReplacementCall(node, returnSymbol));
161 0 : mMultiReplacements.push_back(
162 0 : NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
163 : }
164 0 : return false;
165 : }
166 : }
167 : }
168 0 : return true;
169 : }
170 :
171 0 : bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node)
172 : {
173 0 : if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
174 : {
175 : // Instead of returning a value, assign to the out parameter and then return.
176 0 : TIntermSequence replacements;
177 :
178 0 : TIntermTyped *expression = node->getExpression();
179 0 : ASSERT(expression != nullptr);
180 0 : TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType());
181 : TIntermBinary *replacementAssignment =
182 0 : new TIntermBinary(EOpAssign, returnValueSymbol, expression);
183 0 : replacementAssignment->setLine(expression->getLine());
184 0 : replacements.push_back(replacementAssignment);
185 :
186 0 : TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr);
187 0 : replacementBranch->setLine(node->getLine());
188 0 : replacements.push_back(replacementBranch);
189 :
190 0 : mMultiReplacements.push_back(
191 0 : NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, replacements));
192 : }
193 0 : return false;
194 : }
195 :
196 0 : bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBinary *node)
197 : {
198 0 : if (node->getOp() == EOpAssign && node->getLeft()->isArray())
199 : {
200 0 : TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
201 0 : if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && rightAgg->isUserDefined())
202 : {
203 0 : TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft());
204 0 : queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED);
205 : }
206 : }
207 0 : return false;
208 : }
209 :
210 : } // namespace
211 :
212 0 : void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex)
213 : {
214 0 : ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex);
215 0 : }
216 :
217 : } // namespace sh
|