@@ -226,6 +226,9 @@ struct ContData {
226226 // suspend).
227227 Literals resumeArguments;
228228
229+ // If set, this is the exception to be thrown at the resume point.
230+ Tag* exceptionTag = nullptr ;
231+
229232 // Whether we executed. Continuations are one-shot, so they may not be
230233 // executed a second time.
231234 bool executed = false ;
@@ -528,9 +531,8 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
528531 }
529532 if (!hasValue) {
530533 // 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
534536 StackValueNoter noter (this );
535537
536538 if (Properties::isControlFlowStructure (curr)) {
@@ -592,12 +594,16 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
592594 // values on the stack, not values sent on a break/suspend; suspending is
593595 // handled above).
594596 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 );
598602#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 ' ;
600605#endif
606+ }
601607 }
602608 }
603609
@@ -4863,6 +4869,12 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
48634869 // restoredValues map.
48644870 assert (currContinuation->resumeInfo .empty ());
48654871 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+ }
48664878 return currContinuation->resumeArguments ;
48674879 }
48684880
@@ -4895,7 +4907,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
48954907 new_->resumeExpr = curr;
48964908 return Flow (SUSPEND_FLOW, tag, std::move (arguments));
48974909 }
4898- Flow visitResume (Resume * curr) {
4910+ template < typename T> Flow doResume (T * curr, Tag* exceptionTag = nullptr ) {
48994911 Literals arguments;
49004912 Flow flow = self ()->generateArguments (curr->operands , arguments);
49014913 if (flow.breaking ()) {
@@ -4923,6 +4935,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
49234935 // the immediate ones here. TODO
49244936 contData->resumeArguments = arguments;
49254937 }
4938+ contData->exceptionTag = exceptionTag;
49264939 self ()->pushCurrContinuation (contData);
49274940 self ()->continuationStore ->resuming = true ;
49284941#if WASM_INTERPRETER_DEBUG
@@ -4931,15 +4944,8 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
49314944#endif
49324945 }
49334946
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+
49434949#if WASM_INTERPRETER_DEBUG
49444950 if (!self ()->isResuming ()) {
49454951 std::cout << self ()->indent () << " finished resuming, with " << ret
@@ -4995,7 +5001,11 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
49955001 // No suspension; all done.
49965002 return ret;
49975003 }
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+ }
49995009 Flow visitStackSwitch (StackSwitch* curr) { return Flow (NONCONSTANT_FLOW); }
50005010
50015011 void trap (const char * why) override {
@@ -5058,14 +5068,20 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
50585068
50595069 if (self ()->isResuming ()) {
50605070 // The arguments are in the continuation data.
5061- arguments = self ()->getCurrContinuation ()->resumeArguments ;
5071+ auto currContinuation = self ()->getCurrContinuation ();
5072+ arguments = currContinuation->resumeArguments ;
50625073
5063- if (!self ()-> getCurrContinuation () ->resumeExpr ) {
5074+ if (!currContinuation ->resumeExpr ) {
50645075 // This is the first time we resume, that is, there is no suspend which
50655076 // is the resume expression that we need to execute up to. All we need
50665077 // 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.)
50685079 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+ }
50695085 }
50705086 }
50715087
0 commit comments