Skip to content

Commit 8ad26cb

Browse files
authored
[Stack Switching] Support call_indirect (#7783)
Normal call and even call_ref just work, but call_indirect must be careful as the table might change - we must not reload the table entry to call. Instead, just stash it. Add tests for all call types.
1 parent d4b7bdb commit 8ad26cb

File tree

2 files changed

+154
-1
lines changed

2 files changed

+154
-1
lines changed

src/wasm-interpreter.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3567,7 +3567,16 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
35673567

35683568
auto index = target.getSingleValue().getUnsigned();
35693569
auto info = getTableInstanceInfo(curr->table);
3570-
auto funcref = info.interface()->tableLoad(info.name, index);
3570+
Literal funcref;
3571+
if (!self()->resuming) {
3572+
// Normal execution: Load from the table.
3573+
funcref = info.interface()->tableLoad(info.name, index);
3574+
} else {
3575+
// Use the stashed funcref (see below).
3576+
auto entry = self()->popResumeEntry("call_indirect");
3577+
assert(entry.size() == 1);
3578+
funcref = entry[0];
3579+
}
35713580

35723581
if (curr->isReturn) {
35733582
// Return calls are represented by their arguments followed by a reference
@@ -3599,6 +3608,14 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
35993608
std::cout << self()->indent() << "(returned to " << scope->function->name
36003609
<< ")\n";
36013610
#endif
3611+
3612+
if (ret.suspendTag) {
3613+
// Save the function reference we are calling, as when we resume we need
3614+
// to call it - we cannot do another load from the table, which might have
3615+
// changed.
3616+
self()->pushResumeEntry({funcref}, "call_indirect");
3617+
}
3618+
36023619
return ret;
36033620
}
36043621

test/lit/exec/cont_simple.wast

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
(tag $more)
1818
(tag $more-i32 (result i32))
1919

20+
(table $table 10 10 funcref)
21+
2022
(func $run (param $k (ref $k))
2123
;; Run a coroutine, continuing to resume it until it is complete.
2224
(call $log (i32.const 100)) ;; start
@@ -671,4 +673,138 @@
671673
(cont.new $k-get-i32 (ref.func $param))
672674
)
673675
)
676+
677+
(func $calls
678+
;; Suspend before and after calling a child, who also suspends.
679+
(local $x i32)
680+
;; Set a value here to check that we do not get confused between locals in
681+
;; different scopes.
682+
(local.set $x (i32.const -1))
683+
(suspend $more)
684+
(call $calls-child (i32.const 41))
685+
(suspend $more)
686+
(call $calls-child (i32.const 1336))
687+
(suspend $more)
688+
(call $log (local.get $x))
689+
)
690+
691+
(func $calls-child (param $x i32)
692+
(suspend $more)
693+
(local.set $x
694+
(i32.add
695+
(local.get $x)
696+
(i32.const 1)
697+
)
698+
)
699+
(suspend $more)
700+
(call $log (local.get $x))
701+
(suspend $more)
702+
)
703+
704+
;; CHECK: [fuzz-exec] calling run-calls
705+
;; CHECK-NEXT: [LoggingExternalInterface logging 100]
706+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
707+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
708+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
709+
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
710+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
711+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
712+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
713+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
714+
;; CHECK-NEXT: [LoggingExternalInterface logging 1337]
715+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
716+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
717+
;; CHECK-NEXT: [LoggingExternalInterface logging -1]
718+
;; CHECK-NEXT: [LoggingExternalInterface logging 300]
719+
(func $run-calls (export "run-calls")
720+
(call $run
721+
(cont.new $k (ref.func $calls))
722+
)
723+
)
724+
725+
(func $call_indirect
726+
;; Test that indirect calls go to the right place, even if we modify the
727+
;; table in between.
728+
(table.set $table
729+
(i32.const 7)
730+
(ref.func $call_indirect-child)
731+
)
732+
(call $log (i32.const -1))
733+
(call_indirect (type $f)
734+
(i32.const 7)
735+
)
736+
(call $log (i32.const -2))
737+
)
738+
739+
(func $call_indirect-child
740+
;; When we resume the suspend below, the table entry will have null, but we
741+
;; should still rewind the stack properly.
742+
(call $log (i32.const -10))
743+
(call $log
744+
(ref.is_null
745+
(table.get $table
746+
(i32.const 7)
747+
)
748+
)
749+
)
750+
(table.set $table
751+
(i32.const 7)
752+
(ref.null func)
753+
)
754+
(suspend $more)
755+
(call $log (i32.const -20))
756+
(call $log
757+
(ref.is_null
758+
(table.get $table
759+
(i32.const 7)
760+
)
761+
)
762+
)
763+
(suspend $more)
764+
)
765+
766+
;; CHECK: [fuzz-exec] calling run-call_indirect
767+
;; CHECK-NEXT: [LoggingExternalInterface logging 100]
768+
;; CHECK-NEXT: [LoggingExternalInterface logging -1]
769+
;; CHECK-NEXT: [LoggingExternalInterface logging -10]
770+
;; CHECK-NEXT: [LoggingExternalInterface logging 0]
771+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
772+
;; CHECK-NEXT: [LoggingExternalInterface logging -20]
773+
;; CHECK-NEXT: [LoggingExternalInterface logging 1]
774+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
775+
;; CHECK-NEXT: [LoggingExternalInterface logging -2]
776+
;; CHECK-NEXT: [LoggingExternalInterface logging 300]
777+
(func $run-call_indirect (export "run-call_indirect")
778+
(call $run
779+
(cont.new $k (ref.func $call_indirect))
780+
)
781+
)
782+
783+
(func $call_ref
784+
(suspend $more)
785+
(call_ref $f
786+
(ref.func $call_ref-child)
787+
)
788+
(suspend $more)
789+
)
790+
791+
(func $call_ref-child
792+
(suspend $more)
793+
(call $log (i32.const -20))
794+
(suspend $more)
795+
)
796+
797+
;; CHECK: [fuzz-exec] calling run-call_ref
798+
;; CHECK-NEXT: [LoggingExternalInterface logging 100]
799+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
800+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
801+
;; CHECK-NEXT: [LoggingExternalInterface logging -20]
802+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
803+
;; CHECK-NEXT: [LoggingExternalInterface logging 200]
804+
;; CHECK-NEXT: [LoggingExternalInterface logging 300]
805+
(func $run-call_ref (export "run-call_ref")
806+
(call $run
807+
(cont.new $k (ref.func $call_ref))
808+
)
809+
)
674810
)

0 commit comments

Comments
 (0)