1 /*
2 * Copyright 2020 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "stack-utils.h"
18 #include "ir/iteration.h"
19 #include "ir/properties.h"
20
21 namespace wasm {
22
23 namespace StackUtils {
24
removeNops(Block * block)25 void removeNops(Block* block) {
26 size_t newIndex = 0;
27 for (size_t i = 0, size = block->list.size(); i < size; ++i) {
28 if (!block->list[i]->is<Nop>()) {
29 block->list[newIndex++] = block->list[i];
30 }
31 }
32 block->list.resize(newIndex);
33 }
34
mayBeUnreachable(Expression * expr)35 bool mayBeUnreachable(Expression* expr) {
36 if (Properties::isControlFlowStructure(expr)) {
37 return true;
38 }
39 switch (expr->_id) {
40 case Expression::Id::BreakId:
41 return expr->cast<Break>()->condition == nullptr;
42 case Expression::Id::CallId:
43 return expr->cast<Call>()->isReturn;
44 case Expression::Id::CallIndirectId:
45 return expr->cast<CallIndirect>()->isReturn;
46 case Expression::Id::ReturnId:
47 case Expression::Id::SwitchId:
48 case Expression::Id::UnreachableId:
49 case Expression::Id::ThrowId:
50 case Expression::Id::RethrowId:
51 return true;
52 default:
53 return false;
54 }
55 }
56
57 } // namespace StackUtils
58
StackSignature(Expression * expr)59 StackSignature::StackSignature(Expression* expr) {
60 std::vector<Type> inputs;
61 for (auto* child : ValueChildIterator(expr)) {
62 assert(child->type.isConcrete());
63 // Children might be tuple pops, so expand their types
64 inputs.insert(inputs.end(), child->type.begin(), child->type.end());
65 }
66 params = Type(inputs);
67 if (expr->type == Type::unreachable) {
68 unreachable = true;
69 results = Type::none;
70 } else {
71 unreachable = false;
72 results = expr->type;
73 }
74 }
75
composes(const StackSignature & next) const76 bool StackSignature::composes(const StackSignature& next) const {
77 auto checked = std::min(results.size(), next.params.size());
78 return std::equal(results.end() - checked,
79 results.end(),
80 next.params.end() - checked,
81 [](const Type& produced, const Type& consumed) {
82 return Type::isSubType(produced, consumed);
83 });
84 }
85
satisfies(Signature sig) const86 bool StackSignature::satisfies(Signature sig) const {
87 if (sig.params.size() < params.size() ||
88 sig.results.size() < results.size()) {
89 // Not enough values provided or too many produced
90 return false;
91 }
92 bool paramSuffixMatches =
93 std::equal(params.begin(),
94 params.end(),
95 sig.params.end() - params.size(),
96 [](const Type& consumed, const Type& provided) {
97 return Type::isSubType(provided, consumed);
98 });
99 if (!paramSuffixMatches) {
100 return false;
101 }
102 bool resultSuffixMatches =
103 std::equal(results.begin(),
104 results.end(),
105 sig.results.end() - results.size(),
106 [](const Type& produced, const Type& expected) {
107 return Type::isSubType(produced, expected);
108 });
109 if (!resultSuffixMatches) {
110 return false;
111 }
112 if (unreachable) {
113 // The unreachable can consume any additional provided params and produce
114 // any additional expected results, so we are done.
115 return true;
116 }
117 // Any additional provided params will pass through untouched, so they must be
118 // equivalent to the additional produced results.
119 return std::equal(sig.params.begin(),
120 sig.params.end() - params.size(),
121 sig.results.begin(),
122 sig.results.end() - results.size(),
123 [](const Type& produced, const Type& expected) {
124 return Type::isSubType(produced, expected);
125 });
126 }
127
operator +=(const StackSignature & next)128 StackSignature& StackSignature::operator+=(const StackSignature& next) {
129 assert(composes(next));
130 std::vector<Type> stack(results.begin(), results.end());
131 size_t required = next.params.size();
132 // Consume stack values according to next's parameters
133 if (stack.size() >= required) {
134 stack.resize(stack.size() - required);
135 } else {
136 if (!unreachable) {
137 // Prepend the unsatisfied params of `next` to the current params
138 size_t unsatisfied = required - stack.size();
139 std::vector<Type> newParams(next.params.begin(),
140 next.params.begin() + unsatisfied);
141 newParams.insert(newParams.end(), params.begin(), params.end());
142 params = Type(newParams);
143 }
144 stack.clear();
145 }
146 // Add stack values according to next's results
147 if (next.unreachable) {
148 results = next.results;
149 unreachable = true;
150 } else {
151 stack.insert(stack.end(), next.results.begin(), next.results.end());
152 results = Type(stack);
153 }
154 return *this;
155 }
156
operator +(const StackSignature & next) const157 StackSignature StackSignature::operator+(const StackSignature& next) const {
158 StackSignature sig = *this;
159 sig += next;
160 return sig;
161 }
162
StackFlow(Block * block)163 StackFlow::StackFlow(Block* block) {
164 // Encapsulates the logic for treating the block and its children
165 // uniformly. The end of the block is treated as if it consumed values
166 // corresponding to the its result type and produced no values, which is why
167 // the block's result type is used as the params of its processed stack
168 // signature.
169 auto processBlock = [&block](auto process) {
170 // TODO: Once we support block parameters, set up the stack by calling
171 // `process` before iterating through the block.
172 for (auto* expr : block->list) {
173 process(expr, StackSignature(expr));
174 }
175 bool unreachable = block->type == Type::unreachable;
176 Type params = unreachable ? Type::none : block->type;
177 process(block, StackSignature(params, Type::none, unreachable));
178 };
179
180 // We need to make an initial pass through the block to figure out how many
181 // values each unreachable instruction produces.
182 std::unordered_map<Expression*, size_t> producedByUnreachable;
183 {
184 size_t stackSize = 0;
185 size_t produced = 0;
186 Expression* lastUnreachable = nullptr;
187 processBlock([&](Expression* expr, const StackSignature sig) {
188 // Consume params
189 if (sig.params.size() > stackSize) {
190 // We need more values than are available, so they must come from the
191 // last unreachable.
192 assert(lastUnreachable);
193 produced += sig.params.size() - stackSize;
194 stackSize = 0;
195 } else {
196 stackSize -= sig.params.size();
197 }
198
199 // Handle unreachable or produce results
200 if (sig.unreachable) {
201 if (lastUnreachable) {
202 producedByUnreachable[lastUnreachable] = produced;
203 produced = 0;
204 }
205 assert(produced == 0);
206 lastUnreachable = expr;
207 stackSize = 0;
208 } else {
209 stackSize += sig.results.size();
210 }
211 });
212
213 // Finish record for final unreachable
214 if (lastUnreachable) {
215 producedByUnreachable[lastUnreachable] = produced;
216 }
217 }
218
219 // Take another pass through the block, recording srcs and dests.
220 std::vector<Location> values;
221 Expression* lastUnreachable = nullptr;
222 processBlock([&](Expression* expr, const StackSignature sig) {
223 assert((sig.params.size() <= values.size() || lastUnreachable) &&
224 "Block inputs not yet supported");
225
226 // Unreachable instructions consume all available values
227 size_t consumed = sig.unreachable
228 ? std::max(values.size(), sig.params.size())
229 : sig.params.size();
230
231 // We previously calculated how many values unreachable instructions produce
232 size_t produced =
233 sig.unreachable ? producedByUnreachable[expr] : sig.results.size();
234
235 srcs[expr] = std::vector<Location>(consumed);
236 dests[expr] = std::vector<Location>(produced);
237
238 // Figure out what kind of unreachable values we have
239 assert(sig.params.size() <= consumed);
240 size_t unreachableBeyondStack = 0;
241 size_t unreachableFromStack = 0;
242 if (consumed > values.size()) {
243 assert(consumed == sig.params.size());
244 unreachableBeyondStack = consumed - values.size();
245 } else if (consumed > sig.params.size()) {
246 assert(consumed == values.size());
247 unreachableFromStack = consumed - sig.params.size();
248 }
249
250 // Consume values
251 for (Index i = 0; i < consumed; ++i) {
252 if (i < unreachableBeyondStack) {
253 // This value comes from the polymorphic stack of the last unreachable
254 // because the stack did not have enough values to satisfy this
255 // instruction.
256 assert(lastUnreachable);
257 assert(producedByUnreachable[lastUnreachable] >=
258 unreachableBeyondStack);
259 Index destIndex =
260 producedByUnreachable[lastUnreachable] - unreachableBeyondStack + i;
261 Type type = sig.params[i];
262 srcs[expr][i] = {lastUnreachable, destIndex, type, true};
263 dests[lastUnreachable][destIndex] = {expr, i, type, false};
264 } else {
265 // A normal value from the value stack
266 bool unreachable = i < unreachableFromStack;
267 auto& src = values[values.size() + i - consumed];
268 srcs[expr][i] = src;
269 dests[src.expr][src.index] = {expr, i, src.type, unreachable};
270 }
271 }
272
273 // Update available values
274 if (unreachableBeyondStack) {
275 producedByUnreachable[lastUnreachable] -= unreachableBeyondStack;
276 values.resize(0);
277 } else {
278 values.resize(values.size() - consumed);
279 }
280
281 // Produce values
282 for (Index i = 0; i < sig.results.size(); ++i) {
283 values.push_back({expr, i, sig.results[i], false});
284 }
285
286 // Update the last unreachable instruction
287 if (sig.unreachable) {
288 assert(producedByUnreachable[lastUnreachable] == 0);
289 lastUnreachable = expr;
290 }
291 });
292 }
293
getSignature(Expression * expr)294 StackSignature StackFlow::getSignature(Expression* expr) {
295 auto exprSrcs = srcs.find(expr);
296 auto exprDests = dests.find(expr);
297 assert(exprSrcs != srcs.end() && exprDests != dests.end());
298 std::vector<Type> params, results;
299 for (auto& src : exprSrcs->second) {
300 params.push_back(src.type);
301 }
302 for (auto& dest : exprDests->second) {
303 results.push_back(dest.type);
304 }
305 bool unreachable = expr->type == Type::unreachable;
306 return StackSignature(Type(params), Type(results), unreachable);
307 }
308
309 } // namespace wasm
310