Skip to content

Commit f6d887e

Browse files
authored
[Stack Switching] Implement the simple case of cont.bind (#7793)
In the simple case, all parameters are bound. This is common in the spec test, and will unblock a bunch of stuff there. To do this, add full support for resuming without a resume expression, that is, without a Suspend that we need to unwind back to. Specifically, when we resume a bound continuation, the only thing we need to do when resuming is to apply the bound arguments, and then continue execution normally.
1 parent 6986a9b commit f6d887e

File tree

4 files changed

+88
-12
lines changed

4 files changed

+88
-12
lines changed

src/wasm-interpreter.h

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3499,9 +3499,10 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
34993499
parent.functionStack.push_back(function->name);
35003500
locals.resize(function->getNumLocals());
35013501

3502-
if (parent.resuming) {
3503-
// Nothing more to do here: we are resuming execution, so there is old
3504-
// locals state that will be restored.
3502+
if (parent.resuming && parent.currContinuation->resumeExpr) {
3503+
// Nothing more to do here: we are resuming execution to some
3504+
// suspended expression (resumeExpr), so there is old locals state that
3505+
// will be restored.
35053506
return;
35063507
}
35073508

@@ -4709,7 +4710,29 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
47094710
Name func = funcFlow.getSingleValue().getFunc();
47104711
return Literal(std::make_shared<ContData>(func, curr->type.getHeapType()));
47114712
}
4712-
Flow visitContBind(ContBind* curr) { return Flow(NONCONSTANT_FLOW); }
4713+
Flow visitContBind(ContBind* curr) {
4714+
Literals arguments;
4715+
Flow flow = self()->generateArguments(curr->operands, arguments);
4716+
if (flow.breaking()) {
4717+
return flow;
4718+
}
4719+
Flow cont = self()->visit(curr->cont);
4720+
if (cont.breaking()) {
4721+
return cont;
4722+
}
4723+
4724+
// Create a new continuation, copying the old but with the new type +
4725+
// arguments.
4726+
auto old = cont.getSingleValue().getContData();
4727+
auto newData = *old;
4728+
newData.type = curr->type.getHeapType();
4729+
newData.resumeArguments = arguments;
4730+
// We handle only the simple case of applying all parameters, for now. TODO
4731+
assert(old->resumeArguments.empty());
4732+
// The old one is done.
4733+
old->executed = true;
4734+
return Literal(std::make_shared<ContData>(newData));
4735+
}
47134736
Flow visitSuspend(Suspend* curr) {
47144737
// Process the arguments, whether or not we are resuming. If we are resuming
47154738
// then we don't need these values (we sent them as part of the suspension),
@@ -4771,15 +4794,15 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
47714794
trap("continuation already executed");
47724795
}
47734796
contData->executed = true;
4774-
contData->resumeArguments = arguments;
4797+
if (contData->resumeArguments.empty()) {
4798+
// The continuation has no bound arguments. For now, we just handle the
4799+
// simple case of binding all of them, so that means we can just use all
4800+
// the immediate ones here. TODO
4801+
contData->resumeArguments = arguments;
4802+
}
47754803
Name func = contData->func;
47764804
self()->currContinuation = contData;
4777-
if (contData->resumeExpr) {
4778-
// There is an expression to resume execution at, so this is not the first
4779-
// time we run this function. Mark us as resuming, until we reach that
4780-
// expression.
4781-
self()->resuming = true;
4782-
}
4805+
self()->resuming = true;
47834806
#if WASM_INTERPRETER_DEBUG
47844807
std::cout << self()->indent() << "resuming func " << func << '\n';
47854808
#endif
@@ -4897,6 +4920,19 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
48974920
hostLimit("stack limit");
48984921
}
48994922

4923+
if (self()->resuming) {
4924+
// The arguments are in the continuation data.
4925+
arguments = self()->currContinuation->resumeArguments;
4926+
4927+
if (!self()->currContinuation->resumeExpr) {
4928+
// This is the first time we resume, that is, there is no suspend which
4929+
// is the resume expression that we need to execute up to. All we need
4930+
// to do is just start calling this function (with the arguments we've
4931+
// set), so resuming is done
4932+
self()->resuming = false;
4933+
}
4934+
}
4935+
49004936
Flow flow;
49014937
std::optional<Type> resultType;
49024938

src/wasm/literal.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
740740
if (!data->resumeInfo.empty()) {
741741
o << " |resumeInfo|=" << data->resumeInfo.size();
742742
}
743+
if (!data->resumeArguments.empty()) {
744+
o << " resumeArguments=" << data->resumeArguments;
745+
}
743746
o << " executed=" << data->executed << ')';
744747
} else {
745748
assert(literal.isData());

test/lit/exec/cont_simple.wast

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,4 +807,41 @@
807807
(cont.new $k (ref.func $call_ref))
808808
)
809809
)
810+
811+
(func $bind (param $x i32)
812+
;; Test that cont.bind works when used on this.
813+
(suspend $more)
814+
(call $log (local.get $x))
815+
(suspend $more)
816+
;; Test that the bound arguments do not get applied to child calls.
817+
(call $bind-child
818+
(i32.const 1337)
819+
)
820+
(suspend $more)
821+
)
822+
823+
(func $bind-child (param $x i32)
824+
(suspend $more)
825+
(call $log (local.get $x))
826+
(suspend $more)
827+
)
828+
829+
;; CHECK: [fuzz-exec] calling run-bind
830+
;; CHECK-NEXT: [LoggingExternalInterface logging 100]
831+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
832+
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
833+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
834+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
835+
;; CHECK-NEXT: [LoggingExternalInterface logging 1337]
836+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
837+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
838+
;; CHECK-NEXT: [LoggingExternalInterface logging 300]
839+
(func $run-bind (export "run-bind")
840+
(call $run
841+
(cont.bind $k-get-i32 $k
842+
(i32.const 42)
843+
(cont.new $k-get-i32 (ref.func $bind))
844+
)
845+
)
846+
)
810847
)

test/spec/cont.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
(assert_trap (invoke "non-linear-1") "continuation already consumed")
131131
(assert_trap (invoke "non-linear-2") "continuation already consumed")
132132
(assert_trap (invoke "non-linear-3") "continuation already consumed")
133-
;; TODO: cont.bind (assert_trap (invoke "non-linear-4") "continuation already consumed")
133+
(assert_trap (invoke "non-linear-4") "continuation already consumed")
134134

135135
(assert_invalid
136136
(module

0 commit comments

Comments
 (0)