* [PATCH 00/33] tcg: Add WebAssembly backend
@ 2025-05-20 12:51 Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 01/33] tcg: Fork TCI for wasm32 backend Kohei Tokunaga
` (32 more replies)
0 siblings, 33 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This patch series is split from the original "Enable QEMU to run on
browsers" series, focusing solely on introducing a TCG backend for
WebAssembly. This implemention is based on the latest master which already
includes the essential changes required to compile QEMU (in 32bit TCI mode)
using Emscripten.
# New TCG Backend for Browsers
A new TCG backend translates IR instructions into Wasm instructions and runs
them using the browser's WebAssembly APIs (WebAssembly.Module and
WebAssembly.instantiate). To minimize compilation overhead and avoid hitting
the browser's limitation of the number of instances, this backend integrates
a forked TCI. TBs run on TCI by default, with frequently executed TBs
compiled into WebAssembly.
# Workaround for Running 64bit Guests
The current implementation uses Wasm's 32bit memory model. This series
explores supporting TCG 64bit instructions while relying on SoftMMU for
address translation. To enable 64bit guest support in Wasm today, it was
necessary to partially revert recent changes that removed support for 64bit
guests on 32bit hosts (e.g. commits a70af12addd9060fdf8f3dbd42b42e3072c3914f
and bf455ec50b6fea15b4d2493059365bf94c706273) when compiling with
Emscripten. The reverting is partial and addresses only pointer width
differences between hosts and guests since the Wasm backend supports 64bit
word operations. While this serves as a temporary workaround, a long-term
solution could involve migrating to Wasm's 64bit memory model once it gains
broader support, as it is currently not widely adopted (e.g. unsupported by
Safari and libffi).
# Overview of build process
This section provides an overview of the build process for compiling QEMU
using Emscripten. Full instructions are available in the sample
repository[1].
To compile QEMU with Emscripten, the following dependencies are required.
The emsdk-wasm32-cross.docker environment includes all necessary components
and can be used as the build environment:
- Emscripten SDK (emsdk) v3.1.50
- Libraries cross-compiled with Emscripten (refer to
emsdk-wasm32-cross.docker for build steps)
- GLib v2.84.0
- zlib v1.3.1
- libffi v3.4.7
- Pixman v0.44.2
QEMU can be compiled using Emscripten's emconfigure and emmake, which
automatically set environment variables such as CC for targeting Emscripten.
emconfigure configure --static --disable-tools --target-list=x86_64-softmmu
emmake make -j$(nproc)
This process generates the following files:
- qemu-system-x86_64.js
- qemu-system-x86_64.wasm
- qemu-system-x86_64.worker.js
Guest images can be packaged using Emscripten's file_packager.py tool.
For example, if the images are stored in a directory named "pack", the
following command packages them, allowing QEMU to access them through
Emscripten's virtual filesystem:
/path/to/file_packager.py qemu-system-x86_64.data --preload pack > load.js
This process generates the following files:
- qemu-system-x86_64.data
- load.js
Emscripten allows passing arguments to the QEMU command via the Module
object in JavaScript:
Module['arguments'] = [
'-nographic', '-m', '512M', '-accel', 'tcg,tb-size=500',
'-L', 'pack/',
'-drive', 'if=virtio,format=raw,file=pack/rootfs.bin',
'-kernel', 'pack/bzImage',
'-append', 'earlyprintk=ttyS0 console=ttyS0 root=/dev/vda loglevel=7',
];
The sample repository[1] provides a complete setup, including an HTML file
that implements a terminal UI.
[1] https://github.com/ktock/qemu-wasm-sample
# Additional references
- A talk at FOSDEM 2025:
https://fosdem.org/2025/schedule/event/fosdem-2025-6290-running-qemu-inside-browser/
- Demo page on GitHub Pages: https://ktock.github.io/qemu-wasm-demo/
Kohei Tokunaga (33):
tcg: Fork TCI for wasm32 backend
tcg/wasm32: Do not use TCI disassembler in Wasm backend
meson: Enable to build wasm backend
tcg/wasm32: Set TCG_TARGET_INSN_UNIT_SIZE to 1
tcg/wasm32: Add and/or/xor instructions
tcg/wasm32: Add add/sub/mul instructions
tcg/wasm32: Add shl/shr/sar instructions
tcg/wasm32: Add setcond/negsetcond/movcond instructions
tcg/wasm32: Add deposit/sextract/extract instrcutions
tcg/wasm32: Add load and store instructions
tcg/wasm32: Add mov/movi instructions
tcg/wasm32: Add ext instructions
tcg/wasm32: Add bswap instructions
tcg/wasm32: Add rem/div instructions
tcg/wasm32: Add andc/orc/eqv/nand/nor instructions
tcg/wasm32: Add neg/not/ctpop instructions
tcg/wasm32: Add rot/clz/ctz instructions
tcg/wasm32: Add addc/subb instructions
tcg/wasm32: Add br/brcond instructions
tcg/wasm32: Add exit_tb/goto_tb/goto_ptr instructions
tcg/wasm32: Add call instruction
tcg/wasm32: Add qemu_ld/qemu_st instructions
include/exec: Allow using 64bit guest addresses on emscripten
tcg/wasm32: Set TCG_TARGET_REG_BITS to 64
tcg/wasm32: Set mulu2/muls2 as unimplemented
tcg/wasm32: Add initialization of fundamental registers
tcg/wasm32: Write wasm binary to TB
tcg/wasm32: Implement instantiation of Wasm binary
tcg/wasm32: Allow Asyncify unwinding from TB
tcg/wasm32: Enable instantiation of TBs executed many times
tcg/wasm32: Enable TLB lookup
meson: Propagate optimization flag for linking on Emscripten
.gitlab-ci.d: build wasm backend in CI
.gitlab-ci.d/buildtest.yml | 2 +-
MAINTAINERS | 7 +
accel/tcg/cputlb.c | 8 +-
include/accel/tcg/getpc.h | 2 +-
include/exec/helper-head.h.inc | 6 +
include/exec/tlb-common.h | 14 +-
include/exec/vaddr.h | 11 +
include/qemu/atomic.h | 4 +
include/tcg/helper-info.h | 4 +-
include/tcg/tcg.h | 6 +-
meson.build | 16 +-
tcg/aarch64/tcg-target.c.inc | 11 +
tcg/arm/tcg-target.c.inc | 11 +
tcg/i386/tcg-target.c.inc | 11 +
tcg/loongarch64/tcg-target.c.inc | 11 +
tcg/meson.build | 5 +
tcg/mips/tcg-target.c.inc | 11 +
tcg/ppc/tcg-target.c.inc | 11 +
tcg/region.c | 10 +-
tcg/riscv/tcg-target.c.inc | 11 +
tcg/s390x/tcg-target.c.inc | 11 +
tcg/sparc64/tcg-target.c.inc | 11 +
tcg/tcg.c | 23 +-
tcg/tci/tcg-target.c.inc | 11 +
tcg/wasm32.c | 1096 ++++++++
tcg/wasm32.h | 119 +
tcg/wasm32/tcg-target-con-set.h | 21 +
tcg/wasm32/tcg-target-con-str.h | 11 +
tcg/wasm32/tcg-target-has.h | 22 +
tcg/wasm32/tcg-target-mo.h | 17 +
tcg/wasm32/tcg-target-opc.h.inc | 15 +
tcg/wasm32/tcg-target-reg-bits.h | 12 +
tcg/wasm32/tcg-target.c.inc | 3985 ++++++++++++++++++++++++++++++
tcg/wasm32/tcg-target.h | 76 +
34 files changed, 5569 insertions(+), 33 deletions(-)
create mode 100644 tcg/wasm32.c
create mode 100644 tcg/wasm32.h
create mode 100644 tcg/wasm32/tcg-target-con-set.h
create mode 100644 tcg/wasm32/tcg-target-con-str.h
create mode 100644 tcg/wasm32/tcg-target-has.h
create mode 100644 tcg/wasm32/tcg-target-mo.h
create mode 100644 tcg/wasm32/tcg-target-opc.h.inc
create mode 100644 tcg/wasm32/tcg-target-reg-bits.h
create mode 100644 tcg/wasm32/tcg-target.c.inc
create mode 100644 tcg/wasm32/tcg-target.h
--
2.43.0
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH 01/33] tcg: Fork TCI for wasm32 backend
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 02/33] tcg/wasm32: Do not use TCI disassembler in Wasm backend Kohei Tokunaga
` (31 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Wasm backend is implemented based on the TCI backend and utilizes a forked
TCI to execute TBs.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
MAINTAINERS | 6 +
include/accel/tcg/getpc.h | 2 +-
include/tcg/helper-info.h | 4 +-
include/tcg/tcg.h | 2 +-
tcg/meson.build | 5 +
tcg/region.c | 10 +-
tcg/tcg.c | 16 +-
tcg/wasm32.c | 1076 ++++++++++++++++++++++++
tcg/wasm32/tcg-target-con-set.h | 21 +
tcg/wasm32/tcg-target-con-str.h | 11 +
tcg/wasm32/tcg-target-has.h | 22 +
tcg/wasm32/tcg-target-mo.h | 17 +
tcg/wasm32/tcg-target-opc.h.inc | 15 +
tcg/wasm32/tcg-target-reg-bits.h | 18 +
tcg/wasm32/tcg-target.c.inc | 1320 ++++++++++++++++++++++++++++++
tcg/wasm32/tcg-target.h | 76 ++
16 files changed, 2604 insertions(+), 17 deletions(-)
create mode 100644 tcg/wasm32.c
create mode 100644 tcg/wasm32/tcg-target-con-set.h
create mode 100644 tcg/wasm32/tcg-target-con-str.h
create mode 100644 tcg/wasm32/tcg-target-has.h
create mode 100644 tcg/wasm32/tcg-target-mo.h
create mode 100644 tcg/wasm32/tcg-target-opc.h.inc
create mode 100644 tcg/wasm32/tcg-target-reg-bits.h
create mode 100644 tcg/wasm32/tcg-target.c.inc
create mode 100644 tcg/wasm32/tcg-target.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 7060cf49b9..ac5070d058 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3948,6 +3948,12 @@ F: tcg/tci/
F: tcg/tci.c
F: disas/tci.c
+WebAssembly TCG target
+M: Kohei Tokunaga <ktokunaga.mail@gmail.com>
+S: Maintained
+F: tcg/wasm32/
+F: tcg/wasm32.c
+
Block drivers
-------------
VMDK
diff --git a/include/accel/tcg/getpc.h b/include/accel/tcg/getpc.h
index 0fc08addcf..3901655715 100644
--- a/include/accel/tcg/getpc.h
+++ b/include/accel/tcg/getpc.h
@@ -9,7 +9,7 @@
#define ACCEL_TCG_GETPC_H
/* GETPC is the true target of the return instruction that we'll execute. */
-#ifdef CONFIG_TCG_INTERPRETER
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
extern __thread uintptr_t tci_tb_ptr;
# define GETPC() tci_tb_ptr
#else
diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h
index 909fe73afa..9b4e8832a8 100644
--- a/include/tcg/helper-info.h
+++ b/include/tcg/helper-info.h
@@ -9,7 +9,7 @@
#ifndef TCG_HELPER_INFO_H
#define TCG_HELPER_INFO_H
-#ifdef CONFIG_TCG_INTERPRETER
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
#include <ffi.h>
#endif
#include "tcg-target-reg-bits.h"
@@ -48,7 +48,7 @@ struct TCGHelperInfo {
const char *name;
/* Used with g_once_init_enter. */
-#ifdef CONFIG_TCG_INTERPRETER
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
ffi_cif *cif;
#else
uintptr_t init;
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 3fa5a7aed2..041d8035bc 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -967,7 +967,7 @@ static inline size_t tcg_current_code_size(TCGContext *s)
#define TB_EXIT_IDXMAX 1
#define TB_EXIT_REQUESTED 3
-#ifdef CONFIG_TCG_INTERPRETER
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
uintptr_t tcg_qemu_tb_exec(CPUArchState *env, const void *tb_ptr);
#else
typedef uintptr_t tcg_prologue_fn(CPUArchState *env, const void *tb_ptr);
diff --git a/tcg/meson.build b/tcg/meson.build
index bd2821e4b5..a20acfd44f 100644
--- a/tcg/meson.build
+++ b/tcg/meson.build
@@ -20,6 +20,11 @@ if get_option('tcg_interpreter')
method: 'pkg-config')
tcg_ss.add(libffi)
tcg_ss.add(files('tci.c'))
+elif host_os == 'emscripten'
+ libffi = dependency('libffi', version: '>=3.0', required: true,
+ method: 'pkg-config')
+ specific_ss.add(libffi)
+ specific_ss.add(files('wasm32.c'))
endif
tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
diff --git a/tcg/region.c b/tcg/region.c
index 7ea0b37a84..68cb6f18b7 100644
--- a/tcg/region.c
+++ b/tcg/region.c
@@ -94,7 +94,7 @@ bool in_code_gen_buffer(const void *p)
return (size_t)(p - region.start_aligned) <= region.total_size;
}
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
static int host_prot_read_exec(void)
{
#if defined(CONFIG_LINUX) && defined(HOST_AARCH64) && defined(PROT_BTI)
@@ -569,7 +569,7 @@ static int alloc_code_gen_buffer_anon(size_t size, int prot,
return prot;
}
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
#ifdef CONFIG_POSIX
#include "qemu/memfd.h"
@@ -667,11 +667,11 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
return PROT_READ | PROT_WRITE;
}
#endif /* CONFIG_DARWIN */
-#endif /* CONFIG_TCG_INTERPRETER */
+#endif /* !CONFIG_TCG_INTERPRETER && !EMSCRIPTEN */
static int alloc_code_gen_buffer_splitwx(size_t size, Error **errp)
{
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
# ifdef CONFIG_DARWIN
return alloc_code_gen_buffer_splitwx_vmremap(size, errp);
# endif
@@ -813,7 +813,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads)
* Work with the page protections set up with the initial mapping.
*/
need_prot = PROT_READ | PROT_WRITE;
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
if (tcg_splitwx_diff == 0) {
need_prot |= host_prot_read_exec();
}
diff --git a/tcg/tcg.c b/tcg/tcg.c
index ae27a2607d..2746458a64 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -253,7 +253,7 @@ TCGv_env tcg_env;
const void *tcg_code_gen_epilogue;
uintptr_t tcg_splitwx_diff;
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
tcg_prologue_fn *tcg_qemu_tb_exec;
#endif
@@ -1117,7 +1117,7 @@ typedef struct TCGOutOpSubtract {
#include "tcg-target.c.inc"
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
/* Validate CPUTLBDescFast placement. */
QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) -
sizeof(CPUNegativeOffsetState))
@@ -1438,7 +1438,7 @@ static TCGHelperInfo info_helper_st128_mmu = {
| dh_typemask(ptr, 5) /* uintptr_t ra */
};
-#ifdef CONFIG_TCG_INTERPRETER
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
static ffi_type *typecode_to_ffi(int argmask)
{
/*
@@ -1515,7 +1515,7 @@ static ffi_cif *init_ffi_layout(TCGHelperInfo *info)
#else
#define HELPER_INFO_INIT(I) (&(I)->init)
#define HELPER_INFO_INIT_VAL(I) 1
-#endif /* CONFIG_TCG_INTERPRETER */
+#endif /* CONFIG_TCG_INTERPRETER || EMSCRIPTEN */
static inline bool arg_slot_reg_p(unsigned arg_slot)
{
@@ -1892,7 +1892,7 @@ void tcg_prologue_init(void)
s->code_buf = s->code_gen_ptr;
s->data_gen_ptr = NULL;
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
tcg_qemu_tb_exec = (tcg_prologue_fn *)tcg_splitwx_to_rx(s->code_ptr);
#endif
@@ -1911,7 +1911,7 @@ void tcg_prologue_init(void)
prologue_size = tcg_current_code_size(s);
perf_report_prologue(s->code_gen_ptr, prologue_size);
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf),
(uintptr_t)s->code_buf, prologue_size);
#endif
@@ -1948,7 +1948,7 @@ void tcg_prologue_init(void)
}
}
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
/*
* Assert that goto_ptr is implemented completely, setting an epilogue.
* For tci, we use NULL as the signal to return from the interpreter,
@@ -7046,7 +7046,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start)
return -2;
}
-#ifndef CONFIG_TCG_INTERPRETER
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
/* flush instruction cache */
flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf),
(uintptr_t)s->code_buf,
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
new file mode 100644
index 0000000000..6de9b26b76
--- /dev/null
+++ b/tcg/wasm32.c
@@ -0,0 +1,1076 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * WebAssembly backend with forked TCI, based on tci.c
+ *
+ * Copyright (c) 2009, 2011, 2016 Stefan Weil
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "tcg/tcg.h"
+#include "tcg/helper-info.h"
+#include "tcg/tcg-ldst.h"
+#include "disas/dis-asm.h"
+#include "tcg-has.h"
+#include <ffi.h>
+
+
+#define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS)
+#define deposit_tr glue(deposit, TCG_TARGET_REG_BITS)
+#define extract_tr glue(extract, TCG_TARGET_REG_BITS)
+#define sextract_tr glue(sextract, TCG_TARGET_REG_BITS)
+
+/*
+ * Enable TCI assertions only when debugging TCG (and without NDEBUG defined).
+ * Without assertions, the interpreter runs much faster.
+ */
+#if defined(CONFIG_DEBUG_TCG)
+# define tci_assert(cond) assert(cond)
+#else
+# define tci_assert(cond) ((void)(cond))
+#endif
+
+__thread uintptr_t tci_tb_ptr;
+
+static void tci_write_reg64(tcg_target_ulong *regs, uint32_t high_index,
+ uint32_t low_index, uint64_t value)
+{
+ regs[low_index] = (uint32_t)value;
+ regs[high_index] = value >> 32;
+}
+
+/* Create a 64 bit value from two 32 bit values. */
+static uint64_t tci_uint64(uint32_t high, uint32_t low)
+{
+ return ((uint64_t)high << 32) + low;
+}
+
+/*
+ * Load sets of arguments all at once. The naming convention is:
+ * tci_args_<arguments>
+ * where arguments is a sequence of
+ *
+ * b = immediate (bit position)
+ * c = condition (TCGCond)
+ * i = immediate (uint32_t)
+ * I = immediate (tcg_target_ulong)
+ * l = label or pointer
+ * m = immediate (MemOpIdx)
+ * n = immediate (call return length)
+ * r = register
+ * s = signed ldst offset
+ */
+
+static void tci_args_l(uint32_t insn, const void *tb_ptr, void **l0)
+{
+ int diff = sextract32(insn, 12, 20);
+ *l0 = diff ? (void *)tb_ptr + diff : NULL;
+}
+
+static void tci_args_r(uint32_t insn, TCGReg *r0)
+{
+ *r0 = extract32(insn, 8, 4);
+}
+
+static void tci_args_nl(uint32_t insn, const void *tb_ptr,
+ uint8_t *n0, void **l1)
+{
+ *n0 = extract32(insn, 8, 4);
+ *l1 = sextract32(insn, 12, 20) + (void *)tb_ptr;
+}
+
+static void tci_args_rl(uint32_t insn, const void *tb_ptr,
+ TCGReg *r0, void **l1)
+{
+ *r0 = extract32(insn, 8, 4);
+ *l1 = sextract32(insn, 12, 20) + (void *)tb_ptr;
+}
+
+static void tci_args_rr(uint32_t insn, TCGReg *r0, TCGReg *r1)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+}
+
+static void tci_args_ri(uint32_t insn, TCGReg *r0, tcg_target_ulong *i1)
+{
+ *r0 = extract32(insn, 8, 4);
+ *i1 = sextract32(insn, 12, 20);
+}
+
+static void tci_args_rrm(uint32_t insn, TCGReg *r0,
+ TCGReg *r1, MemOpIdx *m2)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *m2 = extract32(insn, 16, 16);
+}
+
+static void tci_args_rrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *r2 = extract32(insn, 16, 4);
+}
+
+static void tci_args_rrs(uint32_t insn, TCGReg *r0, TCGReg *r1, int32_t *i2)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *i2 = sextract32(insn, 16, 16);
+}
+
+static void tci_args_rrbb(uint32_t insn, TCGReg *r0, TCGReg *r1,
+ uint8_t *i2, uint8_t *i3)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *i2 = extract32(insn, 16, 6);
+ *i3 = extract32(insn, 22, 6);
+}
+
+static void tci_args_rrrc(uint32_t insn,
+ TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGCond *c3)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *r2 = extract32(insn, 16, 4);
+ *c3 = extract32(insn, 20, 4);
+}
+
+static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1,
+ TCGReg *r2, uint8_t *i3, uint8_t *i4)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *r2 = extract32(insn, 16, 4);
+ *i3 = extract32(insn, 20, 6);
+ *i4 = extract32(insn, 26, 6);
+}
+
+static void tci_args_rrrr(uint32_t insn,
+ TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGReg *r3)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *r2 = extract32(insn, 16, 4);
+ *r3 = extract32(insn, 20, 4);
+}
+
+static void tci_args_rrrrrc(uint32_t insn, TCGReg *r0, TCGReg *r1,
+ TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGCond *c5)
+{
+ *r0 = extract32(insn, 8, 4);
+ *r1 = extract32(insn, 12, 4);
+ *r2 = extract32(insn, 16, 4);
+ *r3 = extract32(insn, 20, 4);
+ *r4 = extract32(insn, 24, 4);
+ *c5 = extract32(insn, 28, 4);
+}
+
+static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition)
+{
+ bool result = false;
+ int32_t i0 = u0;
+ int32_t i1 = u1;
+ switch (condition) {
+ case TCG_COND_EQ:
+ result = (u0 == u1);
+ break;
+ case TCG_COND_NE:
+ result = (u0 != u1);
+ break;
+ case TCG_COND_LT:
+ result = (i0 < i1);
+ break;
+ case TCG_COND_GE:
+ result = (i0 >= i1);
+ break;
+ case TCG_COND_LE:
+ result = (i0 <= i1);
+ break;
+ case TCG_COND_GT:
+ result = (i0 > i1);
+ break;
+ case TCG_COND_LTU:
+ result = (u0 < u1);
+ break;
+ case TCG_COND_GEU:
+ result = (u0 >= u1);
+ break;
+ case TCG_COND_LEU:
+ result = (u0 <= u1);
+ break;
+ case TCG_COND_GTU:
+ result = (u0 > u1);
+ break;
+ case TCG_COND_TSTEQ:
+ result = (u0 & u1) == 0;
+ break;
+ case TCG_COND_TSTNE:
+ result = (u0 & u1) != 0;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return result;
+}
+
+static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition)
+{
+ bool result = false;
+ int64_t i0 = u0;
+ int64_t i1 = u1;
+ switch (condition) {
+ case TCG_COND_EQ:
+ result = (u0 == u1);
+ break;
+ case TCG_COND_NE:
+ result = (u0 != u1);
+ break;
+ case TCG_COND_LT:
+ result = (i0 < i1);
+ break;
+ case TCG_COND_GE:
+ result = (i0 >= i1);
+ break;
+ case TCG_COND_LE:
+ result = (i0 <= i1);
+ break;
+ case TCG_COND_GT:
+ result = (i0 > i1);
+ break;
+ case TCG_COND_LTU:
+ result = (u0 < u1);
+ break;
+ case TCG_COND_GEU:
+ result = (u0 >= u1);
+ break;
+ case TCG_COND_LEU:
+ result = (u0 <= u1);
+ break;
+ case TCG_COND_GTU:
+ result = (u0 > u1);
+ break;
+ case TCG_COND_TSTEQ:
+ result = (u0 & u1) == 0;
+ break;
+ case TCG_COND_TSTNE:
+ result = (u0 & u1) != 0;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return result;
+}
+
+static uint64_t tci_qemu_ld(CPUArchState *env, uint64_t taddr,
+ MemOpIdx oi, const void *tb_ptr)
+{
+ MemOp mop = get_memop(oi);
+ uintptr_t ra = (uintptr_t)tb_ptr;
+
+ switch (mop & MO_SSIZE) {
+ case MO_UB:
+ return helper_ldub_mmu(env, taddr, oi, ra);
+ case MO_SB:
+ return helper_ldsb_mmu(env, taddr, oi, ra);
+ case MO_UW:
+ return helper_lduw_mmu(env, taddr, oi, ra);
+ case MO_SW:
+ return helper_ldsw_mmu(env, taddr, oi, ra);
+ case MO_UL:
+ return helper_ldul_mmu(env, taddr, oi, ra);
+ case MO_SL:
+ return helper_ldsl_mmu(env, taddr, oi, ra);
+ case MO_UQ:
+ return helper_ldq_mmu(env, taddr, oi, ra);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val,
+ MemOpIdx oi, const void *tb_ptr)
+{
+ MemOp mop = get_memop(oi);
+ uintptr_t ra = (uintptr_t)tb_ptr;
+
+ switch (mop & MO_SIZE) {
+ case MO_UB:
+ helper_stb_mmu(env, taddr, val, oi, ra);
+ break;
+ case MO_UW:
+ helper_stw_mmu(env, taddr, val, oi, ra);
+ break;
+ case MO_UL:
+ helper_stl_mmu(env, taddr, val, oi, ra);
+ break;
+ case MO_UQ:
+ helper_stq_mmu(env, taddr, val, oi, ra);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* Interpret pseudo code in tb. */
+/*
+ * Disable CFI checks.
+ * One possible operation in the pseudo code is a call to binary code.
+ * Therefore, disable CFI checks in the interpreter function
+ */
+uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
+ const void *v_tb_ptr)
+{
+ const uint32_t *tb_ptr = v_tb_ptr;
+ tcg_target_ulong regs[TCG_TARGET_NB_REGS];
+ uint64_t stack[(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE)
+ / sizeof(uint64_t)];
+ bool carry = false;
+
+ regs[TCG_AREG0] = (tcg_target_ulong)env;
+ regs[TCG_REG_CALL_STACK] = (uintptr_t)stack;
+ tci_assert(tb_ptr);
+
+ for (;;) {
+ uint32_t insn;
+ TCGOpcode opc;
+ TCGReg r0, r1, r2, r3, r4;
+ tcg_target_ulong t1;
+ TCGCond condition;
+ uint8_t pos, len;
+ uint32_t tmp32;
+ uint64_t tmp64, taddr;
+ MemOpIdx oi;
+ int32_t ofs;
+ void *ptr;
+
+ insn = *tb_ptr++;
+ opc = extract32(insn, 0, 8);
+
+ switch (opc) {
+ case INDEX_op_call:
+ {
+ void *call_slots[MAX_CALL_IARGS];
+ ffi_cif *cif;
+ void *func;
+ unsigned i, s, n;
+
+ tci_args_nl(insn, tb_ptr, &len, &ptr);
+ func = ((void **)ptr)[0];
+ cif = ((void **)ptr)[1];
+
+ n = cif->nargs;
+ for (i = s = 0; i < n; ++i) {
+ ffi_type *t = cif->arg_types[i];
+ call_slots[i] = &stack[s];
+ s += DIV_ROUND_UP(t->size, 8);
+ }
+
+ /* Helper functions may need to access the "return address" */
+ tci_tb_ptr = (uintptr_t)tb_ptr;
+ ffi_call(cif, func, stack, call_slots);
+ }
+
+ switch (len) {
+ case 0: /* void */
+ break;
+ case 1: /* uint32_t */
+ /*
+ * The result winds up "left-aligned" in the stack[0] slot.
+ * Note that libffi has an odd special case in that it will
+ * always widen an integral result to ffi_arg.
+ */
+ if (sizeof(ffi_arg) == 8) {
+ regs[TCG_REG_R0] = (uint32_t)stack[0];
+ } else {
+ regs[TCG_REG_R0] = *(uint32_t *)stack;
+ }
+ break;
+ case 2: /* uint64_t */
+ /*
+ * For TCG_TARGET_REG_BITS == 32, the register pair
+ * must stay in host memory order.
+ */
+ memcpy(®s[TCG_REG_R0], stack, 8);
+ break;
+ case 3: /* Int128 */
+ memcpy(®s[TCG_REG_R0], stack, 16);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+
+ case INDEX_op_br:
+ tci_args_l(insn, tb_ptr, &ptr);
+ tb_ptr = ptr;
+ continue;
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_setcond2_i32:
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
+ regs[r0] = tci_compare64(tci_uint64(regs[r2], regs[r1]),
+ tci_uint64(regs[r4], regs[r3]),
+ condition);
+ break;
+#elif TCG_TARGET_REG_BITS == 64
+ case INDEX_op_setcond:
+ tci_args_rrrc(insn, &r0, &r1, &r2, &condition);
+ regs[r0] = tci_compare64(regs[r1], regs[r2], condition);
+ break;
+ case INDEX_op_movcond:
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
+ tmp32 = tci_compare64(regs[r1], regs[r2], condition);
+ regs[r0] = regs[tmp32 ? r3 : r4];
+ break;
+#endif
+ case INDEX_op_mov:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = regs[r1];
+ break;
+ case INDEX_op_tci_movi:
+ tci_args_ri(insn, &r0, &t1);
+ regs[r0] = t1;
+ break;
+ case INDEX_op_tci_movl:
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
+ regs[r0] = *(tcg_target_ulong *)ptr;
+ break;
+ case INDEX_op_tci_setcarry:
+ carry = true;
+ break;
+
+ /* Load/store operations (32 bit). */
+
+ case INDEX_op_ld8u:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(uint8_t *)ptr;
+ break;
+ case INDEX_op_ld8s:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(int8_t *)ptr;
+ break;
+ case INDEX_op_ld16u:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(uint16_t *)ptr;
+ break;
+ case INDEX_op_ld16s:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(int16_t *)ptr;
+ break;
+ case INDEX_op_ld:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(tcg_target_ulong *)ptr;
+ break;
+ case INDEX_op_st8:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ *(uint8_t *)ptr = regs[r0];
+ break;
+ case INDEX_op_st16:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ *(uint16_t *)ptr = regs[r0];
+ break;
+ case INDEX_op_st:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ *(tcg_target_ulong *)ptr = regs[r0];
+ break;
+
+ /* Arithmetic operations (mixed 32/64 bit). */
+
+ case INDEX_op_add:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] + regs[r2];
+ break;
+ case INDEX_op_sub:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] - regs[r2];
+ break;
+ case INDEX_op_mul:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] * regs[r2];
+ break;
+ case INDEX_op_and:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] & regs[r2];
+ break;
+ case INDEX_op_or:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] | regs[r2];
+ break;
+ case INDEX_op_xor:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] ^ regs[r2];
+ break;
+ case INDEX_op_andc:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] & ~regs[r2];
+ break;
+ case INDEX_op_orc:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] | ~regs[r2];
+ break;
+ case INDEX_op_eqv:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ~(regs[r1] ^ regs[r2]);
+ break;
+ case INDEX_op_nand:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ~(regs[r1] & regs[r2]);
+ break;
+ case INDEX_op_nor:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ~(regs[r1] | regs[r2]);
+ break;
+ case INDEX_op_neg:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = -regs[r1];
+ break;
+ case INDEX_op_not:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = ~regs[r1];
+ break;
+ case INDEX_op_ctpop:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = ctpop_tr(regs[r1]);
+ break;
+ case INDEX_op_addco:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ t1 = regs[r1] + regs[r2];
+ carry = t1 < regs[r1];
+ regs[r0] = t1;
+ break;
+ case INDEX_op_addci:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] + regs[r2] + carry;
+ break;
+ case INDEX_op_addcio:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ if (carry) {
+ t1 = regs[r1] + regs[r2] + 1;
+ carry = t1 <= regs[r1];
+ } else {
+ t1 = regs[r1] + regs[r2];
+ carry = t1 < regs[r1];
+ }
+ regs[r0] = t1;
+ break;
+ case INDEX_op_subbo:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ carry = regs[r1] < regs[r2];
+ regs[r0] = regs[r1] - regs[r2];
+ break;
+ case INDEX_op_subbi:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] - regs[r2] - carry;
+ break;
+ case INDEX_op_subbio:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ if (carry) {
+ carry = regs[r1] <= regs[r2];
+ regs[r0] = regs[r1] - regs[r2] - 1;
+ } else {
+ carry = regs[r1] < regs[r2];
+ regs[r0] = regs[r1] - regs[r2];
+ }
+ break;
+ case INDEX_op_muls2:
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+#if TCG_TARGET_REG_BITS == 32
+ tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3];
+ tci_write_reg64(regs, r1, r0, tmp64);
+#else
+ muls64(®s[r0], ®s[r1], regs[r2], regs[r3]);
+#endif
+ break;
+ case INDEX_op_mulu2:
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+#if TCG_TARGET_REG_BITS == 32
+ tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3];
+ tci_write_reg64(regs, r1, r0, tmp64);
+#else
+ mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]);
+#endif
+ break;
+
+ /* Arithmetic operations (32 bit). */
+
+ case INDEX_op_tci_divs32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2];
+ break;
+ case INDEX_op_tci_divu32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2];
+ break;
+ case INDEX_op_tci_rems32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2];
+ break;
+ case INDEX_op_tci_remu32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2];
+ break;
+ case INDEX_op_tci_clz32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ tmp32 = regs[r1];
+ regs[r0] = tmp32 ? clz32(tmp32) : regs[r2];
+ break;
+ case INDEX_op_tci_ctz32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ tmp32 = regs[r1];
+ regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2];
+ break;
+ case INDEX_op_tci_setcond32:
+ tci_args_rrrc(insn, &r0, &r1, &r2, &condition);
+ regs[r0] = tci_compare32(regs[r1], regs[r2], condition);
+ break;
+ case INDEX_op_tci_movcond32:
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
+ tmp32 = tci_compare32(regs[r1], regs[r2], condition);
+ regs[r0] = regs[tmp32 ? r3 : r4];
+ break;
+
+ /* Shift/rotate operations. */
+
+ case INDEX_op_shl:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] << (regs[r2] % TCG_TARGET_REG_BITS);
+ break;
+ case INDEX_op_shr:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] >> (regs[r2] % TCG_TARGET_REG_BITS);
+ break;
+ case INDEX_op_sar:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ((tcg_target_long)regs[r1]
+ >> (regs[r2] % TCG_TARGET_REG_BITS));
+ break;
+ case INDEX_op_tci_rotl32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = rol32(regs[r1], regs[r2] & 31);
+ break;
+ case INDEX_op_tci_rotr32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ror32(regs[r1], regs[r2] & 31);
+ break;
+ case INDEX_op_deposit:
+ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len);
+ regs[r0] = deposit_tr(regs[r1], pos, len, regs[r2]);
+ break;
+ case INDEX_op_extract:
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
+ regs[r0] = extract_tr(regs[r1], pos, len);
+ break;
+ case INDEX_op_sextract:
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
+ regs[r0] = sextract_tr(regs[r1], pos, len);
+ break;
+ case INDEX_op_brcond:
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
+ if (regs[r0]) {
+ tb_ptr = ptr;
+ }
+ break;
+ case INDEX_op_bswap16:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = bswap16(regs[r1]);
+ break;
+ case INDEX_op_bswap32:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = bswap32(regs[r1]);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ /* Load/store operations (64 bit). */
+
+ case INDEX_op_ld32u:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(uint32_t *)ptr;
+ break;
+ case INDEX_op_ld32s:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ regs[r0] = *(int32_t *)ptr;
+ break;
+ case INDEX_op_st32:
+ tci_args_rrs(insn, &r0, &r1, &ofs);
+ ptr = (void *)(regs[r1] + ofs);
+ *(uint32_t *)ptr = regs[r0];
+ break;
+
+ /* Arithmetic operations (64 bit). */
+
+ case INDEX_op_divs:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2];
+ break;
+ case INDEX_op_divu:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2];
+ break;
+ case INDEX_op_rems:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2];
+ break;
+ case INDEX_op_remu:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2];
+ break;
+ case INDEX_op_clz:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2];
+ break;
+ case INDEX_op_ctz:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2];
+ break;
+
+ /* Shift/rotate operations (64 bit). */
+
+ case INDEX_op_rotl:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = rol64(regs[r1], regs[r2] & 63);
+ break;
+ case INDEX_op_rotr:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ regs[r0] = ror64(regs[r1], regs[r2] & 63);
+ break;
+ case INDEX_op_ext_i32_i64:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = (int32_t)regs[r1];
+ break;
+ case INDEX_op_extu_i32_i64:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = (uint32_t)regs[r1];
+ break;
+ case INDEX_op_bswap64:
+ tci_args_rr(insn, &r0, &r1);
+ regs[r0] = bswap64(regs[r1]);
+ break;
+#endif /* TCG_TARGET_REG_BITS == 64 */
+
+ /* QEMU specific operations. */
+
+ case INDEX_op_exit_tb:
+ tci_args_l(insn, tb_ptr, &ptr);
+ return (uintptr_t)ptr;
+
+ case INDEX_op_goto_tb:
+ tci_args_l(insn, tb_ptr, &ptr);
+ tb_ptr = *(void **)ptr;
+ break;
+
+ case INDEX_op_goto_ptr:
+ tci_args_r(insn, &r0);
+ ptr = (void *)regs[r0];
+ if (!ptr) {
+ return 0;
+ }
+ tb_ptr = ptr;
+ break;
+
+ case INDEX_op_qemu_ld:
+ tci_args_rrm(insn, &r0, &r1, &oi);
+ taddr = regs[r1];
+ regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr);
+ break;
+
+ case INDEX_op_qemu_st:
+ tci_args_rrm(insn, &r0, &r1, &oi);
+ taddr = regs[r1];
+ tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr);
+ break;
+
+ case INDEX_op_qemu_ld2:
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 32);
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+ taddr = regs[r2];
+ oi = regs[r3];
+ tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr);
+ tci_write_reg64(regs, r1, r0, tmp64);
+ break;
+
+ case INDEX_op_qemu_st2:
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 32);
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+ tmp64 = tci_uint64(regs[r1], regs[r0]);
+ taddr = regs[r2];
+ oi = regs[r3];
+ tci_qemu_st(env, taddr, tmp64, oi, tb_ptr);
+ break;
+
+ case INDEX_op_mb:
+ /* Ensure ordering for all kinds */
+ smp_mb();
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+}
+
+/*
+ * Disassembler that matches the interpreter
+ */
+
+static const char *str_r(TCGReg r)
+{
+ static const char regs[TCG_TARGET_NB_REGS][4] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "env", "sp"
+ };
+
+ QEMU_BUILD_BUG_ON(TCG_AREG0 != TCG_REG_R14);
+ QEMU_BUILD_BUG_ON(TCG_REG_CALL_STACK != TCG_REG_R15);
+
+ assert((unsigned)r < TCG_TARGET_NB_REGS);
+ return regs[r];
+}
+
+static const char *str_c(TCGCond c)
+{
+ static const char cond[16][8] = {
+ [TCG_COND_NEVER] = "never",
+ [TCG_COND_ALWAYS] = "always",
+ [TCG_COND_EQ] = "eq",
+ [TCG_COND_NE] = "ne",
+ [TCG_COND_LT] = "lt",
+ [TCG_COND_GE] = "ge",
+ [TCG_COND_LE] = "le",
+ [TCG_COND_GT] = "gt",
+ [TCG_COND_LTU] = "ltu",
+ [TCG_COND_GEU] = "geu",
+ [TCG_COND_LEU] = "leu",
+ [TCG_COND_GTU] = "gtu",
+ [TCG_COND_TSTEQ] = "tsteq",
+ [TCG_COND_TSTNE] = "tstne",
+ };
+
+ assert((unsigned)c < ARRAY_SIZE(cond));
+ assert(cond[c][0] != 0);
+ return cond[c];
+}
+
+/* Disassemble TCI bytecode. */
+int print_insn_tci(bfd_vma addr, disassemble_info *info)
+{
+ const uint32_t *tb_ptr = (const void *)(uintptr_t)addr;
+ const TCGOpDef *def;
+ const char *op_name;
+ uint32_t insn;
+ TCGOpcode op;
+ TCGReg r0, r1, r2, r3, r4;
+ tcg_target_ulong i1;
+ int32_t s2;
+ TCGCond c;
+ MemOpIdx oi;
+ uint8_t pos, len;
+ void *ptr;
+
+ /* TCI is always the host, so we don't need to load indirect. */
+ insn = *tb_ptr++;
+
+ info->fprintf_func(info->stream, "%08x ", insn);
+
+ op = extract32(insn, 0, 8);
+ def = &tcg_op_defs[op];
+ op_name = def->name;
+
+ switch (op) {
+ case INDEX_op_br:
+ case INDEX_op_exit_tb:
+ case INDEX_op_goto_tb:
+ tci_args_l(insn, tb_ptr, &ptr);
+ info->fprintf_func(info->stream, "%-12s %p", op_name, ptr);
+ break;
+
+ case INDEX_op_goto_ptr:
+ tci_args_r(insn, &r0);
+ info->fprintf_func(info->stream, "%-12s %s", op_name, str_r(r0));
+ break;
+
+ case INDEX_op_call:
+ tci_args_nl(insn, tb_ptr, &len, &ptr);
+ info->fprintf_func(info->stream, "%-12s %d, %p", op_name, len, ptr);
+ break;
+
+ case INDEX_op_brcond:
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
+ info->fprintf_func(info->stream, "%-12s %s, 0, ne, %p",
+ op_name, str_r(r0), ptr);
+ break;
+
+ case INDEX_op_setcond:
+ case INDEX_op_tci_setcond32:
+ tci_args_rrrc(insn, &r0, &r1, &r2, &c);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
+ op_name, str_r(r0), str_r(r1), str_r(r2), str_c(c));
+ break;
+
+ case INDEX_op_tci_movi:
+ tci_args_ri(insn, &r0, &i1);
+ info->fprintf_func(info->stream, "%-12s %s, 0x%" TCG_PRIlx,
+ op_name, str_r(r0), i1);
+ break;
+
+ case INDEX_op_tci_movl:
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
+ info->fprintf_func(info->stream, "%-12s %s, %p",
+ op_name, str_r(r0), ptr);
+ break;
+
+ case INDEX_op_tci_setcarry:
+ info->fprintf_func(info->stream, "%-12s", op_name);
+ break;
+
+ case INDEX_op_ld8u:
+ case INDEX_op_ld8s:
+ case INDEX_op_ld16u:
+ case INDEX_op_ld16s:
+ case INDEX_op_ld32u:
+ case INDEX_op_ld:
+ case INDEX_op_st8:
+ case INDEX_op_st16:
+ case INDEX_op_st32:
+ case INDEX_op_st:
+ tci_args_rrs(insn, &r0, &r1, &s2);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %d",
+ op_name, str_r(r0), str_r(r1), s2);
+ break;
+
+ case INDEX_op_bswap16:
+ case INDEX_op_bswap32:
+ case INDEX_op_ctpop:
+ case INDEX_op_mov:
+ case INDEX_op_neg:
+ case INDEX_op_not:
+ case INDEX_op_ext_i32_i64:
+ case INDEX_op_extu_i32_i64:
+ case INDEX_op_bswap64:
+ tci_args_rr(insn, &r0, &r1);
+ info->fprintf_func(info->stream, "%-12s %s, %s",
+ op_name, str_r(r0), str_r(r1));
+ break;
+
+ case INDEX_op_add:
+ case INDEX_op_addci:
+ case INDEX_op_addcio:
+ case INDEX_op_addco:
+ case INDEX_op_and:
+ case INDEX_op_andc:
+ case INDEX_op_clz:
+ case INDEX_op_ctz:
+ case INDEX_op_divs:
+ case INDEX_op_divu:
+ case INDEX_op_eqv:
+ case INDEX_op_mul:
+ case INDEX_op_nand:
+ case INDEX_op_nor:
+ case INDEX_op_or:
+ case INDEX_op_orc:
+ case INDEX_op_rems:
+ case INDEX_op_remu:
+ case INDEX_op_rotl:
+ case INDEX_op_rotr:
+ case INDEX_op_sar:
+ case INDEX_op_shl:
+ case INDEX_op_shr:
+ case INDEX_op_sub:
+ case INDEX_op_subbi:
+ case INDEX_op_subbio:
+ case INDEX_op_subbo:
+ case INDEX_op_xor:
+ case INDEX_op_tci_ctz32:
+ case INDEX_op_tci_clz32:
+ case INDEX_op_tci_divs32:
+ case INDEX_op_tci_divu32:
+ case INDEX_op_tci_rems32:
+ case INDEX_op_tci_remu32:
+ case INDEX_op_tci_rotl32:
+ case INDEX_op_tci_rotr32:
+ tci_args_rrr(insn, &r0, &r1, &r2);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s",
+ op_name, str_r(r0), str_r(r1), str_r(r2));
+ break;
+
+ case INDEX_op_deposit:
+ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s, %d, %d",
+ op_name, str_r(r0), str_r(r1), str_r(r2), pos, len);
+ break;
+
+ case INDEX_op_extract:
+ case INDEX_op_sextract:
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
+ info->fprintf_func(info->stream, "%-12s %s,%s,%d,%d",
+ op_name, str_r(r0), str_r(r1), pos, len);
+ break;
+
+ case INDEX_op_tci_movcond32:
+ case INDEX_op_movcond:
+ case INDEX_op_setcond2_i32:
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &c);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s",
+ op_name, str_r(r0), str_r(r1), str_r(r2),
+ str_r(r3), str_r(r4), str_c(c));
+ break;
+
+ case INDEX_op_muls2:
+ case INDEX_op_mulu2:
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
+ op_name, str_r(r0), str_r(r1),
+ str_r(r2), str_r(r3));
+ break;
+
+ case INDEX_op_qemu_ld:
+ case INDEX_op_qemu_st:
+ tci_args_rrm(insn, &r0, &r1, &oi);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %x",
+ op_name, str_r(r0), str_r(r1), oi);
+ break;
+
+ case INDEX_op_qemu_ld2:
+ case INDEX_op_qemu_st2:
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
+ info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
+ op_name, str_r(r0), str_r(r1),
+ str_r(r2), str_r(r3));
+ break;
+
+ case 0:
+ /* tcg_out_nop_fill uses zeros */
+ if (insn == 0) {
+ info->fprintf_func(info->stream, "align");
+ break;
+ }
+ /* fall through */
+
+ default:
+ info->fprintf_func(info->stream, "illegal opcode %d", op);
+ break;
+ }
+
+ return sizeof(insn);
+}
diff --git a/tcg/wasm32/tcg-target-con-set.h b/tcg/wasm32/tcg-target-con-set.h
new file mode 100644
index 0000000000..ae2dc3b844
--- /dev/null
+++ b/tcg/wasm32/tcg-target-con-set.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * TCI target-specific constraint sets.
+ * Copyright (c) 2021 Linaro
+ */
+
+/*
+ * C_On_Im(...) defines a constraint set with <n> outputs and <m> inputs.
+ * Each operand should be a sequence of constraint letters as defined by
+ * tcg-target-con-str.h; the constraint combination is inclusive or.
+ */
+C_O0_I1(r)
+C_O0_I2(r, r)
+C_O0_I3(r, r, r)
+C_O0_I4(r, r, r, r)
+C_O1_I1(r, r)
+C_O1_I2(r, r, r)
+C_O1_I4(r, r, r, r, r)
+C_O2_I1(r, r, r)
+C_O2_I2(r, r, r, r)
+C_O2_I4(r, r, r, r, r, r)
diff --git a/tcg/wasm32/tcg-target-con-str.h b/tcg/wasm32/tcg-target-con-str.h
new file mode 100644
index 0000000000..87c0f19e9c
--- /dev/null
+++ b/tcg/wasm32/tcg-target-con-str.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Define TCI target-specific operand constraints.
+ * Copyright (c) 2021 Linaro
+ */
+
+/*
+ * Define constraint letters for register sets:
+ * REGS(letter, register_mask)
+ */
+REGS('r', MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS))
diff --git a/tcg/wasm32/tcg-target-has.h b/tcg/wasm32/tcg-target-has.h
new file mode 100644
index 0000000000..ab07ce1fcb
--- /dev/null
+++ b/tcg/wasm32/tcg-target-has.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Define target-specific opcode support
+ * Copyright (c) 2009, 2011 Stefan Weil
+ */
+
+#ifndef TCG_TARGET_HAS_H
+#define TCG_TARGET_HAS_H
+
+#if TCG_TARGET_REG_BITS == 64
+#define TCG_TARGET_HAS_extr_i64_i32 0
+#endif /* TCG_TARGET_REG_BITS == 64 */
+
+#define TCG_TARGET_HAS_qemu_ldst_i128 0
+
+#define TCG_TARGET_HAS_tst 1
+
+#define TCG_TARGET_extract_valid(type, ofs, len) 1
+#define TCG_TARGET_sextract_valid(type, ofs, len) 1
+#define TCG_TARGET_deposit_valid(type, ofs, len) 1
+
+#endif
diff --git a/tcg/wasm32/tcg-target-mo.h b/tcg/wasm32/tcg-target-mo.h
new file mode 100644
index 0000000000..779872e39a
--- /dev/null
+++ b/tcg/wasm32/tcg-target-mo.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Define target-specific memory model
+ * Copyright (c) 2009, 2011 Stefan Weil
+ */
+
+#ifndef TCG_TARGET_MO_H
+#define TCG_TARGET_MO_H
+
+/*
+ * We could notice __i386__ or __s390x__ and reduce the barriers depending
+ * on the host. But if you want performance, you use the normal backend.
+ * We prefer consistency across hosts on this.
+ */
+#define TCG_TARGET_DEFAULT_MO 0
+
+#endif
diff --git a/tcg/wasm32/tcg-target-opc.h.inc b/tcg/wasm32/tcg-target-opc.h.inc
new file mode 100644
index 0000000000..4eb32ed736
--- /dev/null
+++ b/tcg/wasm32/tcg-target-opc.h.inc
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/* These opcodes for use between the tci generator and interpreter. */
+DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT)
+DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT)
+DEF(tci_setcarry, 0, 0, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_clz32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_ctz32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_rotl32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_rotr32, 1, 2, 0, TCG_OPF_NOT_PRESENT)
+DEF(tci_setcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT)
+DEF(tci_movcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT)
diff --git a/tcg/wasm32/tcg-target-reg-bits.h b/tcg/wasm32/tcg-target-reg-bits.h
new file mode 100644
index 0000000000..dcb1a203f8
--- /dev/null
+++ b/tcg/wasm32/tcg-target-reg-bits.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Define target-specific register size
+ * Copyright (c) 2009, 2011 Stefan Weil
+ */
+
+#ifndef TCG_TARGET_REG_BITS_H
+#define TCG_TARGET_REG_BITS_H
+
+#if UINTPTR_MAX == UINT32_MAX
+# define TCG_TARGET_REG_BITS 32
+#elif UINTPTR_MAX == UINT64_MAX
+# define TCG_TARGET_REG_BITS 64
+#else
+# error Unknown pointer size for tci target
+#endif
+
+#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
new file mode 100644
index 0000000000..33b81f1fe2
--- /dev/null
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -0,0 +1,1320 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009, 2011 Stefan Weil
+ *
+ * Based on tci/tcg-target.c.inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Used for function call generation. */
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+#define TCG_TARGET_STACK_ALIGN 8
+#if TCG_TARGET_REG_BITS == 32
+# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EVEN
+# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN
+# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN
+#else
+# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL
+# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL
+# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL
+#endif
+#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL
+
+static TCGConstraintSetIndex
+tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags)
+{
+ return C_NotImplemented;
+}
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ /* Either 2 or 4 of these are call clobbered, so use them last. */
+ TCG_REG_R3,
+ TCG_REG_R2,
+ TCG_REG_R1,
+ TCG_REG_R0,
+};
+
+/* No call arguments via registers. All will be stored on the "stack". */
+static const int tcg_target_call_iarg_regs[] = { };
+
+static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot)
+{
+ tcg_debug_assert(kind == TCG_CALL_RET_NORMAL);
+ tcg_debug_assert(slot >= 0 && slot < 128 / TCG_TARGET_REG_BITS);
+ return TCG_REG_R0 + slot;
+}
+
+#ifdef CONFIG_DEBUG_TCG
+static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "r00",
+ "r01",
+ "r02",
+ "r03",
+ "r04",
+ "r05",
+ "r06",
+ "r07",
+ "r08",
+ "r09",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+};
+#endif
+
+static bool patch_reloc(tcg_insn_unit *code_ptr, int type,
+ intptr_t value, intptr_t addend)
+{
+ intptr_t diff = value - (intptr_t)(code_ptr + 1);
+
+ tcg_debug_assert(addend == 0);
+ tcg_debug_assert(type == 20);
+
+ if (diff == sextract32(diff, 0, type)) {
+ tcg_patch32(code_ptr, deposit32(*code_ptr, 32 - type, type, diff));
+ return true;
+ }
+ return false;
+}
+
+static void stack_bounds_check(TCGReg base, intptr_t offset)
+{
+ if (base == TCG_REG_CALL_STACK) {
+ tcg_debug_assert(offset >= 0);
+ tcg_debug_assert(offset < (TCG_STATIC_CALL_ARGS_SIZE +
+ TCG_STATIC_FRAME_SIZE));
+ }
+}
+
+static void tcg_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_out_reloc(s, s->code_ptr, 20, l0, 0);
+ insn = deposit32(insn, 0, 8, op);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_p(TCGContext *s, TCGOpcode op, void *p0)
+{
+ tcg_insn_unit insn = 0;
+ intptr_t diff;
+
+ /* Special case for exit_tb: map null -> 0. */
+ if (p0 == NULL) {
+ diff = 0;
+ } else {
+ diff = p0 - (void *)(s->code_ptr + 1);
+ tcg_debug_assert(diff != 0);
+ if (diff != sextract32(diff, 0, 20)) {
+ tcg_raise_tb_overflow(s);
+ }
+ }
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 12, 20, diff);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_r(TCGContext *s, TCGOpcode op, TCGReg r0)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_v(TCGContext *s, TCGOpcode op)
+{
+ tcg_out32(s, (uint8_t)op);
+}
+
+static void tcg_out_op_ri(TCGContext *s, TCGOpcode op, TCGReg r0, int32_t i1)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_debug_assert(i1 == sextract32(i1, 0, 20));
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 20, i1);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rl(TCGContext *s, TCGOpcode op, TCGReg r0, TCGLabel *l1)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_out_reloc(s, s->code_ptr, 20, l1, 0);
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrm(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, TCGArg m2)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_debug_assert(m2 == extract32(m2, 0, 16));
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 16, m2);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrr(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, TCGReg r2)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 4, r2);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrs(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, intptr_t i2)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_debug_assert(i2 == sextract32(i2, 0, 16));
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 16, i2);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
+ TCGReg r1, uint8_t b2, uint8_t b3)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_debug_assert(b2 == extract32(b2, 0, 6));
+ tcg_debug_assert(b3 == extract32(b3, 0, 6));
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 6, b2);
+ insn = deposit32(insn, 22, 6, b3);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrrc(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, TCGReg r2, TCGCond c3)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 4, r2);
+ insn = deposit32(insn, 20, 4, c3);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
+ TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4)
+{
+ tcg_insn_unit insn = 0;
+
+ tcg_debug_assert(b3 == extract32(b3, 0, 6));
+ tcg_debug_assert(b4 == extract32(b4, 0, 6));
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 4, r2);
+ insn = deposit32(insn, 20, 6, b3);
+ insn = deposit32(insn, 26, 6, b4);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrrr(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 4, r2);
+ insn = deposit32(insn, 20, 4, r3);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
+ TCGReg r0, TCGReg r1, TCGReg r2,
+ TCGReg r3, TCGReg r4, TCGCond c5)
+{
+ tcg_insn_unit insn = 0;
+
+ insn = deposit32(insn, 0, 8, op);
+ insn = deposit32(insn, 8, 4, r0);
+ insn = deposit32(insn, 12, 4, r1);
+ insn = deposit32(insn, 16, 4, r2);
+ insn = deposit32(insn, 20, 4, r3);
+ insn = deposit32(insn, 24, 4, r4);
+ insn = deposit32(insn, 28, 4, c5);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ stack_bounds_check(base, offset);
+ if (offset != sextract32(offset, 0, 16)) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset);
+ tcg_out_op_rrr(s, INDEX_op_add, TCG_REG_TMP, TCG_REG_TMP, base);
+ base = TCG_REG_TMP;
+ offset = 0;
+ }
+ tcg_out_op_rrs(s, op, val, base, offset);
+}
+
+static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
+ intptr_t offset)
+{
+ TCGOpcode op = INDEX_op_ld;
+
+ if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) {
+ op = INDEX_op_ld32u;
+ }
+ tcg_out_ldst(s, op, val, base, offset);
+}
+
+static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
+{
+ tcg_out_op_rr(s, INDEX_op_mov, ret, arg);
+ return true;
+}
+
+static void tcg_out_movi(TCGContext *s, TCGType type,
+ TCGReg ret, tcg_target_long arg)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+#if TCG_TARGET_REG_BITS == 64
+ arg = (int32_t)arg;
+ /* fall through */
+ case TCG_TYPE_I64:
+#endif
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (arg == sextract32(arg, 0, 20)) {
+ tcg_out_op_ri(s, INDEX_op_tci_movi, ret, arg);
+ } else {
+ tcg_insn_unit insn = 0;
+
+ new_pool_label(s, arg, 20, s->code_ptr, 0);
+ insn = deposit32(insn, 0, 8, INDEX_op_tci_movl);
+ insn = deposit32(insn, 8, 4, ret);
+ tcg_out32(s, insn);
+ }
+}
+
+static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd,
+ TCGReg rs, unsigned pos, unsigned len)
+{
+ tcg_out_op_rrbb(s, INDEX_op_extract, rd, rs, pos, len);
+}
+
+static const TCGOutOpExtract outop_extract = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tcg_out_extract,
+};
+
+static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd,
+ TCGReg rs, unsigned pos, unsigned len)
+{
+ tcg_out_op_rrbb(s, INDEX_op_sextract, rd, rs, pos, len);
+}
+
+static const TCGOutOpExtract outop_sextract = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tcg_out_sextract,
+};
+
+static const TCGOutOpExtract2 outop_extract2 = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
+{
+ tcg_out_sextract(s, type, rd, rs, 0, 8);
+}
+
+static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8);
+}
+
+static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
+{
+ tcg_out_sextract(s, type, rd, rs, 0, 16);
+}
+
+static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16);
+}
+
+static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
+ tcg_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+}
+
+static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
+ tcg_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+}
+
+static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_out_ext32s(s, rd, rs);
+}
+
+static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_out_ext32u(s, rd, rs);
+}
+
+static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
+ tcg_out_mov(s, TCG_TYPE_I32, rd, rs);
+}
+
+static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2)
+{
+ return false;
+}
+
+static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs,
+ tcg_target_long imm)
+{
+ /* This function is only used for passing structs by reference. */
+ g_assert_not_reached();
+}
+
+static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func,
+ const TCGHelperInfo *info)
+{
+ ffi_cif *cif = info->cif;
+ tcg_insn_unit insn = 0;
+ uint8_t which;
+
+ if (cif->rtype == &ffi_type_void) {
+ which = 0;
+ } else {
+ tcg_debug_assert(cif->rtype->size == 4 ||
+ cif->rtype->size == 8 ||
+ cif->rtype->size == 16);
+ which = ctz32(cif->rtype->size) - 1;
+ }
+ new_pool_l2(s, 20, s->code_ptr, 0, (uintptr_t)func, (uintptr_t)cif);
+ insn = deposit32(insn, 0, 8, INDEX_op_call);
+ insn = deposit32(insn, 8, 4, which);
+ tcg_out32(s, insn);
+}
+
+static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg)
+{
+ tcg_out_op_p(s, INDEX_op_exit_tb, (void *)arg);
+}
+
+static void tcg_out_goto_tb(TCGContext *s, int which)
+{
+ /* indirect jump method. */
+ tcg_out_op_p(s, INDEX_op_goto_tb, (void *)get_jmp_target_addr(s, which));
+ set_jmp_reset_offset(s, which);
+}
+
+static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0)
+{
+ tcg_out_op_r(s, INDEX_op_goto_ptr, a0);
+}
+
+void tb_target_set_jmp_target(const TranslationBlock *tb, int n,
+ uintptr_t jmp_rx, uintptr_t jmp_rw)
+{
+ /* Always indirect, nothing to do */
+}
+
+static void tgen_add(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_add, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_add = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_add,
+};
+
+static TCGConstraintSetIndex cset_addsubcarry(TCGType type, unsigned flags)
+{
+ return type == TCG_TYPE_REG ? C_O1_I2(r, r, r) : C_NotImplemented;
+}
+
+static void tgen_addco(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_addco, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_addco = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_addco,
+};
+
+static void tgen_addci(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_addci, a0, a1, a2);
+}
+
+static const TCGOutOpAddSubCarry outop_addci = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_addci,
+};
+
+static void tgen_addcio(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_addcio, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_addcio = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_addcio,
+};
+
+static void tcg_out_set_carry(TCGContext *s)
+{
+ tcg_out_op_v(s, INDEX_op_tci_setcarry);
+}
+
+static void tgen_and(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_and, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_and = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_and,
+};
+
+static void tgen_andc(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_andc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_andc = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_andc,
+};
+
+static void tgen_clz(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_clz32
+ : INDEX_op_clz);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_clz = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_clz,
+};
+
+static void tgen_ctz(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_ctz32
+ : INDEX_op_ctz);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_ctz = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_ctz,
+};
+
+static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1,
+ TCGReg a2, unsigned ofs, unsigned len)
+{
+ tcg_out_op_rrrbb(s, INDEX_op_deposit, a0, a1, a2, ofs, len);
+}
+
+static const TCGOutOpDeposit outop_deposit = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_deposit,
+};
+
+static void tgen_divs(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_divs32
+ : INDEX_op_divs);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_divs = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_divs,
+};
+
+static const TCGOutOpDivRem outop_divs2 = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static void tgen_divu(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_divu32
+ : INDEX_op_divu);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_divu = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_divu,
+};
+
+static const TCGOutOpDivRem outop_divu2 = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static void tgen_eqv(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_eqv, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_eqv = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_eqv,
+};
+
+#if TCG_TARGET_REG_BITS == 64
+static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1)
+{
+ tcg_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32);
+}
+
+static const TCGOutOpUnary outop_extrh_i64_i32 = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_extrh_i64_i32,
+};
+#endif
+
+static void tgen_mul(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_mul, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_mul = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_mul,
+};
+
+static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags)
+{
+ return type == TCG_TYPE_REG ? C_O2_I2(r, r, r, r) : C_NotImplemented;
+}
+
+static void tgen_muls2(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3)
+{
+ tcg_out_op_rrrr(s, INDEX_op_muls2, a0, a1, a2, a3);
+}
+
+static const TCGOutOpMul2 outop_muls2 = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_mul2,
+ .out_rrrr = tgen_muls2,
+};
+
+static const TCGOutOpBinary outop_mulsh = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static void tgen_mulu2(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3)
+{
+ tcg_out_op_rrrr(s, INDEX_op_mulu2, a0, a1, a2, a3);
+}
+
+static const TCGOutOpMul2 outop_mulu2 = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_mul2,
+ .out_rrrr = tgen_mulu2,
+};
+
+static const TCGOutOpBinary outop_muluh = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static void tgen_nand(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_nand, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_nand = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_nand,
+};
+
+static void tgen_nor(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_nor, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_nor = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_nor,
+};
+
+static void tgen_or(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_or, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_or = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_or,
+};
+
+static void tgen_orc(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_orc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_orc = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_orc,
+};
+
+static void tgen_rems(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_rems32
+ : INDEX_op_rems);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_rems = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_rems,
+};
+
+static void tgen_remu(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_remu32
+ : INDEX_op_remu);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_remu = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_remu,
+};
+
+static void tgen_rotl(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_rotl32
+ : INDEX_op_rotl);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_rotl = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_rotl,
+};
+
+static void tgen_rotr(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_rotr32
+ : INDEX_op_rotr);
+ tcg_out_op_rrr(s, opc, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_rotr = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_rotr,
+};
+
+static void tgen_sar(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ if (type < TCG_TYPE_REG) {
+ tcg_out_ext32s(s, TCG_REG_TMP, a1);
+ a1 = TCG_REG_TMP;
+ }
+ tcg_out_op_rrr(s, INDEX_op_sar, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_sar = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_sar,
+};
+
+static void tgen_shl(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_shl, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_shl = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_shl,
+};
+
+static void tgen_shr(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ if (type < TCG_TYPE_REG) {
+ tcg_out_ext32u(s, TCG_REG_TMP, a1);
+ a1 = TCG_REG_TMP;
+ }
+ tcg_out_op_rrr(s, INDEX_op_shr, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_shr = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_shr,
+};
+
+static void tgen_sub(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_sub, a0, a1, a2);
+}
+
+static const TCGOutOpSubtract outop_sub = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_sub,
+};
+
+static void tgen_subbo(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_subbo, a0, a1, a2);
+}
+
+static const TCGOutOpAddSubCarry outop_subbo = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_subbo,
+};
+
+static void tgen_subbi(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_subbi, a0, a1, a2);
+}
+
+static const TCGOutOpAddSubCarry outop_subbi = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_subbi,
+};
+
+static void tgen_subbio(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_subbio, a0, a1, a2);
+}
+
+static const TCGOutOpAddSubCarry outop_subbio = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_addsubcarry,
+ .out_rrr = tgen_subbio,
+};
+
+static void tcg_out_set_borrow(TCGContext *s)
+{
+ tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */
+}
+
+static void tgen_xor(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_out_op_rrr(s, INDEX_op_xor, a0, a1, a2);
+}
+
+static const TCGOutOpBinary outop_xor = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_xor,
+};
+
+static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+{
+ tcg_out_op_rr(s, INDEX_op_ctpop, a0, a1);
+}
+
+static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags)
+{
+ return type == TCG_TYPE_REG ? C_O1_I1(r, r) : C_NotImplemented;
+}
+
+static const TCGOutOpUnary outop_ctpop = {
+ .base.static_constraint = C_Dynamic,
+ .base.dynamic_constraint = cset_ctpop,
+ .out_rr = tgen_ctpop,
+};
+
+static void tgen_bswap16(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, unsigned flags)
+{
+ tcg_out_op_rr(s, INDEX_op_bswap16, a0, a1);
+ if (flags & TCG_BSWAP_OS) {
+ tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16);
+ }
+}
+
+static const TCGOutOpBswap outop_bswap16 = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_bswap16,
+};
+
+static void tgen_bswap32(TCGContext *s, TCGType type,
+ TCGReg a0, TCGReg a1, unsigned flags)
+{
+ tcg_out_op_rr(s, INDEX_op_bswap32, a0, a1);
+ if (flags & TCG_BSWAP_OS) {
+ tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32);
+ }
+}
+
+static const TCGOutOpBswap outop_bswap32 = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_bswap32,
+};
+
+#if TCG_TARGET_REG_BITS == 64
+static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+{
+ tcg_out_op_rr(s, INDEX_op_bswap64, a0, a1);
+}
+
+static const TCGOutOpUnary outop_bswap64 = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_bswap64,
+};
+#endif
+
+static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+{
+ tcg_out_op_rr(s, INDEX_op_neg, a0, a1);
+}
+
+static const TCGOutOpUnary outop_neg = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_neg,
+};
+
+static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+{
+ tcg_out_op_rr(s, INDEX_op_not, a0, a1);
+}
+
+static const TCGOutOpUnary outop_not = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out_rr = tgen_not,
+};
+
+static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg dest, TCGReg arg1, TCGReg arg2)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_setcond32
+ : INDEX_op_setcond);
+ tcg_out_op_rrrc(s, opc, dest, arg1, arg2, cond);
+}
+
+static const TCGOutOpSetcond outop_setcond = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_setcond,
+};
+
+static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg dest, TCGReg arg1, TCGReg arg2)
+{
+ tgen_setcond(s, type, cond, dest, arg1, arg2);
+ tgen_neg(s, type, dest, dest);
+}
+
+static const TCGOutOpSetcond outop_negsetcond = {
+ .base.static_constraint = C_O1_I2(r, r, r),
+ .out_rrr = tgen_negsetcond,
+};
+
+static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg arg0, TCGReg arg1, TCGLabel *l)
+{
+ tgen_setcond(s, type, cond, TCG_REG_TMP, arg0, arg1);
+ tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l);
+}
+
+static const TCGOutOpBrcond outop_brcond = {
+ .base.static_constraint = C_O0_I2(r, r),
+ .out_rr = tgen_brcond,
+};
+
+static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2,
+ TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf)
+{
+ TCGOpcode opc = (type == TCG_TYPE_I32
+ ? INDEX_op_tci_movcond32
+ : INDEX_op_movcond);
+ tcg_out_op_rrrrrc(s, opc, ret, c1, c2, vt, vf, cond);
+}
+
+static const TCGOutOpMovcond outop_movcond = {
+ .base.static_constraint = C_O1_I4(r, r, r, r, r),
+ .out = tgen_movcond,
+};
+
+static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah,
+ TCGArg bl, bool const_bl,
+ TCGArg bh, bool const_bh, TCGLabel *l)
+{
+ tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP,
+ al, ah, bl, bh, cond);
+ tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l);
+}
+
+#if TCG_TARGET_REG_BITS != 32
+__attribute__((unused))
+#endif
+static const TCGOutOpBrcond2 outop_brcond2 = {
+ .base.static_constraint = C_O0_I4(r, r, r, r),
+ .out = tgen_brcond2,
+};
+
+static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret,
+ TCGReg al, TCGReg ah,
+ TCGArg bl, bool const_bl,
+ TCGArg bh, bool const_bh)
+{
+ tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, ret, al, ah, bl, bh, cond);
+}
+
+#if TCG_TARGET_REG_BITS != 32
+__attribute__((unused))
+#endif
+static const TCGOutOpSetcond2 outop_setcond2 = {
+ .base.static_constraint = C_O1_I4(r, r, r, r, r),
+ .out = tgen_setcond2,
+};
+
+static void tcg_out_mb(TCGContext *s, unsigned a0)
+{
+ tcg_out_op_v(s, INDEX_op_mb);
+}
+
+static void tcg_out_br(TCGContext *s, TCGLabel *l)
+{
+ tcg_out_op_l(s, INDEX_op_br, l);
+}
+
+static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld8u, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld8u = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld8u,
+};
+
+static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld8s, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld8s = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld8s,
+};
+
+static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld16u, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld16u = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld16u,
+};
+
+static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld16s, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld16s = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld16s,
+};
+
+#if TCG_TARGET_REG_BITS == 64
+static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld32u, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld32u = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld32u,
+};
+
+static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_ld32s, dest, base, offset);
+}
+
+static const TCGOutOpLoad outop_ld32s = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_ld32s,
+};
+#endif
+
+static void tgen_st8(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_st8, data, base, offset);
+}
+
+static const TCGOutOpStore outop_st8 = {
+ .base.static_constraint = C_O0_I2(r, r),
+ .out_r = tgen_st8,
+};
+
+static void tgen_st16(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg base, ptrdiff_t offset)
+{
+ tcg_out_ldst(s, INDEX_op_st16, data, base, offset);
+}
+
+static const TCGOutOpStore outop_st16 = {
+ .base.static_constraint = C_O0_I2(r, r),
+ .out_r = tgen_st16,
+};
+
+static const TCGOutOpStore outop_st = {
+ .base.static_constraint = C_O0_I2(r, r),
+ .out_r = tcg_out_st,
+};
+
+static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg addr, MemOpIdx oi)
+{
+ tcg_out_op_rrm(s, INDEX_op_qemu_ld, data, addr, oi);
+}
+
+static const TCGOutOpQemuLdSt outop_qemu_ld = {
+ .base.static_constraint = C_O1_I1(r, r),
+ .out = tgen_qemu_ld,
+};
+
+static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo,
+ TCGReg datahi, TCGReg addr, MemOpIdx oi)
+{
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi);
+ tcg_out_op_rrrr(s, INDEX_op_qemu_ld2, datalo, datahi, addr, TCG_REG_TMP);
+}
+
+static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = {
+ .base.static_constraint =
+ TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r),
+ .out =
+ TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_ld2,
+};
+
+static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg addr, MemOpIdx oi)
+{
+ tcg_out_op_rrm(s, INDEX_op_qemu_st, data, addr, oi);
+}
+
+static const TCGOutOpQemuLdSt outop_qemu_st = {
+ .base.static_constraint = C_O0_I2(r, r),
+ .out = tgen_qemu_st,
+};
+
+static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo,
+ TCGReg datahi, TCGReg addr, MemOpIdx oi)
+{
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi);
+ tcg_out_op_rrrr(s, INDEX_op_qemu_st2, datalo, datahi, addr, TCG_REG_TMP);
+}
+
+static const TCGOutOpQemuLdSt2 outop_qemu_st2 = {
+ .base.static_constraint =
+ TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r),
+ .out =
+ TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_st2,
+};
+
+static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
+ intptr_t offset)
+{
+ TCGOpcode op = INDEX_op_st;
+
+ if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) {
+ op = INDEX_op_st32;
+ }
+ tcg_out_ldst(s, op, val, base, offset);
+}
+
+static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val,
+ TCGReg base, intptr_t ofs)
+{
+ return false;
+}
+
+/* Test if a constant matches the constraint. */
+static bool tcg_target_const_match(int64_t val, int ct,
+ TCGType type, TCGCond cond, int vece)
+{
+ return ct & TCG_CT_CONST;
+}
+
+static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
+{
+ memset(p, 0, sizeof(*p) * count);
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+ /* The current code uses uint8_t for tcg operations. */
+ tcg_debug_assert(tcg_op_defs_max <= UINT8_MAX);
+
+ /* Registers available for 32 bit operations. */
+ tcg_target_available_regs[TCG_TYPE_I32] = BIT(TCG_TARGET_NB_REGS) - 1;
+ /* Registers available for 64 bit operations. */
+ tcg_target_available_regs[TCG_TYPE_I64] = BIT(TCG_TARGET_NB_REGS) - 1;
+ /*
+ * The interpreter "registers" are in the local stack frame and
+ * cannot be clobbered by the called helper functions. However,
+ * the interpreter assumes a 128-bit return value and assigns to
+ * the return value registers.
+ */
+ tcg_target_call_clobber_regs =
+ MAKE_64BIT_MASK(TCG_REG_R0, 128 / TCG_TARGET_REG_BITS);
+
+ s->reserved_regs = 0;
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
+
+ /* The call arguments come first, followed by the temp storage. */
+ tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE,
+ TCG_STATIC_FRAME_SIZE);
+}
+
+/* Generate global QEMU prologue and epilogue code. */
+static inline void tcg_target_qemu_prologue(TCGContext *s)
+{
+}
+
+static void tcg_out_tb_start(TCGContext *s)
+{
+ /* nothing to do */
+}
+
+bool tcg_target_has_memory_bswap(MemOp memop)
+{
+ return true;
+}
+
+static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
+{
+ g_assert_not_reached();
+}
+
+static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
+{
+ g_assert_not_reached();
+}
diff --git a/tcg/wasm32/tcg-target.h b/tcg/wasm32/tcg-target.h
new file mode 100644
index 0000000000..bd03aa1bc4
--- /dev/null
+++ b/tcg/wasm32/tcg-target.h
@@ -0,0 +1,76 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009, 2011 Stefan Weil
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * This code implements a TCG which does not generate machine code for some
+ * real target machine but which generates virtual machine code for an
+ * interpreter. Interpreted pseudo code is slow, but it works on any host.
+ *
+ * Some remarks might help in understanding the code:
+ *
+ * "target" or "TCG target" is the machine which runs the generated code.
+ * This is different to the usual meaning in QEMU where "target" is the
+ * emulated machine. So normally QEMU host is identical to TCG target.
+ * Here the TCG target is a virtual machine, but this virtual machine must
+ * use the same word size like the real machine.
+ * Therefore, we need both 32 and 64 bit virtual machines (interpreter).
+ */
+
+#ifndef TCG_TARGET_H
+#define TCG_TARGET_H
+
+#define TCG_TARGET_INTERPRETER 1
+#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
+
+/* Number of registers available. */
+#define TCG_TARGET_NB_REGS 16
+
+/* List of registers which are used by TCG. */
+typedef enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+
+ TCG_REG_TMP = TCG_REG_R13,
+ TCG_AREG0 = TCG_REG_R14,
+ TCG_REG_CALL_STACK = TCG_REG_R15,
+} TCGReg;
+
+#define HAVE_TCG_QEMU_TB_EXEC
+
+#endif /* TCG_TARGET_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 02/33] tcg/wasm32: Do not use TCI disassembler in Wasm backend
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 01/33] tcg: Fork TCI for wasm32 backend Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 03/33] meson: Enable to build wasm backend Kohei Tokunaga
` (30 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Wasm backend should implement its own disassember for Wasm
instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.c | 243 +--------------------------------------------------
1 file changed, 1 insertion(+), 242 deletions(-)
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
index 6de9b26b76..4bc53d76d0 100644
--- a/tcg/wasm32.c
+++ b/tcg/wasm32.c
@@ -831,246 +831,5 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
}
/*
- * Disassembler that matches the interpreter
+ * TODO: Disassembler is not implemented
*/
-
-static const char *str_r(TCGReg r)
-{
- static const char regs[TCG_TARGET_NB_REGS][4] = {
- "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
- "r8", "r9", "r10", "r11", "r12", "r13", "env", "sp"
- };
-
- QEMU_BUILD_BUG_ON(TCG_AREG0 != TCG_REG_R14);
- QEMU_BUILD_BUG_ON(TCG_REG_CALL_STACK != TCG_REG_R15);
-
- assert((unsigned)r < TCG_TARGET_NB_REGS);
- return regs[r];
-}
-
-static const char *str_c(TCGCond c)
-{
- static const char cond[16][8] = {
- [TCG_COND_NEVER] = "never",
- [TCG_COND_ALWAYS] = "always",
- [TCG_COND_EQ] = "eq",
- [TCG_COND_NE] = "ne",
- [TCG_COND_LT] = "lt",
- [TCG_COND_GE] = "ge",
- [TCG_COND_LE] = "le",
- [TCG_COND_GT] = "gt",
- [TCG_COND_LTU] = "ltu",
- [TCG_COND_GEU] = "geu",
- [TCG_COND_LEU] = "leu",
- [TCG_COND_GTU] = "gtu",
- [TCG_COND_TSTEQ] = "tsteq",
- [TCG_COND_TSTNE] = "tstne",
- };
-
- assert((unsigned)c < ARRAY_SIZE(cond));
- assert(cond[c][0] != 0);
- return cond[c];
-}
-
-/* Disassemble TCI bytecode. */
-int print_insn_tci(bfd_vma addr, disassemble_info *info)
-{
- const uint32_t *tb_ptr = (const void *)(uintptr_t)addr;
- const TCGOpDef *def;
- const char *op_name;
- uint32_t insn;
- TCGOpcode op;
- TCGReg r0, r1, r2, r3, r4;
- tcg_target_ulong i1;
- int32_t s2;
- TCGCond c;
- MemOpIdx oi;
- uint8_t pos, len;
- void *ptr;
-
- /* TCI is always the host, so we don't need to load indirect. */
- insn = *tb_ptr++;
-
- info->fprintf_func(info->stream, "%08x ", insn);
-
- op = extract32(insn, 0, 8);
- def = &tcg_op_defs[op];
- op_name = def->name;
-
- switch (op) {
- case INDEX_op_br:
- case INDEX_op_exit_tb:
- case INDEX_op_goto_tb:
- tci_args_l(insn, tb_ptr, &ptr);
- info->fprintf_func(info->stream, "%-12s %p", op_name, ptr);
- break;
-
- case INDEX_op_goto_ptr:
- tci_args_r(insn, &r0);
- info->fprintf_func(info->stream, "%-12s %s", op_name, str_r(r0));
- break;
-
- case INDEX_op_call:
- tci_args_nl(insn, tb_ptr, &len, &ptr);
- info->fprintf_func(info->stream, "%-12s %d, %p", op_name, len, ptr);
- break;
-
- case INDEX_op_brcond:
- tci_args_rl(insn, tb_ptr, &r0, &ptr);
- info->fprintf_func(info->stream, "%-12s %s, 0, ne, %p",
- op_name, str_r(r0), ptr);
- break;
-
- case INDEX_op_setcond:
- case INDEX_op_tci_setcond32:
- tci_args_rrrc(insn, &r0, &r1, &r2, &c);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
- op_name, str_r(r0), str_r(r1), str_r(r2), str_c(c));
- break;
-
- case INDEX_op_tci_movi:
- tci_args_ri(insn, &r0, &i1);
- info->fprintf_func(info->stream, "%-12s %s, 0x%" TCG_PRIlx,
- op_name, str_r(r0), i1);
- break;
-
- case INDEX_op_tci_movl:
- tci_args_rl(insn, tb_ptr, &r0, &ptr);
- info->fprintf_func(info->stream, "%-12s %s, %p",
- op_name, str_r(r0), ptr);
- break;
-
- case INDEX_op_tci_setcarry:
- info->fprintf_func(info->stream, "%-12s", op_name);
- break;
-
- case INDEX_op_ld8u:
- case INDEX_op_ld8s:
- case INDEX_op_ld16u:
- case INDEX_op_ld16s:
- case INDEX_op_ld32u:
- case INDEX_op_ld:
- case INDEX_op_st8:
- case INDEX_op_st16:
- case INDEX_op_st32:
- case INDEX_op_st:
- tci_args_rrs(insn, &r0, &r1, &s2);
- info->fprintf_func(info->stream, "%-12s %s, %s, %d",
- op_name, str_r(r0), str_r(r1), s2);
- break;
-
- case INDEX_op_bswap16:
- case INDEX_op_bswap32:
- case INDEX_op_ctpop:
- case INDEX_op_mov:
- case INDEX_op_neg:
- case INDEX_op_not:
- case INDEX_op_ext_i32_i64:
- case INDEX_op_extu_i32_i64:
- case INDEX_op_bswap64:
- tci_args_rr(insn, &r0, &r1);
- info->fprintf_func(info->stream, "%-12s %s, %s",
- op_name, str_r(r0), str_r(r1));
- break;
-
- case INDEX_op_add:
- case INDEX_op_addci:
- case INDEX_op_addcio:
- case INDEX_op_addco:
- case INDEX_op_and:
- case INDEX_op_andc:
- case INDEX_op_clz:
- case INDEX_op_ctz:
- case INDEX_op_divs:
- case INDEX_op_divu:
- case INDEX_op_eqv:
- case INDEX_op_mul:
- case INDEX_op_nand:
- case INDEX_op_nor:
- case INDEX_op_or:
- case INDEX_op_orc:
- case INDEX_op_rems:
- case INDEX_op_remu:
- case INDEX_op_rotl:
- case INDEX_op_rotr:
- case INDEX_op_sar:
- case INDEX_op_shl:
- case INDEX_op_shr:
- case INDEX_op_sub:
- case INDEX_op_subbi:
- case INDEX_op_subbio:
- case INDEX_op_subbo:
- case INDEX_op_xor:
- case INDEX_op_tci_ctz32:
- case INDEX_op_tci_clz32:
- case INDEX_op_tci_divs32:
- case INDEX_op_tci_divu32:
- case INDEX_op_tci_rems32:
- case INDEX_op_tci_remu32:
- case INDEX_op_tci_rotl32:
- case INDEX_op_tci_rotr32:
- tci_args_rrr(insn, &r0, &r1, &r2);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s",
- op_name, str_r(r0), str_r(r1), str_r(r2));
- break;
-
- case INDEX_op_deposit:
- tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s, %d, %d",
- op_name, str_r(r0), str_r(r1), str_r(r2), pos, len);
- break;
-
- case INDEX_op_extract:
- case INDEX_op_sextract:
- tci_args_rrbb(insn, &r0, &r1, &pos, &len);
- info->fprintf_func(info->stream, "%-12s %s,%s,%d,%d",
- op_name, str_r(r0), str_r(r1), pos, len);
- break;
-
- case INDEX_op_tci_movcond32:
- case INDEX_op_movcond:
- case INDEX_op_setcond2_i32:
- tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &c);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s",
- op_name, str_r(r0), str_r(r1), str_r(r2),
- str_r(r3), str_r(r4), str_c(c));
- break;
-
- case INDEX_op_muls2:
- case INDEX_op_mulu2:
- tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
- op_name, str_r(r0), str_r(r1),
- str_r(r2), str_r(r3));
- break;
-
- case INDEX_op_qemu_ld:
- case INDEX_op_qemu_st:
- tci_args_rrm(insn, &r0, &r1, &oi);
- info->fprintf_func(info->stream, "%-12s %s, %s, %x",
- op_name, str_r(r0), str_r(r1), oi);
- break;
-
- case INDEX_op_qemu_ld2:
- case INDEX_op_qemu_st2:
- tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
- info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s",
- op_name, str_r(r0), str_r(r1),
- str_r(r2), str_r(r3));
- break;
-
- case 0:
- /* tcg_out_nop_fill uses zeros */
- if (insn == 0) {
- info->fprintf_func(info->stream, "align");
- break;
- }
- /* fall through */
-
- default:
- info->fprintf_func(info->stream, "illegal opcode %d", op);
- break;
- }
-
- return sizeof(insn);
-}
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 03/33] meson: Enable to build wasm backend
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 01/33] tcg: Fork TCI for wasm32 backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 02/33] tcg/wasm32: Do not use TCI disassembler in Wasm backend Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 04/33] tcg/wasm32: Set TCG_TARGET_INSN_UNIT_SIZE to 1 Kohei Tokunaga
` (29 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Now that there is a backend for WebAssembly build (/tcg/wasm32/), the
requirement of --enable-tcg-interpreter in meson.build can be removed.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
meson.build | 4 ----
1 file changed, 4 deletions(-)
diff --git a/meson.build b/meson.build
index ad2053f968..c533243157 100644
--- a/meson.build
+++ b/meson.build
@@ -911,10 +911,6 @@ if have_tcg
if not get_option('tcg_interpreter')
error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu))
endif
- elif host_arch == 'wasm32'
- if not get_option('tcg_interpreter')
- error('WebAssembly host requires --enable-tcg-interpreter')
- endif
elif get_option('tcg_interpreter')
warning('Use of the TCG interpreter is not recommended on this host')
warning('architecture. There is a native TCG execution backend available')
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 04/33] tcg/wasm32: Set TCG_TARGET_INSN_UNIT_SIZE to 1
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (2 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 03/33] meson: Enable to build wasm backend Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 05/33] tcg/wasm32: Add and/or/xor instructions Kohei Tokunaga
` (28 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
WebAssembly instructions vary in size, including single-byte
instructions. This commit sets TCG_TARGET_INSN_UNIT_SIZE to 1 and updates
the TCI fork to use "tcg_insn_unit_tci" (a uint32_t) for 4-byte operations.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 40 ++++++++++++++++++++-----------------
tcg/wasm32/tcg-target.h | 2 +-
2 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 33b81f1fe2..126f9c0de7 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -39,6 +39,8 @@
#endif
#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL
+typedef uint32_t tcg_insn_unit_tci;
+
static TCGConstraintSetIndex
tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags)
{
@@ -96,16 +98,18 @@ static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
};
#endif
-static bool patch_reloc(tcg_insn_unit *code_ptr, int type,
+static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
+ tcg_insn_unit_tci *code_ptr = (tcg_insn_unit_tci *)code_ptr_i;
intptr_t diff = value - (intptr_t)(code_ptr + 1);
tcg_debug_assert(addend == 0);
tcg_debug_assert(type == 20);
if (diff == sextract32(diff, 0, type)) {
- tcg_patch32(code_ptr, deposit32(*code_ptr, 32 - type, type, diff));
+ tcg_patch32((tcg_insn_unit *)code_ptr,
+ deposit32(*code_ptr, 32 - type, type, diff));
return true;
}
return false;
@@ -122,7 +126,7 @@ static void stack_bounds_check(TCGReg base, intptr_t offset)
static void tcg_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_out_reloc(s, s->code_ptr, 20, l0, 0);
insn = deposit32(insn, 0, 8, op);
@@ -131,14 +135,14 @@ static void tcg_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0)
static void tcg_out_op_p(TCGContext *s, TCGOpcode op, void *p0)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
intptr_t diff;
/* Special case for exit_tb: map null -> 0. */
if (p0 == NULL) {
diff = 0;
} else {
- diff = p0 - (void *)(s->code_ptr + 1);
+ diff = p0 - (void *)(s->code_ptr + 4);
tcg_debug_assert(diff != 0);
if (diff != sextract32(diff, 0, 20)) {
tcg_raise_tb_overflow(s);
@@ -151,7 +155,7 @@ static void tcg_out_op_p(TCGContext *s, TCGOpcode op, void *p0)
static void tcg_out_op_r(TCGContext *s, TCGOpcode op, TCGReg r0)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -165,7 +169,7 @@ static void tcg_out_op_v(TCGContext *s, TCGOpcode op)
static void tcg_out_op_ri(TCGContext *s, TCGOpcode op, TCGReg r0, int32_t i1)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_debug_assert(i1 == sextract32(i1, 0, 20));
insn = deposit32(insn, 0, 8, op);
@@ -176,7 +180,7 @@ static void tcg_out_op_ri(TCGContext *s, TCGOpcode op, TCGReg r0, int32_t i1)
static void tcg_out_op_rl(TCGContext *s, TCGOpcode op, TCGReg r0, TCGLabel *l1)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_out_reloc(s, s->code_ptr, 20, l1, 0);
insn = deposit32(insn, 0, 8, op);
@@ -186,7 +190,7 @@ static void tcg_out_op_rl(TCGContext *s, TCGOpcode op, TCGReg r0, TCGLabel *l1)
static void tcg_out_op_rr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -197,7 +201,7 @@ static void tcg_out_op_rr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1)
static void tcg_out_op_rrm(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGArg m2)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_debug_assert(m2 == extract32(m2, 0, 16));
insn = deposit32(insn, 0, 8, op);
@@ -210,7 +214,7 @@ static void tcg_out_op_rrm(TCGContext *s, TCGOpcode op,
static void tcg_out_op_rrr(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGReg r2)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -222,7 +226,7 @@ static void tcg_out_op_rrr(TCGContext *s, TCGOpcode op,
static void tcg_out_op_rrs(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, intptr_t i2)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_debug_assert(i2 == sextract32(i2, 0, 16));
insn = deposit32(insn, 0, 8, op);
@@ -235,7 +239,7 @@ static void tcg_out_op_rrs(TCGContext *s, TCGOpcode op,
static void tcg_out_op_rrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
TCGReg r1, uint8_t b2, uint8_t b3)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_debug_assert(b2 == extract32(b2, 0, 6));
tcg_debug_assert(b3 == extract32(b3, 0, 6));
@@ -250,7 +254,7 @@ static void tcg_out_op_rrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
static void tcg_out_op_rrrc(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGReg r2, TCGCond c3)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -263,7 +267,7 @@ static void tcg_out_op_rrrc(TCGContext *s, TCGOpcode op,
static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
tcg_debug_assert(b3 == extract32(b3, 0, 6));
tcg_debug_assert(b4 == extract32(b4, 0, 6));
@@ -279,7 +283,7 @@ static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
static void tcg_out_op_rrrr(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -293,7 +297,7 @@ static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGReg r2,
TCGReg r3, TCGReg r4, TCGCond c5)
{
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
insn = deposit32(insn, 0, 8, op);
insn = deposit32(insn, 8, 4, r0);
@@ -452,7 +456,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func,
const TCGHelperInfo *info)
{
ffi_cif *cif = info->cif;
- tcg_insn_unit insn = 0;
+ tcg_insn_unit_tci insn = 0;
uint8_t which;
if (cif->rtype == &ffi_type_void) {
diff --git a/tcg/wasm32/tcg-target.h b/tcg/wasm32/tcg-target.h
index bd03aa1bc4..2f0c27c905 100644
--- a/tcg/wasm32/tcg-target.h
+++ b/tcg/wasm32/tcg-target.h
@@ -41,7 +41,7 @@
#define TCG_TARGET_H
#define TCG_TARGET_INTERPRETER 1
-#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_INSN_UNIT_SIZE 1
#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
/* Number of registers available. */
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 05/33] tcg/wasm32: Add and/or/xor instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (3 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 04/33] tcg/wasm32: Set TCG_TARGET_INSN_UNIT_SIZE to 1 Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 06/33] tcg/wasm32: Add add/sub/mul instructions Kohei Tokunaga
` (27 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements and, or and xor operations using Wasm
instructions. Each TCG variable is mapped to a 64bit Wasm variable. In Wasm,
and/or/xor instructions operate on values by first pushing the operands into
the Wasm's stack using get instructions. The result is left on the stack and
this can be assigned to a variable by popping it using a set instruction.
The Wasm binary format is documented at [1]. In this backend, TCI
instructions are emitted to s->code_ptr, while the corresponding Wasm
instructions are generated into a separated buffer allocated via
tcg_malloc(). These two code buffers must be merged into the final code
buffer before tcg_gen_code returns.
[1] https://webassembly.github.io/spec/core/binary/index.html
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 137 +++++++++++++++++++++++++++++++++++-
1 file changed, 136 insertions(+), 1 deletion(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 126f9c0de7..e3a35c8415 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -98,6 +98,138 @@ static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
};
#endif
+/* converts a TCG register to a wasm variable index */
+static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
+ 0, /* TCG_REG_R0 */
+ 1, /* TCG_REG_R1 */
+ 2, /* TCG_REG_R2 */
+ 3, /* TCG_REG_R3 */
+ 4, /* TCG_REG_R4 */
+ 5, /* TCG_REG_R5 */
+ 6, /* TCG_REG_R6 */
+ 7, /* TCG_REG_R7 */
+ 8, /* TCG_REG_R8 */
+ 9, /* TCG_REG_R9 */
+ 10, /* TCG_REG_R10 */
+ 11, /* TCG_REG_R11 */
+ 12, /* TCG_REG_R12 */
+ 13, /* TCG_REG_R13 */
+ 14, /* TCG_REG_R14 */
+ 15, /* TCG_REG_R15 */
+};
+
+#define BUF_SIZE 1024
+typedef struct LinkedBuf {
+ struct LinkedBuf *next;
+ uint8_t data[BUF_SIZE];
+ uint32_t size;
+} LinkedBuf;
+
+static LinkedBuf *new_linked_buf(void)
+{
+ LinkedBuf *p = tcg_malloc(sizeof(LinkedBuf));
+ p->size = 0;
+ p->next = NULL;
+ return p;
+}
+
+static inline LinkedBuf *linked_buf_out8(LinkedBuf *buf, uint8_t v)
+{
+ if (buf->size == BUF_SIZE) {
+ buf->next = new_linked_buf();
+ buf = buf->next;
+ }
+ buf->data[buf->size++] = v;
+ return buf;
+}
+
+static inline int linked_buf_len(LinkedBuf *buf)
+{
+ int total = 0;
+ for (LinkedBuf *p = buf; p; p = p->next) {
+ total += p->size;
+ }
+ return total;
+}
+
+static inline void linked_buf_write(LinkedBuf *buf, void *dst)
+{
+ for (LinkedBuf *p = buf; p; p = p->next) {
+ memcpy(dst, p->data, p->size);
+ dst += p->size;
+ }
+}
+
+/*
+ * wasm code is generataed in the dynamically allocated buffer which
+ * are managed as a linked list.
+ */
+__thread LinkedBuf *sub_buf_root;
+__thread LinkedBuf *sub_buf_cur;
+
+static void init_sub_buf(void)
+{
+ sub_buf_root = new_linked_buf();
+ sub_buf_cur = sub_buf_root;
+}
+
+static inline int sub_buf_len(void)
+{
+ return linked_buf_len(sub_buf_root);
+}
+
+static inline void tcg_wasm_out8(TCGContext *s, uint32_t v)
+{
+ sub_buf_cur = linked_buf_out8(sub_buf_cur, v);
+}
+
+static void tcg_wasm_out_op_i64_and(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x83);
+}
+static void tcg_wasm_out_op_i64_or(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x84);
+}
+static void tcg_wasm_out_op_i64_xor(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x85);
+}
+static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
+{
+ tcg_wasm_out8(s, instr);
+ tcg_wasm_out8(s, i);
+}
+static void tcg_wasm_out_op_global_get(TCGContext *s, uint8_t i)
+{
+ tcg_wasm_out_op_var(s, 0x23, i);
+}
+static void tcg_wasm_out_op_global_set(TCGContext *s, uint8_t i)
+{
+ tcg_wasm_out_op_var(s, 0x24, i);
+}
+static void tcg_wasm_out_op_global_get_r(TCGContext *s, TCGReg r0)
+{
+ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
+}
+static void tcg_wasm_out_op_global_set_r(TCGContext *s, TCGReg r0)
+{
+ tcg_wasm_out_op_global_set(s, tcg_target_reg_index[r0]);
+}
+
+#define tcg_wasm_out_i64_calc(op) \
+ static void tcg_wasm_out_i64_calc_##op( \
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2) \
+ { \
+ tcg_wasm_out_op_global_get_r(s, arg1); \
+ tcg_wasm_out_op_global_get_r(s, arg2); \
+ tcg_wasm_out_op_i64_##op(s); \
+ tcg_wasm_out_op_global_set_r(s, ret); \
+ }
+tcg_wasm_out_i64_calc(and);
+tcg_wasm_out_i64_calc(or);
+tcg_wasm_out_i64_calc(xor);
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -557,6 +689,7 @@ static void tgen_and(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_and, a0, a1, a2);
+ tcg_wasm_out_i64_calc_and(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_and = {
@@ -747,6 +880,7 @@ static void tgen_or(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_or, a0, a1, a2);
+ tcg_wasm_out_i64_calc_or(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_or = {
@@ -918,6 +1052,7 @@ static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_xor, a0, a1, a2);
+ tcg_wasm_out_i64_calc_xor(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_xor = {
@@ -1305,7 +1440,7 @@ static inline void tcg_target_qemu_prologue(TCGContext *s)
static void tcg_out_tb_start(TCGContext *s)
{
- /* nothing to do */
+ init_sub_buf();
}
bool tcg_target_has_memory_bswap(MemOp memop)
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 06/33] tcg/wasm32: Add add/sub/mul instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (4 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 05/33] tcg/wasm32: Add and/or/xor instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 07/33] tcg/wasm32: Add shl/shr/sar instructions Kohei Tokunaga
` (26 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Add, sub and mul operations are implemented using the corresponding
instructions in Wasm.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index e3a35c8415..e4204d3956 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -195,6 +195,18 @@ static void tcg_wasm_out_op_i64_xor(TCGContext *s)
{
tcg_wasm_out8(s, 0x85);
}
+static void tcg_wasm_out_op_i64_add(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7c);
+}
+static void tcg_wasm_out_op_i64_sub(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7d);
+}
+static void tcg_wasm_out_op_i64_mul(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7e);
+}
static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
{
tcg_wasm_out8(s, instr);
@@ -229,6 +241,9 @@ static void tcg_wasm_out_op_global_set_r(TCGContext *s, TCGReg r0)
tcg_wasm_out_i64_calc(and);
tcg_wasm_out_i64_calc(or);
tcg_wasm_out_i64_calc(xor);
+tcg_wasm_out_i64_calc(add);
+tcg_wasm_out_i64_calc(sub);
+tcg_wasm_out_i64_calc(mul);
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
@@ -632,6 +647,7 @@ static void tgen_add(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_add, a0, a1, a2);
+ tcg_wasm_out_i64_calc_add(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_add = {
@@ -810,6 +826,7 @@ static void tgen_mul(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_mul, a0, a1, a2);
+ tcg_wasm_out_i64_calc_mul(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_mul = {
@@ -1000,6 +1017,7 @@ static void tgen_sub(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_sub, a0, a1, a2);
+ tcg_wasm_out_i64_calc_sub(s, a0, a1, a2);
}
static const TCGOutOpSubtract outop_sub = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 07/33] tcg/wasm32: Add shl/shr/sar instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (5 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 06/33] tcg/wasm32: Add add/sub/mul instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 08/33] tcg/wasm32: Add setcond/negsetcond/movcond instructions Kohei Tokunaga
` (25 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements shl, shr and sar operations using Wasm
instructions. The Wasm backend uses 64bit variables so the right shift
operation for 32bit values needs to extract the lower 32bit of the operand
before shifting. Additionally, since constant values must be encoded in
LEB128 format, this commit introduces an encoder function implemented
following [1].
[1] https://en.wikipedia.org/wiki/LEB128
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 107 ++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index e4204d3956..b9f2c9c195 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -207,6 +207,26 @@ static void tcg_wasm_out_op_i64_mul(TCGContext *s)
{
tcg_wasm_out8(s, 0x7e);
}
+static void tcg_wasm_out_op_i64_shl(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x86);
+}
+static void tcg_wasm_out_op_i64_shr_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x87);
+}
+static void tcg_wasm_out_op_i64_shr_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x88);
+}
+static void tcg_wasm_out_op_i32_wrap_i64(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0xa7);
+}
+static void tcg_wasm_out_op_i64_extend_i32_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0xac);
+}
static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
{
tcg_wasm_out8(s, instr);
@@ -245,6 +265,88 @@ tcg_wasm_out_i64_calc(add);
tcg_wasm_out_i64_calc(sub);
tcg_wasm_out_i64_calc(mul);
+static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v)
+{
+ bool more = true;
+ uint8_t b;
+ while (more) {
+ b = v & 0x7f;
+ v >>= 7;
+ if (((v == 0) && ((b & 0x40) == 0)) ||
+ ((v == -1) && ((b & 0x40) != 0))) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+ tcg_wasm_out8(s, b);
+ }
+}
+
+static void tcg_wasm_out_op_i64_const(TCGContext *s, int64_t v)
+{
+ tcg_wasm_out8(s, 0x42);
+ tcg_wasm_out_leb128_sint64_t(s, v);
+}
+
+static void tcg_wasm_out_shl(TCGContext *s, TCGReg ret,
+ TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_shl(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_shr_u(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_const(s, 0x7f);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_shr_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_shr_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_shr_s(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_const(s, 0x7f);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_shr_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_shr_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -975,11 +1077,13 @@ static const TCGOutOpBinary outop_rotr = {
static void tgen_sar(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
+ TCGReg orig_a1 = a1;
if (type < TCG_TYPE_REG) {
tcg_out_ext32s(s, TCG_REG_TMP, a1);
a1 = TCG_REG_TMP;
}
tcg_out_op_rrr(s, INDEX_op_sar, a0, a1, a2);
+ tcg_wasm_out_shr_s(s, type, a0, orig_a1, a2);
}
static const TCGOutOpBinary outop_sar = {
@@ -991,6 +1095,7 @@ static void tgen_shl(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_shl, a0, a1, a2);
+ tcg_wasm_out_shl(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_shl = {
@@ -1001,11 +1106,13 @@ static const TCGOutOpBinary outop_shl = {
static void tgen_shr(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
+ TCGReg orig_a1 = a1;
if (type < TCG_TYPE_REG) {
tcg_out_ext32u(s, TCG_REG_TMP, a1);
a1 = TCG_REG_TMP;
}
tcg_out_op_rrr(s, INDEX_op_shr, a0, a1, a2);
+ tcg_wasm_out_shr_u(s, type, a0, orig_a1, a2);
}
static const TCGOutOpBinary outop_shr = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 08/33] tcg/wasm32: Add setcond/negsetcond/movcond instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (6 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 07/33] tcg/wasm32: Add shl/shr/sar instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 09/33] tcg/wasm32: Add deposit/sextract/extract instrcutions Kohei Tokunaga
` (24 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements setcond and movcond operations using Wasm's if/else
instructions. Support for TCG_COND_TSTEQ and TCG_COND_TSTNE is not yet
implemented, so TCG_TARGET_HAS_tst is set to 0.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target-has.h | 2 +-
tcg/wasm32/tcg-target.c.inc | 136 ++++++++++++++++++++++++++++++++++--
2 files changed, 133 insertions(+), 5 deletions(-)
diff --git a/tcg/wasm32/tcg-target-has.h b/tcg/wasm32/tcg-target-has.h
index ab07ce1fcb..1eaa8f65f6 100644
--- a/tcg/wasm32/tcg-target-has.h
+++ b/tcg/wasm32/tcg-target-has.h
@@ -13,7 +13,7 @@
#define TCG_TARGET_HAS_qemu_ldst_i128 0
-#define TCG_TARGET_HAS_tst 1
+#define TCG_TARGET_HAS_tst 0
#define TCG_TARGET_extract_valid(type, ofs, len) 1
#define TCG_TARGET_sextract_valid(type, ofs, len) 1
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index b9f2c9c195..b1ed16ad7a 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -227,6 +227,23 @@ static void tcg_wasm_out_op_i64_extend_i32_s(TCGContext *s)
{
tcg_wasm_out8(s, 0xac);
}
+static void tcg_wasm_out_op_i64_extend_i32_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0xad);
+}
+static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x04);
+ tcg_wasm_out8(s, 0x7e);
+}
+static void tcg_wasm_out_op_else(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x05);
+}
+static void tcg_wasm_out_op_end(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x0b);
+}
static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
{
tcg_wasm_out8(s, instr);
@@ -265,6 +282,42 @@ tcg_wasm_out_i64_calc(add);
tcg_wasm_out_i64_calc(sub);
tcg_wasm_out_i64_calc(mul);
+static const struct {
+ uint8_t i32;
+ uint8_t i64;
+} tcg_cond_to_inst[] = {
+ [TCG_COND_EQ] = { 0x46 /* i32.eq */ , 0x51 /* i64.eq */},
+ [TCG_COND_NE] = { 0x47 /* i32.ne */ , 0x52 /* i64.ne */},
+ [TCG_COND_LT] = { 0x48 /* i32.lt_s */ , 0x53 /* i64.lt_s */},
+ [TCG_COND_GE] = { 0x4e /* i32.ge_s */ , 0x59 /* i64.ge_s */},
+ [TCG_COND_LE] = { 0x4c /* i32.le_s */ , 0x57 /* i64.le_s */},
+ [TCG_COND_GT] = { 0x4a /* i32.gt_s */ , 0x55 /* i64.gt_s */},
+ [TCG_COND_LTU] = { 0x49 /* i32.lt_u */ , 0x54 /* i64.lt_u */},
+ [TCG_COND_GEU] = { 0x4f /* i32.ge_u */ , 0x5a /* i64.ge_u */},
+ [TCG_COND_LEU] = { 0x4d /* i32.le_u */ , 0x58 /* i64.le_u */},
+ [TCG_COND_GTU] = { 0x4b /* i32.gt_u */ , 0x56 /* i64.gt_u */}
+};
+
+static void tcg_wasm_out_op_cond_i64(
+ TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2)
+{
+ uint8_t op = tcg_cond_to_inst[cond].i64;
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out8(s, op);
+}
+
+static void tcg_wasm_out_op_cond_i32(
+ TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2)
+{
+ uint8_t op = tcg_cond_to_inst[cond].i32;
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out8(s, op);
+}
+
static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v)
{
bool more = true;
@@ -288,6 +341,12 @@ static void tcg_wasm_out_op_i64_const(TCGContext *s, int64_t v)
tcg_wasm_out_leb128_sint64_t(s, v);
}
+static void tcg_wasm_out_op_not(TCGContext *s)
+{
+ tcg_wasm_out_op_i64_const(s, -1);
+ tcg_wasm_out_op_i64_xor(s);
+}
+
static void tcg_wasm_out_shl(TCGContext *s, TCGReg ret,
TCGReg arg1, TCGReg arg2)
{
@@ -347,6 +406,66 @@ static void tcg_wasm_out_shr_s(
}
}
+static void tcg_wasm_out_setcond(TCGContext *s, TCGType type, TCGReg ret,
+ TCGReg arg1, TCGReg arg2, TCGCond cond)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_negsetcond(TCGContext *s, TCGType type, TCGReg ret,
+ TCGReg arg1, TCGReg arg2, TCGCond cond)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_movcond(TCGContext *s, TCGType type, TCGReg ret,
+ TCGReg c1, TCGReg c2,
+ TCGReg v1, TCGReg v2,
+ TCGCond cond)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_cond_i32(s, cond, c1, c2);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_cond_i64(s, cond, c1, c2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_if_ret_i64(s);
+ tcg_wasm_out_op_global_get_r(s, v1);
+ tcg_wasm_out_op_else(s);
+ tcg_wasm_out_op_global_get_r(s, v2);
+ tcg_wasm_out_op_end(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -1261,8 +1380,8 @@ static const TCGOutOpUnary outop_not = {
.out_rr = tgen_not,
};
-static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond,
- TCGReg dest, TCGReg arg1, TCGReg arg2)
+static void tgen_setcond_tci(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg dest, TCGReg arg1, TCGReg arg2)
{
TCGOpcode opc = (type == TCG_TYPE_I32
? INDEX_op_tci_setcond32
@@ -1270,6 +1389,13 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond,
tcg_out_op_rrrc(s, opc, dest, arg1, arg2, cond);
}
+static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond,
+ TCGReg dest, TCGReg arg1, TCGReg arg2)
+{
+ tgen_setcond_tci(s, type, cond, dest, arg1, arg2);
+ tcg_wasm_out_setcond(s, type, dest, arg1, arg2, cond);
+}
+
static const TCGOutOpSetcond outop_setcond = {
.base.static_constraint = C_O1_I2(r, r, r),
.out_rrr = tgen_setcond,
@@ -1278,8 +1404,9 @@ static const TCGOutOpSetcond outop_setcond = {
static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond,
TCGReg dest, TCGReg arg1, TCGReg arg2)
{
- tgen_setcond(s, type, cond, dest, arg1, arg2);
+ tgen_setcond_tci(s, type, cond, dest, arg1, arg2);
tgen_neg(s, type, dest, dest);
+ tcg_wasm_out_negsetcond(s, type, dest, arg1, arg2, cond);
}
static const TCGOutOpSetcond outop_negsetcond = {
@@ -1290,7 +1417,7 @@ static const TCGOutOpSetcond outop_negsetcond = {
static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond,
TCGReg arg0, TCGReg arg1, TCGLabel *l)
{
- tgen_setcond(s, type, cond, TCG_REG_TMP, arg0, arg1);
+ tgen_setcond_tci(s, type, cond, TCG_REG_TMP, arg0, arg1);
tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l);
}
@@ -1307,6 +1434,7 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond,
? INDEX_op_tci_movcond32
: INDEX_op_movcond);
tcg_out_op_rrrrrc(s, opc, ret, c1, c2, vt, vf, cond);
+ tcg_wasm_out_movcond(s, type, ret, c1, c2, vt, vf, cond);
}
static const TCGOutOpMovcond outop_movcond = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 09/33] tcg/wasm32: Add deposit/sextract/extract instrcutions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (7 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 08/33] tcg/wasm32: Add setcond/negsetcond/movcond instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 10/33] tcg/wasm32: Add load and store instructions Kohei Tokunaga
` (23 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This implements deposit, sextract and extract operations. The
tcg_out_[s]extract functions are used by several other functions
(e.g. tcg_out_ext*) and are intended to emit TCI code. So they have been
renamed to tcg_tci_out_[s]extract.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 122 ++++++++++++++++++++++++++++++++----
1 file changed, 109 insertions(+), 13 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index b1ed16ad7a..d732d2f3c0 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -231,6 +231,20 @@ static void tcg_wasm_out_op_i64_extend_i32_u(TCGContext *s)
{
tcg_wasm_out8(s, 0xad);
}
+
+static void tcg_wasm_out_op_i32_and(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x71);
+}
+static void tcg_wasm_out_op_i32_or(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x72);
+}
+static void tcg_wasm_out_op_i32_shl(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x74);
+}
+
static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
{
tcg_wasm_out8(s, 0x04);
@@ -318,6 +332,23 @@ static void tcg_wasm_out_op_cond_i32(
tcg_wasm_out8(s, op);
}
+static void tcg_wasm_out_leb128_sint32_t(TCGContext *s, int32_t v)
+{
+ bool more = true;
+ uint8_t b;
+ while (more) {
+ b = v & 0x7f;
+ v >>= 7;
+ if (((v == 0) && ((b & 0x40) == 0)) ||
+ ((v == -1) && ((b & 0x40) != 0))) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+ tcg_wasm_out8(s, b);
+ }
+}
+
static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v)
{
bool more = true;
@@ -335,6 +366,12 @@ static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v)
}
}
+static void tcg_wasm_out_op_i32_const(TCGContext *s, int32_t v)
+{
+ tcg_wasm_out8(s, 0x41);
+ tcg_wasm_out_leb128_sint32_t(s, v);
+}
+
static void tcg_wasm_out_op_i64_const(TCGContext *s, int64_t v)
{
tcg_wasm_out8(s, 0x42);
@@ -466,6 +503,50 @@ static void tcg_wasm_out_movcond(TCGContext *s, TCGType type, TCGReg ret,
tcg_wasm_out_op_global_set_r(s, ret);
}
+static void tcg_wasm_out_deposit(TCGContext *s,
+ TCGReg dest, TCGReg arg1, TCGReg arg2,
+ int pos, int len)
+{
+ int64_t mask = (((int64_t)1 << len) - 1) << pos;
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_const(s, ~mask);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_const(s, pos);
+ tcg_wasm_out_op_i64_shl(s);
+ tcg_wasm_out_op_i64_const(s, mask);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_or(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
+static void tcg_wasm_out_extract(TCGContext *s, TCGReg dest, TCGReg arg1,
+ int pos, int len)
+{
+ int64_t mask = ~0ULL >> (64 - len);
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_const(s, pos);
+ tcg_wasm_out_op_i64_shr_u(s);
+ tcg_wasm_out_op_i64_const(s, mask);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
+static void tcg_wasm_out_sextract(TCGContext *s, TCGReg dest, TCGReg arg1,
+ int pos, int len)
+{
+ int rs = 64 - len;
+ int sl = rs - pos;
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ if (sl > 0) {
+ tcg_wasm_out_op_i64_const(s, sl);
+ tcg_wasm_out_op_i64_shl(s);
+ }
+ tcg_wasm_out_op_i64_const(s, rs);
+ tcg_wasm_out_op_i64_shr_s(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -677,6 +758,12 @@ static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
tcg_out32(s, insn);
}
+static void tcg_tci_out_extract(TCGContext *s, TCGType type, TCGReg rd,
+ TCGReg rs, unsigned pos, unsigned len)
+{
+ tcg_out_op_rrbb(s, INDEX_op_extract, rd, rs, pos, len);
+}
+
static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val,
TCGReg base, intptr_t offset)
{
@@ -737,7 +824,8 @@ static void tcg_out_movi(TCGContext *s, TCGType type,
static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd,
TCGReg rs, unsigned pos, unsigned len)
{
- tcg_out_op_rrbb(s, INDEX_op_extract, rd, rs, pos, len);
+ tcg_tci_out_extract(s, type, rd, rs, pos, len);
+ tcg_wasm_out_extract(s, rd, rs, pos, len);
}
static const TCGOutOpExtract outop_extract = {
@@ -745,10 +833,17 @@ static const TCGOutOpExtract outop_extract = {
.out_rr = tcg_out_extract,
};
+static void tcg_tci_out_sextract(TCGContext *s, TCGType type, TCGReg rd,
+ TCGReg rs, unsigned pos, unsigned len)
+{
+ tcg_out_op_rrbb(s, INDEX_op_sextract, rd, rs, pos, len);
+}
+
static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd,
TCGReg rs, unsigned pos, unsigned len)
{
- tcg_out_op_rrbb(s, INDEX_op_sextract, rd, rs, pos, len);
+ tcg_tci_out_sextract(s, type, rd, rs, pos, len);
+ tcg_wasm_out_sextract(s, rd, rs, pos, len);
}
static const TCGOutOpExtract outop_sextract = {
@@ -762,34 +857,34 @@ static const TCGOutOpExtract2 outop_extract2 = {
static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
{
- tcg_out_sextract(s, type, rd, rs, 0, 8);
+ tcg_tci_out_sextract(s, type, rd, rs, 0, 8);
}
static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
{
- tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8);
+ tcg_tci_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8);
}
static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
{
- tcg_out_sextract(s, type, rd, rs, 0, 16);
+ tcg_tci_out_sextract(s, type, rd, rs, 0, 16);
}
static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
{
- tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16);
+ tcg_tci_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16);
}
static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
- tcg_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+ tcg_tci_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32);
}
static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
- tcg_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+ tcg_tci_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32);
}
static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
@@ -977,6 +1072,7 @@ static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1,
TCGReg a2, unsigned ofs, unsigned len)
{
tcg_out_op_rrrbb(s, INDEX_op_deposit, a0, a1, a2, ofs, len);
+ tcg_wasm_out_deposit(s, a0, a1, a2, ofs, len);
}
static const TCGOutOpDeposit outop_deposit = {
@@ -1034,7 +1130,7 @@ static const TCGOutOpBinary outop_eqv = {
#if TCG_TARGET_REG_BITS == 64
static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1)
{
- tcg_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32);
+ tcg_tci_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32);
}
static const TCGOutOpUnary outop_extrh_i64_i32 = {
@@ -1198,7 +1294,7 @@ static void tgen_sar(TCGContext *s, TCGType type,
{
TCGReg orig_a1 = a1;
if (type < TCG_TYPE_REG) {
- tcg_out_ext32s(s, TCG_REG_TMP, a1);
+ tcg_tci_out_sextract(s, TCG_TYPE_I64, TCG_REG_TMP, a1, 0, 32);
a1 = TCG_REG_TMP;
}
tcg_out_op_rrr(s, INDEX_op_sar, a0, a1, a2);
@@ -1227,7 +1323,7 @@ static void tgen_shr(TCGContext *s, TCGType type,
{
TCGReg orig_a1 = a1;
if (type < TCG_TYPE_REG) {
- tcg_out_ext32u(s, TCG_REG_TMP, a1);
+ tcg_tci_out_extract(s, TCG_TYPE_I64, TCG_REG_TMP, a1, 0, 32);
a1 = TCG_REG_TMP;
}
tcg_out_op_rrr(s, INDEX_op_shr, a0, a1, a2);
@@ -1325,7 +1421,7 @@ static void tgen_bswap16(TCGContext *s, TCGType type,
{
tcg_out_op_rr(s, INDEX_op_bswap16, a0, a1);
if (flags & TCG_BSWAP_OS) {
- tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16);
+ tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16);
}
}
@@ -1339,7 +1435,7 @@ static void tgen_bswap32(TCGContext *s, TCGType type,
{
tcg_out_op_rr(s, INDEX_op_bswap32, a0, a1);
if (flags & TCG_BSWAP_OS) {
- tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32);
+ tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 10/33] tcg/wasm32: Add load and store instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (8 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 09/33] tcg/wasm32: Add deposit/sextract/extract instrcutions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 11/33] tcg/wasm32: Add mov/movi instructions Kohei Tokunaga
` (22 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements load and store operations using Wasm memory
instructions. Since Wasm's load/store instructions don't support negative
offset, address calculations are performed separately before the memory
access.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 242 ++++++++++++++++++++++++++++++++++++
1 file changed, 242 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index d732d2f3c0..9b024b03b9 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -232,6 +232,10 @@ static void tcg_wasm_out_op_i64_extend_i32_u(TCGContext *s)
tcg_wasm_out8(s, 0xad);
}
+static void tcg_wasm_out_op_i32_add(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x6a);
+}
static void tcg_wasm_out_op_i32_and(TCGContext *s)
{
tcg_wasm_out8(s, 0x71);
@@ -279,6 +283,11 @@ static void tcg_wasm_out_op_global_set_r(TCGContext *s, TCGReg r0)
{
tcg_wasm_out_op_global_set(s, tcg_target_reg_index[r0]);
}
+static void tcg_wasm_out_op_global_get_r_i32(TCGContext *s, TCGReg r0)
+{
+ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+}
#define tcg_wasm_out_i64_calc(op) \
static void tcg_wasm_out_i64_calc_##op( \
@@ -366,6 +375,19 @@ static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v)
}
}
+static void tcg_wasm_out_leb128_uint32_t(TCGContext *s, uint32_t v)
+{
+ uint8_t b;
+ do {
+ b = v & 0x7f;
+ v >>= 7;
+ if (v != 0) {
+ b |= 0x80;
+ }
+ tcg_wasm_out8(s, b);
+ } while (v != 0);
+}
+
static void tcg_wasm_out_op_i32_const(TCGContext *s, int32_t v)
{
tcg_wasm_out8(s, 0x41);
@@ -378,6 +400,68 @@ static void tcg_wasm_out_op_i64_const(TCGContext *s, int64_t v)
tcg_wasm_out_leb128_sint64_t(s, v);
}
+static void tcg_wasm_out_op_loadstore(
+ TCGContext *s, uint8_t instr, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out8(s, instr);
+ tcg_wasm_out_leb128_uint32_t(s, a);
+ tcg_wasm_out_leb128_uint32_t(s, o);
+}
+
+static void tcg_wasm_out_op_i64_store(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x37, a, o);
+}
+static void tcg_wasm_out_op_i64_store8(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x3c, a, o);
+}
+
+static void tcg_wasm_out_op_i64_store16(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x3d, a, o);
+}
+
+static void tcg_wasm_out_op_i64_store32(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x3e, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x29, a, o);
+}
+
+ static void tcg_wasm_out_op_i64_load8_s(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x30, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load8_u(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x31, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load16_s(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x32, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load16_u(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x33, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load32_s(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x34, a, o);
+}
+
+static void tcg_wasm_out_op_i64_load32_u(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x35, a, o);
+}
+
static void tcg_wasm_out_op_not(TCGContext *s)
{
tcg_wasm_out_op_i64_const(s, -1);
@@ -547,6 +631,154 @@ static void tcg_wasm_out_sextract(TCGContext *s, TCGReg dest, TCGReg arg1,
tcg_wasm_out_op_global_set_r(s, dest);
}
+static void tcg_wasm_out_ld(
+ TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_i64_load32_u(s, 0, offset);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_i64_load(s, 0, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld8s(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load8_s(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld8u(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load8_u(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld16s(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load16_s(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld16u(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load16_u(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld32s(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load32_s(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_ld32u(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_i64_load32_u(s, 0, offset);
+ tcg_wasm_out_op_global_set_r(s, val);
+}
+
+static void tcg_wasm_out_st(TCGContext *s, TCGType type, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_global_get_r(s, val);
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_i64_store32(s, 0, offset);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_i64_store(s, 0, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_st8(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_global_get_r(s, val);
+ tcg_wasm_out_op_i64_store8(s, 0, offset);
+}
+
+static void tcg_wasm_out_st16(TCGContext *s, TCGReg val,
+ TCGReg base, intptr_t offset)
+{
+ tcg_wasm_out_op_global_get_r_i32(s, base);
+ if (offset < 0) {
+ tcg_wasm_out_op_i32_const(s, offset);
+ tcg_wasm_out_op_i32_add(s);
+ offset = 0;
+ }
+ tcg_wasm_out_op_global_get_r(s, val);
+ tcg_wasm_out_op_i64_store16(s, 0, offset);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -786,6 +1018,7 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
op = INDEX_op_ld32u;
}
tcg_out_ldst(s, op, val, base, offset);
+ tcg_wasm_out_ld(s, type, val, base, offset);
}
static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
@@ -1585,6 +1818,7 @@ static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld8u, dest, base, offset);
+ tcg_wasm_out_ld8u(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld8u = {
@@ -1596,6 +1830,7 @@ static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld8s, dest, base, offset);
+ tcg_wasm_out_ld8s(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld8s = {
@@ -1607,6 +1842,7 @@ static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld16u, dest, base, offset);
+ tcg_wasm_out_ld16u(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld16u = {
@@ -1618,6 +1854,7 @@ static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld16s, dest, base, offset);
+ tcg_wasm_out_ld16s(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld16s = {
@@ -1630,6 +1867,7 @@ static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld32u, dest, base, offset);
+ tcg_wasm_out_ld32u(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld32u = {
@@ -1641,6 +1879,7 @@ static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_ld32s, dest, base, offset);
+ tcg_wasm_out_ld32s(s, dest, base, offset);
}
static const TCGOutOpLoad outop_ld32s = {
@@ -1653,6 +1892,7 @@ static void tgen_st8(TCGContext *s, TCGType type, TCGReg data,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_st8, data, base, offset);
+ tcg_wasm_out_st8(s, data, base, offset);
}
static const TCGOutOpStore outop_st8 = {
@@ -1664,6 +1904,7 @@ static void tgen_st16(TCGContext *s, TCGType type, TCGReg data,
TCGReg base, ptrdiff_t offset)
{
tcg_out_ldst(s, INDEX_op_st16, data, base, offset);
+ tcg_wasm_out_st16(s, data, base, offset);
}
static const TCGOutOpStore outop_st16 = {
@@ -1735,6 +1976,7 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
op = INDEX_op_st32;
}
tcg_out_ldst(s, op, val, base, offset);
+ tcg_wasm_out_st(s, type, val, base, offset);
}
static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val,
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 11/33] tcg/wasm32: Add mov/movi instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (9 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 10/33] tcg/wasm32: Add load and store instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 12/33] tcg/wasm32: Add ext instructions Kohei Tokunaga
` (21 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements mov/movi instructions. The tcg_out_mov[i] functions
are used by several other functions and are intended to emit TCI code. So
they have been renamed to tcg_tci_out_mov[i].
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 85 ++++++++++++++++++++++++++-----------
1 file changed, 60 insertions(+), 25 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 9b024b03b9..90a5705442 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -779,6 +779,28 @@ static void tcg_wasm_out_st16(TCGContext *s, TCGReg val,
tcg_wasm_out_op_i64_store16(s, 0, offset);
}
+static void tcg_wasm_out_mov(TCGContext *s, TCGReg ret, TCGReg arg)
+{
+ tcg_wasm_out_op_global_get_r(s, arg);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_movi(TCGContext *s, TCGType type,
+ TCGReg ret, tcg_target_long arg)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_i64_const(s, (int32_t)arg);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_i64_const(s, arg);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -990,6 +1012,33 @@ static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
tcg_out32(s, insn);
}
+static void tcg_tci_out_movi(TCGContext *s, TCGType type,
+ TCGReg ret, tcg_target_long arg)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+#if TCG_TARGET_REG_BITS == 64
+ arg = (int32_t)arg;
+ /* fall through */
+ case TCG_TYPE_I64:
+#endif
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (arg == sextract32(arg, 0, 20)) {
+ tcg_out_op_ri(s, INDEX_op_tci_movi, ret, arg);
+ } else {
+ tcg_insn_unit_tci insn = 0;
+
+ new_pool_label(s, arg, 20, s->code_ptr, 0);
+ insn = deposit32(insn, 0, 8, INDEX_op_tci_movl);
+ insn = deposit32(insn, 8, 4, ret);
+ tcg_out32(s, insn);
+ }
+}
+
static void tcg_tci_out_extract(TCGContext *s, TCGType type, TCGReg rd,
TCGReg rs, unsigned pos, unsigned len)
{
@@ -1001,7 +1050,7 @@ static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val,
{
stack_bounds_check(base, offset);
if (offset != sextract32(offset, 0, 16)) {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset);
+ tcg_tci_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset);
tcg_out_op_rrr(s, INDEX_op_add, TCG_REG_TMP, TCG_REG_TMP, base);
base = TCG_REG_TMP;
offset = 0;
@@ -1021,37 +1070,23 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
tcg_wasm_out_ld(s, type, val, base, offset);
}
-static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
+static void tcg_tci_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
{
tcg_out_op_rr(s, INDEX_op_mov, ret, arg);
+}
+
+static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
+{
+ tcg_tci_out_mov(s, type, ret, arg);
+ tcg_wasm_out_mov(s, ret, arg);
return true;
}
static void tcg_out_movi(TCGContext *s, TCGType type,
TCGReg ret, tcg_target_long arg)
{
- switch (type) {
- case TCG_TYPE_I32:
-#if TCG_TARGET_REG_BITS == 64
- arg = (int32_t)arg;
- /* fall through */
- case TCG_TYPE_I64:
-#endif
- break;
- default:
- g_assert_not_reached();
- }
-
- if (arg == sextract32(arg, 0, 20)) {
- tcg_out_op_ri(s, INDEX_op_tci_movi, ret, arg);
- } else {
- tcg_insn_unit insn = 0;
-
- new_pool_label(s, arg, 20, s->code_ptr, 0);
- insn = deposit32(insn, 0, 8, INDEX_op_tci_movl);
- insn = deposit32(insn, 8, 4, ret);
- tcg_out32(s, insn);
- }
+ tcg_tci_out_movi(s, type, ret, arg);
+ tcg_wasm_out_movi(s, type, ret, arg);
}
static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd,
@@ -1133,7 +1168,7 @@ static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
- tcg_out_mov(s, TCG_TYPE_I32, rd, rs);
+ tcg_tci_out_mov(s, TCG_TYPE_I32, rd, rs);
}
static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2)
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 12/33] tcg/wasm32: Add ext instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (10 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 11/33] tcg/wasm32: Add mov/movi instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 13/33] tcg/wasm32: Add bswap instructions Kohei Tokunaga
` (20 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements the ext operations using Wasm's extend instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 79 ++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 90a5705442..dff79a9854 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -231,7 +231,14 @@ static void tcg_wasm_out_op_i64_extend_i32_u(TCGContext *s)
{
tcg_wasm_out8(s, 0xad);
}
-
+static void tcg_wasm_out_op_i64_extend8_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0xc2);
+}
+static void tcg_wasm_out_op_i64_extend16_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0xc3);
+}
static void tcg_wasm_out_op_i32_add(TCGContext *s)
{
tcg_wasm_out8(s, 0x6a);
@@ -631,6 +638,60 @@ static void tcg_wasm_out_sextract(TCGContext *s, TCGReg dest, TCGReg arg1,
tcg_wasm_out_op_global_set_r(s, dest);
}
+static void tcg_wasm_out_ext8s(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_extend8_s(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_const(s, 0xff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_ext16s(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_extend16_s(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_const(s, 0xffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
+static void tcg_wasm_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
+{
+ tcg_wasm_out_op_global_get_r(s, rs);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, rd);
+}
+
static void tcg_wasm_out_ld(
TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset)
{
@@ -1126,33 +1187,39 @@ static const TCGOutOpExtract2 outop_extract2 = {
static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
{
tcg_tci_out_sextract(s, type, rd, rs, 0, 8);
+ tcg_wasm_out_ext8s(s, rd, rs);
}
static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_tci_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8);
+ tcg_wasm_out_ext8u(s, rd, rs);
}
static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
{
tcg_tci_out_sextract(s, type, rd, rs, 0, 16);
+ tcg_wasm_out_ext16s(s, rd, rs);
}
static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_tci_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16);
+ tcg_wasm_out_ext16u(s, rd, rs);
}
static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
tcg_tci_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+ tcg_wasm_out_ext32s(s, rd, rs);
}
static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
tcg_tci_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32);
+ tcg_wasm_out_ext32u(s, rd, rs);
}
static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
@@ -1169,6 +1236,7 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
{
tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
tcg_tci_out_mov(s, TCG_TYPE_I32, rd, rs);
+ tcg_wasm_out_extrl_i64_i32(s, rd, rs);
}
static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2)
@@ -1396,9 +1464,18 @@ static const TCGOutOpBinary outop_eqv = {
};
#if TCG_TARGET_REG_BITS == 64
+static void tcg_wasm_out_extrh_i64_i32(TCGContext *s, TCGReg a0, TCGReg a1)
+{
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_i64_const(s, 32);
+ tcg_wasm_out_op_i64_shr_u(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+}
+
static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1)
{
tcg_tci_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32);
+ tcg_wasm_out_extrh_i64_i32(s, a0, a1);
}
static const TCGOutOpUnary outop_extrh_i64_i32 = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 13/33] tcg/wasm32: Add bswap instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (11 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 12/33] tcg/wasm32: Add ext instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 14/33] tcg/wasm32: Add rem/div instructions Kohei Tokunaga
` (19 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements the bswap operation using Wasm instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 136 ++++++++++++++++++++++++++++++++++++
1 file changed, 136 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index dff79a9854..1a6069e288 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -118,6 +118,11 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
15, /* TCG_REG_R15 */
};
+/* Temporary local variables */
+#define TMP32_LOCAL_0_IDX 1
+#define TMP32_LOCAL_1_IDX 2
+#define TMP64_LOCAL_0_IDX 3
+
#define BUF_SIZE 1024
typedef struct LinkedBuf {
struct LinkedBuf *next;
@@ -219,6 +224,10 @@ static void tcg_wasm_out_op_i64_shr_u(TCGContext *s)
{
tcg_wasm_out8(s, 0x88);
}
+static void tcg_wasm_out_op_i64_rotr(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x8a);
+}
static void tcg_wasm_out_op_i32_wrap_i64(TCGContext *s)
{
tcg_wasm_out8(s, 0xa7);
@@ -255,6 +264,18 @@ static void tcg_wasm_out_op_i32_shl(TCGContext *s)
{
tcg_wasm_out8(s, 0x74);
}
+static void tcg_wasm_out_op_i32_shr_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x75);
+}
+static void tcg_wasm_out_op_i32_shr_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x76);
+}
+static void tcg_wasm_out_op_i32_rotr(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x78);
+}
static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
{
@@ -295,6 +316,14 @@ static void tcg_wasm_out_op_global_get_r_i32(TCGContext *s, TCGReg r0)
tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
tcg_wasm_out_op_i32_wrap_i64(s);
}
+static void tcg_wasm_out_op_local_get(TCGContext *s, uint8_t i)
+{
+ tcg_wasm_out_op_var(s, 0x20, i);
+}
+static void tcg_wasm_out_op_local_set(TCGContext *s, uint8_t i)
+{
+ tcg_wasm_out_op_var(s, 0x21, i);
+}
#define tcg_wasm_out_i64_calc(op) \
static void tcg_wasm_out_i64_calc_##op( \
@@ -692,6 +721,110 @@ static void tcg_wasm_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
tcg_wasm_out_op_global_set_r(s, rd);
}
+static void tcg_wasm_out_bswap64(
+ TCGContext *s, TCGReg dest, TCGReg src)
+{
+ tcg_wasm_out_op_global_get_r(s, src);
+ tcg_wasm_out_op_i64_const(s, 32);
+ tcg_wasm_out_op_i64_rotr(s);
+ tcg_wasm_out_op_local_set(s, TMP64_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP64_LOCAL_0_IDX);
+ tcg_wasm_out_op_i64_const(s, 0xff000000ff000000);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_const(s, 24);
+ tcg_wasm_out_op_i64_shr_u(s);
+
+ tcg_wasm_out_op_local_get(s, TMP64_LOCAL_0_IDX);
+ tcg_wasm_out_op_i64_const(s, 0x00ff000000ff0000);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_const(s, 8);
+ tcg_wasm_out_op_i64_shr_u(s);
+
+ tcg_wasm_out_op_i64_or(s);
+
+ tcg_wasm_out_op_local_get(s, TMP64_LOCAL_0_IDX);
+ tcg_wasm_out_op_i64_const(s, 0x0000ff000000ff00);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_const(s, 8);
+ tcg_wasm_out_op_i64_shl(s);
+
+ tcg_wasm_out_op_local_get(s, TMP64_LOCAL_0_IDX);
+ tcg_wasm_out_op_i64_const(s, 0x000000ff000000ff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_const(s, 24);
+ tcg_wasm_out_op_i64_shl(s);
+
+ tcg_wasm_out_op_i64_or(s);
+
+ tcg_wasm_out_op_i64_or(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
+static void tcg_wasm_out_bswap32(
+ TCGContext *s, TCGReg dest, TCGReg src, int flags)
+{
+ tcg_wasm_out_op_global_get_r(s, src);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 16);
+ tcg_wasm_out_op_i32_rotr(s);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 0xff00ff00);
+ tcg_wasm_out_op_i32_and(s);
+ tcg_wasm_out_op_i32_const(s, 8);
+ tcg_wasm_out_op_i32_shr_u(s);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 0x00ff00ff);
+ tcg_wasm_out_op_i32_and(s);
+ tcg_wasm_out_op_i32_const(s, 8);
+ tcg_wasm_out_op_i32_shl(s);
+
+ tcg_wasm_out_op_i32_or(s);
+ if (flags & TCG_BSWAP_OS) {
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ } else {
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ }
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
+static void tcg_wasm_out_bswap16(
+ TCGContext *s, TCGReg dest, TCGReg src, int flags)
+{
+ tcg_wasm_out_op_global_get_r(s, src);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 8);
+ tcg_wasm_out_op_i32_rotr(s);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 0x000000ff);
+ tcg_wasm_out_op_i32_and(s);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 0xff000000);
+ tcg_wasm_out_op_i32_and(s);
+ tcg_wasm_out_op_i32_const(s, 16);
+ if (flags & TCG_BSWAP_OS) {
+ tcg_wasm_out_op_i32_shr_s(s);
+ } else {
+ tcg_wasm_out_op_i32_shr_u(s);
+ }
+
+ tcg_wasm_out_op_i32_or(s);
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
static void tcg_wasm_out_ld(
TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset)
{
@@ -1768,6 +1901,7 @@ static void tgen_bswap16(TCGContext *s, TCGType type,
if (flags & TCG_BSWAP_OS) {
tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16);
}
+ tcg_wasm_out_bswap16(s, a0, a1, flags);
}
static const TCGOutOpBswap outop_bswap16 = {
@@ -1782,6 +1916,7 @@ static void tgen_bswap32(TCGContext *s, TCGType type,
if (flags & TCG_BSWAP_OS) {
tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32);
}
+ tcg_wasm_out_bswap32(s, a0, a1, flags);
}
static const TCGOutOpBswap outop_bswap32 = {
@@ -1793,6 +1928,7 @@ static const TCGOutOpBswap outop_bswap32 = {
static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
{
tcg_out_op_rr(s, INDEX_op_bswap64, a0, a1);
+ tcg_wasm_out_bswap64(s, a0, a1);
}
static const TCGOutOpUnary outop_bswap64 = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 14/33] tcg/wasm32: Add rem/div instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (12 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 13/33] tcg/wasm32: Add bswap instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 15/33] tcg/wasm32: Add andc/orc/eqv/nand/nor instructions Kohei Tokunaga
` (18 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements rem and div operations using Wasm's rem/div
instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 120 ++++++++++++++++++++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 1a6069e288..66d3977d31 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -212,6 +212,22 @@ static void tcg_wasm_out_op_i64_mul(TCGContext *s)
{
tcg_wasm_out8(s, 0x7e);
}
+static void tcg_wasm_out_op_i64_div_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7f);
+}
+static void tcg_wasm_out_op_i64_div_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x80);
+}
+static void tcg_wasm_out_op_i64_rem_s(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x81);
+}
+static void tcg_wasm_out_op_i64_rem_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x82);
+}
static void tcg_wasm_out_op_i64_shl(TCGContext *s)
{
tcg_wasm_out8(s, 0x86);
@@ -995,6 +1011,106 @@ static void tcg_wasm_out_movi(TCGContext *s, TCGType type,
tcg_wasm_out_op_global_set_r(s, ret);
}
+static void tcg_wasm_out_rem_s(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_i64_rem_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_rem_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_rem_u(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_rem_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_rem_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_div_s(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_i64_div_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_div_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_div_u(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_i64_div_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_div_u(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -1556,6 +1672,7 @@ static void tgen_divs(TCGContext *s, TCGType type,
? INDEX_op_tci_divs32
: INDEX_op_divs);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_div_s(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_divs = {
@@ -1574,6 +1691,7 @@ static void tgen_divu(TCGContext *s, TCGType type,
? INDEX_op_tci_divu32
: INDEX_op_divu);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_div_u(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_divu = {
@@ -1718,6 +1836,7 @@ static void tgen_rems(TCGContext *s, TCGType type,
? INDEX_op_tci_rems32
: INDEX_op_rems);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_rem_s(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_rems = {
@@ -1732,6 +1851,7 @@ static void tgen_remu(TCGContext *s, TCGType type,
? INDEX_op_tci_remu32
: INDEX_op_remu);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_rem_u(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_remu = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 15/33] tcg/wasm32: Add andc/orc/eqv/nand/nor instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (13 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 14/33] tcg/wasm32: Add rem/div instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 16/33] tcg/wasm32: Add neg/not/ctpop instructions Kohei Tokunaga
` (17 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements andc, orc, eqv, nand and nor operations using Wasm
instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 55 +++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 66d3977d31..50d772f3d6 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -520,6 +520,56 @@ static void tcg_wasm_out_op_not(TCGContext *s)
tcg_wasm_out_op_i64_xor(s);
}
+static void tcg_wasm_out_andc(
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_orc(
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_i64_or(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_eqv(
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_xor(s);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_nand(
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_and(s);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_nor(
+ TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i64_or(s);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
static void tcg_wasm_out_shl(TCGContext *s, TCGReg ret,
TCGReg arg1, TCGReg arg2)
{
@@ -1618,6 +1668,7 @@ static void tgen_andc(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_andc, a0, a1, a2);
+ tcg_wasm_out_andc(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_andc = {
@@ -1707,6 +1758,7 @@ static void tgen_eqv(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_eqv, a0, a1, a2);
+ tcg_wasm_out_eqv(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_eqv = {
@@ -1788,6 +1840,7 @@ static void tgen_nand(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_nand, a0, a1, a2);
+ tcg_wasm_out_nand(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_nand = {
@@ -1799,6 +1852,7 @@ static void tgen_nor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_nor, a0, a1, a2);
+ tcg_wasm_out_nor(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_nor = {
@@ -1822,6 +1876,7 @@ static void tgen_orc(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_orc, a0, a1, a2);
+ tcg_wasm_out_orc(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_orc = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 16/33] tcg/wasm32: Add neg/not/ctpop instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (14 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 15/33] tcg/wasm32: Add andc/orc/eqv/nand/nor instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 17/33] tcg/wasm32: Add rot/clz/ctz instructions Kohei Tokunaga
` (16 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements neg, not and ctpop operations using Wasm
instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 39 +++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 50d772f3d6..e5de2f69bd 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -200,6 +200,10 @@ static void tcg_wasm_out_op_i64_xor(TCGContext *s)
{
tcg_wasm_out8(s, 0x85);
}
+static void tcg_wasm_out_op_i64_popcnt(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7b);
+}
static void tcg_wasm_out_op_i64_add(TCGContext *s)
{
tcg_wasm_out8(s, 0x7c);
@@ -570,6 +574,29 @@ static void tcg_wasm_out_nor(
tcg_wasm_out_op_global_set_r(s, ret);
}
+static void tcg_wasm_out_neg(TCGContext *s, TCGReg ret, TCGReg arg)
+{
+ tcg_wasm_out_op_global_get_r(s, arg);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_not(TCGContext *s, TCGReg ret, TCGReg arg)
+{
+ tcg_wasm_out_op_global_get_r(s, arg);
+ tcg_wasm_out_op_not(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+}
+
+static void tcg_wasm_out_ctpop(TCGContext *s, TCGReg dest, TCGReg src)
+{
+ tcg_wasm_out_op_global_get_r(s, src);
+ tcg_wasm_out_op_i64_popcnt(s);
+ tcg_wasm_out_op_global_set_r(s, dest);
+}
+
static void tcg_wasm_out_shl(TCGContext *s, TCGReg ret,
TCGReg arg1, TCGReg arg2)
{
@@ -2056,6 +2083,7 @@ static const TCGOutOpBinary outop_xor = {
static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
{
tcg_out_op_rr(s, INDEX_op_ctpop, a0, a1);
+ tcg_wasm_out_ctpop(s, a0, a1);
}
static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags)
@@ -2112,9 +2140,15 @@ static const TCGOutOpUnary outop_bswap64 = {
};
#endif
-static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+static void tgen_neg_tci(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
{
tcg_out_op_rr(s, INDEX_op_neg, a0, a1);
+ }
+
+static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
+{
+ tgen_neg_tci(s, type, a0, a1);
+ tcg_wasm_out_neg(s, a0, a1);
}
static const TCGOutOpUnary outop_neg = {
@@ -2125,6 +2159,7 @@ static const TCGOutOpUnary outop_neg = {
static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
{
tcg_out_op_rr(s, INDEX_op_not, a0, a1);
+ tcg_wasm_out_not(s, a0, a1);
}
static const TCGOutOpUnary outop_not = {
@@ -2157,7 +2192,7 @@ static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond,
TCGReg dest, TCGReg arg1, TCGReg arg2)
{
tgen_setcond_tci(s, type, cond, dest, arg1, arg2);
- tgen_neg(s, type, dest, dest);
+ tgen_neg_tci(s, type, dest, dest);
tcg_wasm_out_negsetcond(s, type, dest, arg1, arg2, cond);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 17/33] tcg/wasm32: Add rot/clz/ctz instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (15 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 16/33] tcg/wasm32: Add neg/not/ctpop instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 18/33] tcg/wasm32: Add addc/subb instructions Kohei Tokunaga
` (15 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements rot, clz and ctz operations using Wasm instructions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 155 ++++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index e5de2f69bd..75e47f8c8c 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -200,6 +200,14 @@ static void tcg_wasm_out_op_i64_xor(TCGContext *s)
{
tcg_wasm_out8(s, 0x85);
}
+static void tcg_wasm_out_op_i64_clz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x79);
+}
+static void tcg_wasm_out_op_i64_ctz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x7a);
+}
static void tcg_wasm_out_op_i64_popcnt(TCGContext *s)
{
tcg_wasm_out8(s, 0x7b);
@@ -244,6 +252,10 @@ static void tcg_wasm_out_op_i64_shr_u(TCGContext *s)
{
tcg_wasm_out8(s, 0x88);
}
+static void tcg_wasm_out_op_i64_rotl(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x89);
+}
static void tcg_wasm_out_op_i64_rotr(TCGContext *s)
{
tcg_wasm_out8(s, 0x8a);
@@ -268,6 +280,14 @@ static void tcg_wasm_out_op_i64_extend16_s(TCGContext *s)
{
tcg_wasm_out8(s, 0xc3);
}
+static void tcg_wasm_out_op_i32_clz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x67);
+}
+static void tcg_wasm_out_op_i32_ctz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x68);
+}
static void tcg_wasm_out_op_i32_add(TCGContext *s)
{
tcg_wasm_out8(s, 0x6a);
@@ -292,16 +312,33 @@ static void tcg_wasm_out_op_i32_shr_u(TCGContext *s)
{
tcg_wasm_out8(s, 0x76);
}
+static void tcg_wasm_out_op_i32_rotl(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x77);
+}
static void tcg_wasm_out_op_i32_rotr(TCGContext *s)
{
tcg_wasm_out8(s, 0x78);
}
+static void tcg_wasm_out_op_i32_eqz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x45);
+}
+static void tcg_wasm_out_op_i64_eqz(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x50);
+}
static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
{
tcg_wasm_out8(s, 0x04);
tcg_wasm_out8(s, 0x7e);
}
+static void tcg_wasm_out_op_if_ret_i32(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x04);
+ tcg_wasm_out8(s, 0x7f);
+}
static void tcg_wasm_out_op_else(TCGContext *s)
{
tcg_wasm_out8(s, 0x05);
@@ -360,6 +397,8 @@ tcg_wasm_out_i64_calc(xor);
tcg_wasm_out_i64_calc(add);
tcg_wasm_out_i64_calc(sub);
tcg_wasm_out_i64_calc(mul);
+tcg_wasm_out_i64_calc(rotl);
+tcg_wasm_out_i64_calc(rotr);
static const struct {
uint8_t i32;
@@ -918,6 +957,118 @@ static void tcg_wasm_out_bswap16(
tcg_wasm_out_op_global_set_r(s, dest);
}
+static void tcg_wasm_out_rotl(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_rotl(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_i64_calc_rotl(s, ret, arg1, arg2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_rotr(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_rotr(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_i64_calc_rotr(s, ret, arg1, arg2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_clz(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_eqz(s);
+ tcg_wasm_out_op_if_ret_i32(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_else(s);
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_clz(s);
+ tcg_wasm_out_op_end(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_ret_i64(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_else(s);
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_clz(s);
+ tcg_wasm_out_op_end(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_ctz(
+ TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_eqz(s);
+ tcg_wasm_out_op_if_ret_i32(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_else(s);
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_ctz(s);
+ tcg_wasm_out_op_end(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_ret_i64(s);
+ tcg_wasm_out_op_global_get_r(s, arg2);
+ tcg_wasm_out_op_else(s);
+ tcg_wasm_out_op_global_get_r(s, arg1);
+ tcg_wasm_out_op_i64_ctz(s);
+ tcg_wasm_out_op_end(s);
+ tcg_wasm_out_op_global_set_r(s, ret);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static void tcg_wasm_out_ld(
TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset)
{
@@ -1710,6 +1861,7 @@ static void tgen_clz(TCGContext *s, TCGType type,
? INDEX_op_tci_clz32
: INDEX_op_clz);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_clz(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_clz = {
@@ -1724,6 +1876,7 @@ static void tgen_ctz(TCGContext *s, TCGType type,
? INDEX_op_tci_ctz32
: INDEX_op_ctz);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_ctz(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_ctz = {
@@ -1948,6 +2101,7 @@ static void tgen_rotl(TCGContext *s, TCGType type,
? INDEX_op_tci_rotl32
: INDEX_op_rotl);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_rotl(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_rotl = {
@@ -1962,6 +2116,7 @@ static void tgen_rotr(TCGContext *s, TCGType type,
? INDEX_op_tci_rotr32
: INDEX_op_rotr);
tcg_out_op_rrr(s, opc, a0, a1, a2);
+ tcg_wasm_out_rotr(s, type, a0, a1, a2);
}
static const TCGOutOpBinary outop_rotr = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 18/33] tcg/wasm32: Add addc/subb instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (16 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 17/33] tcg/wasm32: Add rot/clz/ctz instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 19/33] tcg/wasm32: Add br/brcond instructions Kohei Tokunaga
` (14 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit implements addc and subb operations using Wasm instructions. A
carry flag is introduced as the 16th variable in the module following other
15 variables that represent TCG variables.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 151 ++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 75e47f8c8c..167850ea7c 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -118,6 +118,11 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
15, /* TCG_REG_R15 */
};
+/*
+ * Global variable to store the carry flag
+ */
+#define CARRY_IDX 16
+
/* Temporary local variables */
#define TMP32_LOCAL_0_IDX 1
#define TMP32_LOCAL_1_IDX 2
@@ -324,10 +329,23 @@ static void tcg_wasm_out_op_i32_eqz(TCGContext *s)
{
tcg_wasm_out8(s, 0x45);
}
+static void tcg_wasm_out_op_i64_lt_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x54);
+}
+static void tcg_wasm_out_op_i64_le_u(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x58);
+}
static void tcg_wasm_out_op_i64_eqz(TCGContext *s)
{
tcg_wasm_out8(s, 0x50);
}
+static void tcg_wasm_out_op_if_noret(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x04);
+ tcg_wasm_out8(s, 0x40);
+}
static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
{
@@ -1789,10 +1807,28 @@ static TCGConstraintSetIndex cset_addsubcarry(TCGType type, unsigned flags)
return type == TCG_TYPE_REG ? C_O1_I2(r, r, r) : C_NotImplemented;
}
+static void tcg_wasm_out_addco(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+ tcg_wasm_out_op_global_get_r(s, a0);
+ if (a0 == a1) {
+ tcg_wasm_out_op_global_get_r(s, a2);
+ } else {
+ tcg_wasm_out_op_global_get_r(s, a1);
+ }
+ tcg_wasm_out_op_i64_lt_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+}
+
static void tgen_addco(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_addco, a0, a1, a2);
+ tcg_wasm_out_addco(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_addco = {
@@ -1801,10 +1837,21 @@ static const TCGOutOpBinary outop_addco = {
.out_rrr = tgen_addco,
};
+static void tcg_wasm_out_addci(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_get(s, CARRY_IDX);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+}
+
static void tgen_addci(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_addci, a0, a1, a2);
+ tcg_wasm_out_addci(s, a0, a1, a2);
}
static const TCGOutOpAddSubCarry outop_addci = {
@@ -1813,10 +1860,51 @@ static const TCGOutOpAddSubCarry outop_addci = {
.out_rrr = tgen_addci,
};
+static void tcg_wasm_out_addcio(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get(s, CARRY_IDX);
+ tcg_wasm_out_op_if_noret(s);
+
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+ tcg_wasm_out_op_global_get_r(s, a0);
+ if (a0 == a1) {
+ tcg_wasm_out_op_global_get_r(s, a2);
+ } else {
+ tcg_wasm_out_op_global_get_r(s, a1);
+ }
+ tcg_wasm_out_op_i64_le_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+
+ tcg_wasm_out_op_else(s);
+
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_add(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+ tcg_wasm_out_op_global_get_r(s, a0);
+ if (a0 == a1) {
+ tcg_wasm_out_op_global_get_r(s, a2);
+ } else {
+ tcg_wasm_out_op_global_get_r(s, a1);
+ }
+ tcg_wasm_out_op_i64_lt_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+
+ tcg_wasm_out_op_end(s);
+}
+
static void tgen_addcio(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_addcio, a0, a1, a2);
+ tcg_wasm_out_addcio(s, a0, a1, a2);
}
static const TCGOutOpBinary outop_addcio = {
@@ -1828,6 +1916,8 @@ static const TCGOutOpBinary outop_addcio = {
static void tcg_out_set_carry(TCGContext *s)
{
tcg_out_op_v(s, INDEX_op_tci_setcarry);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
}
static void tgen_and(TCGContext *s, TCGType type,
@@ -2182,10 +2272,25 @@ static const TCGOutOpSubtract outop_sub = {
.out_rrr = tgen_sub,
};
+static void tcg_wasm_out_subbo(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_lt_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+}
+
static void tgen_subbo(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_subbo, a0, a1, a2);
+ tcg_wasm_out_subbo(s, a0, a1, a2);
}
static const TCGOutOpAddSubCarry outop_subbo = {
@@ -2194,10 +2299,21 @@ static const TCGOutOpAddSubCarry outop_subbo = {
.out_rrr = tgen_subbo,
};
+static void tcg_wasm_out_subbi(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_global_get(s, CARRY_IDX);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+}
+
static void tgen_subbi(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_subbi, a0, a1, a2);
+ tcg_wasm_out_subbi(s, a0, a1, a2);
}
static const TCGOutOpAddSubCarry outop_subbi = {
@@ -2206,10 +2322,43 @@ static const TCGOutOpAddSubCarry outop_subbi = {
.out_rrr = tgen_subbi,
};
+static void tcg_wasm_out_subbio(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2)
+{
+ tcg_wasm_out_op_global_get(s, CARRY_IDX);
+ tcg_wasm_out_op_if_noret(s);
+
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_le_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+
+ tcg_wasm_out_op_else(s);
+
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_lt_u(s);
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
+ tcg_wasm_out_op_global_get_r(s, a1);
+ tcg_wasm_out_op_global_get_r(s, a2);
+ tcg_wasm_out_op_i64_sub(s);
+ tcg_wasm_out_op_global_set_r(s, a0);
+
+ tcg_wasm_out_op_end(s);
+}
+
static void tgen_subbio(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
tcg_out_op_rrr(s, INDEX_op_subbio, a0, a1, a2);
+ tcg_wasm_out_subbio(s, a0, a1, a2);
}
static const TCGOutOpAddSubCarry outop_subbio = {
@@ -2221,6 +2370,8 @@ static const TCGOutOpAddSubCarry outop_subbio = {
static void tcg_out_set_borrow(TCGContext *s)
{
tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_global_set(s, CARRY_IDX);
}
static void tgen_xor(TCGContext *s, TCGType type,
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 19/33] tcg/wasm32: Add br/brcond instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (17 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 18/33] tcg/wasm32: Add addc/subb instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 20/33] tcg/wasm32: Add exit_tb/goto_tb/goto_ptr instructions Kohei Tokunaga
` (13 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Wasm does not support direct jumps to arbitrary code addresses, so
label-based control flow is implemented using Wasm's control flow
instructions. As illustrated in the pseudo-code below, each TB wraps its
instructions inside a large loop. Each set of codes separated by labels is
placed inside an "if" block. Br is implemented by breaking out of the
current block and conditionally entering the target block:
loop
if
... code after label1
end
if
... code after label2
end
...
end
Each block within the TB is assigned a unique int32 ID. The topmost "if"
block is assigned ID 0, and subsequent blocks are assigned incrementally.
To control br, this commit introduces a 17th Wasm variable BLOCK_PTR_IDX
which holds the ID of the target block. The br instruction sets this
variable to the target block's ID, breaks from the current if block, and
allows the control flow to move forward. Each if block checks whether the
BLOCK_PTR_IDX variable matches its assigned ID. If it does, execution
proceeds within that block.
The start of the global loop and the first if block is generated in
tcg_out_tb_start. To properly close the blocks, this commit also introduces
a new TCG backend callback tcg_out_tb_end which emits the "end" instructions
for the final if block and the loop block in the Wasm backend.
Another new callback tcg_out_label_cb is used to emit block boundaries,
specifically the end of the previous block and the if of the next block, at
label positions. In this callback, the mapping between label IDs and block
IDs is recorded in LabelInfo, which is later used to resolve br
instructions.
Since the block ID for a label might not be known at the time a br
instruction is generated, a placeholder (longer than 32bit and encoded as
LEB128) is emitted instead. These placeholders are tracked in
BlockPlaceholder and resolved later.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/aarch64/tcg-target.c.inc | 11 ++
tcg/arm/tcg-target.c.inc | 11 ++
tcg/i386/tcg-target.c.inc | 11 ++
tcg/loongarch64/tcg-target.c.inc | 11 ++
tcg/mips/tcg-target.c.inc | 11 ++
tcg/ppc/tcg-target.c.inc | 11 ++
tcg/riscv/tcg-target.c.inc | 11 ++
tcg/s390x/tcg-target.c.inc | 11 ++
tcg/sparc64/tcg-target.c.inc | 11 ++
tcg/tcg.c | 7 ++
tcg/tci/tcg-target.c.inc | 11 ++
tcg/wasm32/tcg-target.c.inc | 180 +++++++++++++++++++++++++++++++
12 files changed, 297 insertions(+)
diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc
index 4cb647cb34..78ad3e913a 100644
--- a/tcg/aarch64/tcg-target.c.inc
+++ b/tcg/aarch64/tcg-target.c.inc
@@ -3518,6 +3518,17 @@ static void tcg_out_tb_start(TCGContext *s)
tcg_out_bti(s, BTI_J);
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
{
int i;
diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc
index 447e43583e..2d911b1fe6 100644
--- a/tcg/arm/tcg-target.c.inc
+++ b/tcg/arm/tcg-target.c.inc
@@ -3441,6 +3441,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
typedef struct {
DebugFrameHeader h;
uint8_t fde_def_cfa[4];
diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc
index 09fce27b06..2c7bad092f 100644
--- a/tcg/i386/tcg-target.c.inc
+++ b/tcg/i386/tcg-target.c.inc
@@ -4761,6 +4761,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
{
memset(p, 0x90, count);
diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc
index e5580d69a8..113c5df7fc 100644
--- a/tcg/loongarch64/tcg-target.c.inc
+++ b/tcg/loongarch64/tcg-target.c.inc
@@ -2658,6 +2658,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
{
for (int i = 0; i < count; ++i) {
diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc
index 2c0457e588..965c4717c6 100644
--- a/tcg/mips/tcg-target.c.inc
+++ b/tcg/mips/tcg-target.c.inc
@@ -2745,6 +2745,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_target_init(TCGContext *s)
{
tcg_target_detect_isa();
diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc
index 2e94778104..d0b1e46709 100644
--- a/tcg/ppc/tcg-target.c.inc
+++ b/tcg/ppc/tcg-target.c.inc
@@ -2859,6 +2859,17 @@ static void tcg_out_tb_start(TCGContext *s)
}
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg)
{
tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, arg);
diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc
index f9417d15f7..de76d9fa8d 100644
--- a/tcg/riscv/tcg-target.c.inc
+++ b/tcg/riscv/tcg-target.c.inc
@@ -2983,6 +2983,17 @@ static void tcg_out_tb_start(TCGContext *s)
init_setting_vtype(s);
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static bool vtype_check(unsigned vtype)
{
unsigned long tmp;
diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc
index 7ca0071f24..c4404c999c 100644
--- a/tcg/s390x/tcg-target.c.inc
+++ b/tcg/s390x/tcg-target.c.inc
@@ -3830,6 +3830,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
{
memset(p, 0x07, count * sizeof(tcg_insn_unit));
diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc
index 9e004fb511..7f9b8e5aad 100644
--- a/tcg/sparc64/tcg-target.c.inc
+++ b/tcg/sparc64/tcg-target.c.inc
@@ -1017,6 +1017,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
{
int i;
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 2746458a64..778e84c40c 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -115,6 +115,7 @@ static void tcg_register_jit_int(const void *buf, size_t size,
/* Forward declarations for functions declared and used in tcg-target.c.inc. */
static void tcg_out_tb_start(TCGContext *s);
+static int tcg_out_tb_end(TCGContext *s);
static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1,
intptr_t arg2);
static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg);
@@ -186,6 +187,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target,
static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot);
static bool tcg_target_const_match(int64_t val, int ct,
TCGType type, TCGCond cond, int vece);
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l);
#ifndef CONFIG_USER_ONLY
#define guest_base ({ qemu_build_not_reached(); (uintptr_t)0; })
@@ -360,6 +362,7 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l)
tcg_debug_assert(!l->has_value);
l->has_value = 1;
l->u.value_ptr = tcg_splitwx_to_rx(s->code_ptr);
+ tcg_out_label_cb(s, l);
}
TCGLabel *gen_new_label(void)
@@ -7045,6 +7048,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start)
if (!tcg_resolve_relocs(s)) {
return -2;
}
+ i = tcg_out_tb_end(s);
+ if (i < 0) {
+ return i;
+ }
#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
/* flush instruction cache */
diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc
index 35c66a4836..d99d06c1da 100644
--- a/tcg/tci/tcg-target.c.inc
+++ b/tcg/tci/tcg-target.c.inc
@@ -1301,6 +1301,17 @@ static void tcg_out_tb_start(TCGContext *s)
/* nothing to do */
}
+static int tcg_out_tb_end(TCGContext *s)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ /* nothing to do */
+}
+
bool tcg_target_has_memory_bswap(MemOp memop)
{
return true;
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 167850ea7c..ea0d1ca874 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -123,6 +123,11 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
*/
#define CARRY_IDX 16
+/*
+ * Global variable Index used for storing the current block index
+ */
+#define BLOCK_PTR_IDX 17
+
/* Temporary local variables */
#define TMP32_LOCAL_0_IDX 1
#define TMP32_LOCAL_1_IDX 2
@@ -341,6 +346,16 @@ static void tcg_wasm_out_op_i64_eqz(TCGContext *s)
{
tcg_wasm_out8(s, 0x50);
}
+static void tcg_wasm_out_op_br(TCGContext *s, int i)
+{
+ tcg_wasm_out8(s, 0x0c);
+ tcg_wasm_out8(s, i);
+}
+static void tcg_wasm_out_op_loop_noret(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x03);
+ tcg_wasm_out8(s, 0x40);
+}
static void tcg_wasm_out_op_if_noret(TCGContext *s)
{
tcg_wasm_out8(s, 0x04);
@@ -1357,6 +1372,152 @@ static void tcg_wasm_out_div_u(
}
}
+typedef struct LabelInfo {
+ struct LabelInfo *next;
+ int label;
+ int block;
+} LabelInfo;
+
+__thread LabelInfo *label_info;
+
+static void init_label_info(void)
+{
+ label_info = NULL;
+}
+
+static void add_label(int label, int block)
+{
+ LabelInfo *e = tcg_malloc(sizeof(LabelInfo));
+ e->label = label;
+ e->block = block;
+ e->next = NULL;
+ if (label_info == NULL) {
+ label_info = e;
+ return;
+ }
+ LabelInfo *last = label_info;
+ for (LabelInfo *p = last; p; p = p->next) {
+ last = p;
+ }
+ last->next = e;
+}
+
+typedef struct BlockPlaceholder {
+ struct BlockPlaceholder *next;
+ int label;
+ int pos;
+} BlockPlaceholder;
+
+__thread BlockPlaceholder *block_placeholder;
+
+__thread int block_idx;
+
+static void init_blocks(void)
+{
+ block_placeholder = NULL;
+ block_idx = 0;
+}
+
+static void add_block_placeholder(int label, int pos)
+{
+ BlockPlaceholder *e = tcg_malloc(sizeof(BlockPlaceholder));
+ e->label = label;
+ e->pos = pos;
+ e->next = NULL;
+ if (block_placeholder == NULL) {
+ block_placeholder = e;
+ return;
+ }
+ BlockPlaceholder *last = block_placeholder;
+ for (BlockPlaceholder *p = last; p; p = p->next) {
+ last = p;
+ }
+ last->next = e;
+}
+
+static int get_block_of_label(int label)
+{
+ for (LabelInfo *p = label_info; p; p = p->next) {
+ if (p->label == label) {
+ return p->block;
+ }
+ }
+ return -1;
+}
+
+static void tcg_wasm_out_new_block(TCGContext *s)
+{
+ tcg_wasm_out_op_end(s); /* close this block */
+
+ /* next block */
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_op_i64_const(s, ++block_idx);
+ tcg_wasm_out_op_i64_le_u(s);
+ tcg_wasm_out_op_if_noret(s);
+}
+
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
+{
+ add_label(l->id, block_idx + 1);
+ tcg_wasm_out_new_block(s);
+}
+
+static void tcg_wasm_out_op_br_to_label(TCGContext *s, TCGLabel *l, bool br_if)
+{
+ int toploop_depth = 1;
+ if (br_if) {
+ tcg_wasm_out_op_if_noret(s);
+ toploop_depth++;
+ }
+ tcg_wasm_out8(s, 0x42); /* i64.const */
+
+ add_block_placeholder(l->id, sub_buf_len());
+
+ tcg_wasm_out8(s, 0x80); /* filled before instantiation */
+ tcg_wasm_out8(s, 0x80);
+ tcg_wasm_out8(s, 0x80);
+ tcg_wasm_out8(s, 0x80);
+ tcg_wasm_out8(s, 0x00);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ if (get_block_of_label(l->id) != -1) {
+ /*
+ * The label is placed before this br, branch to the top of loop
+ */
+ tcg_wasm_out_op_br(s, toploop_depth);
+ } else {
+ /*
+ * The label will be generated after this br,
+ * branch to the end of the current block
+ */
+ tcg_wasm_out_op_br(s, toploop_depth - 1);
+ }
+ if (br_if) {
+ tcg_wasm_out_op_end(s);
+ }
+}
+
+static void tcg_wasm_out_br(TCGContext *s, TCGLabel *l)
+{
+ tcg_wasm_out_op_br_to_label(s, l, false);
+}
+
+static void tcg_wasm_out_brcond(TCGContext *s, TCGType type,
+ TCGReg arg1, TCGReg arg2,
+ TCGCond cond, TCGLabel *l)
+{
+ switch (type) {
+ case TCG_TYPE_I32:
+ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2);
+ break;
+ case TCG_TYPE_I64:
+ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_wasm_out_op_br_to_label(s, l, true);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -2512,6 +2673,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond,
{
tgen_setcond_tci(s, type, cond, TCG_REG_TMP, arg0, arg1);
tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l);
+ tcg_wasm_out_brcond(s, type, arg0, arg1, cond, l);
}
static const TCGOutOpBrcond outop_brcond = {
@@ -2576,6 +2738,7 @@ static void tcg_out_mb(TCGContext *s, unsigned a0)
static void tcg_out_br(TCGContext *s, TCGLabel *l)
{
tcg_out_op_l(s, INDEX_op_br, l);
+ tcg_wasm_out_br(s, l);
}
static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest,
@@ -2796,6 +2959,23 @@ static inline void tcg_target_qemu_prologue(TCGContext *s)
static void tcg_out_tb_start(TCGContext *s)
{
init_sub_buf();
+ init_blocks();
+ init_label_info();
+
+ tcg_wasm_out_op_loop_noret(s);
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_noret(s);
+}
+
+static int tcg_out_tb_end(TCGContext *s)
+{
+ tcg_wasm_out_op_end(s); /* end if */
+ tcg_wasm_out_op_end(s); /* end loop */
+ tcg_wasm_out8(s, 0x0); /* unreachable */
+ tcg_wasm_out_op_end(s); /* end func */
+
+ return 0;
}
bool tcg_target_has_memory_bswap(MemOp memop)
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 20/33] tcg/wasm32: Add exit_tb/goto_tb/goto_ptr instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (18 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 19/33] tcg/wasm32: Add br/brcond instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 21/33] tcg/wasm32: Add call instruction Kohei Tokunaga
` (12 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
In the Wasm backend, each TB is compiled to a separeted Wasm
module. Control transfer between TBs (i.e. from one Wasm module to
another) is handled by the caller of the module.
The goto_tb and goto_ptr operations are implemented by returning
control to the caller using the return instruction. The destination
TB's pointer is passed to the caller via a shared wasmContext
structure which is accessible from both the Wasm module and the caller. This
wasmContext must be provided to the module as an argument which is
accessible as the local variable at index 0.
If the destination TB is the current TB itself, there is no need to
return control to the caller. Instead, execution can jump directly to
the top of the loop within the TB.
The exit_tb operation sets the pointer in wasmContext to 0, indicating that
there is no destination TB.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
MAINTAINERS | 1 +
tcg/wasm32.h | 17 ++++++
tcg/wasm32/tcg-target.c.inc | 111 ++++++++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+)
create mode 100644 tcg/wasm32.h
diff --git a/MAINTAINERS b/MAINTAINERS
index ac5070d058..3ca93f90de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3953,6 +3953,7 @@ M: Kohei Tokunaga <ktokunaga.mail@gmail.com>
S: Maintained
F: tcg/wasm32/
F: tcg/wasm32.c
+F: tcg/wasm32.h
Block drivers
-------------
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
new file mode 100644
index 0000000000..ffa359b7dc
--- /dev/null
+++ b/tcg/wasm32.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef TCG_WASM32_H
+#define TCG_WASM32_H
+
+/*
+ * wasmContext is a data shared among QEMU and wasm modules.
+ */
+struct wasmContext {
+ /*
+ * Pointer to the TB to be executed.
+ */
+ void *tb_ptr;
+};
+
+#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index ea0d1ca874..77db50cf85 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -25,6 +25,8 @@
* THE SOFTWARE.
*/
+#include "../wasm32.h"
+
/* Used for function call generation. */
#define TCG_TARGET_CALL_STACK_OFFSET 0
#define TCG_TARGET_STACK_ALIGN 8
@@ -128,6 +130,11 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
*/
#define BLOCK_PTR_IDX 17
+/*
+ * pointer to wasmContext
+ */
+#define CTX_IDX 0
+
/* Temporary local variables */
#define TMP32_LOCAL_0_IDX 1
#define TMP32_LOCAL_1_IDX 2
@@ -334,6 +341,14 @@ static void tcg_wasm_out_op_i32_eqz(TCGContext *s)
{
tcg_wasm_out8(s, 0x45);
}
+static void tcg_wasm_out_op_i32_eq(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x46);
+}
+static void tcg_wasm_out_op_i32_ne(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x47);
+}
static void tcg_wasm_out_op_i64_lt_u(TCGContext *s)
{
tcg_wasm_out8(s, 0x54);
@@ -380,6 +395,10 @@ static void tcg_wasm_out_op_end(TCGContext *s)
{
tcg_wasm_out8(s, 0x0b);
}
+static void tcg_wasm_out_op_return(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x0f);
+}
static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
{
tcg_wasm_out8(s, instr);
@@ -590,6 +609,16 @@ static void tcg_wasm_out_op_i64_load32_u(TCGContext *s, uint32_t a, uint32_t o)
tcg_wasm_out_op_loadstore(s, 0x35, a, o);
}
+static void tcg_wasm_out_op_i32_load(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x28, a, o);
+}
+
+static void tcg_wasm_out_op_i32_store(TCGContext *s, uint32_t a, uint32_t o)
+{
+ tcg_wasm_out_op_loadstore(s, 0x36, a, o);
+}
+
static void tcg_wasm_out_op_not(TCGContext *s)
{
tcg_wasm_out_op_i64_const(s, -1);
@@ -1518,6 +1547,85 @@ static void tcg_wasm_out_brcond(TCGContext *s, TCGType type,
tcg_wasm_out_op_br_to_label(s, l, true);
}
+#define tcg_wasm_out_ctx_i32_store_const(s, f, v) \
+ do { \
+ tcg_wasm_out_op_local_get(s, CTX_IDX); \
+ tcg_wasm_out_op_i32_const(s, v); \
+ tcg_wasm_out_op_i32_store(s, 0, offsetof(struct wasmContext, f)); \
+ } while (0)
+
+#define tcg_wasm_out_ctx_i32_store_r(s, f, r) \
+ do { \
+ tcg_wasm_out_op_local_get(s, CTX_IDX); \
+ tcg_wasm_out_op_global_get_r(s, r); \
+ tcg_wasm_out_op_i32_wrap_i64(s); \
+ tcg_wasm_out_op_i32_store(s, 0, offsetof(struct wasmContext, f)); \
+ } while (0)
+
+#define tcg_wasm_out_ctx_i32_store_local32(s, f, var) \
+ do { \
+ tcg_wasm_out_op_local_get(s, CTX_IDX); \
+ tcg_wasm_out_op_local_get(s, var); \
+ tcg_wasm_out_op_i32_store(s, 0, offsetof(struct wasmContext, f)); \
+ } while (0)
+
+#define tcg_wasm_out_ctx_i32_load(s, f) \
+ do { \
+ tcg_wasm_out_op_local_get(s, CTX_IDX); \
+ tcg_wasm_out_op_i32_load(s, 0, offsetof(struct wasmContext, f)); \
+ } while (0)
+
+static void tcg_wasm_out_exit_tb(TCGContext *s, uintptr_t arg)
+{
+ tcg_wasm_out_ctx_i32_store_const(s, tb_ptr, 0);
+ tcg_wasm_out_op_i32_const(s, (int32_t)arg);
+ tcg_wasm_out_op_return(s);
+}
+
+static void tcg_wasm_out_goto_ptr(TCGContext *s, TCGReg arg)
+{
+ tcg_wasm_out_op_global_get_r(s, arg);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_ctx_i32_load(s, tb_ptr);
+ tcg_wasm_out_op_i32_eq(s);
+ tcg_wasm_out_op_if_noret(s);
+ tcg_wasm_out_op_i64_const(s, 0);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_op_br(s, 2); /* br to the top of loop */
+ tcg_wasm_out_op_end(s);
+
+ tcg_wasm_out_ctx_i32_store_r(s, tb_ptr, arg);
+ tcg_wasm_out_op_i32_const(s, 0);
+ tcg_wasm_out_op_return(s);
+}
+
+static void tcg_wasm_out_goto_tb(
+ TCGContext *s, int which, uint32_t cur_reset_ptr)
+{
+ tcg_wasm_out_op_i32_const(s, (int32_t)get_jmp_target_addr(s, which));
+ tcg_wasm_out_op_i32_load(s, 0, 0);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, cur_reset_ptr);
+ tcg_wasm_out_op_i32_ne(s);
+ tcg_wasm_out_op_if_noret(s);
+
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_ctx_i32_load(s, tb_ptr);
+ tcg_wasm_out_op_i32_eq(s);
+ tcg_wasm_out_op_if_noret(s);
+ tcg_wasm_out_op_i64_const(s, 0);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_op_br(s, 3); /* br to the top of loop */
+ tcg_wasm_out_op_end(s);
+
+ tcg_wasm_out_ctx_i32_store_local32(s, tb_ptr, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_op_i32_const(s, 0);
+ tcg_wasm_out_op_return(s);
+ tcg_wasm_out_op_end(s);
+}
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -1931,6 +2039,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func,
static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg)
{
tcg_out_op_p(s, INDEX_op_exit_tb, (void *)arg);
+ tcg_wasm_out_exit_tb(s, arg);
}
static void tcg_out_goto_tb(TCGContext *s, int which)
@@ -1938,11 +2047,13 @@ static void tcg_out_goto_tb(TCGContext *s, int which)
/* indirect jump method. */
tcg_out_op_p(s, INDEX_op_goto_tb, (void *)get_jmp_target_addr(s, which));
set_jmp_reset_offset(s, which);
+ tcg_wasm_out_goto_tb(s, which, (uint32_t)s->code_ptr);
}
static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0)
{
tcg_out_op_r(s, INDEX_op_goto_ptr, a0);
+ tcg_wasm_out_goto_ptr(s, a0);
}
void tb_target_set_jmp_target(const TranslationBlock *tb, int n,
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 21/33] tcg/wasm32: Add call instruction
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (19 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 20/33] tcg/wasm32: Add exit_tb/goto_tb/goto_ptr instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 22/33] tcg/wasm32: Add qemu_ld/qemu_st instructions Kohei Tokunaga
` (11 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
To call QEMU functions from a TB (i.e. a Wasm module), those functions must
be imported into the module.
Wasm's call instruction can invoke an imported function using a locally
assigned function index. When a call TCG operation is generated, the Wasm
backend assigns a unique ID (starting from 0) to the target function. The
mapping between the function pointer and its assigned ID is recorded in the
HelperInfo structure.
Since Wasm's call instruction requires arguments to be pushed onto the Wasm
stack, the backend retrieves the function arguments from TCG's stack array
and pushes them to the stack before the call. After the function returns,
the result is retrieved from the stack and set in the corresponding TCG
variable.
In our Emscripten build configuration with !has_int128_type, a 128-bit value
is represented by the Int128 struct. These values are passed indirectly via
pointer parameters and returned via a prepended pointer argument, as
described in [1].
[1] https://github.com/WebAssembly/tool-conventions/blob/060cf4073e46931160c2e9ecd43177ee1fe93866/BasicCABI.md#function-arguments-and-return-values
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.h | 10 +++
tcg/wasm32/tcg-target.c.inc | 170 ++++++++++++++++++++++++++++++++++++
2 files changed, 180 insertions(+)
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
index ffa359b7dc..1944249891 100644
--- a/tcg/wasm32.h
+++ b/tcg/wasm32.h
@@ -12,6 +12,16 @@ struct wasmContext {
* Pointer to the TB to be executed.
*/
void *tb_ptr;
+
+ /*
+ * Pointer to the tci_tb_ptr variable.
+ */
+ void *tci_tb_ptr;
+
+ /*
+ * Buffer to store 128bit return value on call.
+ */
+ void *buf128;
};
#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 77db50cf85..708af1fbb6 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -140,6 +140,9 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
#define TMP32_LOCAL_1_IDX 2
#define TMP64_LOCAL_0_IDX 3
+/* function index */
+#define HELPER_IDX_START 0 /* helper funcitons */
+
#define BUF_SIZE 1024
typedef struct LinkedBuf {
struct LinkedBuf *next;
@@ -619,6 +622,12 @@ static void tcg_wasm_out_op_i32_store(TCGContext *s, uint32_t a, uint32_t o)
tcg_wasm_out_op_loadstore(s, 0x36, a, o);
}
+static void tcg_wasm_out_op_call(TCGContext *s, uint32_t func_idx)
+{
+ tcg_wasm_out8(s, 0x10);
+ tcg_wasm_out_leb128_uint32_t(s, func_idx);
+}
+
static void tcg_wasm_out_op_not(TCGContext *s)
{
tcg_wasm_out_op_i64_const(s, -1);
@@ -1626,6 +1635,165 @@ static void tcg_wasm_out_goto_tb(
tcg_wasm_out_op_end(s);
}
+static void push_arg_i64(TCGContext *s, int *stack_offset)
+{
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i64_load(s, 0, *stack_offset);
+ *stack_offset = *stack_offset + 8;
+}
+
+static void gen_call(TCGContext *s, const TCGHelperInfo *info, int func_idx)
+{
+ unsigned typemask = info->typemask;
+ int rettype = typemask & 7;
+ int stack_offset = 0;
+
+ if (rettype == dh_typecode_i128) {
+ /* receive 128bit return value via the buffer */
+ tcg_wasm_out_ctx_i32_load(s, buf128);
+ }
+
+ for (typemask >>= 3; typemask; typemask >>= 3) {
+ int typecode = typemask & 7;
+ if (typecode == dh_typecode_void) {
+ continue;
+ }
+ switch (typecode) {
+ case dh_typecode_i32:
+ case dh_typecode_s32:
+ case dh_typecode_ptr:
+ push_arg_i64(s, &stack_offset);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ break;
+ case dh_typecode_i64:
+ case dh_typecode_s64:
+ push_arg_i64(s, &stack_offset);
+ break;
+ case dh_typecode_i128:
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_const(s, stack_offset);
+ tcg_wasm_out_op_i32_add(s);
+ stack_offset += 16;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ tcg_wasm_out_op_call(s, func_idx);
+
+ if (rettype != dh_typecode_void) {
+ switch (rettype) {
+ case dh_typecode_i32:
+ case dh_typecode_s32:
+ case dh_typecode_ptr:
+ tcg_wasm_out_op_i64_extend_i32_s(s);
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
+ break;
+ case dh_typecode_i64:
+ case dh_typecode_s64:
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
+ break;
+ case dh_typecode_i128:
+ tcg_wasm_out_ctx_i32_load(s, buf128);
+ tcg_wasm_out_op_i64_load(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
+ tcg_wasm_out_ctx_i32_load(s, buf128);
+ tcg_wasm_out_op_i64_load(s, 0, 8);
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ return;
+}
+
+typedef struct HelperInfo {
+ struct HelperInfo *next;
+ uint32_t idx_on_qemu;
+} HelperInfo;
+
+__thread HelperInfo *helpers;
+
+static void init_helpers(void)
+{
+ helpers = NULL;
+}
+
+static int register_helper(TCGContext *s, int helper_idx_on_qemu)
+{
+ int idx = HELPER_IDX_START;
+
+ tcg_debug_assert(helper_idx_on_qemu >= 0);
+
+ HelperInfo *e = tcg_malloc(sizeof(HelperInfo));
+ e->idx_on_qemu = helper_idx_on_qemu;
+ e->next = NULL;
+ if (helpers == NULL) {
+ helpers = e;
+ return idx;
+ }
+ HelperInfo *last = helpers;
+ for (HelperInfo *p = last; p; p = p->next) {
+ last = p;
+ idx++;
+ }
+ last->next = e;
+ return idx;
+}
+
+static int helpers_len(void)
+{
+ int n = 0;
+ for (HelperInfo *p = helpers; p; p = p->next) {
+ n++;
+ }
+ return n;
+}
+
+static inline int helpers_copy(uint32_t *dst)
+{
+ void *start = dst;
+ for (HelperInfo *p = helpers; p; p = p->next) {
+ *dst++ = p->idx_on_qemu;
+ }
+ return (int)dst - (int)start;
+}
+
+
+static int get_helper_idx(TCGContext *s, int helper_idx_on_qemu)
+{
+ int idx = HELPER_IDX_START;
+
+ for (HelperInfo *p = helpers; p; p = p->next) {
+ if (p->idx_on_qemu == helper_idx_on_qemu) {
+ return idx;
+ }
+ idx++;
+ }
+ return -1;
+}
+
+static void tcg_wasm_out_call(TCGContext *s, int func,
+ const TCGHelperInfo *info)
+{
+ int func_idx = get_helper_idx(s, (int)func);
+ if (func_idx < 0) {
+ func_idx = register_helper(s, (int)func);
+ }
+
+ tcg_wasm_out_ctx_i32_load(s, tci_tb_ptr);
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
+ tcg_wasm_out_op_i32_store(s, 0, 0);
+
+ gen_call(s, info, func_idx);
+}
+
+
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
{
@@ -2034,6 +2202,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func,
insn = deposit32(insn, 0, 8, INDEX_op_call);
insn = deposit32(insn, 8, 4, which);
tcg_out32(s, insn);
+ tcg_wasm_out_call(s, (int)func, info);
}
static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg)
@@ -3072,6 +3241,7 @@ static void tcg_out_tb_start(TCGContext *s)
init_sub_buf();
init_blocks();
init_label_info();
+ init_helpers();
tcg_wasm_out_op_loop_noret(s);
tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 22/33] tcg/wasm32: Add qemu_ld/qemu_st instructions
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (20 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 21/33] tcg/wasm32: Add call instruction Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten Kohei Tokunaga
` (10 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit adds qemu_ld and qemu_st by calling the helper functions
corresponding to MemOp.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 108 ++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 708af1fbb6..ea9131e6fe 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -1793,6 +1793,112 @@ static void tcg_wasm_out_call(TCGContext *s, int func,
gen_call(s, info, func_idx);
}
+static void *qemu_ld_helper_ptr(uint32_t oi)
+{
+ MemOp mop = get_memop(oi);
+ switch (mop & MO_SSIZE) {
+ case MO_UB:
+ return helper_ldub_mmu;
+ case MO_SB:
+ return helper_ldsb_mmu;
+ case MO_UW:
+ return helper_lduw_mmu;
+ case MO_SW:
+ return helper_ldsw_mmu;
+ case MO_UL:
+ return helper_ldul_mmu;
+ case MO_SL:
+ return helper_ldsl_mmu;
+ case MO_UQ:
+ return helper_ldq_mmu;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
+ TCGReg addr_reg, MemOpIdx oi)
+{
+ int helper_idx;
+ int func_idx;
+ bool addr64 = s->addr_type == TCG_TYPE_I64;
+
+ helper_idx = (uint32_t)qemu_ld_helper_ptr(oi);
+ func_idx = get_helper_idx(s, helper_idx);
+ if (func_idx < 0) {
+ func_idx = register_helper(s, helper_idx);
+ }
+
+ if (!addr64) {
+ tcg_wasm_out_ext32u(s, TCG_REG_TMP, addr_reg);
+ addr_reg = TCG_REG_TMP;
+ }
+
+ /* call helper */
+ tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
+ tcg_wasm_out_op_i32_const(s, oi);
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
+
+ tcg_wasm_out_op_call(s, func_idx);
+ tcg_wasm_out_op_global_set_r(s, data_reg);
+}
+
+static void *qemu_st_helper_ptr(uint32_t oi)
+{
+ MemOp mop = get_memop(oi);
+ switch (mop & MO_SIZE) {
+ case MO_8:
+ return helper_stb_mmu;
+ case MO_16:
+ return helper_stw_mmu;
+ case MO_32:
+ return helper_stl_mmu;
+ case MO_64:
+ return helper_stq_mmu;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
+ TCGReg addr_reg, MemOpIdx oi)
+{
+ int helper_idx;
+ int func_idx;
+ bool addr64 = s->addr_type == TCG_TYPE_I64;
+ MemOp mop = get_memop(oi);
+
+ helper_idx = (uint32_t)qemu_st_helper_ptr(oi);
+ func_idx = get_helper_idx(s, helper_idx);
+ if (func_idx < 0) {
+ func_idx = register_helper(s, helper_idx);
+ }
+
+ if (!addr64) {
+ tcg_wasm_out_ext32u(s, TCG_REG_TMP, addr_reg);
+ addr_reg = TCG_REG_TMP;
+ }
+
+ /* call helper */
+ tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
+ switch (mop & MO_SSIZE) {
+ case MO_UQ:
+ tcg_wasm_out_op_global_get_r(s, data_reg);
+ break;
+ default:
+ tcg_wasm_out_op_global_get_r(s, data_reg);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ break;
+ }
+ tcg_wasm_out_op_i32_const(s, oi);
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
+
+ tcg_wasm_out_op_call(s, func_idx);
+}
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
intptr_t value, intptr_t addend)
@@ -3128,6 +3234,7 @@ static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data,
TCGReg addr, MemOpIdx oi)
{
tcg_out_op_rrm(s, INDEX_op_qemu_ld, data, addr, oi);
+ tcg_wasm_out_qemu_ld(s, data, addr, oi);
}
static const TCGOutOpQemuLdSt outop_qemu_ld = {
@@ -3153,6 +3260,7 @@ static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data,
TCGReg addr, MemOpIdx oi)
{
tcg_out_op_rrm(s, INDEX_op_qemu_st, data, addr, oi);
+ tcg_wasm_out_qemu_st(s, data, addr, oi);
}
static const TCGOutOpQemuLdSt outop_qemu_st = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (21 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 22/33] tcg/wasm32: Add qemu_ld/qemu_st instructions Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 14:32 ` Paolo Bonzini
2025-05-20 12:51 ` [PATCH 24/33] tcg/wasm32: Set TCG_TARGET_REG_BITS to 64 Kohei Tokunaga
` (9 subsequent siblings)
32 siblings, 1 reply; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
To enable 64-bit guest support in Wasm 32bit memory model today, it was
necessary to partially revert recent changes that removed support for
different pointer widths between the host and guest (e.g. commits
a70af12addd9060fdf8f3dbd42b42e3072c3914f and
bf455ec50b6fea15b4d2493059365bf94c706273) when compiling with
Emscripten. While this serves as a temporary workaround, a long-term
solution could involve adopting Wasm's 64-bit memory model once it gains
broader support, as it is currently not widely adopted (e.g. unsupported by
Safari and libffi).
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
accel/tcg/cputlb.c | 8 ++++----
include/exec/helper-head.h.inc | 6 ++++++
include/exec/tlb-common.h | 14 ++++++++++----
include/exec/vaddr.h | 11 +++++++++++
include/qemu/atomic.h | 4 ++++
include/tcg/tcg.h | 4 ++++
meson.build | 8 +++++---
7 files changed, 44 insertions(+), 11 deletions(-)
V1:
- Although I tried to use "#if HOST_LONG_BITS >= TARGET_LONG_BITS" based on
Paolo's suggestion from the previous patch series, TARGET_LONG_BITS is
marked as poisoned in include/exec/poison.h and cannot be used directly.
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 5f6d7c601c..b15e9e80ee 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -109,13 +109,13 @@ static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry,
{
/* Do not rearrange the CPUTLBEntry structure members. */
QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) !=
- MMU_DATA_LOAD * sizeof(uintptr_t));
+ MMU_DATA_LOAD * sizeof(tlb_addr));
QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) !=
- MMU_DATA_STORE * sizeof(uintptr_t));
+ MMU_DATA_STORE * sizeof(tlb_addr));
QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) !=
- MMU_INST_FETCH * sizeof(uintptr_t));
+ MMU_INST_FETCH * sizeof(tlb_addr));
- const uintptr_t *ptr = &entry->addr_idx[access_type];
+ const tlb_addr *ptr = &entry->addr_idx[access_type];
/* ofs might correspond to .addr_write, so use qatomic_read */
return qatomic_read(ptr);
}
diff --git a/include/exec/helper-head.h.inc b/include/exec/helper-head.h.inc
index 5b248fd713..7dfb4cfa46 100644
--- a/include/exec/helper-head.h.inc
+++ b/include/exec/helper-head.h.inc
@@ -58,6 +58,7 @@
# define dh_ctype_tl target_ulong
#endif /* COMPILING_PER_TARGET */
+#ifndef EMSCRIPTEN
#if __SIZEOF_POINTER__ == 4
# define dh_alias_vaddr i32
# define dh_typecode_vaddr dh_typecode_i32
@@ -68,6 +69,11 @@
# error "sizeof pointer is different from {4,8}"
#endif /* __SIZEOF_POINTER__ */
# define dh_ctype_vaddr uintptr_t
+#else
+# define dh_alias_vaddr i64
+# define dh_typecode_vaddr dh_typecode_i64
+# define dh_ctype_vaddr uint64_t
+#endif
/* We can't use glue() here because it falls foul of C preprocessor
recursive expansion rules. */
diff --git a/include/exec/tlb-common.h b/include/exec/tlb-common.h
index 03b5a8ffc7..679054bb44 100644
--- a/include/exec/tlb-common.h
+++ b/include/exec/tlb-common.h
@@ -19,14 +19,20 @@
#ifndef EXEC_TLB_COMMON_H
#define EXEC_TLB_COMMON_H 1
+#ifndef EMSCRIPTEN
#define CPU_TLB_ENTRY_BITS (HOST_LONG_BITS == 32 ? 4 : 5)
+typedef uintptr_t tlb_addr;
+#else
+#define CPU_TLB_ENTRY_BITS 5
+typedef uint64_t tlb_addr;
+#endif
/* Minimalized TLB entry for use by TCG fast path. */
typedef union CPUTLBEntry {
struct {
- uintptr_t addr_read;
- uintptr_t addr_write;
- uintptr_t addr_code;
+ tlb_addr addr_read;
+ tlb_addr addr_write;
+ tlb_addr addr_code;
/*
* Addend to virtual address to get host address. IO accesses
* use the corresponding iotlb value.
@@ -37,7 +43,7 @@ typedef union CPUTLBEntry {
* Padding to get a power of two size, as well as index
* access to addr_{read,write,code}.
*/
- uintptr_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uintptr_t)];
+ tlb_addr addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(tlb_addr)];
} CPUTLBEntry;
QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS));
diff --git a/include/exec/vaddr.h b/include/exec/vaddr.h
index 28bec632fb..ff57f944dd 100644
--- a/include/exec/vaddr.h
+++ b/include/exec/vaddr.h
@@ -9,6 +9,7 @@
* We do not support 64-bit guest on 32-host and detect at configure time.
* Therefore, a host pointer width will always fit a guest pointer.
*/
+#ifndef EMSCRIPTEN
typedef uintptr_t vaddr;
#define VADDR_PRId PRIdPTR
#define VADDR_PRIu PRIuPTR
@@ -16,5 +17,15 @@ typedef uintptr_t vaddr;
#define VADDR_PRIx PRIxPTR
#define VADDR_PRIX PRIXPTR
#define VADDR_MAX UINTPTR_MAX
+#else
+/* Explicitly define this as 64bit on emscripten */
+typedef uint64_t vaddr;
+#define VADDR_PRId PRId64
+#define VADDR_PRIu PRIu64
+#define VADDR_PRIo PRIo64
+#define VADDR_PRIx PRIx64
+#define VADDR_PRIX PRIX64
+#define VADDR_MAX UINT64_MAX
+#endif
#endif
diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h
index f80cba24cf..76a8fbcd8c 100644
--- a/include/qemu/atomic.h
+++ b/include/qemu/atomic.h
@@ -56,6 +56,7 @@
*/
#define signal_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST)
+#ifndef EMSCRIPTEN
/*
* Sanity check that the size of an atomic operation isn't "overly large".
* Despite the fact that e.g. i686 has 64-bit atomic operations, we do not
@@ -63,6 +64,9 @@
* bit of sanity checking that other 32-bit hosts might build.
*/
#define ATOMIC_REG_SIZE sizeof(void *)
+#else
+#define ATOMIC_REG_SIZE 8 /* wasm supports 64bit atomics */
+#endif
/* Weak atomic operations prevent the compiler moving other
* loads/stores past the atomic operation load/store. However there is
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 041d8035bc..62bc2c4ea8 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -218,6 +218,7 @@ typedef struct TCGv_ptr_d *TCGv_ptr;
typedef struct TCGv_vec_d *TCGv_vec;
typedef TCGv_ptr TCGv_env;
+#ifndef EMSCRIPTEN
#if __SIZEOF_POINTER__ == 4
typedef TCGv_i32 TCGv_vaddr;
#elif __SIZEOF_POINTER__ == 8
@@ -225,6 +226,9 @@ typedef TCGv_i64 TCGv_vaddr;
#else
# error "sizeof pointer is different from {4,8}"
#endif /* __SIZEOF_POINTER__ */
+#else
+typedef TCGv_i64 TCGv_vaddr;
+#endif
/* call flags */
/* Helper does not read globals (either directly or through an exception). It
diff --git a/meson.build b/meson.build
index c533243157..f6ed867819 100644
--- a/meson.build
+++ b/meson.build
@@ -3322,9 +3322,11 @@ foreach target : target_dirs
target_kconfig = []
foreach sym: accelerators
- # Disallow 64-bit on 32-bit emulation and virtualization
- if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
- continue
+ if host_arch != 'wasm32'
+ # Disallow 64-bit on 32-bit emulation and virtualization
+ if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
+ continue
+ endif
endif
if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, [])
config_target += { sym: 'y' }
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 24/33] tcg/wasm32: Set TCG_TARGET_REG_BITS to 64
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (22 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 25/33] tcg/wasm32: Set mulu2/muls2 as unimplemented Kohei Tokunaga
` (8 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit enables to Wasm backend to run as a 64bit backend with removing
TCG_TARGET_REG_BITS = 32 macros.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.c | 5 ++-
tcg/wasm32/tcg-target-reg-bits.h | 8 +---
tcg/wasm32/tcg-target.c.inc | 69 +++-----------------------------
3 files changed, 9 insertions(+), 73 deletions(-)
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
index 4bc53d76d0..b238ccf6d6 100644
--- a/tcg/wasm32.c
+++ b/tcg/wasm32.c
@@ -370,8 +370,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
unsigned i, s, n;
tci_args_nl(insn, tb_ptr, &len, &ptr);
- func = ((void **)ptr)[0];
- cif = ((void **)ptr)[1];
+ tcg_target_ulong *data = (tcg_target_ulong *)ptr;
+ func = (void *)data[0];
+ cif = (void *)data[1];
n = cif->nargs;
for (i = s = 0; i < n; ++i) {
diff --git a/tcg/wasm32/tcg-target-reg-bits.h b/tcg/wasm32/tcg-target-reg-bits.h
index dcb1a203f8..375feccf91 100644
--- a/tcg/wasm32/tcg-target-reg-bits.h
+++ b/tcg/wasm32/tcg-target-reg-bits.h
@@ -7,12 +7,6 @@
#ifndef TCG_TARGET_REG_BITS_H
#define TCG_TARGET_REG_BITS_H
-#if UINTPTR_MAX == UINT32_MAX
-# define TCG_TARGET_REG_BITS 32
-#elif UINTPTR_MAX == UINT64_MAX
-# define TCG_TARGET_REG_BITS 64
-#else
-# error Unknown pointer size for tci target
-#endif
+#define TCG_TARGET_REG_BITS 64
#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index ea9131e6fe..9fad96d0fd 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -30,15 +30,9 @@
/* Used for function call generation. */
#define TCG_TARGET_CALL_STACK_OFFSET 0
#define TCG_TARGET_STACK_ALIGN 8
-#if TCG_TARGET_REG_BITS == 32
-# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EVEN
-# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN
-# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN
-#else
-# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL
-# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL
-# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL
-#endif
+#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL
+#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL
+#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL
#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL
typedef uint32_t tcg_insn_unit_tci;
@@ -3083,39 +3077,6 @@ static const TCGOutOpMovcond outop_movcond = {
.out = tgen_movcond,
};
-static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah,
- TCGArg bl, bool const_bl,
- TCGArg bh, bool const_bh, TCGLabel *l)
-{
- tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP,
- al, ah, bl, bh, cond);
- tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l);
-}
-
-#if TCG_TARGET_REG_BITS != 32
-__attribute__((unused))
-#endif
-static const TCGOutOpBrcond2 outop_brcond2 = {
- .base.static_constraint = C_O0_I4(r, r, r, r),
- .out = tgen_brcond2,
-};
-
-static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret,
- TCGReg al, TCGReg ah,
- TCGArg bl, bool const_bl,
- TCGArg bh, bool const_bh)
-{
- tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, ret, al, ah, bl, bh, cond);
-}
-
-#if TCG_TARGET_REG_BITS != 32
-__attribute__((unused))
-#endif
-static const TCGOutOpSetcond2 outop_setcond2 = {
- .base.static_constraint = C_O1_I4(r, r, r, r, r),
- .out = tgen_setcond2,
-};
-
static void tcg_out_mb(TCGContext *s, unsigned a0)
{
tcg_out_op_v(s, INDEX_op_mb);
@@ -3242,18 +3203,8 @@ static const TCGOutOpQemuLdSt outop_qemu_ld = {
.out = tgen_qemu_ld,
};
-static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo,
- TCGReg datahi, TCGReg addr, MemOpIdx oi)
-{
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi);
- tcg_out_op_rrrr(s, INDEX_op_qemu_ld2, datalo, datahi, addr, TCG_REG_TMP);
-}
-
static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = {
- .base.static_constraint =
- TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r),
- .out =
- TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_ld2,
+ .base.static_constraint = C_NotImplemented,
};
static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data,
@@ -3268,18 +3219,8 @@ static const TCGOutOpQemuLdSt outop_qemu_st = {
.out = tgen_qemu_st,
};
-static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo,
- TCGReg datahi, TCGReg addr, MemOpIdx oi)
-{
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi);
- tcg_out_op_rrrr(s, INDEX_op_qemu_st2, datalo, datahi, addr, TCG_REG_TMP);
-}
-
static const TCGOutOpQemuLdSt2 outop_qemu_st2 = {
- .base.static_constraint =
- TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r),
- .out =
- TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_st2,
+ .base.static_constraint = C_NotImplemented,
};
static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 25/33] tcg/wasm32: Set mulu2/muls2 as unimplemented
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (23 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 24/33] tcg/wasm32: Set TCG_TARGET_REG_BITS to 64 Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 26/33] tcg/wasm32: Add initialization of fundamental registers Kohei Tokunaga
` (7 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
These operations have no direct equivalents in Wasm, so they are left
unimplemented and delegated to helper functions.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 38 ++-----------------------------------
1 file changed, 2 insertions(+), 36 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 9fad96d0fd..df34097267 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -2076,19 +2076,6 @@ static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
tcg_out32(s, insn);
}
-static void tcg_out_op_rrrr(TCGContext *s, TCGOpcode op,
- TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3)
-{
- tcg_insn_unit_tci insn = 0;
-
- insn = deposit32(insn, 0, 8, op);
- insn = deposit32(insn, 8, 4, r0);
- insn = deposit32(insn, 12, 4, r1);
- insn = deposit32(insn, 16, 4, r2);
- insn = deposit32(insn, 20, 4, r3);
- tcg_out32(s, insn);
-}
-
static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
TCGReg r0, TCGReg r1, TCGReg r2,
TCGReg r3, TCGReg r4, TCGCond c5)
@@ -2610,37 +2597,16 @@ static const TCGOutOpBinary outop_mul = {
.out_rrr = tgen_mul,
};
-static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags)
-{
- return type == TCG_TYPE_REG ? C_O2_I2(r, r, r, r) : C_NotImplemented;
-}
-
-static void tgen_muls2(TCGContext *s, TCGType type,
- TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3)
-{
- tcg_out_op_rrrr(s, INDEX_op_muls2, a0, a1, a2, a3);
-}
-
static const TCGOutOpMul2 outop_muls2 = {
- .base.static_constraint = C_Dynamic,
- .base.dynamic_constraint = cset_mul2,
- .out_rrrr = tgen_muls2,
+ .base.static_constraint = C_NotImplemented,
};
static const TCGOutOpBinary outop_mulsh = {
.base.static_constraint = C_NotImplemented,
};
-static void tgen_mulu2(TCGContext *s, TCGType type,
- TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3)
-{
- tcg_out_op_rrrr(s, INDEX_op_mulu2, a0, a1, a2, a3);
-}
-
static const TCGOutOpMul2 outop_mulu2 = {
- .base.static_constraint = C_Dynamic,
- .base.dynamic_constraint = cset_mul2,
- .out_rrrr = tgen_mulu2,
+ .base.static_constraint = C_NotImplemented,
};
static const TCGOutOpBinary outop_muluh = {
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 26/33] tcg/wasm32: Add initialization of fundamental registers
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (24 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 25/33] tcg/wasm32: Set mulu2/muls2 as unimplemented Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 27/33] tcg/wasm32: Write wasm binary to TB Kohei Tokunaga
` (6 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit adds initialization of TCG_AREG0 and TCG_REG_CALL_STACK at the
beginning of each TB. The CPUArchState struct and the stack array are passed
from the caller via the wasmContext structure. Since TB execution begins at
the first block, the BLOCK_PTR_IDX variable is initialized to 0.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.h | 10 ++++++++++
tcg/wasm32/tcg-target.c.inc | 17 +++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
index 1944249891..ab23e26eca 100644
--- a/tcg/wasm32.h
+++ b/tcg/wasm32.h
@@ -22,6 +22,16 @@ struct wasmContext {
* Buffer to store 128bit return value on call.
*/
void *buf128;
+
+ /*
+ * Pointer to CPUArchState struct.
+ */
+ CPUArchState *env;
+
+ /*
+ * Pointer to a stack array.
+ */
+ uint64_t *stack;
};
#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index df34097267..4b7cd784cb 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -3258,6 +3258,23 @@ static void tcg_out_tb_start(TCGContext *s)
init_label_info();
init_helpers();
+ /* generate wasm code to initialize fundamental registers */
+ tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_noret(s);
+
+ tcg_wasm_out_ctx_i32_load(s, env);
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ tcg_wasm_out_op_global_set_r(s, TCG_AREG0);
+
+ tcg_wasm_out_ctx_i32_load(s, stack);
+ tcg_wasm_out_op_i64_extend_i32_u(s);
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_CALL_STACK);
+ tcg_wasm_out_op_end(s);
+
+ tcg_wasm_out_op_i64_const(s, 0);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+
tcg_wasm_out_op_loop_noret(s);
tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
tcg_wasm_out_op_i64_eqz(s);
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 27/33] tcg/wasm32: Write wasm binary to TB
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (25 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 26/33] tcg/wasm32: Add initialization of fundamental registers Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 28/33] tcg/wasm32: Implement instantiation of Wasm binary Kohei Tokunaga
` (5 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit updates tcg_out_tb_start and tcg_out_tb_end to emit Wasm
binaries into the TB code buffer. The generated Wasm binary defines a
function of type wasm_tb_func which takes a wasmContext, executes the TB,
and returns a result. In the Wasm backend, each TB starts with a
wasmTBHeader, followed by the following data:
- TCI code
- Wasm code
- Array of function indices imported into the Wasm instance
The wasmTBHeader contains pointers to each of these elements.
tcg_out_tb_start writes the wasmTBHeader to the code buffer. tcg_out_tb_end
generates the full Wasm executable binary by creating the Wasm module header
following the spec[1][2] and copying the Wasm code body from sub_buf to the
code buffer. Wasm binary is placed after the TCI code which was emitted
earlier.
Additionally, an array of imported function pointers is appended to the TB.
They are used during Wasm module instantiation. Function are imported to
Wasm with names like "helper.0", "helper.1", etc., where the number
corresponds to the assigned function IDs.
Each function's type signature must also be encoded in the Wasm module header.
To support this, each call, qemu_ld and qemu_st operation records the target
function's type information to a buffer.
Memory is shared between QEMU and the TBs and is imported to the Wasm module
with the name "env.buffer".
[1] https://webassembly.github.io/spec/core/binary/modules.html
[2] https://github.com/WebAssembly/threads/blob/b2567bff61ee6fbe731934f0ed17a5d48dc9ab01/proposals/threads/Overview.md
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.h | 26 +++
tcg/wasm32/tcg-target.c.inc | 390 ++++++++++++++++++++++++++++++++++++
2 files changed, 416 insertions(+)
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
index ab23e26eca..f8651af4ee 100644
--- a/tcg/wasm32.h
+++ b/tcg/wasm32.h
@@ -34,4 +34,30 @@ struct wasmContext {
uint64_t *stack;
};
+/* Instantiated Wasm function of a TB */
+typedef int32_t (*wasm_tb_func)(struct wasmContext *);
+
+/*
+ * TB of wasm backend starts from a header which stores pointers for each data
+ * stored in the following region in the TB.
+ */
+struct wasmTBHeader {
+ /*
+ * Pointer to the region containing TCI instructions.
+ */
+ void *tci_ptr;
+
+ /*
+ * Pointer to the region containing Wasm instructions.
+ */
+ void *wasm_ptr;
+ int wasm_size;
+
+ /*
+ * Pointer to the array containing imported function pointers.
+ */
+ void *import_ptr;
+ int import_size;
+};
+
#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 4b7cd784cb..d9a3abae70 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -485,6 +485,30 @@ static void tcg_wasm_out_op_cond_i32(
tcg_wasm_out8(s, op);
}
+static void fill_uint32_leb128(uint8_t *b, uint32_t v)
+{
+ do {
+ *b |= v & 0x7f;
+ v >>= 7;
+ b++;
+ } while (v != 0);
+}
+
+static int write_uint32_leb128(uint8_t *b, uint32_t v)
+{
+ uint8_t *base = b;
+ do {
+ *b = v & 0x7f;
+ v >>= 7;
+ if (v != 0) {
+ *b |= 0x80;
+ }
+ b++;
+ } while (v != 0);
+
+ return (int)(b - base);
+}
+
static void tcg_wasm_out_leb128_sint32_t(TCGContext *s, int32_t v)
{
bool more = true;
@@ -1706,6 +1730,105 @@ static void gen_call(TCGContext *s, const TCGHelperInfo *info, int func_idx)
return;
}
+__thread LinkedBuf *types_buf_root;
+__thread LinkedBuf *types_buf_cur;
+
+static void init_types_buf(void)
+{
+ types_buf_root = new_linked_buf();
+ types_buf_cur = types_buf_root;
+}
+
+static inline void types_buf_out8(uint8_t v)
+{
+ types_buf_cur = linked_buf_out8(types_buf_cur, v);
+}
+
+static inline int types_buf_len(void)
+{
+ return linked_buf_len(types_buf_root);
+}
+
+static void types_out_leb128_uint32(uint32_t v)
+{
+ uint8_t b;
+ do {
+ b = v & 0x7f;
+ v >>= 7;
+ if (v != 0) {
+ b |= 0x80;
+ }
+ types_buf_out8(b);
+ } while (v != 0);
+}
+
+static void gen_func_type_call(TCGContext *s, const TCGHelperInfo *info)
+{
+ unsigned typemask = info->typemask;
+ int rettype = typemask & 7;
+ int vec_size = 0;
+
+ if (rettype == dh_typecode_i128) {
+ vec_size++;
+ }
+ for (int m = typemask >> 3; m; m >>= 3) {
+ int typecode = m & 7;
+ if (typecode != dh_typecode_void) {
+ vec_size++;
+ }
+ }
+
+ types_buf_out8(0x60);
+ types_out_leb128_uint32(vec_size);
+
+ if (rettype == dh_typecode_i128) {
+ types_buf_out8(0x7f);
+ }
+
+ for (int m = typemask >> 3; m; m >>= 3) {
+ int typecode = m & 7;
+ if (typecode == dh_typecode_void) {
+ continue;
+ }
+ switch (typecode) {
+ case dh_typecode_i32:
+ case dh_typecode_s32:
+ case dh_typecode_ptr:
+ types_buf_out8(0x7f);
+ break;
+ case dh_typecode_i64:
+ case dh_typecode_s64:
+ types_buf_out8(0x7e);
+ break;
+ case dh_typecode_i128:
+ types_buf_out8(0x7f);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ if ((rettype == dh_typecode_void) || (rettype == dh_typecode_i128)) {
+ types_buf_out8(0x0);
+ } else {
+ types_buf_out8(0x1);
+ switch (rettype) {
+ case dh_typecode_i32:
+ case dh_typecode_s32:
+ case dh_typecode_ptr:
+ types_buf_out8(0x7f);
+ break;
+ case dh_typecode_i64:
+ case dh_typecode_s64:
+ types_buf_out8(0x7e);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+ return;
+}
+
typedef struct HelperInfo {
struct HelperInfo *next;
uint32_t idx_on_qemu;
@@ -1778,6 +1901,7 @@ static void tcg_wasm_out_call(TCGContext *s, int func,
int func_idx = get_helper_idx(s, (int)func);
if (func_idx < 0) {
func_idx = register_helper(s, (int)func);
+ gen_func_type_call(s, info);
}
tcg_wasm_out_ctx_i32_load(s, tci_tb_ptr);
@@ -1787,6 +1911,39 @@ static void tcg_wasm_out_call(TCGContext *s, int func,
gen_call(s, info, func_idx);
}
+static void gen_func_type_qemu_ld(TCGContext *s, uint32_t oi)
+{
+ types_buf_out8(0x60);
+ types_buf_out8(0x4);
+ types_buf_out8(0x7f);
+ types_buf_out8(0x7e);
+ types_buf_out8(0x7f);
+ types_buf_out8(0x7f);
+ types_buf_out8(0x1);
+ types_buf_out8(0x7e);
+}
+
+static void gen_func_type_qemu_st(TCGContext *s, uint32_t oi)
+{
+ MemOp mop = get_memop(oi);
+
+ types_buf_out8(0x60);
+ types_buf_out8(0x5);
+ types_buf_out8(0x7f);
+ types_buf_out8(0x7e);
+ switch (mop & MO_SSIZE) {
+ case MO_UQ:
+ types_buf_out8(0x7e);
+ break;
+ default:
+ types_buf_out8(0x7f);
+ break;
+ }
+ types_buf_out8(0x7f);
+ types_buf_out8(0x7f);
+ types_buf_out8(0x0);
+}
+
static void *qemu_ld_helper_ptr(uint32_t oi)
{
MemOp mop = get_memop(oi);
@@ -1821,6 +1978,7 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
func_idx = get_helper_idx(s, helper_idx);
if (func_idx < 0) {
func_idx = register_helper(s, helper_idx);
+ gen_func_type_qemu_ld(s, oi);
}
if (!addr64) {
@@ -1868,6 +2026,7 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
func_idx = get_helper_idx(s, helper_idx);
if (func_idx < 0) {
func_idx = register_helper(s, helper_idx);
+ gen_func_type_qemu_st(s, oi);
}
if (!addr64) {
@@ -3251,12 +3410,207 @@ static inline void tcg_target_qemu_prologue(TCGContext *s)
{
}
+static const uint8_t mod_1[] = {
+ 0x0, 0x61, 0x73, 0x6d, /* magic */
+ 0x01, 0x0, 0x0, 0x0, /* version */
+
+ 0x01, /* type section */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placehodler for size */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placehodler for num of types vec */
+ 0x60, /* 0: Type of "start" function */
+ 0x01, 0x7f, /* arg: ctx pointer (i32) */
+ 0x01, 0x7f, /* return: res (i32) */
+};
+
+static const uint8_t mod_2[] = {
+ 0x02, /* import section */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placehodler for size */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placehodler for imports num */
+ 0x03, 0x65, 0x6e, 0x76, /* module: "env" */
+ 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, /* name: "buffer" */
+ 0x02, 0x03, /* shared mem */
+ 0x00, 0xff, 0xff, 0x03, /* min: 0, max: 0xffff pages */
+};
+
+static const uint8_t mod_3[] = {
+ 0x03, /* function section */
+ 2, 1, 0x00, /* function type 0 */
+
+ 0x06, /* global section */
+ 0x5b, /* section size */
+ 18, /* num of global vars */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+ 0x7e, 0x01, 0x42, 0x00, 0x0b, /* 0-cleared 64bit var */
+
+ 0x07, /* export section */
+ 13, /* size of section */
+ 1, /* num of funcs */
+ 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, /* "start" function */
+ 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, /* placeholder for func index*/
+};
+
+static const uint8_t mod_4[] = {
+ 0x0a, /* code section */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placeholder for section size*/
+ 1, /* num of codes */
+ 0x80, 0x80, 0x80, 0x80, 0x00, /* placeholder for code size */
+ 0x2, 0x2, 0x7f, 0x1, 0x7e, /* variables (32bit*2, 64bit*1) */
+};
+
+static int write_mod_1(TCGContext *s)
+{
+ void *base = s->code_ptr;
+ int helpers_num = helpers_len();
+
+ if (unlikely(((void *)s->code_ptr + sizeof(mod_1) + types_buf_len())
+ > s->code_gen_highwater)) {
+ return -1;
+ }
+
+ memcpy(s->code_ptr, mod_1, sizeof(mod_1));
+ s->code_ptr += sizeof(mod_1);
+ linked_buf_write(types_buf_root, s->code_ptr);
+ s->code_ptr += types_buf_len();
+
+ uint32_t type_section_size = types_buf_len() + 10;
+ fill_uint32_leb128(base + 9, type_section_size);
+ fill_uint32_leb128(base + 14, HELPER_IDX_START + helpers_num + 1);
+
+ return 0;
+}
+
+static int write_mod_2(TCGContext *s)
+{
+ void *base = s->code_ptr;
+ int helpers_num = helpers_len();
+
+ if (unlikely(((void *)s->code_ptr + sizeof(mod_2))
+ > s->code_gen_highwater)) {
+ return -1;
+ }
+
+ tcg_debug_assert(helpers_num <= INT_MAX);
+ memcpy(s->code_ptr, mod_2, sizeof(mod_2));
+ s->code_ptr += sizeof(mod_2);
+ for (int i = 0; i < helpers_num; i++) {
+ int typeidx = HELPER_IDX_START + i + 1;
+ char buf[11]; /* enough for decimal int max + NULL*/
+ int n = snprintf(buf, sizeof(buf), "%d", i);
+ tcg_debug_assert(n < sizeof(buf));
+ *(uint8_t *)s->code_ptr++ = 6; /* helper */
+ *(uint8_t *)s->code_ptr++ = 0x68;
+ *(uint8_t *)s->code_ptr++ = 0x65;
+ *(uint8_t *)s->code_ptr++ = 0x6c;
+ *(uint8_t *)s->code_ptr++ = 0x70;
+ *(uint8_t *)s->code_ptr++ = 0x65;
+ *(uint8_t *)s->code_ptr++ = 0x72;
+ s->code_ptr += write_uint32_leb128((uint8_t *)s->code_ptr, n);
+ memcpy(s->code_ptr, buf, n);
+ s->code_ptr += n;
+ *(uint8_t *)s->code_ptr++ = 0x00; /* type(0) */
+ s->code_ptr += write_uint32_leb128((uint8_t *)s->code_ptr, typeidx);
+ if (unlikely(((void *)s->code_ptr > s->code_gen_highwater))) {
+ return -1;
+ }
+ }
+
+ uint32_t import_section_size = (int)s->code_ptr - (int)base - 6;
+ fill_uint32_leb128(base + 1, import_section_size);
+ fill_uint32_leb128(base + 6, HELPER_IDX_START + helpers_num + 1);
+
+ return 0;
+}
+
+static int write_mod_3(TCGContext *s)
+{
+ void *base = s->code_ptr;
+
+ if (unlikely(((void *)s->code_ptr + sizeof(mod_3))
+ > s->code_gen_highwater)) {
+ return -1;
+ }
+
+ memcpy(s->code_ptr, mod_3, sizeof(mod_3));
+ s->code_ptr += sizeof(mod_3);
+
+ int startidx = HELPER_IDX_START + helpers_len();
+ fill_uint32_leb128(base + 107, startidx);
+
+ return 0;
+}
+
+static int write_mod_4(TCGContext *s)
+{
+ void *base = s->code_ptr;
+
+ if (unlikely(((void *)s->code_ptr + sizeof(mod_4))
+ > s->code_gen_highwater)) {
+ return -1;
+ }
+
+ memcpy(s->code_ptr, mod_4, sizeof(mod_4));
+ s->code_ptr += sizeof(mod_4);
+
+ int code_size = sub_buf_len() + 5;
+ fill_uint32_leb128(base + 1, code_size + 6);
+ fill_uint32_leb128(base + 7, code_size);
+
+ return 0;
+}
+
+static int write_mod_code(TCGContext *s)
+{
+ void *base = s->code_ptr;
+ int code_size = sub_buf_len();
+
+ if (unlikely(((void *)s->code_ptr + code_size) > s->code_gen_highwater)) {
+ return -1;
+ }
+ linked_buf_write(sub_buf_root, s->code_ptr);
+ s->code_ptr += code_size;
+ for (BlockPlaceholder *p = block_placeholder; p; p = p->next) {
+ uint8_t *ph = p->pos + base;
+ int blk = get_block_of_label(p->label);
+ tcg_debug_assert(blk >= 0);
+ fill_uint32_leb128(ph, blk);
+ }
+
+ return 0;
+}
+
static void tcg_out_tb_start(TCGContext *s)
{
+ int size;
+ struct wasmTBHeader *h;
+
init_sub_buf();
init_blocks();
init_label_info();
init_helpers();
+ init_types_buf();
+
+ /* TB starts from a header */
+ h = (struct wasmTBHeader *)(s->code_ptr);
+ s->code_ptr += sizeof(struct wasmTBHeader);
+
+ /* Followed by TCI code */
+ h->tci_ptr = s->code_ptr;
/* generate wasm code to initialize fundamental registers */
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
@@ -3283,11 +3637,47 @@ static void tcg_out_tb_start(TCGContext *s)
static int tcg_out_tb_end(TCGContext *s)
{
+ int res;
+ struct wasmTBHeader *h = (struct wasmTBHeader *)(s->code_buf);
+
tcg_wasm_out_op_end(s); /* end if */
tcg_wasm_out_op_end(s); /* end loop */
tcg_wasm_out8(s, 0x0); /* unreachable */
tcg_wasm_out_op_end(s); /* end func */
+ /* write wasm blob */
+ h->wasm_ptr = s->code_ptr;
+ res = write_mod_1(s);
+ if (res < 0) {
+ return res;
+ }
+ res = write_mod_2(s);
+ if (res < 0) {
+ return res;
+ }
+ res = write_mod_3(s);
+ if (res < 0) {
+ return res;
+ }
+ res = write_mod_4(s);
+ if (res < 0) {
+ return res;
+ }
+ res = write_mod_code(s);
+ if (res < 0) {
+ return res;
+ }
+ h->wasm_size = (int)s->code_ptr - (int)h->wasm_ptr;
+
+ /* record imported helper functions */
+ if (unlikely(((void *)s->code_ptr + helpers_len() * 4)
+ > s->code_gen_highwater)) {
+ return -1;
+ }
+ h->import_ptr = s->code_ptr;
+ s->code_ptr += helpers_copy((uint32_t *)s->code_ptr);
+ h->import_size = (int)s->code_ptr - (int)h->import_ptr;
+
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 28/33] tcg/wasm32: Implement instantiation of Wasm binary
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (26 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 27/33] tcg/wasm32: Write wasm binary to TB Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 29/33] tcg/wasm32: Allow Asyncify unwinding from TB Kohei Tokunaga
` (4 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
instantiate_wasm is a function that instantiates a TB's Wasm binary,
importing the functions as specified by its arguments. Following the header
definition in wasm32/tcg-target.c.inc, QEMU's memory is imported into the
module as "env.buffer", and helper functions are imported as
"helper.<id>". The instantiated Wasm module is imported to QEMU using
Emscripten's "addFunction" feature[1] which returns a function pointer. This
allows QEMU to call this module directly from C code via that pointer.
Note Since FireFox 138, WebAssembly.Module no longer accepts a
SharedArrayBuffer as input [2] as reported by Nicolas Vandeginste in my
downstream fork[3]. This commit ensures that WebAssembly.Module() is passed
a Uint8Array created from the binary data on a SharedArrayBuffer.
[1] https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-javascript-functions-as-function-pointers-from-c
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1965217
[3] https://github.com/ktock/qemu-wasm/pull/25
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
index b238ccf6d6..f2269c0a22 100644
--- a/tcg/wasm32.c
+++ b/tcg/wasm32.c
@@ -25,6 +25,7 @@
#include "disas/dis-asm.h"
#include "tcg-has.h"
#include <ffi.h>
+#include <emscripten.h>
#define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS)
@@ -44,6 +45,29 @@
__thread uintptr_t tci_tb_ptr;
+EM_JS(int, instantiate_wasm, (int wasm_begin,
+ int wasm_size,
+ int import_vec_begin,
+ int import_vec_size),
+{
+ const memory_v = new DataView(HEAP8.buffer);
+ const wasm = HEAP8.subarray(wasm_begin, wasm_begin + wasm_size);
+ var helper = {};
+ for (var i = 0; i < import_vec_size / 4; i++) {
+ helper[i] = wasmTable.get(
+ memory_v.getInt32(import_vec_begin + i * 4, true));
+ }
+ const mod = new WebAssembly.Module(new Uint8Array(wasm));
+ const inst = new WebAssembly.Instance(mod, {
+ "env" : {
+ "buffer" : wasmMemory,
+ },
+ "helper" : helper,
+ });
+
+ return addFunction(inst.exports.start, 'ii');
+});
+
static void tci_write_reg64(tcg_target_ulong *regs, uint32_t high_index,
uint32_t low_index, uint64_t value)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 29/33] tcg/wasm32: Allow Asyncify unwinding from TB
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (27 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 28/33] tcg/wasm32: Implement instantiation of Wasm binary Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 30/33] tcg/wasm32: Enable instantiation of TBs executed many times Kohei Tokunaga
` (3 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Emscripten's Fiber coroutine implements coroutine switching using the stack
unwinding and rewinding capabilities of Asyncify [1]. When a coroutine
yields (i.e. switches out), Asyncify unwinds the stack, returning control to
Emscripten's JS code (Fiber.trampoline()), which then performs stack
rewinding to resume execution in the target coroutine. Stack unwinding is
implemented by a sequence of immediate function returns, while rewinding
works by re-entering the functions in the call stack, skipping any code
between the top of the function and the original call position [2].
This commit modifies the Wasm TB modules to support Fiber
coroutines. Assuming the TCG CPU loop is executed by only one coroutine per
thread, a TB module must allow helper functions to unwind and be resumed via
rewinding.
Specifically:
- When a helper returns due to an unwind, the module must immediately return
to its caller, allowing unwinding to propagate.
- When being called again for a rewind, the module must skip any code
between the top of the function and the call position that triggered the
unwind, and directly enter the helper.
To support this:
- TBs now check the Asyncify.state JS object after each helper call. If
unwinding is in progress, the TB immediately returns control to the
caller.
- Each function call is preceded by a block boundary and an update of the
BLOCK_PTR_IDX variable. This enables the TB to re-enter execution at the
correct point during a rewind, skipping earlier blocks.
Additionally, this commit introduces wasmContext.do_init which is a flag
indicating whether the TB should reset the BLOCK_PTR_IDX variable to 0
(i.e. start from the beginning). In call_wasm_tb, this is always set
(ctx.do_init = 1) to ensure normal TB execution begins at the first
block. Once the TB resets the BLOCK_PTR_IDX variable, it also clears
do_init. During a rewind, the C code does not set ctx.do_init to 1, allowing
the TB to preserve the BLOCK_PTR_IDX value from the previous unwind 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/wasm32.c | 3 ++
tcg/wasm32.h | 11 ++++++++
tcg/wasm32/tcg-target.c.inc | 56 +++++++++++++++++++++++++++++++++++--
3 files changed, 68 insertions(+), 2 deletions(-)
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
index f2269c0a22..e6a3dbf750 100644
--- a/tcg/wasm32.c
+++ b/tcg/wasm32.c
@@ -53,6 +53,9 @@ EM_JS(int, instantiate_wasm, (int wasm_begin,
const memory_v = new DataView(HEAP8.buffer);
const wasm = HEAP8.subarray(wasm_begin, wasm_begin + wasm_size);
var helper = {};
+ helper.u = () => {
+ return (Asyncify.state != Asyncify.State.Unwinding) ? 1 : 0;
+ };
for (var i = 0; i < import_vec_size / 4; i++) {
helper[i] = wasmTable.get(
memory_v.getInt32(import_vec_begin + i * 4, true));
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
index f8651af4ee..f2749f1e0e 100644
--- a/tcg/wasm32.h
+++ b/tcg/wasm32.h
@@ -32,11 +32,22 @@ struct wasmContext {
* Pointer to a stack array.
*/
uint64_t *stack;
+
+ /*
+ * Flag indicates whether to initialize basic registers(1) or not(0).
+ */
+ uint32_t do_init;
};
/* Instantiated Wasm function of a TB */
typedef int32_t (*wasm_tb_func)(struct wasmContext *);
+static inline int32_t call_wasm_tb(wasm_tb_func f, struct wasmContext *ctx)
+{
+ ctx->do_init = 1; /* reset block index (rewinding will skip this) */
+ return f(ctx);
+}
+
/*
* TB of wasm backend starts from a header which stores pointers for each data
* stored in the following region in the TB.
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index d9a3abae70..04cd9b6e4a 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -135,7 +135,8 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
#define TMP64_LOCAL_0_IDX 3
/* function index */
-#define HELPER_IDX_START 0 /* helper funcitons */
+#define CHECK_UNWINDING_IDX 0 /* a funtion of checking Asyncify status */
+#define HELPER_IDX_START 1 /* helper funcitons */
#define BUF_SIZE 1024
typedef struct LinkedBuf {
@@ -1895,6 +1896,17 @@ static int get_helper_idx(TCGContext *s, int helper_idx_on_qemu)
return -1;
}
+static void tcg_wasm_out_handle_unwinding(TCGContext *s)
+{
+ tcg_wasm_out_op_call(s, CHECK_UNWINDING_IDX);
+ tcg_wasm_out_op_i32_eqz(s);
+ tcg_wasm_out_op_if_noret(s);
+ tcg_wasm_out_op_i32_const(s, 0);
+ /* returns if unwinding */
+ tcg_wasm_out_op_return(s);
+ tcg_wasm_out_op_end(s);
+}
+
static void tcg_wasm_out_call(TCGContext *s, int func,
const TCGHelperInfo *info)
{
@@ -1908,7 +1920,16 @@ static void tcg_wasm_out_call(TCGContext *s, int func,
tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
tcg_wasm_out_op_i32_store(s, 0, 0);
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_i64_const(s, block_idx + 1);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_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)
@@ -1986,6 +2007,14 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
addr_reg = TCG_REG_TMP;
}
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_i64_const(s, block_idx + 1);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_new_block(s);
+
/* call helper */
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
tcg_wasm_out_op_i32_wrap_i64(s);
@@ -1995,6 +2024,7 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_call(s, func_idx);
tcg_wasm_out_op_global_set_r(s, data_reg);
+ tcg_wasm_out_handle_unwinding(s);
}
static void *qemu_st_helper_ptr(uint32_t oi)
@@ -2034,6 +2064,14 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
addr_reg = TCG_REG_TMP;
}
+ /*
+ * update the block index so that the possible rewinding will
+ * skip this block
+ */
+ tcg_wasm_out_op_i64_const(s, block_idx + 1);
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_new_block(s);
+
/* call helper */
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
tcg_wasm_out_op_i32_wrap_i64(s);
@@ -2051,6 +2089,7 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
tcg_wasm_out_op_call(s, func_idx);
+ tcg_wasm_out_handle_unwinding(s);
}
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
@@ -3420,6 +3459,9 @@ static const uint8_t mod_1[] = {
0x60, /* 0: Type of "start" function */
0x01, 0x7f, /* arg: ctx pointer (i32) */
0x01, 0x7f, /* return: res (i32) */
+ 0x60, /* 1: Type of the asyncify helper */
+ 0x0, /* no argument */
+ 0x01, 0x7f, /* return: res (i32) */
};
static const uint8_t mod_2[] = {
@@ -3430,6 +3472,9 @@ static const uint8_t mod_2[] = {
0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, /* name: "buffer" */
0x02, 0x03, /* shared mem */
0x00, 0xff, 0xff, 0x03, /* min: 0, max: 0xffff pages */
+ 0x06, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, /* module: "helper" */
+ 0x01, 0x75, /* name: "u" */
+ 0x00, 0x01, /* func type 1 */
};
static const uint8_t mod_3[] = {
@@ -3488,7 +3533,7 @@ static int write_mod_1(TCGContext *s)
linked_buf_write(types_buf_root, s->code_ptr);
s->code_ptr += types_buf_len();
- uint32_t type_section_size = types_buf_len() + 10;
+ uint32_t type_section_size = types_buf_len() + 14;
fill_uint32_leb128(base + 9, type_section_size);
fill_uint32_leb128(base + 14, HELPER_IDX_START + helpers_num + 1);
@@ -3613,6 +3658,11 @@ static void tcg_out_tb_start(TCGContext *s)
h->tci_ptr = s->code_ptr;
/* generate wasm code to initialize fundamental registers */
+ tcg_wasm_out_ctx_i32_load(s, do_init);
+ tcg_wasm_out_op_i32_const(s, 0);
+ tcg_wasm_out_op_i32_ne(s);
+ tcg_wasm_out_op_if_noret(s);
+
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
tcg_wasm_out_op_i64_eqz(s);
tcg_wasm_out_op_if_noret(s);
@@ -3626,8 +3676,10 @@ static void tcg_out_tb_start(TCGContext *s)
tcg_wasm_out_op_global_set_r(s, TCG_REG_CALL_STACK);
tcg_wasm_out_op_end(s);
+ tcg_wasm_out_ctx_i32_store_const(s, do_init, 0);
tcg_wasm_out_op_i64_const(s, 0);
tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
+ tcg_wasm_out_op_end(s);
tcg_wasm_out_op_loop_noret(s);
tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 30/33] tcg/wasm32: Enable instantiation of TBs executed many times
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (28 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 29/33] tcg/wasm32: Allow Asyncify unwinding from TB Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 31/33] tcg/wasm32: Enable TLB lookup Kohei Tokunaga
` (2 subsequent siblings)
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit enables instantiations of TBs in wasm32.c. Browsers cause out of
memory error if too many Wasm instances are created so the number of
instances needs to be limited. So this commit restricts instantiation only
for TBs that are called many times.
This commit adds a counter (or its array if there are multiple threads) to
the TB. Each time a TB is executed on TCI, the counter on TB is
incremented. If it reaches to a threshold, that TB is instantiated as Wasm
via instantiate_wasm.
The total number of instances are tracked by the instances_global variable
and its max number is limited by MAX_INSTANCES. When a Wasm module is
instantiated, instances_global is incremented and the instance's function
pointer is recorded to an array of wasmInstanceInfo.
Each TB refers to the wasmInstanceInfo via wasmTBHeader's info_ptr (or its
array if there are multiple threads). This allows tcg_qemu_tb_exec to
resolve the instance function pointer from TB.
When a new instantiation risks exceeding the limit, the Wasm backend doesn't
perform the instantiation (i.e. TB continues to be executed on TCI),
instead, removal of older Wasm instances is triggered using Emscripten's
removeFunction function. Once the removal of the instance is detected via
FinalizationRegistry API[1], instances_global is decremented, which allows
instantiation of new modules again.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32.c | 241 +++++++++++++++++++++++++++++++++++-
tcg/wasm32.h | 45 +++++++
tcg/wasm32/tcg-target.c.inc | 17 +++
3 files changed, 299 insertions(+), 4 deletions(-)
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
index e6a3dbf750..fe6f2f17da 100644
--- a/tcg/wasm32.c
+++ b/tcg/wasm32.c
@@ -26,6 +26,7 @@
#include "tcg-has.h"
#include <ffi.h>
#include <emscripten.h>
+#include "wasm32.h"
#define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS)
@@ -45,6 +46,9 @@
__thread uintptr_t tci_tb_ptr;
+/* TBs executed more than this value will be compiled to wasm */
+#define INSTANTIATE_NUM 1500
+
EM_JS(int, instantiate_wasm, (int wasm_begin,
int wasm_size,
int import_vec_begin,
@@ -68,6 +72,8 @@ EM_JS(int, instantiate_wasm, (int wasm_begin,
"helper" : helper,
});
+ Module.__wasm32_tb.inst_gc_registry.register(inst, "tbinstance");
+
return addFunction(inst.exports.start, 'ii');
});
@@ -353,16 +359,44 @@ static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val,
}
}
+__thread int thread_idx;
+
+static inline int32_t get_counter_local(void *tb_ptr)
+{
+ return get_counter(tb_ptr, thread_idx);
+}
+
+static inline void set_counter_local(void *tb_ptr, int v)
+{
+ set_counter(tb_ptr, thread_idx, v);
+}
+
+static inline struct wasmInstanceInfo *get_info_local(void *tb_ptr)
+{
+ return get_info(tb_ptr, thread_idx);
+}
+
+static inline void set_info_local(void *tb_ptr, struct wasmInstanceInfo *info)
+{
+ set_info(tb_ptr, thread_idx, info);
+}
+
+__thread struct wasmContext ctx = {
+ .tb_ptr = 0,
+ .stack = NULL,
+ .do_init = 1,
+ .buf128 = NULL,
+};
+
/* Interpret pseudo code in tb. */
/*
* Disable CFI checks.
* One possible operation in the pseudo code is a call to binary code.
* Therefore, disable CFI checks in the interpreter function
*/
-uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
- const void *v_tb_ptr)
+static uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec_tci(CPUArchState *env)
{
- const uint32_t *tb_ptr = v_tb_ptr;
+ uint32_t *tb_ptr = get_tci_ptr(ctx.tb_ptr);
tcg_target_ulong regs[TCG_TARGET_NB_REGS];
uint64_t stack[(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE)
/ sizeof(uint64_t)];
@@ -384,6 +418,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
MemOpIdx oi;
int32_t ofs;
void *ptr;
+ int32_t counter;
insn = *tb_ptr++;
opc = extract32(insn, 0, 8);
@@ -802,20 +837,40 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
case INDEX_op_exit_tb:
tci_args_l(insn, tb_ptr, &ptr);
+ ctx.tb_ptr = 0;
return (uintptr_t)ptr;
case INDEX_op_goto_tb:
tci_args_l(insn, tb_ptr, &ptr);
- tb_ptr = *(void **)ptr;
+ if (*(uint32_t **)ptr != tb_ptr) {
+ tb_ptr = *(uint32_t **)ptr;
+ ctx.tb_ptr = tb_ptr;
+ counter = get_counter_local(tb_ptr);
+ if ((counter >= 0) && (counter < INSTANTIATE_NUM)) {
+ set_counter_local(tb_ptr, counter + 1);
+ } else {
+ return 0; /* enter to wasm TB */
+ }
+ tb_ptr = get_tci_ptr(tb_ptr);
+ }
break;
case INDEX_op_goto_ptr:
tci_args_r(insn, &r0);
ptr = (void *)regs[r0];
if (!ptr) {
+ ctx.tb_ptr = 0;
return 0;
}
tb_ptr = ptr;
+ ctx.tb_ptr = tb_ptr;
+ counter = get_counter_local(tb_ptr);
+ if ((counter >= 0) && (counter < INSTANTIATE_NUM)) {
+ set_counter_local(tb_ptr, counter + 1);
+ } else {
+ return 0; /* enter to wasm TB */
+ }
+ tb_ptr = get_tci_ptr(tb_ptr);
break;
case INDEX_op_qemu_ld:
@@ -861,3 +916,181 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
/*
* TODO: Disassembler is not implemented
*/
+
+/*
+ * Max number of instances can exist simultaneously.
+ *
+ * If the number of instances reaches this and a new instance needs to be
+ * created, old instances are removed so that new instances can be created
+ * without hitting the browser's limit.
+ */
+#define MAX_INSTANCES 15000
+
+int instances_global;
+
+/* Avoid overwrapping of begin/end pointers */
+#define INSTANCES_BUF_MAX (MAX_INSTANCES + 1)
+
+__thread struct wasmInstanceInfo instances[INSTANCES_BUF_MAX];
+__thread int instances_begin;
+__thread int instances_end;
+
+static void add_instance(wasm_tb_func tb_func, void *tb_ptr)
+{
+ instances[instances_end].tb_func = tb_func;
+ instances[instances_end].tb_ptr = tb_ptr;
+ set_info_local(tb_ptr, &(instances[instances_end]));
+ instances_end = (instances_end + 1) % INSTANCES_BUF_MAX;
+
+ qatomic_inc(&instances_global);
+}
+
+__thread int instance_pending_gc;
+__thread int instance_done_gc;
+
+static void remove_old_instances(void)
+{
+ int num;
+ if (instance_pending_gc > 0) {
+ return;
+ }
+ if (instances_begin <= instances_end) {
+ num = instances_end - instances_begin;
+ } else {
+ num = instances_end + (INSTANCES_BUF_MAX - instances_begin);
+ }
+ /* removes the half of the oldest instances in the buffer */
+ num /= 2;
+ for (int i = 0; i < num; i++) {
+ EM_ASM({ removeFunction($0); }, instances[instances_begin].tb_func);
+ instances[instances_begin].tb_ptr = NULL;
+ instances_begin = (instances_begin + 1) % INSTANCES_BUF_MAX;
+ }
+ instance_pending_gc += num;
+}
+
+static bool can_add_instance(void)
+{
+ return qatomic_read(&instances_global) < MAX_INSTANCES;
+}
+
+static wasm_tb_func get_instance_from_tb(void *tb_ptr)
+{
+ struct wasmInstanceInfo *elm = get_info_local(tb_ptr);
+ if (elm == NULL) {
+ return NULL;
+ }
+ if (elm->tb_ptr != tb_ptr) {
+ /*
+ * This TB was instantiated but has been removed. Set counter to the
+ * max value so that this will be instantiated again at the next
+ * invocation.
+ */
+ set_counter_local(tb_ptr, INSTANTIATE_NUM);
+ set_info_local(tb_ptr, NULL);
+ return NULL;
+ }
+ return elm->tb_func;
+}
+
+static void check_instance_garbage_collected(void)
+{
+ if (instance_done_gc > 0) {
+ qatomic_sub(&instances_global, instance_done_gc);
+ instance_pending_gc -= instance_done_gc;
+ instance_done_gc = 0;
+ }
+}
+
+EM_JS(void, init_wasm32_js, (int instance_done_gc_ptr),
+{
+ Module.__wasm32_tb = {
+ inst_gc_registry: new FinalizationRegistry((i) => {
+ if (i == "tbinstance") {
+ const memory_v = new DataView(HEAP8.buffer);
+ let v = memory_v.getInt32(instance_done_gc_ptr, true);
+ memory_v.setInt32(instance_done_gc_ptr, v + 1, true);
+ }
+ })
+ };
+});
+
+#define MAX_EXEC_NUM 50000
+__thread int exec_cnt = MAX_EXEC_NUM;
+static inline void trysleep(void)
+{
+ /*
+ * Even during running TBs continuously, try to return the control
+ * to the browser periodically and allow browsers doing tasks.
+ */
+ if (--exec_cnt == 0) {
+ if (!can_add_instance()) {
+ emscripten_sleep(0);
+ check_instance_garbage_collected();
+ }
+ exec_cnt = MAX_EXEC_NUM;
+ }
+}
+
+int thread_idx_max;
+
+static void init_wasm32(void)
+{
+ thread_idx = qatomic_fetch_inc(&thread_idx_max);
+ ctx.stack = g_malloc(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE);
+ ctx.buf128 = g_malloc(16);
+ ctx.tci_tb_ptr = (uint32_t *)&tci_tb_ptr;
+ init_wasm32_js((int)&instance_done_gc);
+}
+
+__thread bool initdone;
+
+uintptr_t tcg_qemu_tb_exec(CPUArchState *env, const void *v_tb_ptr)
+{
+ if (!initdone) {
+ init_wasm32();
+ initdone = true;
+ }
+ ctx.env = env;
+ ctx.tb_ptr = (void *)v_tb_ptr;
+ while (true) {
+ trysleep();
+ struct wasmTBHeader *header = (struct wasmTBHeader *)ctx.tb_ptr;
+ int32_t counter = get_counter_local(header);
+ uint32_t res;
+ wasm_tb_func tb_func = get_instance_from_tb(ctx.tb_ptr);
+ if (tb_func) {
+ /*
+ * call the instance if available
+ */
+ res = call_wasm_tb(tb_func, &ctx);
+ } else if (counter < INSTANTIATE_NUM) {
+ /*
+ * run it on TCI if the counter value is small
+ */
+ set_counter_local(ctx.tb_ptr, counter + 1);
+ res = tcg_qemu_tb_exec_tci(env);
+ } else if (!can_add_instance()) {
+ /*
+ * too many instances has been created, try removing older
+ * instances and keep running this TB on TCI
+ */
+ remove_old_instances();
+ check_instance_garbage_collected();
+ res = tcg_qemu_tb_exec_tci(env);
+ } else {
+ /*
+ * instantiate and run TB as Wasm
+ */
+ tb_func = (wasm_tb_func)instantiate_wasm((int)header->wasm_ptr,
+ header->wasm_size,
+ (int)header->import_ptr,
+ header->import_size);
+ add_instance(tb_func, ctx.tb_ptr);
+ res = call_wasm_tb(tb_func, &ctx);
+ }
+ if (!ctx.tb_ptr) {
+ return res;
+ }
+ }
+}
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
index f2749f1e0e..9a3230e87c 100644
--- a/tcg/wasm32.h
+++ b/tcg/wasm32.h
@@ -48,6 +48,14 @@ static inline int32_t call_wasm_tb(wasm_tb_func f, struct wasmContext *ctx)
return f(ctx);
}
+/*
+ * wasmInstanceInfo holds the relationship between TB and Wasm instance.
+ */
+struct wasmInstanceInfo {
+ void *tb_ptr;
+ wasm_tb_func tb_func;
+};
+
/*
* TB of wasm backend starts from a header which stores pointers for each data
* stored in the following region in the TB.
@@ -69,6 +77,43 @@ struct wasmTBHeader {
*/
void *import_ptr;
int import_size;
+
+ /*
+ * Counter holds how many times the TB is executed before instantiation
+ * for each thread.
+ */
+ int32_t *counter_ptr;
+
+ /*
+ * Pointer to the instance information on each thread.
+ */
+ struct wasmInstanceInfo **info_ptr;
};
+static inline uint32_t *get_tci_ptr(void *tb_ptr)
+{
+ return (uint32_t *)(((struct wasmTBHeader *)tb_ptr)->tci_ptr);
+}
+
+static inline int32_t get_counter(void *tb_ptr, int idx)
+{
+ return ((struct wasmTBHeader *)tb_ptr)->counter_ptr[idx];
+}
+
+static inline void set_counter(void *tb_ptr, int idx, int v)
+{
+ ((struct wasmTBHeader *)tb_ptr)->counter_ptr[idx] = v;
+}
+
+static inline struct wasmInstanceInfo *get_info(void *tb_ptr, int idx)
+{
+ return ((struct wasmTBHeader *)tb_ptr)->info_ptr[idx];
+}
+
+static inline void set_info(void *tb_ptr, int idx,
+ struct wasmInstanceInfo *info)
+{
+ ((struct wasmTBHeader *)tb_ptr)->info_ptr[idx] = info;
+}
+
#endif
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index 04cd9b6e4a..f0c51a5d3d 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -27,6 +27,11 @@
#include "../wasm32.h"
+/*
+ * This is included to get the max number of threads via tcg_max_ctxs.
+ */
+#include "../tcg-internal.h"
+
/* Used for function call generation. */
#define TCG_TARGET_CALL_STACK_OFFSET 0
#define TCG_TARGET_STACK_ALIGN 8
@@ -3654,6 +3659,18 @@ static void tcg_out_tb_start(TCGContext *s)
h = (struct wasmTBHeader *)(s->code_ptr);
s->code_ptr += sizeof(struct wasmTBHeader);
+ /* locate counters */
+ h->counter_ptr = (int32_t *)s->code_ptr;
+ size = tcg_max_ctxs * sizeof(int32_t);
+ memset(s->code_ptr, 0, size);
+ s->code_ptr += size;
+
+ /* locate instance information */
+ h->info_ptr = (struct wasmInstanceInfo **)s->code_ptr;
+ size = tcg_max_ctxs * sizeof(void *);
+ memset(s->code_ptr, 0, size);
+ s->code_ptr += size;
+
/* Followed by TCI code */
h->tci_ptr = s->code_ptr;
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 31/33] tcg/wasm32: Enable TLB lookup
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (29 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 30/33] tcg/wasm32: Enable instantiation of TBs executed many times Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 32/33] meson: Propagate optimization flag for linking on Emscripten Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 33/33] .gitlab-ci.d: build wasm backend in CI Kohei Tokunaga
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
This commit enables qemu_ld and qemu_st to perform TLB lookups, following
the approach used in other backends such as RISC-V. Unlike other backends,
the Wasm backend cannot use ldst labels, as jumping to specific code
addresses (e.g. raddr) is not possible in Wasm. Instead, each TLB lookup is
followed by a if branch: if the lookup succeeds, the memory is accessed
directly; otherwise, a fallback helper function is invoked. Support for
MO_BSWAP is not yet implemented, so has_memory_bswap is set to false.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
tcg/wasm32/tcg-target.c.inc | 223 +++++++++++++++++++++++++++++++++++-
1 file changed, 221 insertions(+), 2 deletions(-)
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
index f0c51a5d3d..a2815db6b5 100644
--- a/tcg/wasm32/tcg-target.c.inc
+++ b/tcg/wasm32/tcg-target.c.inc
@@ -3,8 +3,12 @@
* Tiny Code Generator for QEMU
*
* Copyright (c) 2009, 2011 Stefan Weil
+ * Copyright (c) 2018 SiFive, Inc
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
+ * Copyright (c) 2008 Fabrice Bellard
*
- * Based on tci/tcg-target.c.inc
+ * Based on tci/tcg-target.c.inc and riscv/tcg-target.c.inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -364,6 +368,10 @@ static void tcg_wasm_out_op_i64_eqz(TCGContext *s)
{
tcg_wasm_out8(s, 0x50);
}
+static void tcg_wasm_out_op_i64_eq(TCGContext *s)
+{
+ tcg_wasm_out8(s, 0x51);
+}
static void tcg_wasm_out_op_br(TCGContext *s, int i)
{
tcg_wasm_out8(s, 0x0c);
@@ -436,6 +444,10 @@ static void tcg_wasm_out_op_local_set(TCGContext *s, uint8_t i)
{
tcg_wasm_out_op_var(s, 0x21, i);
}
+static void tcg_wasm_out_op_local_tee(TCGContext *s, uint8_t i)
+{
+ tcg_wasm_out_op_var(s, 0x22, i);
+}
#define tcg_wasm_out_i64_calc(op) \
static void tcg_wasm_out_i64_calc_##op( \
@@ -1993,12 +2005,161 @@ static void *qemu_ld_helper_ptr(uint32_t oi)
}
}
+static void tcg_wasm_out_i32_load_s(TCGContext *s, int off)
+{
+ if (off < 0) {
+ tcg_wasm_out_op_i32_const(s, off);
+ tcg_wasm_out_op_i32_add(s);
+ off = 0;
+ }
+ tcg_wasm_out_op_i32_load(s, 0, off);
+}
+
+static void tcg_wasm_out_i64_load_s(TCGContext *s, int off)
+{
+ if (off < 0) {
+ tcg_wasm_out_op_i32_const(s, off);
+ tcg_wasm_out_op_i32_add(s);
+ off = 0;
+ }
+ tcg_wasm_out_op_i64_load(s, 0, off);
+}
+
+#define MIN_TLB_MASK_TABLE_OFS INT_MIN
+
+static uint8_t prepare_host_addr_wasm(TCGContext *s, uint8_t *hit_var,
+ TCGReg addr_reg, MemOpIdx oi,
+ bool is_ld)
+{
+ MemOp opc = get_memop(oi);
+ TCGAtomAlign aa;
+ unsigned a_mask;
+ unsigned s_bits = opc & MO_SIZE;
+ unsigned s_mask = (1u << s_bits) - 1;
+ int mem_index = get_mmuidx(oi);
+ int fast_ofs = tlb_mask_table_ofs(s, mem_index);
+ int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask);
+ int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table);
+ int add_off = offsetof(CPUTLBEntry, addend);
+ tcg_target_long compare_mask;
+
+ if (!tcg_use_softmmu) {
+ g_assert_not_reached();
+ }
+
+ *hit_var = TMP64_LOCAL_0_IDX;
+ tcg_wasm_out_op_i64_const(s, 0);
+ tcg_wasm_out_op_local_set(s, *hit_var);
+
+ aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false);
+ a_mask = (1u << aa.align) - 1;
+
+ /* Get the CPUTLBEntry offset */
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
+ tcg_wasm_out_op_i64_const(s, s->page_bits - CPU_TLB_ENTRY_BITS);
+ tcg_wasm_out_op_i64_shr_u(s);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_global_get_r_i32(s, TCG_AREG0);
+ tcg_wasm_out_i32_load_s(s, mask_ofs);
+ tcg_wasm_out_op_i32_and(s);
+
+ /* Get the pointer to the target CPUTLBEntry */
+ tcg_wasm_out_op_global_get_r_i32(s, TCG_AREG0);
+ tcg_wasm_out_i32_load_s(s, table_ofs);
+ tcg_wasm_out_op_i32_add(s);
+ tcg_wasm_out_op_local_tee(s, TMP32_LOCAL_0_IDX);
+
+ /* Load the tlb copmarator */
+ tcg_wasm_out_i64_load_s(
+ s, is_ld ? offsetof(CPUTLBEntry, addr_read)
+ : offsetof(CPUTLBEntry, addr_write));
+
+ /*
+ * For aligned accesses, we check the first byte and include the
+ * alignment bits within the address. For unaligned access, we
+ * check that we don't cross pages using the address of the last
+ * byte of the access.
+ */
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
+ if (a_mask < s_mask) {
+ tcg_wasm_out_op_i64_const(s, s_mask - a_mask);
+ tcg_wasm_out_op_i64_add(s);
+ }
+ compare_mask = (uint64_t)s->page_mask | a_mask;
+ tcg_wasm_out_op_i64_const(s, compare_mask);
+ tcg_wasm_out_op_i64_and(s);
+
+ /* Compare masked address with the TLB entry. */
+ tcg_wasm_out_op_i64_eq(s);
+ tcg_wasm_out_op_if_noret(s);
+
+ /* TLB Hit - translate address using addend. */
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
+ tcg_wasm_out_i32_load_s(s, add_off);
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
+ tcg_wasm_out_op_i32_wrap_i64(s);
+ tcg_wasm_out_op_i32_add(s);
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_1_IDX);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_local_set(s, *hit_var);
+
+ tcg_wasm_out_op_end(s);
+
+ return TMP32_LOCAL_1_IDX;
+}
+
+static void tcg_wasm_out_qemu_ld_direct(
+ TCGContext *s, TCGReg r, uint8_t base, MemOp opc)
+{
+ switch (opc & (MO_SSIZE)) {
+ case MO_UB:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load8_u(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_SB:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load8_s(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_UW:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load16_u(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_SW:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load16_s(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_UL:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load32_u(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_SL:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load32_s(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ case MO_UQ:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_i64_load(s, 0, 0);
+ tcg_wasm_out_op_global_set_r(s, r);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
TCGReg addr_reg, MemOpIdx oi)
{
int helper_idx;
int func_idx;
bool addr64 = s->addr_type == TCG_TYPE_I64;
+ MemOp mop = get_memop(oi);
+ uint8_t base_var, hit_var;
helper_idx = (uint32_t)qemu_ld_helper_ptr(oi);
func_idx = get_helper_idx(s, helper_idx);
@@ -2012,6 +2173,14 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
addr_reg = TCG_REG_TMP;
}
+ base_var = prepare_host_addr_wasm(s, &hit_var, addr_reg, oi, true);
+ tcg_wasm_out_op_local_get(s, hit_var);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_eq(s);
+ tcg_wasm_out_op_if_noret(s);
+ tcg_wasm_out_qemu_ld_direct(s, data_reg, base_var, mop); /* fast path */
+ tcg_wasm_out_op_end(s);
+
/*
* update the block index so that the possible rewinding will
* skip this block
@@ -2020,6 +2189,10 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
tcg_wasm_out_new_block(s);
+ tcg_wasm_out_op_local_get(s, hit_var);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_noret(s);
+
/* call helper */
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
tcg_wasm_out_op_i32_wrap_i64(s);
@@ -2030,6 +2203,8 @@ static void tcg_wasm_out_qemu_ld(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_call(s, func_idx);
tcg_wasm_out_op_global_set_r(s, data_reg);
tcg_wasm_out_handle_unwinding(s);
+
+ tcg_wasm_out_op_end(s);
}
static void *qemu_st_helper_ptr(uint32_t oi)
@@ -2049,6 +2224,35 @@ static void *qemu_st_helper_ptr(uint32_t oi)
}
}
+static void tcg_wasm_out_qemu_st_direct(
+ TCGContext *s, TCGReg lo, uint8_t base, MemOp opc)
+{
+ switch (opc & (MO_SSIZE)) {
+ case MO_8:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_global_get_r(s, lo);
+ tcg_wasm_out_op_i64_store8(s, 0, 0);
+ break;
+ case MO_16:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_global_get_r(s, lo);
+ tcg_wasm_out_op_i64_store16(s, 0, 0);
+ break;
+ case MO_32:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_global_get_r(s, lo);
+ tcg_wasm_out_op_i64_store32(s, 0, 0);
+ break;
+ case MO_64:
+ tcg_wasm_out_op_local_get(s, base);
+ tcg_wasm_out_op_global_get_r(s, lo);
+ tcg_wasm_out_op_i64_store(s, 0, 0);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
TCGReg addr_reg, MemOpIdx oi)
{
@@ -2056,6 +2260,7 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
int func_idx;
bool addr64 = s->addr_type == TCG_TYPE_I64;
MemOp mop = get_memop(oi);
+ uint8_t base_var, hit_var;
helper_idx = (uint32_t)qemu_st_helper_ptr(oi);
func_idx = get_helper_idx(s, helper_idx);
@@ -2069,6 +2274,14 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
addr_reg = TCG_REG_TMP;
}
+ base_var = prepare_host_addr_wasm(s, &hit_var, addr_reg, oi, false);
+ tcg_wasm_out_op_local_get(s, hit_var);
+ tcg_wasm_out_op_i64_const(s, 1);
+ tcg_wasm_out_op_i64_eq(s);
+ tcg_wasm_out_op_if_noret(s);
+ tcg_wasm_out_qemu_st_direct(s, data_reg, base_var, mop); /* fast path */
+ tcg_wasm_out_op_end(s);
+
/*
* update the block index so that the possible rewinding will
* skip this block
@@ -2077,6 +2290,10 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
tcg_wasm_out_new_block(s);
+ tcg_wasm_out_op_local_get(s, hit_var);
+ tcg_wasm_out_op_i64_eqz(s);
+ tcg_wasm_out_op_if_noret(s);
+
/* call helper */
tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
tcg_wasm_out_op_i32_wrap_i64(s);
@@ -2095,6 +2312,8 @@ static void tcg_wasm_out_qemu_st(TCGContext *s, TCGReg data_reg,
tcg_wasm_out_op_call(s, func_idx);
tcg_wasm_out_handle_unwinding(s);
+
+ tcg_wasm_out_op_end(s);
}
static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
@@ -3752,7 +3971,7 @@ static int tcg_out_tb_end(TCGContext *s)
bool tcg_target_has_memory_bswap(MemOp memop)
{
- return true;
+ return false;
}
static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 32/33] meson: Propagate optimization flag for linking on Emscripten
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (30 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 31/33] tcg/wasm32: Enable TLB lookup Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 33/33] .gitlab-ci.d: build wasm backend in CI Kohei Tokunaga
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Emscripten uses the optimization flag at link time to enable optimizations
via Binaryen [1]. While meson.build currently recognizes the -Doptimization
option, it does not propagate it to the linking. This commit updates
meson.build to propagate the optimization flag to the linking when targeting
WebAssembly.
[1] https://emscripten.org/docs/optimizing/Optimizing-Code.html#how-emscripten-optimizes
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
meson.build | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/meson.build b/meson.build
index f6ed867819..91e182092d 100644
--- a/meson.build
+++ b/meson.build
@@ -869,6 +869,10 @@ elif host_os == 'openbsd'
# Disable OpenBSD W^X if available
emulator_link_args = cc.get_supported_link_arguments('-Wl,-z,wxneeded')
endif
+elif host_os == 'emscripten'
+ if get_option('optimization') != 'plain'
+ emulator_link_args += ['-O' + get_option('optimization')]
+ endif
endif
###############################################
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH 33/33] .gitlab-ci.d: build wasm backend in CI
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
` (31 preceding siblings ...)
2025-05-20 12:51 ` [PATCH 32/33] meson: Propagate optimization flag for linking on Emscripten Kohei Tokunaga
@ 2025-05-20 12:51 ` Kohei Tokunaga
32 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-20 12:51 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Paolo Bonzini, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
Kohei Tokunaga, qemu-arm, qemu-riscv
Check if wasm backend can be built in CI.
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
.gitlab-ci.d/buildtest.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index 248aaed137..fc42de231a 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -800,4 +800,4 @@ build-wasm:
job: wasm-emsdk-cross-container
variables:
IMAGE: emsdk-wasm32-cross
- CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
+ CONFIGURE_ARGS: --static --disable-tools --enable-debug
--
2.43.0
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten
2025-05-20 12:51 ` [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten Kohei Tokunaga
@ 2025-05-20 14:32 ` Paolo Bonzini
2025-05-21 8:29 ` Kohei Tokunaga
0 siblings, 1 reply; 36+ messages in thread
From: Paolo Bonzini @ 2025-05-20 14:32 UTC (permalink / raw)
To: Kohei Tokunaga, qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
qemu-arm, qemu-riscv
On 5/20/25 14:51, Kohei Tokunaga wrote:
> target_kconfig = []
> foreach sym: accelerators
> - # Disallow 64-bit on 32-bit emulation and virtualization
> - if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
> - continue
> + if host_arch != 'wasm32'
> + # Disallow 64-bit on 32-bit emulation and virtualization
> + if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
> + continue
> + endif
> endif
> if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, [])
> config_target += { sym: 'y' }
I'd prefer something like
# Detect host pointer size for the target configuration loop.
host_long_bits = cc.sizeof('void *') * 8
tcg_vaddr_bits = host_arch == 'wasm32' ? 64 : host_long_bits
...
config_host_data.set('TCG_VADDR_BITS', tcg_vaddr_bits)
Then in the target configuration loop
- if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
+ if tcg_vaddr_bits < config_target['TARGET_LONG_BITS'].to_int()
and throughout the code you can have
#ifdef TCG_VADDR_BITS == 32
...
#else
...
#endif
instead of
#ifdef EMSCRIPTEN
...
#else
...
#endif
In fact, I think this patch would be acceptable as a separate
submission, because it could be tested using TCI already.
Paolo
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten
2025-05-20 14:32 ` Paolo Bonzini
@ 2025-05-21 8:29 ` Kohei Tokunaga
0 siblings, 0 replies; 36+ messages in thread
From: Kohei Tokunaga @ 2025-05-21 8:29 UTC (permalink / raw)
To: Paolo Bonzini, qemu-devel
Cc: Alex Bennée, Philippe Mathieu-Daudé, Thomas Huth,
Richard Henderson, Marc-André Lureau,
Daniel P . Berrangé, WANG Xuerui, Aurelien Jarno,
Huacai Chen, Jiaxun Yang, Aleksandar Rikalo, Palmer Dabbelt,
Alistair Francis, Stefan Weil, Stefan Hajnoczi, Pierrick Bouvier,
qemu-arm, qemu-riscv
[-- Attachment #1: Type: text/plain, Size: 1486 bytes --]
Hi Paolo,
> On 5/20/25 14:51, Kohei Tokunaga wrote:
> > target_kconfig = []
> > foreach sym: accelerators
> > - # Disallow 64-bit on 32-bit emulation and virtualization
> > - if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
> > - continue
> > + if host_arch != 'wasm32'
> > + # Disallow 64-bit on 32-bit emulation and virtualization
> > + if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
> > + continue
> > + endif
> > endif
> > if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym,
[])
> > config_target += { sym: 'y' }
>
> I'd prefer something like
>
> # Detect host pointer size for the target configuration loop.
> host_long_bits = cc.sizeof('void *') * 8
> tcg_vaddr_bits = host_arch == 'wasm32' ? 64 : host_long_bits
> ...
> config_host_data.set('TCG_VADDR_BITS', tcg_vaddr_bits)
>
> Then in the target configuration loop
>
> - if host_long_bits < config_target['TARGET_LONG_BITS'].to_int()
> + if tcg_vaddr_bits < config_target['TARGET_LONG_BITS'].to_int()
>
> and throughout the code you can have
>
> #ifdef TCG_VADDR_BITS == 32
> ...
> #else
> ...
> #endif
>
> instead of
>
> #ifdef EMSCRIPTEN
> ...
> #else
> ...
> #endif
>
> In fact, I think this patch would be acceptable as a separate
> submission, because it could be tested using TCI already.
Thank you for the feedback. I'll work on implementing this using
TCG_VADDR_BITS and submit it as a separated series.
[-- Attachment #2: Type: text/html, Size: 2005 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2025-05-21 8:30 UTC | newest]
Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-20 12:51 [PATCH 00/33] tcg: Add WebAssembly backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 01/33] tcg: Fork TCI for wasm32 backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 02/33] tcg/wasm32: Do not use TCI disassembler in Wasm backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 03/33] meson: Enable to build wasm backend Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 04/33] tcg/wasm32: Set TCG_TARGET_INSN_UNIT_SIZE to 1 Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 05/33] tcg/wasm32: Add and/or/xor instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 06/33] tcg/wasm32: Add add/sub/mul instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 07/33] tcg/wasm32: Add shl/shr/sar instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 08/33] tcg/wasm32: Add setcond/negsetcond/movcond instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 09/33] tcg/wasm32: Add deposit/sextract/extract instrcutions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 10/33] tcg/wasm32: Add load and store instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 11/33] tcg/wasm32: Add mov/movi instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 12/33] tcg/wasm32: Add ext instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 13/33] tcg/wasm32: Add bswap instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 14/33] tcg/wasm32: Add rem/div instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 15/33] tcg/wasm32: Add andc/orc/eqv/nand/nor instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 16/33] tcg/wasm32: Add neg/not/ctpop instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 17/33] tcg/wasm32: Add rot/clz/ctz instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 18/33] tcg/wasm32: Add addc/subb instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 19/33] tcg/wasm32: Add br/brcond instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 20/33] tcg/wasm32: Add exit_tb/goto_tb/goto_ptr instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 21/33] tcg/wasm32: Add call instruction Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 22/33] tcg/wasm32: Add qemu_ld/qemu_st instructions Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 23/33] include/exec: Allow using 64bit guest addresses on emscripten Kohei Tokunaga
2025-05-20 14:32 ` Paolo Bonzini
2025-05-21 8:29 ` Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 24/33] tcg/wasm32: Set TCG_TARGET_REG_BITS to 64 Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 25/33] tcg/wasm32: Set mulu2/muls2 as unimplemented Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 26/33] tcg/wasm32: Add initialization of fundamental registers Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 27/33] tcg/wasm32: Write wasm binary to TB Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 28/33] tcg/wasm32: Implement instantiation of Wasm binary Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 29/33] tcg/wasm32: Allow Asyncify unwinding from TB Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 30/33] tcg/wasm32: Enable instantiation of TBs executed many times Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 31/33] tcg/wasm32: Enable TLB lookup Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 32/33] meson: Propagate optimization flag for linking on Emscripten Kohei Tokunaga
2025-05-20 12:51 ` [PATCH 33/33] .gitlab-ci.d: build wasm backend in CI Kohei Tokunaga
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).