@@ -230,6 +230,7 @@ struct ContData {
230
230
// executed a second time.
231
231
bool executed = false ;
232
232
233
+ ContData () {}
233
234
ContData (Literal func, HeapType type) : func(func), type(type) {}
234
235
};
235
236
@@ -338,8 +339,14 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
338
339
339
340
#if WASM_INTERPRETER_DEBUG
340
341
std::string indent () {
341
- std::string ret =
342
- ' [' + std::to_string (reinterpret_cast <size_t >(this )) + " ] " ;
342
+ std::string id;
343
+ if (auto * module = getModule ()) {
344
+ id = module ->name .toString ();
345
+ }
346
+ if (id.empty ()) {
347
+ id = std::to_string (reinterpret_cast <size_t >(this ));
348
+ }
349
+ auto ret = ' [' + id + " ] " ;
343
350
for (Index i = 0 ; i < depth; i++) {
344
351
ret += ' ' ;
345
352
}
@@ -581,9 +588,10 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
581
588
// Outside the scope of StackValueNoter, the scope of our own child values
582
589
// has been removed (we don't need those values any more). What is now on
583
590
// the top of |valueStack| is the list of child values of our parent,
584
- // which is the place our own value can go, if we have one (and if we are
585
- // not suspending - suspending is handled above).
586
- if (!ret.suspendTag && ret.getType ().isConcrete ()) {
591
+ // which is the place our own value can go, if we have one (we only save
592
+ // values on the stack, not values sent on a break/suspend; suspending is
593
+ // handled above).
594
+ if (!ret.breaking () && ret.getType ().isConcrete ()) {
587
595
assert (!valueStack.empty ());
588
596
auto & values = valueStack.back ();
589
597
values.push_back (ret.values );
@@ -4841,6 +4849,10 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4841
4849
}
4842
4850
4843
4851
if (self ()->isResuming ()) {
4852
+ #if WASM_INTERPRETER_DEBUG
4853
+ std::cout << self ()->indent ()
4854
+ << " returned to suspend; continuing normally\n " ;
4855
+ #endif
4844
4856
// This is a resume, so we have found our way back to where we
4845
4857
// suspended.
4846
4858
auto currContinuation = self ()->getCurrContinuation ();
@@ -4864,12 +4876,15 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4864
4876
if (!old) {
4865
4877
return Flow (SUSPEND_FLOW, tag, std::move (arguments));
4866
4878
}
4867
- // An old one exists, so we can create a proper new one.
4868
4879
assert (old->executed );
4869
- auto new_ = std::make_shared<ContData>(old->func , old->type );
4880
+ // An old one exists, so we can create a proper new one. It starts out
4881
+ // empty here, and as we unwind, info will be added to it (and the function
4882
+ // to resume as well, once we find the right resume handler).
4883
+ //
4870
4884
// Note we cannot update the type yet, so it will be wrong in debug
4871
4885
// logging. To update it, we must find the block that receives this value,
4872
4886
// which means we cannot do it here (we don't even know what that block is).
4887
+ auto new_ = std::make_shared<ContData>();
4873
4888
4874
4889
// Switch to the new continuation, so that as we unwind, we will save the
4875
4890
// information we need to resume it later in the proper place.
@@ -4893,22 +4908,29 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4893
4908
4894
4909
// Get and execute the continuation.
4895
4910
auto contData = flow.getSingleValue ().getContData ();
4896
- if (contData->executed ) {
4897
- trap (" continuation already executed" );
4898
- }
4899
- contData->executed = true ;
4900
- if (contData->resumeArguments .empty ()) {
4901
- // The continuation has no bound arguments. For now, we just handle the
4902
- // simple case of binding all of them, so that means we can just use all
4903
- // the immediate ones here. TODO
4904
- contData->resumeArguments = arguments;
4905
- }
4906
4911
auto func = contData->func ;
4907
- self ()->pushCurrContinuation (contData);
4908
- self ()->continuationStore ->resuming = true ;
4912
+
4913
+ // If we are resuming a nested suspend then we should just rewind the call
4914
+ // stack, and therefore do not change or test the state here.
4915
+ if (!self ()->isResuming ()) {
4916
+ if (contData->executed ) {
4917
+ trap (" continuation already executed" );
4918
+ }
4919
+ contData->executed = true ;
4920
+ if (contData->resumeArguments .empty ()) {
4921
+ // The continuation has no bound arguments. For now, we just handle the
4922
+ // simple case of binding all of them, so that means we can just use all
4923
+ // the immediate ones here. TODO
4924
+ contData->resumeArguments = arguments;
4925
+ }
4926
+ self ()->pushCurrContinuation (contData);
4927
+ self ()->continuationStore ->resuming = true ;
4909
4928
#if WASM_INTERPRETER_DEBUG
4910
- std::cout << self ()->indent () << " resuming func " << func.getFunc () << ' \n ' ;
4929
+ std::cout << self ()->indent () << " resuming func " << func.getFunc ()
4930
+ << ' \n ' ;
4911
4931
#endif
4932
+ }
4933
+
4912
4934
Flow ret;
4913
4935
{
4914
4936
// Create a stack value scope. This ensures that we always have a scope,
@@ -4919,7 +4941,10 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4919
4941
ret = func.getFuncData ()->doCall (arguments);
4920
4942
}
4921
4943
#if WASM_INTERPRETER_DEBUG
4922
- std::cout << self ()->indent () << " finished resuming, with " << ret << ' \n ' ;
4944
+ if (!self ()->isResuming ()) {
4945
+ std::cout << self ()->indent () << " finished resuming, with " << ret
4946
+ << ' \n ' ;
4947
+ }
4923
4948
#endif
4924
4949
if (!ret.suspendTag ) {
4925
4950
// No suspension: the coroutine finished normally. Mark it as no longer
@@ -4953,11 +4978,14 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
4953
4978
assert (finder.type .isConcrete ());
4954
4979
assert (finder.type .size () >= 1 );
4955
4980
// The continuation is the final value/type there.
4956
- auto cont = self ()->getCurrContinuation ();
4957
- cont->type = finder.type [finder.type .size () - 1 ].getHeapType ();
4981
+ auto newCont = self ()->getCurrContinuation ();
4982
+ newCont->type = finder.type [finder.type .size () - 1 ].getHeapType ();
4983
+ // And we can set the function to be called, to resume it from here
4984
+ // (the same function we called, that led to a suspension).
4985
+ newCont->func = contData->func ;
4958
4986
// Add the continuation as the final value being sent.
4959
- ret.values .push_back (Literal (cont ));
4960
- // We are not longer processing that continuation.
4987
+ ret.values .push_back (Literal (newCont ));
4988
+ // We are no longer processing that continuation.
4961
4989
self ()->popCurrContinuation ();
4962
4990
return ret;
4963
4991
}
@@ -5067,9 +5095,17 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
5067
5095
for (Index i = 0 ; i < scope.locals .size (); i++) {
5068
5096
auto l = scope.locals .size () - 1 - i;
5069
5097
scope.locals [l] = self ()->popResumeEntry (" function" );
5070
- // Must have restored valid data.
5071
- assert (Type::isSubType (scope.locals [l].getType (),
5072
- function->getLocalType (l)));
5098
+ #ifndef NDEBUG
5099
+ // Must have restored valid data. The type must match the local's
5100
+ // type, except for the case of a non-nullable local that has not yet
5101
+ // been accessed: that will contain a null (but the wasm type system
5102
+ // ensures it will not be read by code, until a non-null value is
5103
+ // assigned).
5104
+ auto value = scope.locals [l];
5105
+ auto localType = function->getLocalType (l);
5106
+ assert (Type::isSubType (value.getType (), localType) ||
5107
+ value == Literal::makeZeros (localType));
5108
+ #endif
5073
5109
}
5074
5110
}
5075
5111
0 commit comments