From: Kohei Tokunaga <ktokunaga.mail@gmail.com>
To: qemu-devel@nongnu.org
Cc: "Alex Bennée" <alex.bennee@linaro.org>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Thomas Huth" <thuth@redhat.com>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Daniel P . Berrangé" <berrange@redhat.com>,
"WANG Xuerui" <git@xen0n.name>,
"Aurelien Jarno" <aurelien@aurel32.net>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Jiaxun Yang" <jiaxun.yang@flygoat.com>,
"Aleksandar Rikalo" <arikalo@gmail.com>,
"Palmer Dabbelt" <palmer@dabbelt.com>,
"Alistair Francis" <Alistair.Francis@wdc.com>,
"Stefan Weil" <sw@weilnetz.de>,
qemu-arm@nongnu.org, qemu-riscv@nongnu.org,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
ktokunaga.mail@gmail.com
Subject: [PATCH v2 29/35] tcg/wasm: Allow switching coroutine from a helper
Date: Wed, 27 Aug 2025 01:10:34 +0900 [thread overview]
Message-ID: <18770eb1458b4824b97608563ce6a65dc381e994.1756216429.git.ktokunaga.mail@gmail.com> (raw)
In-Reply-To: <cover.1756216429.git.ktokunaga.mail@gmail.com>
Emscripten's Fiber coroutine implements coroutine switching using Asyncify's
stack unwinding and rewinding features [1]. When a coroutine yields
(i.e. switches out), Asyncify unwinds the stack, returning control to
Emscripten's JS code (Fiber.trampoline()). Then execution resumes in the
target coroutine by rewinding the stack. Stack unwinding is implemented by a
sequence of immediate function returns, while rewinding re-enters the
functions in the call stack, skipping any code between the function's entry
point and the original call position [2].
This commit updates the TB's Wasm module to allow helper functions to
trigger coroutine switching. Particaully, the TB handles the unwinding and
rewinding flows as follows:
- The TB check the Asyncify.state JS object after each helper call. If
unwinding is in progress, the TB immediately returns to the caller so that
the unwinding can continue.
- Each function call is preceded by a block boundary and an update of the
BLOCK_IDX variable. This enables rewinding to skip any code between the
function's entry point and the original call position.
Additionally, this commit introduces WasmContext.do_init which is a flag
indicating whether the TB should reset the BLOCK_IDX variable to 0
(i.e. start from the beginning). call_wasm_tb is a newly introduced wrapper
function for the Wasm module's entrypoint and this sets "do_init = 1" to
ensure normal TB execution begins at the first block. During a rewinding,
the C code does not set do_init to 1, allowing the TB to preserve the
BLOCK_IDX value from the previous unwinding and correctly resume execution
from the last unwound block.
[1] https://emscripten.org/docs/api_reference/fiber.h.html
[2] https://kripken.github.io/blog/wasm/2019/07/16/asyncify.html#new-asyncify
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm.c | 3 ++
tcg/wasm.h | 11 ++++++++
tcg/wasm/tcg-target.c.inc | 58 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/tcg/wasm.c b/tcg/wasm.c
index 15db1f9a8a..82987e9dff 100644
--- a/tcg/wasm.c
+++ b/tcg/wasm.c
@@ -44,6 +44,9 @@ EM_JS_PRE(void*, instantiate_wasm, (void *wasm_begin,
const wasm = HEAP8.subarray(DEC_PTR(wasm_begin),
DEC_PTR(wasm_begin) + wasm_size);
var helper = {};
+ helper.u = () => {
+ return (Asyncify.state != Asyncify.State.Unwinding) ? 1 : 0;
+ };
const entsize = TCG_TARGET_REG_BITS / 8;
for (var i = 0; i < import_vec_size / entsize; i++) {
const idx = memory_v.getBigInt64(
diff --git a/tcg/wasm.h b/tcg/wasm.h
index b5d9ce75da..fdde908557 100644
--- a/tcg/wasm.h
+++ b/tcg/wasm.h
@@ -30,11 +30,22 @@ struct WasmContext {
* Pointer to a stack array.
*/
uint64_t *stack;
+
+ /*
+ * Flag indicating whether to initialize the block index(1) or not(0).
+ */
+ uint32_t do_init;
};
/* Instantiated Wasm function of a TB */
typedef uintptr_t (*wasm_tb_func)(struct WasmContext *);
+static inline uintptr_t call_wasm_tb(wasm_tb_func f, struct WasmContext *ctx)
+{
+ ctx->do_init = 1; /* reset the block index (rewinding will skip this) */
+ return f(ctx);
+}
+
/*
* A TB of the Wasm backend starts from a header which contains pointers for
* each data stored in the following region in the TB.
diff --git a/tcg/wasm/tcg-target.c.inc b/tcg/wasm/tcg-target.c.inc
index 7663f03eaf..6af4d6eb07 100644
--- a/tcg/wasm/tcg-target.c.inc
+++ b/tcg/wasm/tcg-target.c.inc
@@ -152,7 +152,8 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
#define CTX_IDX 0
/* Function index */
-#define HELPER_IDX_START 0 /* The first index of helper functions */
+#define CHECK_UNWINDING_IDX 0 /* A function to check the Asyncify status */
+#define HELPER_IDX_START 1 /* The first index of helper functions */
#define PTR_TYPE 0x7e
@@ -169,6 +170,7 @@ typedef enum {
OPC_GLOBAL_GET = 0x23,
OPC_GLOBAL_SET = 0x24,
+ OPC_I32_LOAD = 0x28,
OPC_I64_LOAD = 0x29,
OPC_I64_LOAD8_S = 0x30,
OPC_I64_LOAD8_U = 0x31,
@@ -176,6 +178,7 @@ typedef enum {
OPC_I64_LOAD16_U = 0x33,
OPC_I64_LOAD32_S = 0x34,
OPC_I64_LOAD32_U = 0x35,
+ OPC_I32_STORE = 0x36,
OPC_I64_STORE = 0x37,
OPC_I64_STORE8 = 0x3c,
OPC_I64_STORE16 = 0x3d,
@@ -1116,6 +1119,17 @@ static int64_t get_helper_idx(TCGContext *s, intptr_t helper_idx_on_qemu)
return -1;
}
+static void tcg_wasm_out_handle_unwinding(TCGContext *s)
+{
+ tcg_wasm_out_op_idx(s, OPC_CALL, CHECK_UNWINDING_IDX);
+ tcg_wasm_out_op(s, OPC_I32_EQZ);
+ tcg_wasm_out_op_block(s, OPC_IF, BLOCK_NORET);
+ tcg_wasm_out_op_const(s, OPC_I64_CONST, 0);
+ /* returns if unwinding */
+ tcg_wasm_out_op(s, OPC_RETURN);
+ tcg_wasm_out_op(s, OPC_END);
+}
+
static void tcg_wasm_out_call(TCGContext *s, intptr_t func,
const TCGHelperInfo *info)
{
@@ -1132,7 +1146,16 @@ static void tcg_wasm_out_call(TCGContext *s, intptr_t func,
tcg_wasm_out_op_const(s, OPC_I64_CONST, (uint64_t)s->code_ptr);
tcg_wasm_out_op_ldst(s, OPC_I64_STORE, 0, ofs);
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_const(s, OPC_I64_CONST, cur_block_idx + 1);
+ tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, BLOCK_IDX);
+ tcg_wasm_out_new_block(s);
+
gen_call(s, info, func_idx);
+ tcg_wasm_out_handle_unwinding(s);
}
static void gen_func_type_qemu_ld(TCGContext *s, uint32_t oi)
@@ -1204,6 +1227,14 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
gen_func_type_qemu_ld(s, oi);
}
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_const(s, OPC_I64_CONST, cur_block_idx + 1);
+ tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, BLOCK_IDX);
+ tcg_wasm_out_new_block(s);
+
/* call the target helper */
tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(TCG_AREG0));
tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(addr_reg));
@@ -1212,6 +1243,7 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_idx(s, OPC_CALL, func_idx);
tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(data_reg));
+ tcg_wasm_out_handle_unwinding(s);
}
static void *qemu_st_helper_ptr(uint32_t oi)
@@ -1245,6 +1277,14 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
gen_func_type_qemu_st(s, oi);
}
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_const(s, OPC_I64_CONST, cur_block_idx + 1);
+ tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, BLOCK_IDX);
+ tcg_wasm_out_new_block(s);
+
/* call the target helper */
tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(TCG_AREG0));
tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(addr_reg));
@@ -1261,6 +1301,7 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_const(s, OPC_I64_CONST, (intptr_t)s->code_ptr);
tcg_wasm_out_op_idx(s, OPC_CALL, func_idx);
+ tcg_wasm_out_handle_unwinding(s);
}
static void tcg_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0)
@@ -2264,6 +2305,9 @@ static const uint8_t mod_1[] = {
0x60, /* 0: Type of "start" function */
0x01, PTR_TYPE, /* arg: ctx pointer */
0x01, PTR_TYPE, /* return: res */
+ 0x60, /* 1: Type of the asyncify helper */
+ 0x0, /* no argument */
+ 0x01, 0x7f, /* return: res (i32) */
};
#define MOD_1_PH_TYPE_SECTION_SIZE_OFF 9
@@ -2289,6 +2333,9 @@ static const uint8_t mod_2[] = {
0x02, 0x07, /* shared mem(64bit) */
0x00, 0x80, 0x80, 0x10, /* min: 0, max: 262144 pages */
#endif
+ 0x06, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, /* module: "helper" */
+ 0x01, 0x75, /* name: "u" */
+ 0x00, 0x01, /* func type 1 */
};
#define MOD_2_PH_IMPORT_SECTION_SIZE_OFF 1
@@ -2427,8 +2474,17 @@ static void tcg_out_tb_start(TCGContext *s)
tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(TCG_REG_CALL_STACK));
tcg_wasm_out_op(s, OPC_END);
+ ofs = tcg_wasm_out_get_ctx(s, CTX_OFFSET(do_init));
+ tcg_wasm_out_op_ldst(s, OPC_I32_LOAD, 0, ofs);
+ tcg_wasm_out_op_const(s, OPC_I32_CONST, 0);
+ tcg_wasm_out_op(s, OPC_I32_NE);
+ tcg_wasm_out_op_block(s, OPC_IF, BLOCK_NORET);
tcg_wasm_out_op_const(s, OPC_I64_CONST, 0);
tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, BLOCK_IDX);
+ ofs = tcg_wasm_out_get_ctx(s, CTX_OFFSET(do_init));
+ tcg_wasm_out_op_const(s, OPC_I32_CONST, 0);
+ tcg_wasm_out_op_ldst(s, OPC_I32_STORE, 0, ofs);
+ tcg_wasm_out_op(s, OPC_END);
tcg_wasm_out_op_block(s, OPC_LOOP, BLOCK_NORET);
tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, BLOCK_IDX);
--
2.43.0
next prev parent reply other threads:[~2025-08-26 16:27 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-26 16:10 [PATCH v2 00/35] wasm: Add Wasm TCG backend based on wasm64 Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 01/35] meson: Add wasm64 support to the --cpu flag Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 02/35] configure: Enable to propagate -sMEMORY64 flag to Emscripten Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 03/35] dockerfiles: Add support for wasm64 to the wasm Dockerfile Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 04/35] .gitlab-ci.d: Add build tests for wasm64 Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 05/35] tcg/wasm: Add tcg-target.h and tcg-target-reg-bits.h Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 06/35] tcg/wasm: Add register-related definitions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 07/35] tcg/wasm: Add constraint definitions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 08/35] tcg/wasm: Add relocation callbacks Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 09/35] tcg/wasm: Add and/or/xor instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 10/35] tcg/wasm: Add add/sub/mul instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 11/35] tcg/wasm: Add shl/shr/sar instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 12/35] tcg/wasm: Add setcond/negsetcond/movcond instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 13/35] tcg/wasm: Add sextract instruction Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 14/35] tcg/wasm: Add load and store instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 15/35] tcg/wasm: Add mov/movi instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 16/35] tcg/wasm: Add ext instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 17/35] tcg/wasm: Add div/rem instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 18/35] tcg/wasm: Add neg/ctpop instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 19/35] tcg/wasm: Add rot/clz/ctz instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 20/35] tcg/wasm: Add br/brcond instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 21/35] tcg/wasm: Add exit_tb/goto_tb/goto_ptr instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 22/35] tcg/wasm: Add call instruction Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 23/35] tcg/wasm: Add qemu_ld/qemu_st instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 24/35] tcg/wasm: Add mb instruction Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 25/35] tcg/wasm: Mark unimplemented instructions Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 26/35] tcg/wasm: Add initialization of fundamental registers Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 27/35] tcg/wasm: Write wasm binary to TB Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 28/35] tcg/wasm: Implement instantiation of Wasm binary Kohei Tokunaga
2025-08-26 16:10 ` Kohei Tokunaga [this message]
2025-08-26 16:10 ` [PATCH v2 30/35] tcg/wasm: Enable instantiation of TBs executed many times Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 31/35] tcg/wasm: Enable TLB lookup Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 32/35] tcg/wasm: Add tcg_target_init function Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 33/35] meson.build: enable to build Wasm backend Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 34/35] meson.build: Propagate optimization flag for linking on Emscripten Kohei Tokunaga
2025-08-26 16:10 ` [PATCH v2 35/35] .gitlab-ci.d: build wasm backend in CI Kohei Tokunaga
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=18770eb1458b4824b97608563ce6a65dc381e994.1756216429.git.ktokunaga.mail@gmail.com \
--to=ktokunaga.mail@gmail.com \
--cc=Alistair.Francis@wdc.com \
--cc=alex.bennee@linaro.org \
--cc=arikalo@gmail.com \
--cc=aurelien@aurel32.net \
--cc=berrange@redhat.com \
--cc=chenhuacai@kernel.org \
--cc=git@xen0n.name \
--cc=jiaxun.yang@flygoat.com \
--cc=marcandre.lureau@redhat.com \
--cc=palmer@dabbelt.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=stefanha@redhat.com \
--cc=sw@weilnetz.de \
--cc=thuth@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).