@@ -411,27 +411,57 @@ struct IRBuilder::ChildPopper
411
411
Result<> popConstrainedChildren (std::vector<Child>& children) {
412
412
auto & scope = builder.getScope ();
413
413
414
- // Two-part indices into the stack of available expressions and the vector
415
- // of requirements, allowing them to move independently with the granularity
416
- // of a single tuple element.
417
- size_t stackIndex = scope.exprStack .size ();
418
- size_t stackTupleIndex = 0 ;
419
- size_t childIndex = children.size ();
420
- size_t childTupleIndex = 0 ;
421
-
422
- // The index of the shallowest unreachable instruction on the stack.
414
+ // The index of the shallowest unreachable instruction on the stack, found
415
+ // by checkNeedsUnreachableFallback.
423
416
std::optional<size_t > unreachableIndex;
424
417
425
418
// Whether popping the children past the unreachable would produce a type
426
419
// mismatch or try to pop from an empty stack.
427
420
bool needUnreachableFallback = false ;
428
421
429
- if (!scope.unreachable ) {
430
- // We only need to check requirements if there is an unreachable.
431
- // Otherwise the validator will catch any problems.
432
- goto pop;
422
+ // We only need to check requirements if there is an unreachable.
423
+ // Otherwise the validator will catch any problems.
424
+ if (scope.unreachable ) {
425
+ needUnreachableFallback =
426
+ checkNeedsUnreachableFallback (children, unreachableIndex);
433
427
}
434
428
429
+ // We have checked all the constraints, so we are ready to pop children.
430
+ for (int i = children.size () - 1 ; i >= 0 ; --i) {
431
+ if (needUnreachableFallback &&
432
+ scope.exprStack .size () == *unreachableIndex + 1 && i > 0 ) {
433
+ // The next item on the stack is the unreachable instruction we must
434
+ // not pop past. We cannot insert unreachables in front of it because
435
+ // it might be a branch we actually have to execute, so this next item
436
+ // must be child 0. But we are not ready to pop child 0 yet, so
437
+ // synthesize an unreachable instead of popping. The deeper
438
+ // instructions that would otherwise have been popped will remain on
439
+ // the stack to become prior children of future expressions or to be
440
+ // implicitly dropped at the end of the scope.
441
+ *children[i].childp = builder.builder .makeUnreachable ();
442
+ continue ;
443
+ }
444
+
445
+ // Pop a child normally.
446
+ auto val = pop (children[i].constraint .size ());
447
+ CHECK_ERR (val);
448
+ *children[i].childp = *val;
449
+ }
450
+ return Ok{};
451
+ }
452
+
453
+ bool checkNeedsUnreachableFallback (const std::vector<Child>& children,
454
+ std::optional<size_t >& unreachableIndex) {
455
+ auto & scope = builder.getScope ();
456
+
457
+ // Two-part indices into the stack of available expressions and the vector
458
+ // of requirements, allowing them to move independently with the granularity
459
+ // of a single tuple element.
460
+ size_t stackIndex = scope.exprStack .size ();
461
+ size_t stackTupleIndex = 0 ;
462
+ size_t childIndex = children.size ();
463
+ size_t childTupleIndex = 0 ;
464
+
435
465
// Check whether the values on the stack will be able to meet the given
436
466
// requirements.
437
467
while (true ) {
@@ -458,8 +488,7 @@ struct IRBuilder::ChildPopper
458
488
// the input unreachable instruction is executed first. If we are
459
489
// not reaching past an unreachable, the error will be caught when
460
490
// we pop.
461
- needUnreachableFallback = true ;
462
- goto pop;
491
+ return true ;
463
492
}
464
493
--stackIndex;
465
494
stackTupleIndex = scope.exprStack [stackIndex]->type .size () - 1 ;
@@ -483,13 +512,11 @@ struct IRBuilder::ChildPopper
483
512
// Always succeeds.
484
513
} else if (constraint.isAnyReference ()) {
485
514
if (!type.isRef () && type != Type::unreachable) {
486
- needUnreachableFallback = true ;
487
- break ;
515
+ return true ;
488
516
}
489
517
} else if (auto bound = constraint.getSubtype ()) {
490
518
if (!Type::isSubType (type, *bound)) {
491
- needUnreachableFallback = true ;
492
- break ;
519
+ return true ;
493
520
}
494
521
} else if (constraint.isAnyI8ArrayReference ()) {
495
522
bool isI8Array =
@@ -498,8 +525,7 @@ struct IRBuilder::ChildPopper
498
525
bool isNone =
499
526
type.isRef () && type.getHeapType ().isMaybeShared (HeapType::none);
500
527
if (!isI8Array && !isNone && type != Type::unreachable) {
501
- needUnreachableFallback = true ;
502
- break ;
528
+ return true ;
503
529
}
504
530
} else if (constraint.isAnyI16ArrayReference ()) {
505
531
bool isI16Array =
@@ -508,8 +534,7 @@ struct IRBuilder::ChildPopper
508
534
bool isNone =
509
535
type.isRef () && type.getHeapType ().isMaybeShared (HeapType::none);
510
536
if (!isI16Array && !isNone && type != Type::unreachable) {
511
- needUnreachableFallback = true ;
512
- break ;
537
+ return true ;
513
538
}
514
539
} else {
515
540
WASM_UNREACHABLE (" unexpected constraint" );
@@ -518,34 +543,10 @@ struct IRBuilder::ChildPopper
518
543
519
544
// No problems for children after this unreachable.
520
545
if (type == Type::unreachable) {
521
- assert (!needUnreachableFallback);
522
546
unreachableIndex = stackIndex;
523
547
}
524
548
}
525
-
526
- pop:
527
- // We have checked all the constraints, so we are ready to pop children.
528
- for (int i = children.size () - 1 ; i >= 0 ; --i) {
529
- if (needUnreachableFallback &&
530
- scope.exprStack .size () == *unreachableIndex + 1 && i > 0 ) {
531
- // The next item on the stack is the unreachable instruction we must
532
- // not pop past. We cannot insert unreachables in front of it because
533
- // it might be a branch we actually have to execute, so this next item
534
- // must be child 0. But we are not ready to pop child 0 yet, so
535
- // synthesize an unreachable instead of popping. The deeper
536
- // instructions that would otherwise have been popped will remain on
537
- // the stack to become prior children of future expressions or to be
538
- // implicitly dropped at the end of the scope.
539
- *children[i].childp = builder.builder .makeUnreachable ();
540
- continue ;
541
- }
542
-
543
- // Pop a child normally.
544
- auto val = pop (children[i].constraint .size ());
545
- CHECK_ERR (val);
546
- *children[i].childp = *val;
547
- }
548
- return Ok{};
549
+ return false ;
549
550
}
550
551
551
552
Result<Expression*> pop (size_t size) {
0 commit comments