171
171
// calls, so that you know when to start an asynchronous operation and
172
172
// when to propagate results back.
173
173
//
174
- // * asyncify_get_catch_counter(): call this to get the current value of the
175
- // internal "__asyncify_catch_counter" variable (only when assertions
176
- // or ignore mode are enabled).
177
- //
178
- // * asyncify_get_catch_counter(): call this to get the current value of the
179
- // internal "__asyncify_catch_counter" variable (only when assertions
180
- // or ignore mode are enabled).
181
- //
182
- // These six functions are exported so that you can call them from the
174
+ // These five functions are exported so that you can call them from the
183
175
// outside. If you want to manage things from inside the wasm, then you
184
176
// couldn't have called them before they were created by this pass. To work
185
177
// around that, you can create imports to asyncify.start_unwind,
256
248
//
257
249
// --pass-arg=asyncify-ignore-unwind-from-catch
258
250
//
259
- // This enables extra check before unwind, if it called from within catch
260
- // block then it silently ignored (-fwasm-exceptions support)
251
+ // This enables additional check to be performed before unwinding. In
252
+ // cases where the unwind operation is triggered from the catch block,
253
+ // it will be silently ignored (-fwasm-exceptions support)
261
254
//
262
255
// --pass-arg=asyncify-verbose
263
256
//
335
328
#include " ir/memory-utils.h"
336
329
#include " ir/module-utils.h"
337
330
#include " ir/names.h"
331
+ #include " ir/parents.h"
338
332
#include " ir/utils.h"
339
333
#include " pass.h"
340
334
#include " support/file.h"
@@ -1166,15 +1160,17 @@ struct AsyncifyFlow : public Pass {
1166
1160
// here as well.
1167
1161
results.push_back (makeCallSupport (curr));
1168
1162
continue ;
1169
- } else if (auto * iTry = curr->dynCast <Try>()) {
1163
+ } else if (auto * try_ = curr->dynCast <Try>()) {
1170
1164
if (item.phase == Work::Scan) {
1171
1165
work.push_back (Work{curr, Work::Finish});
1172
- work.push_back (Work{iTry->body , Work::Scan});
1166
+ work.push_back (Work{try_->body , Work::Scan});
1167
+ // catchBodies are ignored because we assume that pause/resume will
1168
+ // not happen inside them
1173
1169
continue ;
1174
1170
}
1175
- iTry ->body = results.back ();
1171
+ try_ ->body = results.back ();
1176
1172
results.pop_back ();
1177
- results.push_back (iTry );
1173
+ results.push_back (try_ );
1178
1174
continue ;
1179
1175
}
1180
1176
// We must handle all control flow above, and all things that can change
@@ -1256,7 +1252,8 @@ struct AsyncifyFlow : public Pass {
1256
1252
}
1257
1253
};
1258
1254
1259
- // Add catch block counters to verify that unwind is not called from catch block
1255
+ // Add catch block counters to verify that unwind is not called from catch
1256
+ // block.
1260
1257
struct AsyncifyAddCatchCounters : public Pass {
1261
1258
bool isFunctionParallel () override { return true ; }
1262
1259
@@ -1281,80 +1278,43 @@ struct AsyncifyAddCatchCounters : public Pass {
1281
1278
makeBinary (SubInt32,
1282
1279
makeGlobalGet (ASYNCIFY_CATCH_COUNTER, Type::i32),
1283
1280
makeConst (int32_t (amount))));
1284
- };
1285
- };
1286
- CountersBuilder builder (*module_);
1287
- BranchUtils::BranchTargets branchTargets (func->body );
1288
-
1289
- // with this walker we will assign count of enclosing catch block to
1290
- // each expression
1291
- // ... - 0
1292
- // catch
1293
- // ... - 1
1294
- // catch
1295
- // ... - 2
1296
- std::unordered_map<Expression*, int > expressionCatchCount;
1297
- struct NestedLevelWalker
1298
- : public PostWalker<NestedLevelWalker,
1299
- UnifiedExpressionVisitor<NestedLevelWalker>> {
1300
- std::unordered_map<Expression*, int >* expressionCatchCount;
1301
- int catchCount = 0 ;
1302
-
1303
- static void doStartCatch (NestedLevelWalker* self, Expression** currp) {
1304
- self->catchCount ++;
1305
- }
1306
-
1307
- static void doEndCatch (NestedLevelWalker* self, Expression** currp) {
1308
- self->catchCount --;
1309
- }
1310
-
1311
- static void scan (NestedLevelWalker* self, Expression** currp) {
1312
- auto curr = *currp;
1313
- if (curr->_id == Expression::Id::TryId) {
1314
- self->expressionCatchCount ->insert (
1315
- std::make_pair<>(curr, self->catchCount ));
1316
- auto & catchBodies = curr->cast <Try>()->catchBodies ;
1317
- for (Index i = 0 ; i < catchBodies.size (); i++) {
1318
- self->expressionCatchCount ->insert (
1319
- std::make_pair<>(catchBodies[i], self->catchCount ));
1320
- self->pushTask (doEndCatch, currp);
1321
- self->pushTask (NestedLevelWalker::scan, &catchBodies[i]);
1322
- self->pushTask (doStartCatch, currp);
1323
- }
1324
- self->pushTask (NestedLevelWalker::scan, &curr->cast <Try>()->body );
1325
- return ;
1326
- }
1327
-
1328
- PostWalker<NestedLevelWalker,
1329
- UnifiedExpressionVisitor<NestedLevelWalker>>::scan (self,
1330
- currp);
1331
- }
1332
-
1333
- void visitExpression (Expression* curr) {
1334
- expressionCatchCount->insert (std::make_pair<>(curr, catchCount));
1335
1281
}
1336
1282
};
1337
- NestedLevelWalker nestedLevelWalker;
1338
- nestedLevelWalker.expressionCatchCount = &expressionCatchCount;
1339
- nestedLevelWalker.walk (func->body );
1340
1283
1341
1284
// with this walker we will handle those changes of counter:
1342
- // - entering into catch (= pop) +1
1343
- // - return -1
1344
- // - break -1
1345
- // - exiting from catch -1
1285
+ // - entering top-level catch (= pop) +1
1286
+ // - entering nested catch (= pop) 0 (ignored)
1287
+ //
1288
+ // - return inside top-level/nested catch -1
1289
+ // - return outside top-level/nested catch 0 (ignored)
1290
+ //
1291
+ // - break target outside of top-level catch -1
1292
+ // - break target inside of top-level catch 0 (ignored)
1293
+ // - break outside top-level/nested catch 0 (ignored)
1294
+ //
1295
+ // - exiting from top-level catch -1
1296
+ // - exiting from nested catch 0 (ignored)
1346
1297
struct AddCountersWalker : public PostWalker <AddCountersWalker> {
1347
1298
Function* func;
1348
1299
CountersBuilder* builder;
1349
1300
BranchUtils::BranchTargets* branchTargets;
1350
- std::unordered_map<Expression*, int >* expressionCatchCount ;
1301
+ Parents* parents ;
1351
1302
int finallyNum = 0 ;
1352
1303
int popNum = 0 ;
1353
1304
1354
1305
int getCatchCount (Expression* expression) {
1355
- auto it = expressionCatchCount->find (expression);
1356
- assert (it != expressionCatchCount->end ());
1357
- return it->second ;
1306
+ int catchCount = 0 ;
1307
+ while (expression != func->body ) {
1308
+ auto parent = parents->getParent (expression);
1309
+ if (auto * try_ = parent->dynCast <Try>()) {
1310
+ if (try_->body != expression) {
1311
+ catchCount++;
1312
+ }
1313
+ }
1314
+ expression = parent;
1315
+ }
1316
+
1317
+ return catchCount;
1358
1318
}
1359
1319
1360
1320
// Each catch block except catch_all should have pop instruction
@@ -1473,11 +1433,15 @@ struct AsyncifyAddCatchCounters : public Pass {
1473
1433
}
1474
1434
};
1475
1435
1436
+ Parents parents (func->body );
1437
+ CountersBuilder builder (*module_);
1438
+ BranchUtils::BranchTargets branchTargets (func->body );
1439
+
1476
1440
AddCountersWalker addCountersWalker;
1477
1441
addCountersWalker.func = func;
1478
1442
addCountersWalker.builder = &builder;
1479
1443
addCountersWalker.branchTargets = &branchTargets;
1480
- addCountersWalker.expressionCatchCount = &expressionCatchCount ;
1444
+ addCountersWalker.parents = &parents ;
1481
1445
addCountersWalker.walk (func->body );
1482
1446
1483
1447
EHUtils::handleBlockNestedPops (func, *module_);
0 commit comments