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