@@ -226,6 +226,9 @@ struct ContData {
226
226
// suspend).
227
227
Literals resumeArguments;
228
228
229
+ // If set, this is the exception to be thrown at the resume point.
230
+ Tag* exceptionTag = nullptr ;
231
+
229
232
// Whether we executed. Continuations are one-shot, so they may not be
230
233
// executed a second time.
231
234
bool executed = false ;
@@ -528,9 +531,8 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
528
531
}
529
532
if (!hasValue) {
530
533
// We must execute this instruction. Set up the logic to note the values
531
- // of children (we mainly need this for non-control flow structures,
532
- // but even control flow ones must add a scope on the value stack, to
533
- // not confuse the others).
534
+ // of children. TODO: as an optimization, we could avoid this for
535
+ // control flow structures, at the cost of more complexity
534
536
StackValueNoter noter (this );
535
537
536
538
if (Properties::isControlFlowStructure (curr)) {
@@ -592,12 +594,16 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
592
594
// values on the stack, not values sent on a break/suspend; suspending is
593
595
// handled above).
594
596
if (!ret.breaking () && ret.getType ().isConcrete ()) {
595
- assert (!valueStack.empty ());
596
- auto & values = valueStack.back ();
597
- values.push_back (ret.values );
597
+ // The value stack may be empty, if we lack a parent that needs our
598
+ // value. That is the case when we are the toplevel expression, etc.
599
+ if (!valueStack.empty ()) {
600
+ auto & values = valueStack.back ();
601
+ values.push_back (ret.values );
598
602
#if WASM_INTERPRETER_DEBUG
599
- std::cout << indent () << " added to valueStack: " << ret.values << ' \n ' ;
603
+ std::cout << indent () << " added to valueStack: " << ret.values
604
+ << ' \n ' ;
600
605
#endif
606
+ }
601
607
}
602
608
}
603
609
@@ -4863,6 +4869,12 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4863
4869
// restoredValues map.
4864
4870
assert (currContinuation->resumeInfo .empty ());
4865
4871
assert (self ()->restoredValuesMap .empty ());
4872
+ // Throw, if we were resumed by resume_throw;
4873
+ if (auto * tag = currContinuation->exceptionTag ) {
4874
+ // XXX tag->name lacks cross-module support
4875
+ throwException (WasmException{
4876
+ self ()->makeExnData (tag->name , currContinuation->resumeArguments )});
4877
+ }
4866
4878
return currContinuation->resumeArguments ;
4867
4879
}
4868
4880
@@ -4895,7 +4907,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4895
4907
new_->resumeExpr = curr;
4896
4908
return Flow (SUSPEND_FLOW, tag, std::move (arguments));
4897
4909
}
4898
- Flow visitResume (Resume * curr) {
4910
+ template < typename T> Flow doResume (T * curr, Tag* exceptionTag = nullptr ) {
4899
4911
Literals arguments;
4900
4912
Flow flow = self ()->generateArguments (curr->operands , arguments);
4901
4913
if (flow.breaking ()) {
@@ -4923,6 +4935,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4923
4935
// the immediate ones here. TODO
4924
4936
contData->resumeArguments = arguments;
4925
4937
}
4938
+ contData->exceptionTag = exceptionTag;
4926
4939
self ()->pushCurrContinuation (contData);
4927
4940
self ()->continuationStore ->resuming = true ;
4928
4941
#if WASM_INTERPRETER_DEBUG
@@ -4931,15 +4944,8 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4931
4944
#endif
4932
4945
}
4933
4946
4934
- Flow ret;
4935
- {
4936
- // Create a stack value scope. This ensures that we always have a scope,
4937
- // and so the code that pushes/pops doesn't need to check if a scope
4938
- // exists. (We do not need the values in this scope, of course, as no
4939
- // expression is above them, so we cannot suspend and need these values).
4940
- typename ExpressionRunner<SubType>::StackValueNoter noter (this );
4941
- ret = func.getFuncData ()->doCall (arguments);
4942
- }
4947
+ Flow ret = func.getFuncData ()->doCall (arguments);
4948
+
4943
4949
#if WASM_INTERPRETER_DEBUG
4944
4950
if (!self ()->isResuming ()) {
4945
4951
std::cout << self ()->indent () << " finished resuming, with " << ret
@@ -4995,7 +5001,11 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4995
5001
// No suspension; all done.
4996
5002
return ret;
4997
5003
}
4998
- Flow visitResumeThrow (ResumeThrow* curr) { return Flow (NONCONSTANT_FLOW); }
5004
+ Flow visitResume (Resume* curr) { return doResume (curr); }
5005
+ Flow visitResumeThrow (ResumeThrow* curr) {
5006
+ // TODO: should the Resume and ResumeThrow classes be merged?
5007
+ return doResume (curr, self ()->getModule ()->getTag (curr->tag ));
5008
+ }
4999
5009
Flow visitStackSwitch (StackSwitch* curr) { return Flow (NONCONSTANT_FLOW); }
5000
5010
5001
5011
void trap (const char * why) override {
@@ -5058,14 +5068,20 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
5058
5068
5059
5069
if (self ()->isResuming ()) {
5060
5070
// The arguments are in the continuation data.
5061
- arguments = self ()->getCurrContinuation ()->resumeArguments ;
5071
+ auto currContinuation = self ()->getCurrContinuation ();
5072
+ arguments = currContinuation->resumeArguments ;
5062
5073
5063
- if (!self ()-> getCurrContinuation () ->resumeExpr ) {
5074
+ if (!currContinuation ->resumeExpr ) {
5064
5075
// This is the first time we resume, that is, there is no suspend which
5065
5076
// is the resume expression that we need to execute up to. All we need
5066
5077
// to do is just start calling this function (with the arguments we've
5067
- // set), so resuming is done
5078
+ // set), so resuming is done. (And throw, if resume_throw.)
5068
5079
self ()->continuationStore ->resuming = false ;
5080
+ if (auto * tag = currContinuation->exceptionTag ) {
5081
+ // XXX tag->name lacks cross-module support
5082
+ throwException (WasmException{
5083
+ self ()->makeExnData (tag->name , currContinuation->resumeArguments )});
5084
+ }
5069
5085
}
5070
5086
}
5071
5087
0 commit comments