From 6538cec9f694e9e0d02570fa31d81a83e305ca2b Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Sun, 19 Jul 2026 22:31:52 -0400 Subject: [PATCH] feat(wasm): add browser QEMU build --- HANDOFF.md | 41 +- Makefile | 7 +- README.md | 25 +- ROADMAP.md | 14 +- qemu/patches/0001-xteink-machine.patch | 9 +- qemu/patches/0002-qemu-wasm.patch | 6776 ++++++++++++++++++++++++ qemu/wasm/Dockerfile | 92 + scripts/build-qemu-wasm.sh | 79 + scripts/smoke-qemu-wasm.mjs | 9 + scripts/smoke-qemu-wasm.sh | 19 + 10 files changed, 7046 insertions(+), 25 deletions(-) create mode 100644 qemu/patches/0002-qemu-wasm.patch create mode 100644 qemu/wasm/Dockerfile create mode 100755 scripts/build-qemu-wasm.sh create mode 100644 scripts/smoke-qemu-wasm.mjs create mode 100755 scripts/smoke-qemu-wasm.sh diff --git a/HANDOFF.md b/HANDOFF.md index 8510793..c4e228e 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -12,10 +12,11 @@ touching QEMU internals. Pairs with `ROADMAP.md` (the plan) — this is the - One `xteink` QEMU machine selects `variant=x3|x4` at launch. **Unmodified canonical firmware** detects X3 through I²C fingerprints and boots the home screen; X4 detects through all-NAK probes and reaches a blank SSD1677 stub. -- All Phase-1 device models live in **one patch**: - `qemu/patches/0001-xteink-machine.patch`, applied onto exact espressif/qemu - commit `40edccac415693c5130f91c01d84176ae6008566`. -- Next: compile to WASM (qemu-wasm) → static browser UI. Full X4 rendering waits. +- Phase-1 device models live in `qemu/patches/0001-xteink-machine.patch`; + `0002-qemu-wasm.patch` ports qemu-wasm's host backend onto the same pinned + Espressif QEMU commit `40edccac415693c5130f91c01d84176ae6008566`. +- `make qemu-wasm` produces a smoke-tested ES module, WASM binary, and pthread + worker under `dist/wasm/`. Next: static browser UI. Full X4 rendering waits. --- @@ -207,8 +208,8 @@ ADC ladders on GPIO1 & GPIO2; power button GPIO3; UC8253 @ 16 MHz. ## 8. Reproducibility model -- **Durable = the patch** (`qemu/patches/0001-xteink-machine.patch`), verified - against exact QEMU commit `40edccac415693c5130f91c01d84176ae6008566` (tag +- **Durable = the patches**: apply `0001-xteink-machine.patch`, then + `0002-qemu-wasm.patch` for the browser build. Both target exact QEMU commit `40edccac415693c5130f91c01d84176ae6008566` (tag `esp-develop-9.2.2-20260417`). When editing `_scratch/qemu-src`, regenerate: ```sh cd && git add -N && git diff --binary > /qemu/patches/0001-xteink-machine.patch @@ -253,15 +254,27 @@ ADC ladders on GPIO1 & GPIO2; power button GPIO3; UC8253 @ 16 MHz. --- -## 10. Next steps (in priority order) +## 10. WASM build and next steps -1. **Compile to WASM**: combine this pinned Espressif fork with - `ktock/qemu-wasm`'s WASM TCG/TCI backend + Emscripten. De-risk by building an - unpatched esp32c3 target under qemu-wasm first, then apply our patch. -2. **Static browser front-end**: display surface → canvas; physical buttons → +- `make qemu-wasm` uses Podman or Docker and the pinned Emscripten 3.1.50 image + in `qemu/wasm/Dockerfile`. It builds only `riscv32-softmmu` with the xteink + device graph; unrelated boards, networking, audio, virtfs, tools, and docs are + disabled. Outputs go to `dist/wasm/`. +- qemu-wasm commit `0ef7b4e2814b231705d8371dd7997f5b72e70baf` is based on + QEMU 8.2; `0002-qemu-wasm.patch` is the resolved port to Espressif QEMU 9.2. + `-sEMULATE_FUNCTION_POINTER_CASTS=1` is required for QEMU's QOM callbacks. +- The WASM dependency image omits libgcrypt. AES/RSA/DS/XTS devices are therefore + not instantiated; their MMIO falls through to the existing zero-return stub. + Canonical firmware boot still needs browser-level validation; add cross-built + libgcrypt only if firmware proves it uses those accelerators. +- Emscripten pthreads require cross-origin isolation (COOP/COEP) in the browser. + +Next: + +1. **Static browser front-end**: display surface → canvas; physical buttons → ADC/GPIO controls; uploaded flash; SD block backend; `variant=x3|x4` chosen before boot. Must run fully from static assets (airgapped). -3. **Full X4 rendering later**: SSD1677 is 800×480 controller / 480×800 portrait, +2. **Full X4 rendering later**: SSD1677 is 800×480 controller / 480×800 portrait, BUSY active-high. Implement its RAM/window/update commands when needed. --- @@ -271,7 +284,9 @@ ADC ladders on GPIO1 & GPIO2; power button GPIO3; UC8253 @ 16 MHz. | What | Where | |------|-------| | Emulator repo | `/home/evanreichard/Development/git/personal/xteink-web-emulator` | -| The patch | `qemu/patches/0001-xteink-machine.patch` | +| Native/WASM patches | `qemu/patches/0001-xteink-machine.patch`, `0002-qemu-wasm.patch` | +| WASM environment | `qemu/wasm/Dockerfile` | +| WASM build | `scripts/build-qemu-wasm.sh` → `dist/wasm/` | | QEMU source (ephemeral) | `_scratch/qemu-src` (via `make qemu`) | | Canonical firmware | `https://github.com/crosspoint-reader/crosspoint-reader` | | Tested firmware checkout | `/tmp/crosspoint-reader-main` @ `556b8ae` (unmodified) | diff --git a/Makefile b/Makefile index 46f008d..335b88d 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,11 @@ FIRMWARE_DIR ?= $(FIRMWARE_REPO)/.pio/build/default SD_SRC ?= $(FIRMWARE_REPO)/sdcard QEMU_SRC ?= _scratch/qemu-src QEMU_BIN = $(QEMU_SRC)/build/qemu-system-riscv32 +QEMU_WASM_SRC ?= _scratch/qemu-wasm-src +WASM_OUT ?= dist/wasm VARIANT ?= x3 -.PHONY: firmware sdimage qemu run run-upstream chip clean +.PHONY: firmware sdimage qemu qemu-wasm run run-upstream chip clean firmware: esptool --chip esp32c3 merge-bin -o flash.bin \ @@ -24,6 +26,9 @@ sdimage: qemu: QEMU_SRC=$(QEMU_SRC) scripts/build-qemu.sh +qemu-wasm: + QEMU_WASM_SRC=$(QEMU_WASM_SRC) WASM_OUT=$(WASM_OUT) JOBS=2 scripts/build-qemu-wasm.sh + # Select X4 with `make run VARIANT=x4`. Add `-display none` for headless. run: qemu $(QEMU_BIN) -machine xteink,variant=$(VARIANT) -L $(QEMU_SRC)/pc-bios \ diff --git a/README.md b/README.md index d35924b..590a7f5 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@ An offline, in-browser emulator for the xteink **X3/X4** (ESP32-C3 + e-ink): upload a firmware `.bin`, run the real machine code client-side, see the screen, press buttons, mount a virtual SD — no server, no proprietary engine. -**Status:** one native `xteink` machine selects X3 or X4 at launch. Unmodified +**Status:** native and WebAssembly builds provide one `xteink` machine that +selects X3 or X4 at launch. Unmodified [canonical firmware](https://github.com/crosspoint-reader/crosspoint-reader) auto-detects the selected variant: X3 boots with its UC8253 display and SD card; -X4 reaches its SSD1677 driver through a blank protocol stub. The browser (WASM) -port is next. See [ROADMAP.md](./ROADMAP.md) for architecture and plan. +X4 reaches its SSD1677 driver through a blank protocol stub. The static browser +frontend is next. See [ROADMAP.md](./ROADMAP.md) for architecture and plan. ## Quick start (native QEMU) @@ -25,12 +26,30 @@ make run VARIANT=x4 # X4 detection + blank SSD1677 stub Point at a firmware build with `make firmware FIRMWARE_DIR=/path/to/.pio/build/gh_release`. `make qemu` is a long compile on modest hardware — set `JOBS` to taste. +## WebAssembly build + +Podman or Docker is required. The build is limited to `riscv32-softmmu` and the +X3/X4 device graph; networking, audio, virtfs, tools, docs, and unrelated boards +are disabled. + +```sh +make qemu-wasm +# dist/wasm/qemu-system-riscv32.{js,wasm,worker.js} +``` + +The build runs a Node smoke check when Node is available. Browser execution +requires cross-origin isolation for Emscripten pthreads; the frontend and its +COOP/COEP serving setup are the next phase. + ## Contents - `qemu/patches/0001-xteink-machine.patch` — the `xteink,variant=x3|x4` machine and its device models (I²C fingerprints, ADC, GPIO, SPI2, X3 UC8253, X4 panel stub, SD-over-SPI fix), applied onto the pinned espressif/qemu commit by `scripts/build-qemu.sh`. +- `qemu/patches/0002-qemu-wasm.patch` and `qemu/wasm/Dockerfile` — the pinned + qemu-wasm host backend port and minimal Emscripten dependency environment, + built by `scripts/build-qemu-wasm.sh`. - `flake.nix` — the espressif-qemu *release* binary as the fast generic baseline (`nix run .#run-upstream -- flash.bin`), plus a devShell with QEMU's full build environment. `Makefile` — `qemu`, `firmware`, `sdimage`, `run`, `chip`. diff --git a/ROADMAP.md b/ROADMAP.md index 6263fc6..e66c8b2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,7 +19,7 @@ running in-browser from static files. The endgame merges those two. ## Status -**Phase 1 (native foundation) is done for X3 and X4 selection.** Unmodified +**The native foundation and WASM compile phase are done.** Unmodified [canonical firmware](https://github.com/crosspoint-reader/crosspoint-reader) auto-detects `xteink,variant=x3` through its real I²C fingerprint and boots the X3 home screen with SD support. `variant=x4` omits those chips, so the same @@ -35,6 +35,8 @@ make run VARIANT=x4 # X4 detection + blank panel stub QMP screenshots confirmed the X3 home screen and 528×792 orientation; captured X4 SPI traffic confirmed the firmware selected its SSD1677 driver. +`make qemu-wasm` produces the Emscripten module, WASM binary, and pthread worker +under `dist/wasm/`; a Node smoke check confirms `xteink` is registered. ### Device selection (X3 vs X4) @@ -80,11 +82,9 @@ reuses unchanged — no bespoke abstraction layer: ## Remaining phases -1. **Compile the patched fork to WASM** — combine Espressif's SoC models with - qemu-wasm's WASM TCG/TCI backend; build `qemu-system-riscv32` with Emscripten. -2. **Browser front-end (static)** — uploaded flash, canvas, physical buttons, +1. **Browser front-end (static)** — uploaded flash, canvas, physical buttons, SD block backend, and pre-boot X3/X4 picker. Fully airgapped. -3. **Full X4 panel** — implement SSD1677 RAM/window/update commands when visible +2. **Full X4 panel** — implement SSD1677 RAM/window/update commands when visible X4 rendering is needed; the selection, dimensions, wiring, and stub exist. ## Repo layout @@ -94,7 +94,9 @@ reuses unchanged — no bespoke abstraction layer: | `flake.nix` | espressif-qemu release pkg (`nix run .#run-upstream`) + full QEMU build devShell | | `Makefile` | `qemu` (source build), `firmware`, `sdimage`, `run`, `chip` | | `scripts/build-qemu.sh` | clone pinned espressif/qemu, apply patch, build riscv32-softmmu | -| `qemu/patches/` | xteink machine + device models (applied onto the pinned QEMU commit) | +| `qemu/patches/` | xteink devices plus the qemu-wasm host port, applied onto pinned Espressif QEMU | +| `qemu/wasm/Dockerfile` | Minimal pinned Emscripten dependency environment | +| `scripts/build-qemu-wasm.sh` | X3/X4-only WASM build → `dist/wasm/` | | `chip/eink-x3.chip.c` | legacy Wokwi model — the reverse-engineered X3 e-ink protocol reference | | `web/` | (phase 3) static front-end: upload + canvas + buttons | diff --git a/qemu/patches/0001-xteink-machine.patch b/qemu/patches/0001-xteink-machine.patch index 0f81bb3..fda810b 100644 --- a/qemu/patches/0001-xteink-machine.patch +++ b/qemu/patches/0001-xteink-machine.patch @@ -564,12 +564,13 @@ index 5f4022f..1477f15 100644 switch (opcode) { case I2C_OPCODE_RSTART: i2c_end_transfer(s->bus); -@@ -261,9 +272,16 @@ static void esp32_i2c_init(Object * obj) +@@ -261,9 +272,17 @@ static void esp32_i2c_init(Object * obj) fifo8_create(&s->rx_fifo, ESP32_I2C_FIFO_LENGTH); } +static const Property esp32_i2c_properties[] = { + DEFINE_PROP_BOOL("c3", Esp32I2CState, c3, false), ++ DEFINE_PROP_END_OF_LIST(), +}; + static void esp32_i2c_class_init(ObjectClass * klass, void * data) @@ -708,11 +709,15 @@ diff --git i/hw/riscv/Kconfig w/hw/riscv/Kconfig index 192ab47..293808d 100644 --- i/hw/riscv/Kconfig +++ w/hw/riscv/Kconfig -@@ -119,4 +119,5 @@ config RISCV_ESP32C3 +@@ -119,4 +119,9 @@ config RISCV_ESP32C3 select OPENCORES_ETH select UNIMP select ESP_RGB + select I2C ++ select SSI ++ select SSI_SD ++ select SSI_M25P80 ++ select CAN_SJA1000 diff --git i/hw/riscv/esp32c3.c w/hw/riscv/esp32c3.c index ed698fb..2f1ccbf 100644 diff --git a/qemu/patches/0002-qemu-wasm.patch b/qemu/patches/0002-qemu-wasm.patch new file mode 100644 index 0000000..aa00f9a --- /dev/null +++ b/qemu/patches/0002-qemu-wasm.patch @@ -0,0 +1,6776 @@ +diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c +index b76a4eac4e..a673eb2eb7 100644 +--- a/accel/tcg/cputlb.c ++++ b/accel/tcg/cputlb.c +@@ -225,7 +225,7 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, + rate = desc->window_max_entries * 100 / old_size; + + if (rate > 70) { +- new_size = MIN(old_size << 1, 1 << CPU_TLB_DYN_MAX_BITS); ++ new_size = MIN(old_size << 1, (uint64_t)1 << CPU_TLB_DYN_MAX_BITS); + } else if (rate < 30 && window_expired) { + size_t ceil = pow2ceil(desc->window_max_entries); + size_t expected_rate = desc->window_max_entries * 100 / ceil; +diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc +index ebbf380d76..3ff0b97f27 100644 +--- a/accel/tcg/ldst_common.c.inc ++++ b/accel/tcg/ldst_common.c.inc +@@ -12,32 +12,52 @@ + * Load helpers for tcg-ldst.h + */ + ++#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN) ++#include "../../tcg/wasm32.h" ++#endif ++ + tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); +- return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++ tcg_target_ulong res = do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif ++ return res; + } + + tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); +- return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++ tcg_target_ulong res = do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif ++ return res; + } + + tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); +- return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++ tcg_target_ulong res = do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif ++ return res; + } + + uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); +- return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++ uint64_t res = do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif ++ return res; + } + + /* +@@ -67,7 +87,11 @@ Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); +- return do_ld16_mmu(env_cpu(env), addr, oi, retaddr); ++ Int128 res = do_ld16_mmu(env_cpu(env), addr, oi, retaddr); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif ++ return res; + } + + Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, uint32_t oi) +@@ -84,6 +108,9 @@ void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); + do_st1_mmu(env_cpu(env), addr, val, oi, ra); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, +@@ -91,6 +118,9 @@ void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + do_st2_mmu(env_cpu(env), addr, val, oi, retaddr); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, +@@ -98,6 +128,9 @@ void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + do_st4_mmu(env_cpu(env), addr, val, oi, retaddr); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, +@@ -105,6 +138,9 @@ void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + do_st8_mmu(env_cpu(env), addr, val, oi, retaddr); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, +@@ -112,11 +148,17 @@ void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, + { + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) + { + helper_st16_mmu(env, addr, val, oi, GETPC()); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_done_flag(); ++#endif + } + + /* +diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c +index cc0f5afd47..575be0e359 100644 +--- a/accel/tcg/tb-maint.c ++++ b/accel/tcg/tb-maint.c +@@ -854,7 +854,11 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) + void tb_reset_jump(TranslationBlock *tb, int n) + { + uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[n]); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ tb_set_jmp_target(tb, n, 0); ++#else + tb_set_jmp_target(tb, n, addr); ++#endif + } + + /* remove any jumps to the TB */ +@@ -875,10 +879,18 @@ static inline void tb_jmp_unlink(TranslationBlock *dest) + qemu_spin_unlock(&dest->jmp_lock); + } + ++#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN) ++#include "../../tcg/wasm32.h" ++#endif ++ + static void tb_jmp_cache_inval_tb(TranslationBlock *tb) + { + CPUState *cpu; + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ remove_tb(tb->tc.ptr); ++#endif ++ + if (tb_cflags(tb) & CF_PCREL) { + /* A TB may be at any virtual address */ + CPU_FOREACH(cpu) { +diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c +index 49814ec4af..0715610d37 100644 +--- a/accel/tcg/tcg-accel-ops-mttcg.c ++++ b/accel/tcg/tcg-accel-ops-mttcg.c +@@ -61,12 +61,18 @@ static void mttcg_force_rcu(Notifier *notify, void *data) + * variable current_cpu can be used deep in the code to find the + * current CPUState for a given thread. + */ +- ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++#include "../../tcg/wasm32.h" ++#endif + static void *mttcg_cpu_thread_fn(void *arg) + { + MttcgForceRcuNotifier force_rcu; + CPUState *cpu = arg; + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ init_wasm32(); ++#endif ++ + assert(tcg_enabled()); + g_assert(!icount_enabled()); + +diff --git a/block/file-posix.c b/block/file-posix.c +index 90fa54352c..0fa9cc31b6 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -110,6 +110,10 @@ + #include + #endif + ++#ifdef EMSCRIPTEN ++#include ++#endif ++ + /* OS X does not have O_DSYNC */ + #ifndef O_DSYNC + #ifdef O_SYNC +@@ -2002,6 +2006,13 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) + return handle_aiocb_write_zeroes(aiocb); + } + ++#ifdef EMSCRIPTEN ++ssize_t copy_file_range(int a, off_t * b, int, off_t * c, size_t d, unsigned e) ++{ ++ errno = ENOSYS; ++ return -1; ++} ++#else + #ifndef HAVE_COPY_FILE_RANGE + static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) +@@ -2015,6 +2026,7 @@ static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + #endif + } + #endif ++#endif + + /* + * parse_zone - Fill a zone descriptor +diff --git a/configs/devices/riscv32-softmmu/xteink.mak b/configs/devices/riscv32-softmmu/xteink.mak +new file mode 100644 +index 0000000000..a9179b4119 +--- /dev/null ++++ b/configs/devices/riscv32-softmmu/xteink.mak +@@ -0,0 +1 @@ ++CONFIG_RISCV_ESP32C3=y +diff --git a/configure b/configure +index 18336376bf..4a4b9e7754 100755 +--- a/configure ++++ b/configure +@@ -340,7 +340,9 @@ int main(void) { return 0; } + EOF + } + +-if check_define __linux__ ; then ++if check_define __EMSCRIPTEN__ ; then ++ host_os=emscripten ++elif check_define __linux__ ; then + host_os=linux + elif check_define _WIN32 ; then + host_os=windows +@@ -526,6 +528,10 @@ case "$cpu" in + linux_arch=x86 + CPU_CFLAGS="-m64" + ;; ++ wasm32) ++ host_arch=wasm32 ++ CPU_CFLAGS="-m32" ++ ;; + esac + + # Now we have our CPU_CFLAGS we can check if we are targeting a 32 or +@@ -547,7 +553,7 @@ else + host_bits=32 + fi + +-if test -n "$host_arch" && { ++if test -n "$host_arch" && test "$host_arch" != wasm32 && { + ! test -d "$source_path/linux-user/include/host/$host_arch" || + ! test -d "$source_path/common-user/host/$host_arch"; }; then + error_exit "linux-user/include/host/$host_arch does not exist." \ +diff --git a/disas/disas-host.c b/disas/disas-host.c +index 8146fafe80..2ee0605dba 100644 +--- a/disas/disas-host.c ++++ b/disas/disas-host.c +@@ -42,7 +42,7 @@ static void initialize_debug_host(CPUDebug *s) + #else + s->info.endian = BFD_ENDIAN_LITTLE; + #endif +-#if defined(CONFIG_TCG_INTERPRETER) ++#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN) + s->info.print_insn = print_insn_tci; + #elif defined(__i386__) + s->info.mach = bfd_mach_i386_i386; +diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c +index 2f1ccbfbb0..55ab156533 100644 +--- a/hw/riscv/esp32c3.c ++++ b/hw/riscv/esp32c3.c +@@ -421,12 +421,18 @@ static void esp32c3_machine_init(MachineState *machine) + object_initialize_child(OBJECT(machine), "efuse", &ms->efuse, TYPE_ESP32C3_EFUSE); + object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK); + object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA); ++#ifdef CONFIG_GCRYPT + object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES); ++#endif + object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA); ++#ifdef CONFIG_GCRYPT + object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA); ++#endif + object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC); ++#ifdef CONFIG_GCRYPT + object_initialize_child(OBJECT(machine), "ds", &ms->ds, TYPE_ESP32C3_DS); + object_initialize_child(OBJECT(machine), "xts_aes", &ms->xts_aes, TYPE_ESP32C3_XTS_AES); ++#endif + object_initialize_child(OBJECT(machine), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG); + object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], TYPE_ESP32C3_TIMG); + object_initialize_child(OBJECT(machine), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER); +@@ -484,7 +490,9 @@ static void esp32c3_machine_init(MachineState *machine) + + /* SPI1 controller (SPI Flash) */ + { ++#ifdef CONFIG_GCRYPT + ms->spi1.xts_aes = &ms->xts_aes; ++#endif + sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi1), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI1_BASE, mr, 0); +@@ -539,7 +547,9 @@ static void esp32c3_machine_init(MachineState *machine) + if (blk) { + ms->cache.flash_blk = blk; + } ++#ifdef CONFIG_GCRYPT + ms->cache.xts_aes = &ms->xts_aes; ++#endif + sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->cache), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_EXTMEM_BASE, mr, 0); +@@ -635,6 +645,7 @@ static void esp32c3_machine_init(MachineState *machine) + qdev_get_gpio_in(intmatrix_dev, ETS_SHA_INTR_SOURCE)); + } + ++#ifdef CONFIG_GCRYPT + /* AES realization */ + { + ms->aes.parent.gdma = ESP_GDMA(&ms->gdma); +@@ -654,6 +665,8 @@ static void esp32c3_machine_init(MachineState *machine) + qdev_get_gpio_in(intmatrix_dev, ETS_RSA_INTR_SOURCE)); + } + ++#endif ++ + /* HMAC realization */ + { + ms->hmac.parent.efuse = ESP_EFUSE(&ms->efuse); +@@ -662,6 +675,7 @@ static void esp32c3_machine_init(MachineState *machine) + memory_region_add_subregion_overlap(sys_mem, DR_REG_HMAC_BASE, mr, 0); + } + ++#ifdef CONFIG_GCRYPT + /* Digital Signature realization */ + { + ms->ds.parent.hmac = ESP_HMAC(&ms->hmac); +@@ -682,6 +696,8 @@ static void esp32c3_machine_init(MachineState *machine) + memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_XTS_BASE, mr, 0); + } + ++#endif ++ + /* RGB display realization */ + if (!ms->xteink) { + /* Give the internal RAM memory region to the display */ +diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h +index 2e4c4cc4b4..99e6da08c9 100644 +--- a/include/exec/exec-all.h ++++ b/include/exec/exec-all.h +@@ -453,7 +453,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); + void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); + + /* GETPC is the true target of the return instruction that we'll execute. */ +-#if defined(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/qemu/atomic.h b/include/qemu/atomic.h +index 7a3f2e6576..50dc235043 100644 +--- a/include/qemu/atomic.h ++++ b/include/qemu/atomic.h +@@ -75,7 +75,6 @@ + #else + # define ATOMIC_REG_SIZE sizeof(void *) + #endif +- + /* Weak atomic operations prevent the compiler moving other + * loads/stores past the atomic operation load/store. However there is + * no explicit memory barrier for the processor. +@@ -91,7 +90,7 @@ + + #define qatomic_read(ptr) \ + ({ \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + qatomic_read__nocheck(ptr); \ + }) + +@@ -99,7 +98,7 @@ + __atomic_store_n(ptr, i, __ATOMIC_RELAXED) + + #define qatomic_set(ptr, i) do { \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + qatomic_set__nocheck(ptr, i); \ + } while(0) + +@@ -122,7 +121,7 @@ + */ + #define qatomic_rcu_read_internal(ptr, _val) \ + ({ \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + typeof_strip_qual(*ptr) _val; \ + qatomic_rcu_read__nocheck(ptr, &_val); \ + _val; \ +@@ -131,20 +130,20 @@ + qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) + + #define qatomic_rcu_set(ptr, i) do { \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ + } while(0) + + #define qatomic_load_acquire(ptr) \ + ({ \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + typeof_strip_qual(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ + _val; \ + }) + + #define qatomic_store_release(ptr, i) do { \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ + } while(0) + +@@ -156,7 +155,7 @@ + }) + + #define qatomic_xchg(ptr, i) ({ \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + qatomic_xchg__nocheck(ptr, i); \ + }) + +@@ -169,7 +168,7 @@ + }) + + #define qatomic_cmpxchg(ptr, old, new) ({ \ +- qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \ + qatomic_cmpxchg__nocheck(ptr, old, new); \ + }) + +diff --git a/include/qemu/cacheflush.h b/include/qemu/cacheflush.h +index ae20bcda73..3f414fb7c9 100644 +--- a/include/qemu/cacheflush.h ++++ b/include/qemu/cacheflush.h +@@ -19,7 +19,7 @@ + * mappings of the same physical page(s). + */ + +-#if defined(__i386__) || defined(__x86_64__) || defined(__s390__) ++#if defined(__i386__) || defined(__x86_64__) || defined(__s390__) || defined(EMSCRIPTEN) + + static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) + { +diff --git a/include/qemu/int128.h b/include/qemu/int128.h +index 174bd7dafb..0305e262ef 100644 +--- a/include/qemu/int128.h ++++ b/include/qemu/int128.h +@@ -8,7 +8,7 @@ + * But libffi does not support __int128_t, and therefore cannot pass + * or return values of this type, force use of the Int128 struct. + */ +-#if defined(CONFIG_INT128) && !defined(CONFIG_TCG_INTERPRETER) ++#if defined(CONFIG_INT128) && !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN) + typedef __int128_t Int128; + typedef __int128_t __attribute__((aligned(16))) Int128Aligned; + +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +index fdff07fd99..e95faa4478 100644 +--- a/include/qemu/osdep.h ++++ b/include/qemu/osdep.h +@@ -133,7 +133,7 @@ QEMU_EXTERN_C int daemon(int, int); + #include + #include + +-#ifdef CONFIG_IOVEC ++#if defined(CONFIG_IOVEC) || defined(EMSCRIPTEN) + #include + #endif + +@@ -281,6 +281,7 @@ void QEMU_ERROR("code path is reachable") + #ifndef WCOREDUMP + #define WCOREDUMP(status) 0 + #endif ++#ifndef EMSCRIPTEN + /* + * We have a lot of unaudited code that may fail in strange ways, or + * even be a security risk during migration, if you disable assertions +@@ -296,6 +297,7 @@ void QEMU_ERROR("code path is reachable") + #ifdef G_DISABLE_ASSERT + #error building with G_DISABLE_ASSERT is not supported + #endif ++#endif + + #ifndef OFF_MAX + #define OFF_MAX (sizeof (off_t) == 8 ? INT64_MAX : INT32_MAX) +@@ -630,7 +632,7 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp); + + int qemu_get_thread_id(void); + +-#ifndef CONFIG_IOVEC ++#if !defined(CONFIG_IOVEC) && !defined(EMSCRIPTEN) + struct iovec { + void *iov_base; + size_t iov_len; +diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h +index 909fe73afa..c5064473ac 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 + #endif + #include "tcg-target-reg-bits.h" +@@ -48,11 +48,14 @@ 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; + #endif ++#if defined(EMSCRIPTEN) ++ ffi_cif *cif; ++#endif + + unsigned typemask : 32; + unsigned flags : 8; +diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h +index 546eb49c11..834757c3a4 100644 +--- a/include/tcg/tcg-opc.h ++++ b/include/tcg/tcg-opc.h +@@ -305,7 +305,7 @@ DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) + #include "tcg-target.opc.h" + #endif + +-#ifdef TCG_TARGET_INTERPRETER ++#if defined(TCG_TARGET_INTERPRETER) || defined(EMSCRIPTEN) + /* These opcodes are only 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) +diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h +index 9b82844dab..2cdfe0617b 100644 +--- a/include/tcg/tcg.h ++++ b/include/tcg/tcg.h +@@ -1010,7 +1010,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/meson.build b/meson.build +index 7f28871776..6e558969d4 100644 +--- a/meson.build ++++ b/meson.build +@@ -45,9 +45,9 @@ genh = [] + qapi_trace_events = [] + + bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin'] +-supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] ++supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux', 'emscripten'] + supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', +- 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64'] ++ 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64', 'wasm32'] + + cpu = host_machine.cpu_family() + +@@ -309,6 +309,7 @@ endif + # Compiler flags # + ################## + ++if host_arch != 'wasm32' + foreach lang : all_languages + compiler = meson.get_compiler(lang) + if compiler.get_id() == 'gcc' and compiler.version().version_compare('>=7.4') +@@ -328,6 +329,7 @@ foreach lang : all_languages + error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v15.0) to compile QEMU') + endif + endforeach ++endif + + # default flags for all hosts + # We use -fwrapv to tell the compiler that we require a C dialect where +@@ -482,7 +484,7 @@ ucontext_probe = ''' + # On Windows the only valid backend is the Windows specific one. + # For POSIX prefer ucontext, but it's not always possible. The fallback + # is sigcontext. +-supported_backends = [] ++supported_backends = ['fiber'] + if host_os == 'windows' + supported_backends += ['windows'] + else +@@ -510,7 +512,7 @@ safe_stack_probe = ''' + #endif + return 0; + }''' +-if get_option('safe_stack') != not cc.compiles(safe_stack_probe) ++if host_arch != 'wasm32' and get_option('safe_stack') != not cc.compiles(safe_stack_probe) + safe_stack_arg = get_option('safe_stack') ? '-fsanitize=safe-stack' : '-fno-sanitize=safe-stack' + if get_option('safe_stack') != not cc.compiles(safe_stack_probe, args: safe_stack_arg) + error(get_option('safe_stack') \ +@@ -520,7 +522,7 @@ if get_option('safe_stack') != not cc.compiles(safe_stack_probe) + qemu_cflags += safe_stack_arg + qemu_ldflags += safe_stack_arg + endif +-if get_option('safe_stack') and coroutine_backend != 'ucontext' ++if host_arch != 'wasm32' and get_option('safe_stack') and coroutine_backend != 'ucontext' + error('SafeStack is only supported with the ucontext coroutine backend') + endif + +@@ -884,6 +886,8 @@ if get_option('tcg').allowed() + tcg_arch = 'i386' + elif host_arch == 'ppc64' + tcg_arch = 'ppc' ++ elif host_arch == 'wasm32' ++ tcg_arch = 'wasm32' + endif + add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch, + language: all_languages) +@@ -1016,7 +1020,7 @@ endif + # problems on multi-arch where people try to build + # 32-bit QEMU while pointing at 64-bit glib headers + +-if not cc.compiles(''' ++if host_arch != 'wasm32' and not cc.compiles(''' + #include + #include + +@@ -1227,9 +1231,11 @@ if not get_option('slirp').auto() or have_system + slirp_cflags += ['-DLIBSLIRP_STATIC'] + endif + +- slirp = declare_dependency(dependencies: [slirp_dep], +- compile_args: slirp_cflags, +- version: slirp_dep.version()) ++ if slirp_dep.found() ++ slirp = declare_dependency(dependencies: [slirp_dep], ++ compile_args: slirp_cflags, ++ version: slirp_dep.version()) ++ endif + + # slirp < 4.7 is incompatible with CFI support in QEMU. This is because + # it passes function pointers within libslirp as callbacks for timers. +diff --git a/meson_options.txt b/meson_options.txt +index 5eeaf3eee5..76f7a65b40 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -34,7 +34,7 @@ option('fuzzing_engine', type : 'string', value : '', + option('trace_file', type: 'string', value: 'trace', + description: 'Trace file prefix for simple backend') + option('coroutine_backend', type: 'combo', +- choices: ['ucontext', 'sigaltstack', 'windows', 'auto'], ++ choices: ['ucontext', 'sigaltstack', 'windows', 'auto', 'fiber'], + value: 'auto', description: 'coroutine backend to use') + + # Everything else can be set via --enable/--disable-* option +diff --git a/os-posix.c b/os-posix.c +index 43f9a43f3f..595fa47866 100644 +--- a/os-posix.c ++++ b/os-posix.c +@@ -148,11 +148,13 @@ static void change_process_uid(void) + exit(1); + } + if (user_pwd) { ++#ifndef EMSCRIPTEN + if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { + error_report("Failed to initgroups(\"%s\", %d)", + user_pwd->pw_name, user_pwd->pw_gid); + exit(1); + } ++#endif + } else { + if (setgroups(1, &user_gid) < 0) { + error_report("Failed to setgroups(1, [%d])", +diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh +index a8066aab03..bd3ae90008 100644 +--- a/scripts/meson-buildoptions.sh ++++ b/scripts/meson-buildoptions.sh +@@ -80,7 +80,7 @@ meson_options_help() { + printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string' + printf "%s\n" ' [NORMAL]' + printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:' +- printf "%s\n" ' auto/sigaltstack/ucontext/windows)' ++ printf "%s\n" ' auto/fiber/sigaltstack/ucontext/windows)' + printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the' + printf "%s\n" ' package' + printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories' +diff --git a/tcg/meson.build b/tcg/meson.build +index 69ebb4908a..0894a577c2 100644 +--- a/tcg/meson.build ++++ b/tcg/meson.build +@@ -15,11 +15,18 @@ tcg_ss.add(files( + 'tcg-op-vec.c', + )) + ++cpu = host_machine.cpu_family() ++ + if get_option('tcg_interpreter') + libffi = dependency('libffi', version: '>=3.0', required: true, + method: 'pkg-config') + tcg_ss.add(libffi) + tcg_ss.add(files('tci.c')) ++elif cpu == 'wasm32' ++ 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 478ec051c4..41895c68fa 100644 +--- a/tcg/region.c ++++ b/tcg/region.c +@@ -568,7 +568,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" + +@@ -670,7 +670,7 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp) + + 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 +@@ -816,7 +816,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) + * 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 6dc3072f92..214e66e9d6 100644 +--- a/tcg/tcg.c ++++ b/tcg/tcg.c +@@ -131,6 +131,10 @@ static void tcg_out_goto_tb(TCGContext *s, int which); + static void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++static void tcg_out_label_cb(TCGContext *s, TCGLabel *l); ++static void tcg_out_init(); ++#endif + #if TCG_TARGET_MAYBE_vec + static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg src); +@@ -245,7 +249,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 + +@@ -352,6 +356,9 @@ 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); ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ tcg_out_label_cb(s, l); ++#endif + } + + TCGLabel *gen_new_label(void) +@@ -420,6 +427,143 @@ tlb_mask_table_ofs(TCGContext *s, int which) + sizeof(CPUNegativeOffsetState)); + } + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ ++#define SUB_BUF_MAX 100000 ++__thread uint8_t sub_buf[SUB_BUF_MAX]; ++__thread uint8_t *sub_buf_ptr; ++ ++static inline void tcg_sub_out8(TCGContext *s, uint8_t v) ++{ ++ *sub_buf_ptr++ = v; ++ if ((sub_buf_ptr - sub_buf) > SUB_BUF_MAX) { ++ printf("buffer too small"); fflush(stdout); ++ exit(1); ++ } ++} ++ ++static inline void tcg_sub_out32(TCGContext *s, uint32_t v) ++{ ++ memcpy(sub_buf_ptr, &v, sizeof(v)); ++ sub_buf_ptr += 4; ++} ++ ++static inline void* cur_sub_buf_ptr() ++{ ++ return sub_buf_ptr; ++} ++ ++static inline int cur_sub_buf_off_rel() ++{ ++ return (int)sub_buf_ptr - (int)sub_buf; ++} ++ ++#define LABEL_MAX 200 ++ ++struct label_placeholder { ++ int label; ++ uintptr_t ptr; ++}; ++ ++struct label_context { ++ int block_idx; ++}; ++ ++#define WASM_NUM_HELPER_FUNCS_MAX 200 ++#define WASM_HELPER_ADDED_TYPES_SECTION_MAX 200 ++__thread uint32_t num_helper_funcs; ++__thread uint32_t target_helper_funcs[WASM_NUM_HELPER_FUNCS_MAX]; ++__thread uint8_t target_helper_types[WASM_HELPER_ADDED_TYPES_SECTION_MAX]; ++__thread int target_helper_types_pos; ++__thread int wasm_block_idx; ++__thread struct label_placeholder block_ptr_placeholder[LABEL_MAX]; ++__thread int block_ptr_placeholder_idx_pos; ++__thread int label_to_block[LABEL_MAX]; ++ ++static int wasm_block_current_idx(TCGContext *s) ++{ ++ return wasm_block_idx; ++} ++ ++static int wasm_alloc_block_idx(TCGContext *s) ++{ ++ return ++wasm_block_idx; ++} ++ ++static void fill_uint32_leb128(uintptr_t bi, uint32_t v) { ++ uint8_t *b = (uint8_t *)bi; ++ uint32_t low7 = 0x7f; ++ // assuems higher bit already written as placeholders ++ do { ++ *b |= v & low7; ++ v >>= 7; ++ b++; ++ } while (v != 0); ++} ++ ++static int write_uint32_leb128(uintptr_t bi, uint32_t v) { ++ uint8_t *b = (uint8_t *)bi; ++ uint32_t low7 = 0x7f; ++ do { ++ *b = (uint8_t)(v & low7); ++ v >>= 7; ++ if (v != 0) ++ *b |= 0x80; ++ b++; ++ } while (v != 0); ++ ++ return (int)((uintptr_t)b - (uintptr_t)bi); ++} ++ ++static uint8_t * wasm_get_helper_types_begin(TCGContext *s) ++{ ++ return &(target_helper_types[target_helper_types_pos]); ++} ++ ++static void wasm_add_helper_types_pos(TCGContext *s, int i) ++{ ++ target_helper_types_pos += i; ++ tcg_debug_assert(target_helper_types_pos <= WASM_HELPER_ADDED_TYPES_SECTION_MAX); ++} ++ ++static int wasm_register_helper_alloc_num(TCGContext *s) ++{ ++ tcg_debug_assert(num_helper_funcs <= WASM_NUM_HELPER_FUNCS_MAX); ++ return num_helper_funcs++; ++} ++ ++static void wasm_register_helper(TCGContext *s, int idx_on_tb, int helper_idx_on_qemu) ++{ ++ tcg_debug_assert(idx_on_tb <= WASM_NUM_HELPER_FUNCS_MAX); ++ target_helper_funcs[idx_on_tb] = helper_idx_on_qemu; ++} ++ ++static int get_wasm_helper_idx(TCGContext *s, int helper_idx_on_qemu) ++{ ++ for (int i = 0; i < num_helper_funcs; i++) { ++ if (target_helper_funcs[i] == helper_idx_on_qemu) { ++ return i; ++ } ++ } ++ return -1; ++} ++ ++static void wasm_add_label_context(TCGContext *s, int label, int block) ++{ ++ tcg_debug_assert(label <= LABEL_MAX); ++ label_to_block[label] = block; ++} ++ ++static void wasm_add_label_block_ptr_placeholder(int label, uintptr_t code_ptr) ++{ ++ int i = block_ptr_placeholder_idx_pos++; ++ tcg_debug_assert(i <= LABEL_MAX); ++ block_ptr_placeholder[i].label = label; ++ block_ptr_placeholder[i].ptr = code_ptr; ++} ++ ++#endif ++ + /* Signal overflow, starting over with fewer guest insns. */ + static G_NORETURN + void tcg_raise_tb_overflow(TCGContext *s) +@@ -754,7 +898,7 @@ static const TCGTargetOpDef constraint_sets[] = { + + #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)) +@@ -937,7 +1081,7 @@ static TCGHelperInfo info_helper_st128_mmu = { + | dh_typemask(ptr, 5) /* uintptr_t ra */ + }; + +-#ifdef CONFIG_TCG_INTERPRETER ++#if defined(EMSCRIPTEN) || defined(CONFIG_TCG_INTERPRETER) + static ffi_type *typecode_to_ffi(int argmask) + { + /* +@@ -1406,12 +1550,11 @@ void tcg_prologue_init(void) + { + TCGContext *s = tcg_ctx; + size_t prologue_size; +- + s->code_ptr = s->code_gen_ptr; + 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 + +@@ -1434,7 +1577,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 +@@ -1471,7 +1614,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, +@@ -5904,6 +6047,151 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, + tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg); + } + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ ++static const uint8_t mod_header_a[] = { ++ 0x0, 0x61, 0x73, 0x6d, // magic ++ 0x01, 0x0, 0x0, 0x0, // version ++ // type section ++ 0x01, 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x60, ++ 0x01, 0x7f, ++ 0x01, 0x7f, ++ ++}; ++static const uint8_t mod_header_b[] = { ++ // import section ++ 0x02, 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x03, 0x65, 0x6e, 0x76, ++ 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, ++ 0x02, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, ++}; ++ ++static const uint8_t mod_header_c[] = { ++ // function section ++ 0x03, 2, 1, 0x00, ++ // global section ++ 0x06, 0x7e, ++ 25, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ 0x7e, 0x01, 0x42, 0x00, 0x0b, ++ // export section ++ 0x07, 13, ++ 1, ++ 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, ++ 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, ++}; ++ ++static const uint8_t mod_header_d[] = { ++ 0x0a, 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x80, 0x80, 0x80, 0x80, 0x00, ++ 0x80, 0x80, 0x80, 0x80, 0x00, ++ ++ 0x2, 0x2, 0x7f, 0x5, 0x7e, ++ ++ // initialize the instance ++ 0x20, 0x0, // local.get $ctx ++ 0x28, 0, DO_INIT_OFF, // i32.load do_init_ptr ++ 0x41, 0, // i32.const 0 ++ 0x47, // i32.ne ++ 0x04, 0x40, // if ++ ++ 0x23, 14, // global.get $env ++ 0x50, // i64.eqz ++ 0x04, 0x40, // if ++ // fundamental variables ++ 0x20, 0x0, // local.get $ctx ++ 0x28, 0, ENV_OFF, // i32.load env ++ 0xad, // extend ++ 0x24, 14, // global.set $14 ++ 0x20, 0x0, // local.get $ctx ++ 0x28, 0, STACK_OFF, // i32.load stack ++ 0xad, // extend ++ 0x24, 15, // global.set $15 ++ 0x0b, // end ++ ++ 0x20, 0x0, // local.get $ctx ++ 0x41, 0x00, // i32.const 0 ++ 0x36, 0x00, DO_INIT_OFF, // i32.store do_init ++ 0x42, 0x00, // i64.const 0 ++ 0x24, 16, // global.set $block_ptr ++ 0x0b, // end ++ ++ 0x03, 0x40, // loop ++ 0x23, 16, // global.get $block_ptr ++ 0x50, // i64.eqz ++ 0x04, 0x40, // if ++}; ++ ++static void write_wasm_type_section_size(TCGContext *s, void *header_a_ptr, uint32_t added) { ++ uint32_t type_section_size = added + 10; ++ fill_uint32_leb128((uintptr_t)header_a_ptr + 9, type_section_size); ++ fill_uint32_leb128((uintptr_t)header_a_ptr + 14, num_helper_funcs + 1); ++} ++static void write_wasm_memory_size(TCGContext *s, void *header_b_ptr) { ++ fill_uint32_leb128((uintptr_t)header_b_ptr + 25, (uint32_t)(~0) / 65536); ++} ++static void write_wasm_import_section_size(TCGContext *s, void *header_b_ptr, uint32_t added, uint32_t num_imported_funcs) { ++ uint32_t import_section_size = 35 + added - 11; ++ fill_uint32_leb128((uintptr_t)header_b_ptr + 1, import_section_size); ++ fill_uint32_leb128((uintptr_t)header_b_ptr + 6, num_imported_funcs + 1/*buffer+helpers...*/); ++} ++static void write_wasm_export_section_size(TCGContext *s, void *header_c_ptr, uint32_t startidx) { ++ fill_uint32_leb128((uintptr_t)header_c_ptr + 142, startidx); ++} ++static void write_wasm_code_size(TCGContext *s, void *header_d_ptr, int code_size, int code_nums) { ++ code_size = code_size + 66; ++ fill_uint32_leb128((uintptr_t)header_d_ptr + 1, code_size); ++ fill_uint32_leb128((uintptr_t)header_d_ptr + 6, code_nums); ++ fill_uint32_leb128((uintptr_t)header_d_ptr + 11, code_size - 10); ++} ++ ++uint8_t *tcg_out_import_entry(TCGContext *s, uint8_t* wasm_blob_ptr, int i, int typeidx) ++{ ++ *wasm_blob_ptr++ = 6; // helper ++ *wasm_blob_ptr++ = 0x68; ++ *wasm_blob_ptr++ = 0x65; ++ *wasm_blob_ptr++ = 0x6c; ++ *wasm_blob_ptr++ = 0x70; ++ *wasm_blob_ptr++ = 0x65; ++ *wasm_blob_ptr++ = 0x72; ++ char buf[100]; ++ int n = snprintf(buf, sizeof(buf), "%d", i); ++ wasm_blob_ptr += write_uint32_leb128((uintptr_t)wasm_blob_ptr, n); ++ memcpy(wasm_blob_ptr, buf, n); ++ wasm_blob_ptr += n; ++ *wasm_blob_ptr++ = 0x00; //type(0) ++ *wasm_blob_ptr++ = typeidx; //typeidx ++ return wasm_blob_ptr; ++} ++ ++#endif ++ + static void tcg_out_ld_helper_ret(TCGContext *s, const TCGLabelQemuLdst *ldst, + bool load_sign, + const TCGLdstHelperParam *parm) +@@ -6185,6 +6473,31 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) + s->code_ptr = s->code_buf; + s->data_gen_ptr = NULL; + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ sub_buf_ptr = sub_buf; ++ tcg_out_init(); ++ num_helper_funcs = 0; ++ wasm_block_idx = 0; ++ block_ptr_placeholder_idx_pos = 0; ++ target_helper_types_pos = 0; ++ memset(label_to_block, -1, LABEL_MAX); ++ memset(target_helper_funcs, -1, WASM_NUM_HELPER_FUNCS_MAX); ++ ++ uint32_t *tci_code_off = (uint32_t*)s->code_ptr; ++ s->code_ptr += 4; ++ uint32_t *size_base = (uint32_t*)s->code_ptr; ++ s->code_ptr += 4; ++ uint32_t *export_vec_base = (uint32_t*)s->code_ptr; ++ int export_size = get_core_nums() * 4; ++ memset(s->code_ptr, 0, export_size); ++ s->code_ptr += export_size; ++ *size_base = export_size; ++ ++ uint8_t *code_begin = s->code_ptr; ++ s->code_ptr += 4; // placeholder for size ++ *tci_code_off = s->code_ptr - s->code_buf; ++#endif ++ + #ifdef TCG_TARGET_NEED_LDST_LABELS + QSIMPLEQ_INIT(&s->ldst_labels); + #endif +@@ -6286,13 +6599,88 @@ 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, + tcg_ptr_byte_diff(s->code_ptr, s->code_buf)); + #endif + ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ tcg_sub_out8(s, 0x0b); //end if ++ tcg_sub_out8(s, 0x0b); //end loop ++ tcg_sub_out8(s, 0x0); // unreachable ++ tcg_sub_out8(s, 0x0b); //end func ++ ++ // fill blocks ++ for (int i = 0; i < block_ptr_placeholder_idx_pos; i++) { ++ int label = block_ptr_placeholder[i].label; ++ int ph = block_ptr_placeholder[i].ptr; ++ int blk = label_to_block[label]; ++ tcg_debug_assert(blk >= 0); ++ *(uint8_t*)ph = 0x80; ++ fill_uint32_leb128(ph, blk); ++ } ++ ++ int code_size = (uint32_t)((uintptr_t)s->code_ptr - (uintptr_t)code_begin - 4); ++ *(uint32_t *)code_begin = code_size; ++ ++ int sub_buf_len = sub_buf_ptr - sub_buf; ++ int wasm_body_size = sub_buf_len; ++ ++ // write header ++ uint8_t *wasm_blob_ptr = s->code_ptr; ++ uint8_t *wasm_blob_ptr_base = s->code_ptr; ++ wasm_blob_ptr += 4; // placeholder for size ++ ++ uint8_t *header_a_base = wasm_blob_ptr; ++ memcpy(wasm_blob_ptr, mod_header_a, sizeof(mod_header_a)); ++ wasm_blob_ptr += sizeof(mod_header_a); ++ memcpy(wasm_blob_ptr, target_helper_types, target_helper_types_pos); ++ wasm_blob_ptr += target_helper_types_pos; ++ write_wasm_type_section_size(s, header_a_base, target_helper_types_pos); ++ uint8_t *header_b_base = wasm_blob_ptr; ++ memcpy(wasm_blob_ptr, mod_header_b, sizeof(mod_header_b)); ++ wasm_blob_ptr += sizeof(mod_header_b); ++ uint8_t *header_b_adding_base = wasm_blob_ptr; ++ for (int i = 0; i < num_helper_funcs; i++) { ++ wasm_blob_ptr = tcg_out_import_entry(s, wasm_blob_ptr, i, i+1/*type0=start,1=helpers...*/); ++ } ++ write_wasm_import_section_size(s, header_b_base, (uint32_t)wasm_blob_ptr - (uint32_t)header_b_adding_base, num_helper_funcs); ++ write_wasm_memory_size(s, header_b_base); ++ uint8_t *header_c_base = wasm_blob_ptr; ++ memcpy(wasm_blob_ptr, mod_header_c, sizeof(mod_header_c)); ++ wasm_blob_ptr += sizeof(mod_header_c); ++ write_wasm_export_section_size(s, header_c_base, num_helper_funcs); ++ uint8_t *header_d_ptr = wasm_blob_ptr; ++ memcpy(wasm_blob_ptr, mod_header_d, sizeof(mod_header_d)); ++ wasm_blob_ptr += sizeof(mod_header_d); ++ write_wasm_code_size(s, header_d_ptr, wasm_body_size, 1); ++ s->code_ptr = wasm_blob_ptr; ++ ++ // write body ++ memcpy(s->code_ptr, sub_buf, sub_buf_len); ++ s->code_ptr += sub_buf_len; ++ ++ // write blob size ++ if (sub_buf_len > SUB_BUF_MAX) { ++ printf("sub too large sub_buf_len: %d\n", sub_buf_len); fflush(stdout); ++ exit(1); ++ } ++ *(uint32_t *)wasm_blob_ptr_base = s->code_ptr - wasm_blob_ptr_base - 4; ++ ++ // record importing helper functions ++ size_base = (uint32_t*)s->code_ptr; ++ s->code_ptr += 4; ++ memcpy(s->code_ptr, target_helper_funcs, num_helper_funcs * 4); ++ s->code_ptr += num_helper_funcs * 4; ++ *size_base = num_helper_funcs * 4; ++ ++ if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { ++ return -1; ++ } ++#endif ++ + return tcg_current_code_size(s); + } + +diff --git a/tcg/wasm32.c b/tcg/wasm32.c +new file mode 100644 +index 0000000000..dfcbf1493b +--- /dev/null ++++ b/tcg/wasm32.c +@@ -0,0 +1,1230 @@ ++/* ++ * Tiny Code Generator for QEMU ++ * ++ * Wasm integration + ported TCI interpreter from 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 . ++ */ ++ ++#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN) ++ ++#include "qemu/osdep.h" ++#include "exec/cpu_ldst.h" ++#include "tcg/tcg-op.h" ++#include "tcg/tcg-ldst.h" ++#include ++#include ++#include ++#include "wasm32.h" ++ ++__thread uintptr_t tci_tb_ptr; ++ ++/* Disassemble TCI bytecode. */ ++int print_insn_tci(bfd_vma addr, disassemble_info *info) ++{ ++ return 0; //nop ++} ++ ++EM_JS(int, instantiate_wasm, (), { ++ const memory_v = new DataView(HEAP8.buffer); ++ ++ const tb_ptr = memory_v.getInt32(Module.__wasm32_tb.tb_ptr_ptr, true); ++ const export_vec_size = memory_v.getInt32(tb_ptr + 4, true); ++ const export_vec_begin = tb_ptr + 4 + 4; ++ ++ const tmp_body_size = memory_v.getInt32(export_vec_begin + export_vec_size, true); ++ const tmp_body_begin = export_vec_begin + export_vec_size + 4; ++ const wasm_size = memory_v.getInt32(tmp_body_begin + tmp_body_size, true); ++ const wasm_begin = tmp_body_begin + tmp_body_size + 4; ++ const import_vec_size = memory_v.getInt32(wasm_begin + wasm_size, true); ++ const import_vec_begin = wasm_begin + wasm_size + 4; ++ ++ 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(wasm); ++ const inst = new WebAssembly.Instance(mod, { ++ "env": { ++ "buffer": wasmMemory, ++ }, ++ "helper": helper, ++ }); ++ var ptr = export_vec_begin + 4 * Module.__wasm32_tb.cur_core_num; ++ const fidx = addFunction(inst.exports.start, 'ii'); ++ memory_v.setUint32(ptr, fidx, true); ++ ++ const remove_n = memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, true); ++ if (remove_n > 500) { ++ for (var i = 0; i < remove_n * 4; i += 4) { ++ removeFunction(memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_ptr + i, true)); ++ } ++ memory_v.setInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, 0, true); ++ } ++ ++ return 0; ++}); ++ ++__thread bool initdone = false; ++__thread int cur_core_num = -1; ++__thread int export_vec_off = -1; ++__thread int all_cores_num = -1; ++int cur_core_num_max = 0; ++ ++#define TO_REMOVE_INSTANCE_SIZE 50000 ++__thread static int to_remove_instance[TO_REMOVE_INSTANCE_SIZE]; ++__thread static int to_remove_instance_idx = 0; ++ ++void remove_tb(void *tb_ptr) { ++ int32_t *slot = (int32_t*)((uint8_t*)tb_ptr + export_vec_off); ++ int32_t f = *slot; ++ if (f <= 0) { ++ return; ++ } ++ *slot = 0; ++ to_remove_instance[to_remove_instance_idx++] = f; ++} ++ ++__thread struct wasmContext ctx = { ++ .tb_ptr = 0, ++ .stack = NULL, ++ /* .func_ptr = 0, */ ++ /* .next_func_ptr = 0, */ ++ .do_init = 1, ++ .stack128 = NULL, ++}; ++ ++void set_done_flag() ++{ ++ ctx.done_flag = 1; ++} ++ ++void set_unwinding_flag() ++{ ++ ctx.unwinding = 1; ++} ++ ++typedef uint32_t (*wasm_func_ptr)(struct wasmContext*); ++ ++int get_core_nums() ++{ ++ return emscripten_num_logical_cores(); ++} ++ ++EM_JS(void, init_wasm32_js, (int tb_ptr_ptr, int cur_core_num, int to_remove_instance_ptr, int to_remove_instance_idx_ptr), { ++ Module.__wasm32_tb = { ++ tb_ptr_ptr: tb_ptr_ptr, ++ cur_core_num: cur_core_num, ++ to_remove_instance_ptr: to_remove_instance_ptr, ++ to_remove_instance_idx_ptr: to_remove_instance_idx_ptr, ++ }; ++}); ++ ++void init_wasm32() ++{ ++ if (!initdone) { ++ cur_core_num = qatomic_fetch_inc(&cur_core_num_max); ++ export_vec_off = 4 + 4 + cur_core_num * 4; ++ all_cores_num = get_core_nums(); ++ ctx.stack = (uint64_t*)malloc(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE); ++ ctx.stack128 = (uint64_t*)malloc(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE); ++ ctx.tci_tb_ptr = (uint32_t*)&tci_tb_ptr; ++ init_wasm32_js((int)&ctx.tb_ptr, cur_core_num, (int)to_remove_instance, (int)&to_remove_instance_idx); ++ initdone = true; ++ } ++} ++ ++__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; ++} ++ ++static void tci_args_ldst(uint32_t insn, TCGReg *r0, TCGReg *r1, MemOpIdx *m2, const void *tb_ptr, void **l0) ++{ ++ int diff = sextract32(insn, 12, 20); ++ *l0 = diff ? (uint8_t *)tb_ptr + diff : NULL; ++ ++ uint64_t *data64 = (uint64_t*)*l0; ++ *r0 = (TCGReg)data64[0]; ++ *r1 = (TCGReg)data64[1]; ++ *m2 = (MemOpIdx)data64[2]; ++} ++ ++/* ++ * Load sets of arguments all at once. The naming convention is: ++ * tci_args_ ++ * 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 ? (uint8_t *)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_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_rrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, ++ TCGReg *r2, TCGReg *r3, TCGReg *r4) ++{ ++ *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); ++} ++ ++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 void tci_args_rrrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, ++ TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGReg *r5) ++{ ++ *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); ++ *r5 = 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; ++ 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; ++ default: ++ g_assert_not_reached(); ++ } ++ return result; ++} ++ ++static uint64_t tlb_load(CPUArchState *env, uint64_t taddr, MemOp mop, uint64_t* ptr, bool is_ld) ++{ ++ uint64_t *data64 = (uint64_t*)ptr; ++ unsigned a_mask = (unsigned)data64[3]; ++ int mask_ofs = (int)data64[4]; ++ int8_t page_bits = (int8_t)data64[5]; ++ uint64_t page_mask = (uint64_t)data64[6]; ++ int table_ofs = (uint64_t)data64[7]; ++ ++ unsigned s_bits = mop & MO_SIZE; ++ unsigned s_mask = (1u << s_bits) - 1; ++ tcg_target_long compare_mask; ++ int add_off = offsetof(CPUTLBEntry, addend); ++ ++ uint64_t tmp0 = taddr >> (page_bits - CPU_TLB_ENTRY_BITS); ++ uint64_t tmp2 = *(uint64_t*)((uint8_t*)env + mask_ofs); ++ uint64_t tmp2_b = *(uint64_t*)((uint8_t*)env + table_ofs); ++ uint64_t tmp3 = (tmp0 & tmp2) + tmp2_b; ++ int off = off = is_ld ? offsetof(CPUTLBEntry, addr_read) ++ : offsetof(CPUTLBEntry, addr_write); ++ uint64_t target = *(uint64_t*)((uint8_t*)tmp3 + off); ++ uint64_t c_addr = taddr; ++ if (a_mask < s_mask) { ++ c_addr += s_mask - a_mask; ++ } ++ compare_mask = page_mask | a_mask; ++ c_addr &= compare_mask; ++ ++ if (c_addr == target) { ++ int32_t addend = *(uint32_t*)((uint8_t*)tmp3 + add_off); ++ uint64_t target_addr = taddr + addend; ++ return target_addr; ++ } ++ return 0; ++} ++ ++static uint64_t tci_qemu_ld(CPUArchState *env, uint64_t taddr, ++ MemOpIdx oi, const void *tb_ptr, uint64_t* ptr) ++{ ++ MemOp mop = get_memop(oi); ++ uintptr_t ra = (uintptr_t)tb_ptr; ++ ++ uint64_t target_addr = tlb_load(env, taddr, mop, ptr, true); ++ if (target_addr != 0) { ++ switch (mop & MO_SSIZE) { ++ case MO_UB: ++ return *(uint8_t*)target_addr; ++ case MO_SB: ++ return *(int8_t*)target_addr; ++ case MO_UW: ++ return *(uint16_t*)target_addr; ++ case MO_SW: ++ return *(int16_t*)target_addr; ++ case MO_UL: ++ return *(uint32_t*)target_addr; ++ case MO_SL: ++ return *(int32_t*)target_addr; ++ case MO_UQ: ++ return *(uint64_t*)target_addr; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ 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, uint64_t* ptr) ++{ ++ MemOp mop = get_memop(oi); ++ uintptr_t ra = (uintptr_t)tb_ptr; ++ ++ uint64_t target_addr = tlb_load(env, taddr, mop, ptr, false); ++ if (target_addr != 0) { ++ switch (mop & MO_SIZE) { ++ case MO_UB: ++ *(uint8_t*)target_addr = (uint8_t)val; ++ break; ++ case MO_UW: ++ *(uint16_t*)target_addr = (uint16_t)val; ++ break; ++ case MO_UL: ++ *(uint32_t*)target_addr = (uint32_t)val; ++ break; ++ case MO_UQ: ++ *(uint64_t*)target_addr = (uint64_t)val; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ return; ++ } ++ ++ 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(); ++ } ++} ++ ++#if TCG_TARGET_REG_BITS == 64 ++# define CASE_32_64(x) \ ++ case glue(glue(INDEX_op_, x), _i64): \ ++ case glue(glue(INDEX_op_, x), _i32): ++# define CASE_64(x) \ ++ case glue(glue(INDEX_op_, x), _i64): ++#else ++# define CASE_32_64(x) \ ++ case glue(glue(INDEX_op_, x), _i32): ++# define CASE_64(x) ++#endif ++ ++__thread tcg_target_ulong regs[TCG_TARGET_NB_REGS]; ++ ++static inline uintptr_t tcg_qemu_tb_exec_tci(CPUArchState *env) ++{ ++ uint32_t *tb_ptr = (uint8_t*)ctx.tb_ptr + *(uint32_t*)ctx.tb_ptr; ++ uint64_t *stack = ctx.stack; ++ ++ regs[TCG_AREG0] = (tcg_target_ulong)env; ++ regs[TCG_REG_CALL_STACK] = (uintptr_t)stack; ++ ++ for (;;) { ++ uint32_t insn; ++ TCGOpcode opc; ++ TCGReg r0, r1, r2, r3, r4, r5; ++ tcg_target_ulong t1; ++ TCGCond condition; ++ uint8_t pos, len; ++ uint32_t tmp32; ++ uint64_t tmp64, taddr; ++ uint64_t T1, T2; ++ MemOpIdx oi; ++ int32_t ofs; ++ void *ptr; ++ ++ uint32_t *savep = tb_ptr; ++ insn = *tb_ptr++; ++ opc = extract32(insn, 0, 8); ++ ++ TCGOpDef *def = &tcg_op_defs[opc]; ++ ++ 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); ++ uint64_t *data64 = (uint64_t*)ptr; ++ func = (void*)data64[0]; ++ cif = (void*)data64[1]; ++ ++ int reg_iarg_base = 8; ++ if ((uint32_t)func == (uint32_t)helper_lookup_tb_ptr) { ++ regs[TCG_REG_R0] = (uint32_t)helper_lookup_tb_ptr((CPUArchState *)regs[reg_iarg_base]); ++ break; ++ } ++ ++ int reg_idx = 0; ++ int reg_idx_end = 5; // NUM_OF_IARG_REGS ++ int stack_idx = 0; ++ n = cif->nargs; ++ for (i = s = 0; i < n; ++i) { ++ ffi_type *t = cif->arg_types[i]; ++ if (reg_idx < reg_idx_end) { ++ call_slots[i] = ®s[reg_iarg_base + reg_idx]; ++ reg_idx += DIV_ROUND_UP(t->size, 8); ++ } else { ++ call_slots[i] = &stack[stack_idx]; ++ stack_idx += 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; ++ case INDEX_op_setcond_i32: ++ tci_args_rrrc(insn, &r0, &r1, &r2, &condition); ++ regs[r0] = tci_compare32(regs[r1], regs[r2], condition); ++ break; ++ case INDEX_op_movcond_i32: ++ 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; ++#if TCG_TARGET_REG_BITS == 32 ++ case INDEX_op_setcond2_i32: ++ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); ++ T1 = tci_uint64(regs[r2], regs[r1]); ++ T2 = tci_uint64(regs[r4], regs[r3]); ++ regs[r0] = tci_compare64(T1, T2, condition); ++ break; ++#elif TCG_TARGET_REG_BITS == 64 ++ case INDEX_op_setcond_i64: ++ tci_args_rrrc(insn, &r0, &r1, &r2, &condition); ++ regs[r0] = tci_compare64(regs[r1], regs[r2], condition); ++ break; ++ case INDEX_op_movcond_i64: ++ 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_32_64(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; ++ ++ /* Load/store operations (32 bit). */ ++ ++ CASE_32_64(ld8u) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(uint8_t *)ptr; ++ break; ++ CASE_32_64(ld8s) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(int8_t *)ptr; ++ break; ++ CASE_32_64(ld16u) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(uint16_t *)ptr; ++ break; ++ CASE_32_64(ld16s) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(int16_t *)ptr; ++ break; ++ case INDEX_op_ld_i32: ++ CASE_64(ld32u) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(uint32_t *)ptr; ++ break; ++ CASE_32_64(st8) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ *(uint8_t *)ptr = regs[r0]; ++ break; ++ CASE_32_64(st16) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ *(uint16_t *)ptr = regs[r0]; ++ break; ++ case INDEX_op_st_i32: ++ CASE_64(st32) ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ *(uint32_t *)ptr = regs[r0]; ++ break; ++ ++ /* Arithmetic operations (mixed 32/64 bit). */ ++ ++ CASE_32_64(add) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] + regs[r2]; ++ break; ++ CASE_32_64(sub) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] - regs[r2]; ++ break; ++ CASE_32_64(mul) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] * regs[r2]; ++ break; ++ CASE_32_64(and) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] & regs[r2]; ++ break; ++ CASE_32_64(or) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] | regs[r2]; ++ break; ++ CASE_32_64(xor) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] ^ regs[r2]; ++ break; ++#if TCG_TARGET_HAS_andc_i32 || TCG_TARGET_HAS_andc_i64 ++ CASE_32_64(andc) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] & ~regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_orc_i32 || TCG_TARGET_HAS_orc_i64 ++ CASE_32_64(orc) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] | ~regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_eqv_i32 || TCG_TARGET_HAS_eqv_i64 ++ CASE_32_64(eqv) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = ~(regs[r1] ^ regs[r2]); ++ break; ++#endif ++#if TCG_TARGET_HAS_nand_i32 || TCG_TARGET_HAS_nand_i64 ++ CASE_32_64(nand) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = ~(regs[r1] & regs[r2]); ++ break; ++#endif ++#if TCG_TARGET_HAS_nor_i32 || TCG_TARGET_HAS_nor_i64 ++ CASE_32_64(nor) ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = ~(regs[r1] | regs[r2]); ++ break; ++#endif ++ ++ /* Arithmetic operations (32 bit). */ ++ ++ case INDEX_op_div_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2]; ++ break; ++ case INDEX_op_divu_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2]; ++ break; ++ case INDEX_op_rem_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2]; ++ break; ++ case INDEX_op_remu_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2]; ++ break; ++#if TCG_TARGET_HAS_clz_i32 ++ case INDEX_op_clz_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ tmp32 = regs[r1]; ++ regs[r0] = tmp32 ? clz32(tmp32) : regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ctz_i32 ++ case INDEX_op_ctz_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ tmp32 = regs[r1]; ++ regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ctpop_i32 ++ case INDEX_op_ctpop_i32: ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = ctpop32(regs[r1]); ++ break; ++#endif ++ ++ /* Shift/rotate operations (32 bit). */ ++ ++ case INDEX_op_shl_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint32_t)regs[r1] << (regs[r2] & 31); ++ break; ++ case INDEX_op_shr_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint32_t)regs[r1] >> (regs[r2] & 31); ++ break; ++ case INDEX_op_sar_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int32_t)regs[r1] >> (regs[r2] & 31); ++ break; ++#if TCG_TARGET_HAS_rot_i32 ++ case INDEX_op_rotl_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = rol32(regs[r1], regs[r2] & 31); ++ break; ++ case INDEX_op_rotr_i32: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = ror32(regs[r1], regs[r2] & 31); ++ break; ++#endif ++#if TCG_TARGET_HAS_deposit_i32 ++ case INDEX_op_deposit_i32: ++ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); ++ regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); ++ break; ++#endif ++#if TCG_TARGET_HAS_extract_i32 ++ case INDEX_op_extract_i32: ++ tci_args_rrbb(insn, &r0, &r1, &pos, &len); ++ regs[r0] = extract32(regs[r1], pos, len); ++ break; ++#endif ++#if TCG_TARGET_HAS_sextract_i32 ++ case INDEX_op_sextract_i32: ++ tci_args_rrbb(insn, &r0, &r1, &pos, &len); ++ regs[r0] = sextract32(regs[r1], pos, len); ++ break; ++#endif ++ case INDEX_op_brcond_i32: ++ tci_args_rl(insn, tb_ptr, &r0, &ptr); ++ if ((uint32_t)regs[r0]) { ++ tb_ptr = ptr; ++ } ++ break; ++#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_add2_i32 ++ case INDEX_op_add2_i32: ++ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); ++ T1 = tci_uint64(regs[r3], regs[r2]); ++ T2 = tci_uint64(regs[r5], regs[r4]); ++ tci_write_reg64(regs, r1, r0, T1 + T2); ++ break; ++#endif ++#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_sub2_i32 ++ case INDEX_op_sub2_i32: ++ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); ++ T1 = tci_uint64(regs[r3], regs[r2]); ++ T2 = tci_uint64(regs[r5], regs[r4]); ++ tci_write_reg64(regs, r1, r0, T1 - T2); ++ break; ++#endif ++#if TCG_TARGET_HAS_mulu2_i32 ++ case INDEX_op_mulu2_i32: ++ tci_args_rrrr(insn, &r0, &r1, &r2, &r3); ++ tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; ++ tci_write_reg64(regs, r1, r0, tmp64); ++ break; ++#endif ++#if TCG_TARGET_HAS_muls2_i32 ++ case INDEX_op_muls2_i32: ++ tci_args_rrrr(insn, &r0, &r1, &r2, &r3); ++ tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; ++ tci_write_reg64(regs, r1, r0, tmp64); ++ break; ++#endif ++#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64 ++ CASE_32_64(ext8s) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (int8_t)regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 || \ ++ TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 ++ CASE_32_64(ext16s) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (int16_t)regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ext8u_i32 || TCG_TARGET_HAS_ext8u_i64 ++ CASE_32_64(ext8u) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (uint8_t)regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ext16u_i32 || TCG_TARGET_HAS_ext16u_i64 ++ CASE_32_64(ext16u) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (uint16_t)regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 ++ CASE_32_64(bswap16) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = bswap16(regs[r1]); ++ break; ++#endif ++#if TCG_TARGET_HAS_bswap32_i32 || TCG_TARGET_HAS_bswap32_i64 ++ CASE_32_64(bswap32) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = bswap32(regs[r1]); ++ break; ++#endif ++#if TCG_TARGET_HAS_not_i32 || TCG_TARGET_HAS_not_i64 ++ CASE_32_64(not) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = ~regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_HAS_neg_i32 || TCG_TARGET_HAS_neg_i64 ++ CASE_32_64(neg) ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = -regs[r1]; ++ break; ++#endif ++#if TCG_TARGET_REG_BITS == 64 ++ /* Load/store operations (64 bit). */ ++ ++ case INDEX_op_ld32s_i64: ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(int32_t *)ptr; ++ break; ++ case INDEX_op_ld_i64: ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ regs[r0] = *(uint64_t *)ptr; ++ break; ++ case INDEX_op_st_i64: ++ tci_args_rrs(insn, &r0, &r1, &ofs); ++ ptr = (void *)(regs[r1] + ofs); ++ *(uint64_t *)ptr = regs[r0]; ++ break; ++ ++ /* Arithmetic operations (64 bit). */ ++ ++ case INDEX_op_div_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2]; ++ break; ++ case INDEX_op_divu_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2]; ++ break; ++ case INDEX_op_rem_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2]; ++ break; ++ case INDEX_op_remu_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2]; ++ break; ++#if TCG_TARGET_HAS_clz_i64 ++ case INDEX_op_clz_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ctz_i64 ++ case INDEX_op_ctz_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; ++ break; ++#endif ++#if TCG_TARGET_HAS_ctpop_i64 ++ case INDEX_op_ctpop_i64: ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = ctpop64(regs[r1]); ++ break; ++#endif ++#if TCG_TARGET_HAS_mulu2_i64 ++ case INDEX_op_mulu2_i64: ++ tci_args_rrrr(insn, &r0, &r1, &r2, &r3); ++ mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); ++ break; ++#endif ++#if TCG_TARGET_HAS_muls2_i64 ++ case INDEX_op_muls2_i64: ++ tci_args_rrrr(insn, &r0, &r1, &r2, &r3); ++ muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); ++ break; ++#endif ++#if TCG_TARGET_HAS_add2_i64 ++ case INDEX_op_add2_i64: ++ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); ++ T1 = regs[r2] + regs[r4]; ++ T2 = regs[r3] + regs[r5] + (T1 < regs[r2]); ++ regs[r0] = T1; ++ regs[r1] = T2; ++ break; ++#endif ++#if TCG_TARGET_HAS_add2_i64 ++ case INDEX_op_sub2_i64: ++ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); ++ T1 = regs[r2] - regs[r4]; ++ T2 = regs[r3] - regs[r5] - (regs[r2] < regs[r4]); ++ regs[r0] = T1; ++ regs[r1] = T2; ++ break; ++#endif ++ ++ /* Shift/rotate operations (64 bit). */ ++ ++ case INDEX_op_shl_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] << (regs[r2] & 63); ++ break; ++ case INDEX_op_shr_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = regs[r1] >> (regs[r2] & 63); ++ break; ++ case INDEX_op_sar_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = (int64_t)regs[r1] >> (regs[r2] & 63); ++ break; ++#if TCG_TARGET_HAS_rot_i64 ++ case INDEX_op_rotl_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = rol64(regs[r1], regs[r2] & 63); ++ break; ++ case INDEX_op_rotr_i64: ++ tci_args_rrr(insn, &r0, &r1, &r2); ++ regs[r0] = ror64(regs[r1], regs[r2] & 63); ++ break; ++#endif ++#if TCG_TARGET_HAS_deposit_i64 ++ case INDEX_op_deposit_i64: ++ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); ++ regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); ++ break; ++#endif ++#if TCG_TARGET_HAS_extract_i64 ++ case INDEX_op_extract_i64: ++ tci_args_rrbb(insn, &r0, &r1, &pos, &len); ++ regs[r0] = extract64(regs[r1], pos, len); ++ break; ++#endif ++#if TCG_TARGET_HAS_sextract_i64 ++ case INDEX_op_sextract_i64: ++ tci_args_rrbb(insn, &r0, &r1, &pos, &len); ++ regs[r0] = sextract64(regs[r1], pos, len); ++ break; ++#endif ++ case INDEX_op_brcond_i64: ++ tci_args_rl(insn, tb_ptr, &r0, &ptr); ++ if (regs[r0]) { ++ tb_ptr = ptr; ++ } ++ break; ++ case INDEX_op_ext32s_i64: ++ case INDEX_op_ext_i32_i64: ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (int32_t)regs[r1]; ++ break; ++ case INDEX_op_ext32u_i64: ++ case INDEX_op_extu_i32_i64: ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = (uint32_t)regs[r1]; ++ break; ++#if TCG_TARGET_HAS_bswap64_i64 ++ case INDEX_op_bswap64_i64: ++ tci_args_rr(insn, &r0, &r1); ++ regs[r0] = bswap64(regs[r1]); ++ break; ++#endif ++#endif /* TCG_TARGET_REG_BITS == 64 */ ++ ++ /* QEMU specific operations. */ ++ ++ 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); ++ if (*(uint32_t **)ptr != 0) { ++ tb_ptr = *(uint32_t **)ptr; ++ ctx.tb_ptr = tb_ptr; ++ int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off; ++ if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { ++ *(int32_t*)tb_entry_ptr -= 1; ++ } else { ++ // enter to wasm TB ++ return 0; ++ } ++ tb_ptr = (uint8_t*)tb_ptr + *(uint32_t*)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; ++ int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off; ++ if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) { ++ *(int32_t*)tb_entry_ptr -= 1; ++ } else { ++ // enter to wasm TB ++ return 0; ++ } ++ tb_ptr = (uint8_t*)tb_ptr + *(uint32_t*)tb_ptr; ++ ++ break; ++ ++ case INDEX_op_qemu_ld_a32_i32: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = (uint32_t)regs[r1]; ++ goto do_ld_i32; ++ case INDEX_op_qemu_ld_a64_i32: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = regs[r1]; ++ do_ld_i32: ++ regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr, ptr); ++ break; ++ ++ case INDEX_op_qemu_ld_a32_i64: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = (uint32_t)regs[r1]; ++ goto do_ld_i64; ++ case INDEX_op_qemu_ld_a64_i64: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = regs[r1]; ++ do_ld_i64: ++ tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr, ptr); ++ if (TCG_TARGET_REG_BITS == 32) { ++ tci_write_reg64(regs, r1, r0, tmp64); ++ } else { ++ regs[r0] = tmp64; ++ } ++ break; ++ ++ case INDEX_op_qemu_st_a32_i32: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = (uint32_t)regs[r1]; ++ goto do_st_i32; ++ case INDEX_op_qemu_st_a64_i32: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ taddr = regs[r1]; ++ do_st_i32: ++ tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr, ptr); ++ break; ++ ++ case INDEX_op_qemu_st_a32_i64: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ tmp64 = regs[r0]; ++ taddr = (uint32_t)regs[r1]; ++ goto do_st_i64; ++ case INDEX_op_qemu_st_a64_i64: ++ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr); ++ tmp64 = regs[r0]; ++ taddr = regs[r1]; ++ do_st_i64: ++ tci_qemu_st(env, taddr, tmp64, oi, tb_ptr, ptr); ++ break; ++ ++ case INDEX_op_mb: ++ /* Ensure ordering for all kinds */ ++ smp_mb(); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, ++ const void *v_tb_ptr) ++{ ++ ctx.env = env; ++ ctx.tb_ptr = (uint32_t*)v_tb_ptr; ++ ctx.do_init = 1; ++ while (true) { ++ int tb_entry_ptr = (uint32_t)ctx.tb_ptr + export_vec_off; ++ uint32_t res; ++ if (*(int32_t*)tb_entry_ptr > 0) { ++ res = ((wasm_func_ptr)(*(uint32_t*)tb_entry_ptr))(&ctx); ++ } else if (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM)) { ++ *(int32_t*)tb_entry_ptr -= 1; ++ res = tcg_qemu_tb_exec_tci(env); ++ } else { ++ instantiate_wasm(); ++ res = ((wasm_func_ptr)(*(uint32_t*)tb_entry_ptr))(&ctx); ++ } ++ if ((uint32_t)ctx.tb_ptr == 0) { ++ return res; ++ } ++ } ++} ++ ++#endif +diff --git a/tcg/wasm32.h b/tcg/wasm32.h +new file mode 100644 +index 0000000000..1861510f0b +--- /dev/null ++++ b/tcg/wasm32.h +@@ -0,0 +1,44 @@ ++#ifndef TCG_WASM32_H ++#define TCG_WASM32_H ++ ++struct wasmContext { ++ // 0 ++ CPUArchState *env; ++ // 4 ++ uint64_t *stack; ++ // 8 ++ uint32_t *tb_ptr; ++ // 12 ++ uint32_t *tci_tb_ptr; ++ // 16 ++ uint32_t do_init; ++ // 20 ++ uint32_t done_flag; ++ // 24 ++ uint64_t *stack128; ++ // 28 ++ uint32_t unwinding; ++}; ++ ++#define ENV_OFF 0 ++#define STACK_OFF 4 ++#define TB_PTR_OFF 8 ++#define HELPER_RET_TB_PTR_OFF 12 ++#define DO_INIT_OFF 16 ++#define DONE_FLAG_OFF 20 ++#define STACK128_OFF 24 ++#define UNWINDING_OFF 28 ++ ++void set_done_flag(); ++ ++void set_unwinding_flag(); ++ ++int get_core_nums(); ++ ++void remove_tb(void *tb_ptr); ++ ++void init_wasm32(); ++ ++#define INSTANTIATE_NUM 1500 ++ ++#endif +diff --git a/tcg/wasm32/tcg-target-con-set.h b/tcg/wasm32/tcg-target-con-set.h +new file mode 100644 +index 0000000000..f50a6793b7 +--- /dev/null ++++ b/tcg/wasm32/tcg-target-con-set.h +@@ -0,0 +1,15 @@ ++/* ++ * C_On_Im(...) defines a constraint set with outputs and 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..56d7aff291 +--- /dev/null ++++ b/tcg/wasm32/tcg-target-con-str.h +@@ -0,0 +1,5 @@ ++/* ++ * 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-reg-bits.h b/tcg/wasm32/tcg-target-reg-bits.h +new file mode 100644 +index 0000000000..196395aef9 +--- /dev/null ++++ b/tcg/wasm32/tcg-target-reg-bits.h +@@ -0,0 +1,6 @@ ++#ifndef TCG_TARGET_REG_BITS_H ++#define TCG_TARGET_REG_BITS_H ++ ++#define TCG_TARGET_REG_BITS 64 ++ ++#endif +diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc +new file mode 100644 +index 0000000000..9e82297a3f +--- /dev/null ++++ b/tcg/wasm32/tcg-target.c.inc +@@ -0,0 +1,3824 @@ ++/* ++ * Tiny Code Generator for QEMU ++ * ++ * Copyright (c) 2018 SiFive, Inc ++ * Copyright (c) 2008-2009 Arnaud Patard ++ * Copyright (c) 2009 Aurelien Jarno ++ * Copyright (c) 2008 Fabrice Bellard ++ * Copyright (c) 2009, 2011 Stefan Weil ++ * ++ * Based on riscv/tcg-target.c.inc and tci/tcg-target.c ++ * ++ * 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. ++ */ ++ ++#include "../wasm32.h" ++#include ++#include ++#include "../tcg-pool.c.inc" ++ ++static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) ++{ ++ switch (op) { ++ case INDEX_op_goto_ptr: ++ return C_O0_I1(r); ++ ++ case INDEX_op_ld8u_i32: ++ case INDEX_op_ld8s_i32: ++ case INDEX_op_ld16u_i32: ++ case INDEX_op_ld16s_i32: ++ case INDEX_op_ld_i32: ++ case INDEX_op_ld8u_i64: ++ case INDEX_op_ld8s_i64: ++ case INDEX_op_ld16u_i64: ++ case INDEX_op_ld16s_i64: ++ case INDEX_op_ld32u_i64: ++ case INDEX_op_ld32s_i64: ++ case INDEX_op_ld_i64: ++ case INDEX_op_not_i32: ++ case INDEX_op_not_i64: ++ case INDEX_op_neg_i32: ++ case INDEX_op_neg_i64: ++ case INDEX_op_ext8s_i32: ++ case INDEX_op_ext8s_i64: ++ case INDEX_op_ext16s_i32: ++ case INDEX_op_ext16s_i64: ++ case INDEX_op_ext8u_i32: ++ case INDEX_op_ext8u_i64: ++ case INDEX_op_ext16u_i32: ++ case INDEX_op_ext16u_i64: ++ case INDEX_op_ext32s_i64: ++ case INDEX_op_ext32u_i64: ++ case INDEX_op_ext_i32_i64: ++ case INDEX_op_extu_i32_i64: ++ case INDEX_op_bswap16_i32: ++ case INDEX_op_bswap16_i64: ++ case INDEX_op_bswap32_i32: ++ case INDEX_op_bswap32_i64: ++ case INDEX_op_bswap64_i64: ++ case INDEX_op_extract_i32: ++ case INDEX_op_extract_i64: ++ case INDEX_op_sextract_i32: ++ case INDEX_op_sextract_i64: ++ case INDEX_op_extrl_i64_i32: ++ case INDEX_op_extrh_i64_i32: ++ case INDEX_op_ctpop_i32: ++ case INDEX_op_ctpop_i64: ++ return C_O1_I1(r, r); ++ ++ case INDEX_op_st8_i32: ++ case INDEX_op_st16_i32: ++ case INDEX_op_st_i32: ++ case INDEX_op_st8_i64: ++ case INDEX_op_st16_i64: ++ case INDEX_op_st32_i64: ++ case INDEX_op_st_i64: ++ return C_O0_I2(r, r); ++ ++ case INDEX_op_div_i32: ++ case INDEX_op_div_i64: ++ case INDEX_op_divu_i32: ++ case INDEX_op_divu_i64: ++ case INDEX_op_rem_i32: ++ case INDEX_op_rem_i64: ++ case INDEX_op_remu_i32: ++ case INDEX_op_remu_i64: ++ case INDEX_op_add_i32: ++ case INDEX_op_add_i64: ++ case INDEX_op_sub_i32: ++ case INDEX_op_sub_i64: ++ case INDEX_op_mul_i32: ++ case INDEX_op_mul_i64: ++ case INDEX_op_and_i32: ++ case INDEX_op_and_i64: ++ case INDEX_op_andc_i32: ++ case INDEX_op_andc_i64: ++ case INDEX_op_eqv_i32: ++ case INDEX_op_eqv_i64: ++ case INDEX_op_nand_i32: ++ case INDEX_op_nand_i64: ++ case INDEX_op_nor_i32: ++ case INDEX_op_nor_i64: ++ case INDEX_op_or_i32: ++ case INDEX_op_or_i64: ++ case INDEX_op_orc_i32: ++ case INDEX_op_orc_i64: ++ case INDEX_op_xor_i32: ++ case INDEX_op_xor_i64: ++ case INDEX_op_shl_i32: ++ case INDEX_op_shl_i64: ++ case INDEX_op_shr_i32: ++ case INDEX_op_shr_i64: ++ case INDEX_op_sar_i32: ++ case INDEX_op_sar_i64: ++ case INDEX_op_rotl_i32: ++ case INDEX_op_rotl_i64: ++ case INDEX_op_rotr_i32: ++ case INDEX_op_rotr_i64: ++ case INDEX_op_setcond_i32: ++ case INDEX_op_setcond_i64: ++ case INDEX_op_deposit_i32: ++ case INDEX_op_deposit_i64: ++ case INDEX_op_clz_i32: ++ case INDEX_op_clz_i64: ++ case INDEX_op_ctz_i32: ++ case INDEX_op_ctz_i64: ++ return C_O1_I2(r, r, r); ++ ++ case INDEX_op_brcond_i32: ++ case INDEX_op_brcond_i64: ++ return C_O0_I2(r, r); ++ ++ case INDEX_op_add2_i32: ++ case INDEX_op_add2_i64: ++ case INDEX_op_sub2_i32: ++ case INDEX_op_sub2_i64: ++ return C_O2_I4(r, r, r, r, r, r); ++ ++ case INDEX_op_mulu2_i32: ++ case INDEX_op_mulu2_i64: ++ case INDEX_op_muls2_i32: ++ case INDEX_op_muls2_i64: ++ return C_O2_I2(r, r, r, r); ++ ++ case INDEX_op_movcond_i32: ++ case INDEX_op_movcond_i64: ++ return C_O1_I4(r, r, r, r, r); ++ ++ case INDEX_op_setcond2_i32: ++ return C_O1_I4(r, r, r, r, r); ++ case INDEX_op_brcond2_i32: ++ return C_O0_I4(r, r, r, r); ++ ++ case INDEX_op_qemu_ld_a32_i32: ++ case INDEX_op_qemu_ld_a64_i32: ++ return C_O1_I1(r, r); ++ ++ case INDEX_op_qemu_ld_a32_i64: ++ case INDEX_op_qemu_ld_a64_i64: ++ return C_O1_I1(r, r); ++ case INDEX_op_qemu_st_a32_i32: ++ case INDEX_op_qemu_st_a64_i32: ++ return C_O0_I2(r, r); ++ case INDEX_op_qemu_st_a32_i64: ++ case INDEX_op_qemu_st_a64_i64: ++ return C_O0_I2(r, r); ++ ++ case INDEX_op_muluh_i32: ++ case INDEX_op_mulsh_i32: ++ return C_O1_I2(r, r, r); ++ case INDEX_op_extract2_i32: ++ case INDEX_op_extract2_i64: ++ return C_O1_I2(r, r, r); ++ ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static const int tcg_target_reg_alloc_order[TCG_TARGET_NB_REGS] = { ++ TCG_REG_R0, ++ 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, ++}; ++ ++#define NUM_OF_IARG_REGS 5 ++static const int tcg_target_call_iarg_regs[NUM_OF_IARG_REGS] = { ++ TCG_REG_R8, ++ TCG_REG_R9, ++ TCG_REG_R10, ++ TCG_REG_R11, ++ TCG_REG_R12, ++}; ++ ++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 ++ ++#define REG_INDEX_IARG_BASE 8 ++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 BLOCK_PTR_IDX 16 ++ ++#define CTX_IDX 0 ++#define TMP32_LOCAL_ENV_IDX 1 ++#define TMP32_LOCAL_0_IDX 2 ++#define TMP64_0_IDX 3 ++#define TMP64_1_IDX 4 ++#define TMP64_2_IDX 5 ++#define TMP64_3_IDX 6 ++#define TMP64_4_IDX 7 ++ ++__thread bool env_cached = false; ++ ++// function index ++#define RETURN_CALL_IDX 0 ++#define FUNC_HELPER_CALL_IDX 1 ++#define FUNC_CALL_LD_HELPER_IDX 2 ++#define FUNC_CALL_ST_HELPER_IDX 3 ++ ++// table index ++#define HELPER_TABLE_IDX 0 ++ ++static inline void tcg_wasm_out8(TCGContext *s, uint32_t v) ++{ ++ tcg_sub_out8(s, v); ++} ++ ++static uintptr_t cur_wasm_ptr(TCGContext *s) ++{ ++ return (uintptr_t)cur_sub_buf_ptr(); ++} ++ ++static void tcg_wasm_out_leb128_sint32_t(TCGContext *s, int32_t v) { ++ bool more = true; ++ bool negative = (v < 0); ++ uint8_t b; ++ uint32_t low7 = 0x7f; ++ uint32_t uv = v; ++ while (more) { ++ b = uv & low7; ++ uv >>= 7; ++ if (negative) ++ uv |= (~0 << (32 - 7)); ++ if (((uv == 0) && ((b & 0x40) == 0)) || ((uv == -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; ++ bool negative = (v < 0); ++ uint8_t b; ++ uint64_t low7 = 0x7f; ++ uint64_t uv = v; ++ while (more) { ++ b = uv & low7; ++ uv >>= 7; ++ if (negative) ++ uv |= ((int64_t)(~0) << (64 - 7)); ++ if (((uv == 0) && ((b & 0x40) == 0)) || (((int64_t)uv == -1) && ((b & 0x40) != 0))) ++ more = false; ++ else ++ b |= 0x80; ++ tcg_wasm_out8(s, b); ++ } ++} ++ ++static void tcg_wasm_out_leb128_uint32_t(TCGContext *s, uint32_t v) { ++ uint32_t low7 = 0x7f; ++ uint8_t b; ++ do { ++ b = v & low7; ++ v >>= 7; ++ if (v != 0) ++ b |= 0x80; ++ tcg_wasm_out8(s, b); ++ } while (v != 0); ++} ++ ++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_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) ++{ ++ 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); ++} ++ ++static void tcg_wasm_out_op_end(TCGContext *s) ++{ ++ tcg_wasm_out8(s, 0x0b); ++} ++ ++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_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_xor(TCGContext *s){ tcg_wasm_out8(s, 0x73); } ++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_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_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_popcnt(TCGContext *s){ tcg_wasm_out8(s, 0x69); } ++static void tcg_wasm_out_op_i32_add(TCGContext *s){ tcg_wasm_out8(s, 0x6a); } ++//static void tcg_wasm_out_op_i32_sub(TCGContext *s){ tcg_wasm_out8(s, 0x6b); } ++//static void tcg_wasm_out_op_i32_mul(TCGContext *s){ tcg_wasm_out8(s, 0x6c); } ++//static void tcg_wasm_out_op_i32_div_s(TCGContext *s){ tcg_wasm_out8(s, 0x6d); } ++//static void tcg_wasm_out_op_i32_div_u(TCGContext *s){ tcg_wasm_out8(s, 0x6e); } ++//static void tcg_wasm_out_op_i32_rem_s(TCGContext *s){ tcg_wasm_out8(s, 0x6f); } ++//static void tcg_wasm_out_op_i32_rem_u(TCGContext *s){ tcg_wasm_out8(s, 0x70); } ++static void tcg_wasm_out_op_i32_ne(TCGContext *s){ tcg_wasm_out8(s, 0x47); } ++//static void tcg_wasm_out_op_i32_le_u(TCGContext *s){ tcg_wasm_out8(s, 0x4d); } ++ ++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_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_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_i64_rotl(TCGContext *s){ tcg_wasm_out8(s, 0x89); } ++static void tcg_wasm_out_op_i64_rotr(TCGContext *s){ tcg_wasm_out8(s, 0x8a); } ++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); } ++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_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_ne(TCGContext *s){ tcg_wasm_out8(s, 0x52); } ++static void tcg_wasm_out_op_i64_le_u(TCGContext *s){ tcg_wasm_out8(s, 0x58); } ++static void tcg_wasm_out_op_i64_lt_u(TCGContext *s){ tcg_wasm_out8(s, 0x54); } ++static void tcg_wasm_out_op_i64_gt_u(TCGContext *s){ tcg_wasm_out8(s, 0x56); } ++ ++static void tcg_wasm_out_op_i32_wrap_i64(TCGContext *s){ tcg_wasm_out8(s, 0xa7); } ++ ++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_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); ++} ++ ++static void tcg_wasm_out_op_local_tee(TCGContext *s, uint8_t i) ++{ ++ tcg_wasm_out_op_var(s, 0x22, 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) ++{ ++ if (i == tcg_target_reg_index[TCG_REG_R14]) { ++ env_cached = false; ++ } ++ tcg_wasm_out_op_var(s, 0x24, i); ++} ++ ++static void tcg_wasm_out_op_global_get_r_i32(TCGContext *s, TCGReg r0) ++{ ++ if (r0 == TCG_REG_R14) { ++ if (!env_cached) { ++ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_local_tee(s, TMP32_LOCAL_ENV_IDX); ++ env_cached = true; ++ } else { ++ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_ENV_IDX); ++ } ++ return; ++ } ++ 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_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]); ++} ++ ++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); ++ 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_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_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_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_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_u(TCGContext *s, uint32_t a, uint32_t o) ++{ ++ tcg_wasm_out_op_loadstore(s, 0x35, 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_return(TCGContext *s) ++{ ++ tcg_wasm_out8(s, 0x0f); ++} ++ ++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_i64_extend_i32_u(TCGContext *s) ++{ ++ tcg_wasm_out8(s, 0xad); ++} ++ ++static void tcg_wasm_out_op_i64_extend_i32_s(TCGContext *s) ++{ ++ tcg_wasm_out8(s, 0xac); ++} ++ ++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_not(TCGContext *s){ ++ tcg_wasm_out_op_i64_const(s, -1); ++ tcg_wasm_out_op_i64_xor(s); ++} ++ ++static void tcg_wasm_out_op_set_r_as_i64(TCGContext *s, TCGReg al, TCGReg ah) ++{ ++ tcg_wasm_out_op_local_set(s, TMP64_4_IDX); ++ ++ // set lower bits ++ tcg_wasm_out_op_local_get(s, TMP64_4_IDX); ++ tcg_wasm_out_op_i64_const(s, 0xffffffff); ++ tcg_wasm_out_op_i64_and(s); ++ tcg_wasm_out_op_global_set_r(s, al); ++ ++ // set higher bits ++ tcg_wasm_out_op_local_get(s, TMP64_4_IDX); ++ tcg_wasm_out_op_i64_const(s, 32); ++ tcg_wasm_out_op_i64_shr_u(s); ++ tcg_wasm_out_op_i64_const(s, 0xffffffff); ++ tcg_wasm_out_op_i64_and(s); ++ tcg_wasm_out_op_global_set_r(s, ah); ++} ++ ++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); ++} ++ ++#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); ++tcg_wasm_out_i64_calc(shl); ++tcg_wasm_out_i64_calc(shr_s); ++tcg_wasm_out_i64_calc(shr_u); ++tcg_wasm_out_i64_calc(rotl); ++tcg_wasm_out_i64_calc(rotr); ++tcg_wasm_out_i64_calc(add); ++tcg_wasm_out_i64_calc(sub); ++tcg_wasm_out_i64_calc(mul); ++tcg_wasm_out_i64_calc(div_s); ++tcg_wasm_out_i64_calc(div_u); ++tcg_wasm_out_i64_calc(rem_s); ++tcg_wasm_out_i64_calc(rem_u); ++ ++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 void tcg_wasm_out_shl(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, 31); ++ tcg_wasm_out_op_i64_and(s); ++ tcg_wasm_out_op_i64_shl(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_shl(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++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, 31); ++ 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, 31); ++ 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 void tcg_wasm_out_i32_rotl(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ 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); ++} ++ ++static void tcg_wasm_out_i32_rotr(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ 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); ++} ++ ++static void tcg_wasm_out_clz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ 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); ++} ++ ++static void tcg_wasm_out_clz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ tcg_wasm_out_op_global_get_r(s, arg1); ++ tcg_wasm_out_op_i64_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); ++} ++ ++static void tcg_wasm_out_ctz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ 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); ++} ++ ++static void tcg_wasm_out_ctz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ ++ tcg_wasm_out_op_global_get_r(s, arg1); ++ tcg_wasm_out_op_i64_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); ++} ++ ++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_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_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_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load32_u(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld8s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load8_s(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld8u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load8_u(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld16s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load16_s(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld16u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load16_u(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld32s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load32_s(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_ld32u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_i64_load32_u(s, 0, (uint32_t)offset); ++ tcg_wasm_out_op_global_set_r(s, val); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_global_get_r(s, val); ++ tcg_wasm_out_op_i64_store32(s, 0, (uint32_t)offset); ++ break; ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_global_get_r(s, val); ++ tcg_wasm_out_op_i64_store(s, 0, (uint32_t)offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_st8(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)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, (uint32_t)offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_st16(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)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, (uint32_t)offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_st32(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r_i32(s, base); ++ if ((int32_t)offset < 0) { ++ tcg_wasm_out_op_i32_const(s, (int32_t)offset); ++ tcg_wasm_out_op_i32_add(s); ++ offset = 0; ++ } ++ tcg_wasm_out_op_global_get_r(s, val); ++ tcg_wasm_out_op_i64_store32(s, 0, (uint32_t)offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static inline bool tcg_wasm_out_sti(TCGContext *s, TCGType type, TCGArg val, ++ TCGReg base, intptr_t offset) ++{ ++ return false; ++} ++ ++static bool tcg_wasm_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_wasm_out_op_global_get_r(s, arg); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_i64_extend_i32_u(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++ break; ++ case TCG_TYPE_I64: ++ tcg_wasm_out_op_global_get_r(s, arg); ++ tcg_wasm_out_op_global_set_r(s, ret); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ return true; ++} ++ ++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 void tcg_wasm_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ 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); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++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, TCGType type, TCGReg rd, TCGReg rs) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ case TCG_TYPE_I64: ++ 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); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++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_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_wasm_out_ext32s(s, rd, rs); ++} ++ ++static void tcg_wasm_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_wasm_out_ext32u(s, rd, rs); ++} ++ ++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_extrh_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_wasm_out_op_global_get_r(s, rs); ++ tcg_wasm_out_op_i64_const(s, 32); ++ tcg_wasm_out_op_i64_shr_u(s); ++ tcg_wasm_out_op_global_set_r(s, rd); ++} ++ ++static void tcg_wasm_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, ++ TCGReg arg1, TCGReg arg2) ++{ ++ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2); ++ tcg_wasm_out_op_i64_extend_i32_u(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++} ++ ++static void tcg_wasm_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, ++ TCGReg arg1, TCGReg arg2) ++{ ++ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2); ++ tcg_wasm_out_op_i64_extend_i32_u(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++} ++ ++static void tcg_wasm_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, ++ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) ++{ ++ tcg_wasm_out_op_cond_i32(s, cond, c1, c2); ++ 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 void tcg_wasm_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, ++ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) ++{ ++ tcg_wasm_out_op_cond_i64(s, cond, c1, c2); ++ 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 void tcg_wasm_out_add2(TCGContext *s, TCGReg retl, TCGReg reth, ++ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) ++{ ++ // add higer ++ tcg_wasm_out_op_global_get_r(s, ah); ++ tcg_wasm_out_op_global_get_r(s, bh); ++ tcg_wasm_out_op_i64_add(s); ++ ++ // add lower ++ tcg_wasm_out_op_global_get_r(s, al); ++ tcg_wasm_out_op_global_get_r(s, bl); ++ tcg_wasm_out_op_i64_add(s); ++ ++ // get carry ++ if ((al == retl) && (bl == retl)) { ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_global_get_r(s, al); ++ tcg_wasm_out_op_i64_lt_u(s); ++ tcg_wasm_out_op_i64_extend_i32_s(s); ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_global_set_r(s, retl); ++ } else { ++ tcg_wasm_out_op_global_set_r(s, retl); ++ tcg_wasm_out_op_global_get_r(s, retl); ++ if (al == retl) { ++ tcg_wasm_out_op_global_get_r(s, bl); ++ } else { ++ tcg_wasm_out_op_global_get_r(s, al); ++ } ++ tcg_wasm_out_op_i64_lt_u(s); ++ tcg_wasm_out_op_i64_extend_i32_s(s); ++ } ++ ++ // add carry to higher ++ tcg_wasm_out_op_i64_add(s); ++ tcg_wasm_out_op_global_set_r(s, reth); ++} ++ ++static void tcg_wasm_out_sub2(TCGContext *s, TCGReg retl, TCGReg reth, ++ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) ++{ ++ // sub higher ++ tcg_wasm_out_op_global_get_r(s, ah); ++ tcg_wasm_out_op_global_get_r(s, bh); ++ tcg_wasm_out_op_i64_sub(s); ++ ++ // sub lower ++ tcg_wasm_out_op_global_get_r(s, al); ++ tcg_wasm_out_op_global_get_r(s, bl); ++ tcg_wasm_out_op_i64_sub(s); ++ ++ // get underflow ++ if (al == retl) { ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); ++ ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_global_get_r(s, al); ++ tcg_wasm_out_op_i64_gt_u(s); ++ ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_global_set_r(s, retl); ++ } else { ++ tcg_wasm_out_op_global_set_r(s, retl); ++ ++ tcg_wasm_out_op_global_get_r(s, retl); ++ tcg_wasm_out_op_global_get_r(s, al); ++ tcg_wasm_out_op_i64_gt_u(s); ++ } ++ ++ tcg_wasm_out_op_i64_sub(s); ++ tcg_wasm_out_op_global_set_r(s, reth); ++} ++ ++static void tcg_wasm_out_mulu2_i32(TCGContext *s, TCGReg retl, TCGReg reth, 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_mul(s); ++ tcg_wasm_out_op_set_r_as_i64(s, retl, reth); ++} ++ ++static void tcg_wasm_out_muls2_i32(TCGContext *s, TCGReg retl, TCGReg reth, 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_mul(s); ++ tcg_wasm_out_op_set_r_as_i64(s, retl, reth); ++} ++ ++static void tcg_wasm_out_muluh_i32(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_mul(s); ++ tcg_wasm_out_op_i64_const(s, 32); ++ tcg_wasm_out_op_i64_shr_u(s); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++} ++ ++static void tcg_wasm_out_mulsh_i32(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_mul(s); ++ tcg_wasm_out_op_i64_const(s, 32); ++ tcg_wasm_out_op_i64_shr_u(s); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_global_set_r(s, ret); ++} ++ ++static void tcg_wasm_out_ctpop_i32(TCGContext *s, TCGReg dest, TCGReg src) ++{ ++ tcg_wasm_out_op_global_get_r(s, src); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_i32_popcnt(s); ++ tcg_wasm_out_op_global_set_r(s, dest); ++} ++ ++static void tcg_wasm_out_ctpop_i64(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_deposit_i32(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len) ++{ ++ int32_t mask = ((1< 0) { ++ tcg_wasm_out_op_i64_const(s, sl); ++ tcg_wasm_out_op_i64_shl(s); ++ } ++ tcg_wasm_out_op_i64_const(s, rs); ++ if (sign) { ++ tcg_wasm_out_op_i64_shr_s(s); ++ } else { ++ tcg_wasm_out_op_i64_shr_u(s); ++ } ++ tcg_wasm_out_op_global_set_r(s, dest); ++} ++ ++static void tcg_wasm_out_extract2_i32(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos) ++{ ++ tcg_wasm_out_op_global_get_r(s, arg2); ++ tcg_wasm_out_op_i64_const(s, 32-pos); ++ tcg_wasm_out_op_i64_shl(s); ++ ++ 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_i64_const(s, pos); ++ tcg_wasm_out_op_i64_shr_u(s); ++ ++ tcg_wasm_out_op_i64_or(s); ++ ++ tcg_wasm_out_op_i64_const(s, 0xffffffff); ++ tcg_wasm_out_op_i64_and(s); ++ tcg_wasm_out_op_global_set_r(s, dest); ++} ++ ++static void tcg_wasm_out_extract2_i64(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos) ++{ ++ tcg_wasm_out_op_global_get_r(s, arg2); ++ tcg_wasm_out_op_i64_const(s, 64-pos); ++ tcg_wasm_out_op_i64_shl(s); ++ ++ 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_or(s); ++ tcg_wasm_out_op_global_set_r(s, dest); ++} ++ ++static void tcg_wasm_out_bswap64(TCGContext *s, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_wasm_out_op_global_get_r(s, src); // ABCDEFGH ++ tcg_wasm_out_op_i64_const(s, 32); ++ tcg_wasm_out_op_i64_rotr(s); ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); // EFGHABCD ++ ++ tcg_wasm_out_op_local_get(s, TMP64_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); // ___E___A ++ ++ tcg_wasm_out_op_local_get(s, TMP64_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); // __F___B_ ++ ++ tcg_wasm_out_op_i64_or(s); ++ ++ tcg_wasm_out_op_local_get(s, TMP64_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); // _G___C__ ++ ++ tcg_wasm_out_op_local_get(s, TMP64_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); // H___D___ ++ ++ tcg_wasm_out_op_i64_or(s); ++ ++ tcg_wasm_out_op_i64_or(s); // HGFEDCBA ++ 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); // ABCD ++ 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); // CDAB ++ ++ 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); // _C_A ++ ++ 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); // D_B_ ++ ++ tcg_wasm_out_op_i32_or(s); // DCBA ++ 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); // __AB ++ 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); // B__A ++ ++ 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); // ___A ++ ++ 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); // SSB_ ++ } else { ++ tcg_wasm_out_op_i32_shr_u(s); // 00B_ ++ } ++ ++ tcg_wasm_out_op_i32_or(s); // **BA ++ tcg_wasm_out_op_i64_extend_i32_u(s); ++ tcg_wasm_out_op_global_set_r(s, dest); ++} ++ ++static void tcg_wasm_out_ctx_i32_store_const(TCGContext *s, int off, int32_t v) ++{ ++ 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, off); ++} ++ ++static void tcg_wasm_out_ctx_i32_store_r(TCGContext *s, int off, TCGReg r0) ++{ ++ tcg_wasm_out_op_local_get(s, CTX_IDX); ++ tcg_wasm_out_op_global_get_r(s, r0); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_i32_store(s, 0, off); ++} ++ ++static void tcg_wasm_out_ctx_i32_load(TCGContext *s, int off) ++{ ++ tcg_wasm_out_op_local_get(s, CTX_IDX); ++ tcg_wasm_out_op_i32_load(s, 0, off); ++} ++ ++static void tcg_wasm_out_label_idx(TCGContext *s, int label) ++{ ++ int block_idx = wasm_alloc_block_idx(s); ++ wasm_add_label_context(s, label, block_idx); ++ ++ tcg_wasm_out_op_end(s); // end if of the previous block ++ ++ // following 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); ++ env_cached = false; ++} ++ ++__thread int current_label[100]; ++__thread int current_label_pos; ++ ++static void tcg_out_label_cb(TCGContext *s, TCGLabel *l) ++{ ++ current_label[current_label_pos++] = l->id; ++ tcg_debug_assert(current_label_pos < 100); ++ tcg_wasm_out_label_idx(s, l->id + 1); ++} ++ ++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 ++ wasm_add_label_block_ptr_placeholder(l->id + 1, cur_wasm_ptr(s)); ++ 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); ++ bool found = false; ++ for (int i = 0; i < current_label_pos; i++) { ++ if (current_label[i] == l->id) { ++ found = true; ++ break; ++ } ++ } ++ if (found) { ++ tcg_wasm_out_op_br(s, toploop_depth); // br to the top of loop ++ } else { ++ tcg_wasm_out_op_br(s, toploop_depth - 1); // br to the end of the current block ++ } ++ 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_i32(TCGContext *s, TCGCond cond, TCGReg arg1, ++ TCGReg arg2, TCGLabel *l) ++{ ++ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2); ++ tcg_wasm_out_op_br_to_label(s, l, true); ++} ++ ++static void tcg_wasm_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, ++ TCGReg arg2, TCGLabel *l) ++{ ++ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2); ++ tcg_wasm_out_op_br_to_label(s, l, true); ++} ++ ++static void tcg_wasm_out_exit_tb(TCGContext *s, uintptr_t arg) ++{ ++ tcg_wasm_out_ctx_i32_store_const(s, TB_PTR_OFF, 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_OFF); ++ 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_OFF, arg); ++ tcg_wasm_out_ctx_i32_store_const(s, DO_INIT_OFF, 1); ++ 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) ++{ ++ 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, 0); ++ 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_OFF); ++ 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); ++ ++ // store jmp target address to buf ++ tcg_wasm_out_op_local_get(s, CTX_IDX); ++ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX); ++ tcg_wasm_out_op_i32_store(s, 0, TB_PTR_OFF); ++ tcg_wasm_out_ctx_i32_store_const(s, DO_INIT_OFF, 1); ++ ++ tcg_wasm_out_op_i32_const(s, 0); ++ tcg_wasm_out_op_return(s); ++ tcg_wasm_out_op_end(s); ++} ++ ++static void push_arg_i64(TCGContext *s, int *reg_idx, int *stack_offset) { ++ if (*reg_idx < NUM_OF_IARG_REGS) { ++ tcg_wasm_out_op_global_get_r(s, REG_INDEX_IARG_BASE + *reg_idx); // arg register ++ int addend = 1; ++ *reg_idx = *reg_idx + addend; ++ } else { ++ 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); ++ int addend = 8; ++ *stack_offset = *stack_offset + addend; ++ } ++} ++ ++void gen_func_wrapper_code(TCGContext *s, const tcg_insn_unit *func, const TCGHelperInfo *info, int func_idx) ++{ ++ int nargs; ++ unsigned typemask = info->typemask; ++ int rettype = typemask & 7; ++ ++ if (rettype == dh_typecode_i128) { ++ // receive 128bit return value via the stack buffer ++ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ } ++ ++ nargs = 32 - clz32(typemask >> 3); ++ nargs = DIV_ROUND_UP(nargs, 3); ++ int stack_offset = 0; ++ int reg_idx = 0; ++ int stack128_base = 0; ++ bool cached_128base = false; ++ for (int j = 0; j < nargs; ++j) { ++ int typecode = extract32(typemask, (j + 1) * 3, 3); ++ if (typecode == dh_typecode_void) { ++ continue; ++ } ++ switch (typecode) { ++ case dh_typecode_i32: ++ case dh_typecode_s32: ++ case dh_typecode_ptr: ++ push_arg_i64(s, ®_idx, &stack_offset); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ break; ++ case dh_typecode_i64: ++ case dh_typecode_s64: ++ push_arg_i64(s, ®_idx, &stack_offset); ++ break; ++ case dh_typecode_i128: ++ // copy data to 128stack ++ if (!cached_128base) { ++ tcg_wasm_out_ctx_i32_load(s, STACK128_OFF); ++ tcg_wasm_out_op_i64_extend_i32_s(s); ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); ++ cached_128base = true; ++ } ++ ++ // push current 128stack pointer ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_i32_const(s, stack128_base); ++ tcg_wasm_out_op_i32_add(s); ++ ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ push_arg_i64(s, ®_idx, &stack_offset); ++ tcg_wasm_out_op_i64_store(s, 0, stack128_base); ++ stack128_base += 8; ++ ++ tcg_wasm_out_op_local_get(s, TMP64_0_IDX); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ push_arg_i64(s, ®_idx, &stack_offset); ++ tcg_wasm_out_op_i64_store(s, 0, stack128_base); ++ stack128_base += 8; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ tcg_wasm_out_op_call(s, func_idx); ++ ++ stack_offset = 0; ++ 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_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); ++ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0); ++ stack_offset += 8; ++ ++ 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); ++ tcg_wasm_out_op_global_set_r(s, TCG_REG_R1); ++ stack_offset += 8; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ return; ++} ++ ++static void gen_func_type(TCGContext *s, const TCGHelperInfo *info) ++{ ++ int nargs; ++ unsigned typemask = info->typemask; ++ int rettype = typemask & 7; ++ uint8_t * buf_start = wasm_get_helper_types_begin(s); ++ uint8_t * buf_ptr = buf_start; ++ nargs = 32 - clz32(typemask >> 3); ++ nargs = DIV_ROUND_UP(nargs, 3); ++ ++ *buf_ptr++ = 0x60; ++ uint8_t * buf_ptr_vec_size = buf_ptr; ++ *buf_ptr += 0x80; ++ *buf_ptr += 0x80; ++ *buf_ptr += 0x80; ++ *buf_ptr += 0x80; ++ *buf_ptr += 0x00; ++ *buf_ptr++ = 0; // vector size (placeholder) ++ ++ int vec_size = 0; ++ ++ if (rettype == dh_typecode_i128) { ++ *buf_ptr++ = 0x7f; // i32 (stack buffer pointer) ++ vec_size++; ++ } ++ ++ for (int j = 0; j < nargs; ++j) { ++ int typecode = extract32(typemask, (j + 1) * 3, 3); ++ if (typecode == dh_typecode_void) { ++ continue; ++ } ++ switch (typecode) { ++ case dh_typecode_i32: ++ case dh_typecode_s32: ++ case dh_typecode_ptr: ++ *buf_ptr++ = 0x7f; ++ vec_size++; ++ break; ++ case dh_typecode_i64: ++ case dh_typecode_s64: ++ *buf_ptr++ = 0x7e; ++ vec_size++; ++ break; ++ case dh_typecode_i128: ++ *buf_ptr++ = 0x7f; ++ vec_size++; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ fill_uint32_leb128((uintptr_t)buf_ptr_vec_size, vec_size); // fill size ++ ++ if ((rettype == dh_typecode_void) || (rettype == dh_typecode_i128)) { ++ *buf_ptr++ = 0x0; // no return value ++ } else { ++ *buf_ptr++ = 0x1; ++ switch (rettype) { ++ case dh_typecode_i32: ++ case dh_typecode_s32: ++ case dh_typecode_ptr: ++ *buf_ptr++ = 0x7f; ++ break; ++ case dh_typecode_i64: ++ case dh_typecode_s64: ++ *buf_ptr++ = 0x7e; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ int sz = (uint32_t)(buf_ptr - buf_start); ++ wasm_add_helper_types_pos(s, sz); ++ return; ++} ++ ++static void gen_func_type_qemu_ld(TCGContext *s, uint32_t oi) ++{ ++ uint8_t * buf_start = wasm_get_helper_types_begin(s); ++ uint8_t * buf_ptr = buf_start; ++ *buf_ptr++ = 0x60; ++ *buf_ptr++ = 0x4; ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x7e; ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x1; ++ MemOp mop = get_memop(oi); ++ switch (mop & MO_SSIZE) { ++ case MO_UQ: ++ *buf_ptr++ = 0x7e; ++ break; ++ default: ++ *buf_ptr++ = 0x7e; ++ break; ++ } ++ int sz = (uint32_t)(buf_ptr - buf_start); ++ wasm_add_helper_types_pos(s, sz); ++} ++ ++static void gen_func_type_qemu_st(TCGContext *s, uint32_t oi) ++{ ++ uint8_t * buf_start = wasm_get_helper_types_begin(s); ++ uint8_t * buf_ptr = buf_start; ++ *buf_ptr++ = 0x60; ++ *buf_ptr++ = 0x5; ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x7e; ++ MemOp mop = get_memop(oi); ++ switch (mop & MO_SSIZE) { ++ case MO_UQ: ++ *buf_ptr++ = 0x7e; ++ break; ++ default: ++ *buf_ptr++ = 0x7f; ++ break; ++ } ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x7f; ++ *buf_ptr++ = 0x0; ++ int sz = (uint32_t)(buf_ptr - buf_start); ++ wasm_add_helper_types_pos(s, sz); ++} ++ ++static void tcg_wasm_out_call(TCGContext *s, const tcg_insn_unit *func, ++ const TCGHelperInfo *info) ++{ ++ // set return position ++ tcg_wasm_out_ctx_i32_load(s, HELPER_RET_TB_PTR_OFF); ++ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr); ++ ++ tcg_wasm_out_op_i32_store(s, 0, 0); ++ ++ int func_idx = get_wasm_helper_idx(s, (int)func); ++ if (func_idx < 0) { ++ func_idx = wasm_register_helper_alloc_num(s); ++ tcg_debug_assert(func >= 0); ++ wasm_register_helper(s, func_idx, (int)func); ++ gen_func_type(s, info); ++ } ++ ++ int target_block_idx = wasm_block_current_idx(s) + 1; ++ tcg_wasm_out_op_i64_const(s, target_block_idx); ++ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX); ++ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this) ++ env_cached = false; ++ ++ int block_idx = wasm_alloc_block_idx(s); ++ 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); ++ tcg_wasm_out_ctx_i32_store_const(s, UNWINDING_OFF, 0); ++ gen_func_wrapper_code(s, func, info, func_idx); ++ ++ tcg_wasm_out_op_i32_const(s, 1); ++ tcg_wasm_out_ctx_i32_load(s, UNWINDING_OFF); ++ tcg_wasm_out_op_i32_eq(s); ++ tcg_wasm_out_op_if_noret(s); ++ tcg_wasm_out_op_i32_const(s, 0); ++ tcg_wasm_out_op_return(s); ++ tcg_wasm_out_op_end(s); ++} ++ ++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 uint8_t tcg_wasm_out_tlb_load(TCGContext *s, TCGReg addr, MemOpIdx oi, bool is_ld) ++{ ++ MemOp opc = get_memop(oi); ++ TCGAtomAlign aa; ++ unsigned a_mask; ++ ++ aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); ++ a_mask = (1u << aa.align) - 1; ++ ++ unsigned s_bits = opc & MO_SIZE; ++ unsigned s_mask = (1u << s_bits) - 1; ++ tcg_target_long compare_mask; ++ 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_wasm_out_op_global_get_r(s, addr); ++ 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_global_get_r_i32(s, TCG_AREG0); ++ int off = mask_ofs; ++ if ((int64_t)off < 0) { ++ tcg_wasm_out_op_i32_const(s, (int64_t)off); ++ tcg_wasm_out_op_i32_add(s); ++ off = 0; ++ } ++ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off); ++ tcg_wasm_out_op_local_set(s, TMP64_2_IDX); ++ ++ tcg_wasm_out_op_local_get(s, TMP64_2_IDX); ++ ++ tcg_wasm_out_op_i64_and(s); ++ ++ tcg_wasm_out_op_global_get_r_i32(s, TCG_AREG0); ++ off = table_ofs; ++ if ((int64_t)off < 0) { ++ tcg_wasm_out_op_i32_const(s, (int64_t)off); ++ tcg_wasm_out_op_i32_add(s); ++ off = 0; ++ } ++ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off); ++ tcg_wasm_out_op_i64_add(s); ++ ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ tcg_wasm_out_op_local_tee(s, TMP32_LOCAL_0_IDX); ++ ++ off = is_ld ? offsetof(CPUTLBEntry, addr_read) ++ : offsetof(CPUTLBEntry, addr_write); ++ if ((int64_t)off < 0) { ++ tcg_wasm_out_op_i32_const(s, (int64_t)off); ++ tcg_wasm_out_op_i32_add(s); ++ off = 0; ++ } ++ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off); ++ ++ tcg_wasm_out_op_global_get_r(s, addr); ++ 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); ++ ++ tcg_wasm_out_op_i64_eq(s); ++ ++ tcg_wasm_out_op_i64_const(s, 0); ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); ++ ++ tcg_wasm_out_op_if_noret(s); ++ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX); ++ off = add_off; ++ if ((int64_t)off < 0) { ++ tcg_wasm_out_op_i32_const(s, (int64_t)off); ++ tcg_wasm_out_op_i32_add(s); ++ off = 0; ++ } ++ tcg_wasm_out_op_i32_load(s, 0, (uint64_t)off); ++ tcg_wasm_out_op_i64_extend_i32_u(s); ++ tcg_wasm_out_op_global_get_r(s, addr); ++ tcg_wasm_out_op_i64_add(s); ++ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); ++ ++ tcg_wasm_out_op_end(s); ++ ++ ++ return TMP64_0_IDX; ++} ++ ++static void tcg_wasm_out_qemu_ld_direct(TCGContext *s, TCGReg r, uint8_t base, MemOp opc) ++{ ++ /* Byte swapping is left to middle-end expansion. */ ++ tcg_debug_assert((opc & MO_BSWAP) == 0); ++ ++ switch (opc & (MO_SSIZE)) { ++ case MO_UB: ++ tcg_wasm_out_op_local_get(s, base); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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* 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, const TCGArg *args, bool is_64) ++{ ++ TCGReg addr_reg; ++ TCGReg data_reg; ++ MemOpIdx oi; ++ MemOp opc; ++ ++ data_reg = *args++; ++ addr_reg = *args++; ++ oi = *args++; ++ opc = get_memop(oi); ++ ++ uint8_t base = tcg_wasm_out_tlb_load(s, addr_reg, oi, true); ++ ++ tcg_wasm_out_op_local_get(s, base); ++ tcg_wasm_out_op_i64_eqz(s); ++ tcg_wasm_out_op_if_noret(s); ++ ++ // path for miss case ++ int helper_func_idx = (uint32_t)qemu_ld_helper_ptr(oi); ++ int func_idx = get_wasm_helper_idx(s, helper_func_idx); ++ if (func_idx < 0) { ++ func_idx = wasm_register_helper_alloc_num(s); ++ tcg_debug_assert(helper_func_idx >= 0); ++ wasm_register_helper(s, func_idx, helper_func_idx); ++ gen_func_type_qemu_ld(s, oi); ++ } ++ ++ // save function pointer ++ tcg_wasm_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0); ++ ++ tcg_wasm_out_op_else(s); ++ ++ // fast path ++ tcg_wasm_out_qemu_ld_direct(s, data_reg, base, opc); ++ ++ tcg_wasm_out_op_end(s); ++ ++ int target_block_idx = wasm_block_current_idx(s) + 1; ++ tcg_wasm_out_op_i64_const(s, target_block_idx); ++ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX); ++ ++ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this) ++ env_cached = false; ++ ++ // block for calling helper (+1) ++ int block_idx = wasm_alloc_block_idx(s); ++ 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); ++ ++ tcg_wasm_out_op_local_get(s, base); ++ 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); ++ 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); ++ tcg_wasm_out_ctx_i32_load(s, DONE_FLAG_OFF); ++ tcg_wasm_out_op_i32_eqz(s); ++ ++ tcg_wasm_out_op_if_noret(s); ++ tcg_wasm_out_op_i32_const(s, 0); ++ tcg_wasm_out_op_return(s); ++ tcg_wasm_out_op_end(s); ++ tcg_wasm_out_op_end(s); ++} ++ ++static void tcg_wasm_out_qemu_st_direct(TCGContext *s, TCGReg lo, uint8_t base, MemOp opc) ++{ ++ /* Byte swapping is left to middle-end expansion. */ ++ tcg_debug_assert((opc & MO_BSWAP) == 0); ++ ++ switch (opc & (MO_SSIZE)) { ++ case MO_8: ++ tcg_wasm_out_op_local_get(s, base); ++ tcg_wasm_out_op_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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_i32_wrap_i64(s); ++ 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* 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; ++ case MO_128: ++ return helper_st16_mmu; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_wasm_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) ++{ ++ TCGReg addr_reg; ++ TCGReg data_reg; ++ MemOpIdx oi; ++ MemOp opc; ++ ++ data_reg = *args++; ++ addr_reg = *args++; ++ oi = *args++; ++ opc = get_memop(oi); ++ ++ uint8_t base = tcg_wasm_out_tlb_load(s, addr_reg, oi, false); ++ ++ tcg_wasm_out_op_local_get(s, base); ++ tcg_wasm_out_op_i64_eqz(s); ++ tcg_wasm_out_op_if_noret(s); ++ ++ // path for miss case ++ int helper_func_idx = (uint32_t)qemu_st_helper_ptr(oi); ++ int func_idx = get_wasm_helper_idx(s, helper_func_idx); ++ if (func_idx < 0) { ++ func_idx = wasm_register_helper_alloc_num(s); ++ tcg_debug_assert(helper_func_idx >= 0); ++ wasm_register_helper(s, func_idx, helper_func_idx); ++ gen_func_type_qemu_st(s, oi); ++ } ++ ++ // save function pointer ++ tcg_wasm_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0); ++ ++ tcg_wasm_out_op_else(s); ++ ++ // fast path ++ tcg_wasm_out_qemu_st_direct(s, data_reg, base, opc); ++ ++ tcg_wasm_out_op_end(s); ++ ++ int target_block_idx = wasm_block_current_idx(s) + 1; ++ tcg_wasm_out_op_i64_const(s, target_block_idx); ++ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX); ++ ++ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this) ++ env_cached = false; ++ ++ // block for calling helper (+1) ++ int block_idx = wasm_alloc_block_idx(s); ++ 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); ++ ++ tcg_wasm_out_op_local_get(s, base); ++ 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); ++ tcg_wasm_out_op_global_get_r(s, addr_reg); ++ MemOp mop = get_memop(oi); ++ 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); ++ ++ tcg_wasm_out_ctx_i32_load(s, DONE_FLAG_OFF); ++ tcg_wasm_out_op_i32_eqz(s); ++ tcg_wasm_out_op_if_noret(s); ++ tcg_wasm_out_op_i32_const(s, 0); ++ tcg_wasm_out_op_return(s); ++ tcg_wasm_out_op_end(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) ++{ ++ int32_t *code_ptr = (int32_t*)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)); ++ 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 inline void tcg_tci_out32(TCGContext *s, uint32_t v) ++{ ++ tcg_out32(s, v); ++} ++ ++static uintptr_t cur_tci_ptr(TCGContext *s) ++{ ++ return (uintptr_t)s->code_ptr; ++} ++ ++static void tcg_tci_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0) ++{ ++ uint32_t insn = 0; ++ ++ tcg_out_reloc(s, (void*)cur_tci_ptr(s), 20, l0, 0); ++ insn = deposit32(insn, 0, 8, op); ++ tcg_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_p(TCGContext *s, TCGOpcode op, void *p0) ++{ ++ uint32_t insn = 0; ++ intptr_t diff; ++ ++ /* Special case for exit_tb: map null -> 0. */ ++ if (p0 == NULL) { ++ diff = 0; ++ } else { ++ diff = p0 - (void *)((uint8_t*)cur_tci_ptr(s) + 4); ++ 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_r(TCGContext *s, TCGOpcode op, TCGReg r0) ++{ ++ uint32_t insn = 0; ++ ++ insn = deposit32(insn, 0, 8, op); ++ insn = deposit32(insn, 8, 4, r0); ++ tcg_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_v(TCGContext *s, TCGOpcode op) ++{ ++ tcg_tci_out32(s, (uint8_t)op); ++} ++ ++static void tcg_tci_out_op_ri(TCGContext *s, TCGOpcode op, TCGReg r0, int32_t i1) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rl(TCGContext *s, TCGOpcode op, TCGReg r0, TCGLabel *l1) ++{ ++ uint32_t insn = 0; ++ ++ tcg_out_reloc(s, (void*)cur_tci_ptr(s), 20, l1, 0); ++ insn = deposit32(insn, 0, 8, op); ++ insn = deposit32(insn, 8, 4, r0); ++ tcg_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1) ++{ ++ uint32_t insn = 0; ++ ++ insn = deposit32(insn, 0, 8, op); ++ insn = deposit32(insn, 8, 4, r0); ++ insn = deposit32(insn, 12, 4, r1); ++ tcg_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrm(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGArg m2) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrr(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGReg r2) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrs(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, intptr_t i2) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrbb(TCGContext *s, TCGOpcode op, TCGReg r0, ++ TCGReg r1, uint8_t b2, uint8_t b3) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrc(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGReg r2, TCGCond c3) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0, ++ TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrrr(TCGContext *s, TCGOpcode op, TCGReg r0, ++ TCGReg r1, TCGReg r2, TCGReg r3, TCGReg r4) ++{ ++ uint32_t 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); ++ tcg_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrr(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrrrc(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGReg r2, ++ TCGReg r3, TCGReg r4, TCGCond c5) ++{ ++ uint32_t 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_tci_out32(s, insn); ++} ++ ++static void tcg_tci_out_op_rrrrrr(TCGContext *s, TCGOpcode op, ++ TCGReg r0, TCGReg r1, TCGReg r2, ++ TCGReg r3, TCGReg r4, TCGReg r5) ++{ ++ uint32_t 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, r5); ++ tcg_tci_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_tci_out_op_ri(s, INDEX_op_tci_movi, ret, arg); ++ } else { ++ uint32_t insn = 0; ++ ++ new_pool_label(s, arg, 20, (void*)cur_tci_ptr(s), 0); ++ insn = deposit32(insn, 0, 8, INDEX_op_tci_movl); ++ insn = deposit32(insn, 8, 4, ret); ++ tcg_tci_out32(s, insn); ++ } ++} ++ ++static void tcg_tci_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_tci_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset); ++ tcg_tci_out_op_rrr(s, (TCG_TARGET_REG_BITS == 32 ++ ? INDEX_op_add_i32 : INDEX_op_add_i64), ++ TCG_REG_TMP, TCG_REG_TMP, base); ++ base = TCG_REG_TMP; ++ offset = 0; ++ } ++ tcg_tci_out_op_rrs(s, op, val, base, offset); ++} ++ ++static bool tcg_tci_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_tci_out_op_rr(s, INDEX_op_mov_i32, ret, arg); ++ break; ++#if TCG_TARGET_REG_BITS == 64 ++ case TCG_TYPE_I64: ++ tcg_tci_out_op_rr(s, INDEX_op_mov_i64, ret, arg); ++ break; ++#endif ++ default: ++ g_assert_not_reached(); ++ } ++ return true; ++} ++ ++static void tcg_tci_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); ++ tcg_tci_out_op_rr(s, INDEX_op_ext8s_i32, rd, rs); ++ break; ++#if TCG_TARGET_REG_BITS == 64 ++ case TCG_TYPE_I64: ++ tcg_debug_assert(TCG_TARGET_HAS_ext8s_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext8s_i64, rd, rs); ++ break; ++#endif ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_tci_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ if (TCG_TARGET_REG_BITS == 64) { ++ tcg_debug_assert(TCG_TARGET_HAS_ext8u_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext8u_i64, rd, rs); ++ } else { ++ tcg_debug_assert(TCG_TARGET_HAS_ext8u_i32); ++ tcg_tci_out_op_rr(s, INDEX_op_ext8u_i32, rd, rs); ++ } ++} ++ ++static void tcg_tci_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); ++ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i32, rd, rs); ++ break; ++#if TCG_TARGET_REG_BITS == 64 ++ case TCG_TYPE_I64: ++ tcg_debug_assert(TCG_TARGET_HAS_ext16s_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i64, rd, rs); ++ break; ++#endif ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void tcg_tci_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ if (TCG_TARGET_REG_BITS == 64) { ++ tcg_debug_assert(TCG_TARGET_HAS_ext16u_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext16u_i64, rd, rs); ++ } else { ++ tcg_debug_assert(TCG_TARGET_HAS_ext16u_i32); ++ tcg_tci_out_op_rr(s, INDEX_op_ext16u_i32, rd, rs); ++ } ++} ++ ++static void tcg_tci_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_debug_assert(TCG_TARGET_REG_BITS == 64); ++ tcg_debug_assert(TCG_TARGET_HAS_ext32s_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext32s_i64, rd, rs); ++} ++ ++static void tcg_tci_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_debug_assert(TCG_TARGET_REG_BITS == 64); ++ tcg_debug_assert(TCG_TARGET_HAS_ext32u_i64); ++ tcg_tci_out_op_rr(s, INDEX_op_ext32u_i64, rd, rs); ++} ++ ++static void tcg_tci_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_tci_out_ext32s(s, rd, rs); ++} ++ ++static void tcg_tci_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_tci_out_ext32u(s, rd, rs); ++} ++ ++static void tcg_tci_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); ++} ++ ++static void tcg_tci_out_call(TCGContext *s, const tcg_insn_unit *func, ++ const TCGHelperInfo *info) ++{ ++ ffi_cif *cif = info->cif; ++ uint32_t 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, (void*)cur_tci_ptr(s), 0, (uintptr_t)func, (uintptr_t)cif); ++ insn = deposit32(insn, 0, 8, INDEX_op_call); ++ insn = deposit32(insn, 8, 4, which); ++ tcg_tci_out32(s, insn); ++} ++ ++#if TCG_TARGET_REG_BITS == 64 ++# define CASE_32_64(x) \ ++ case glue(glue(INDEX_op_, x), _i64): \ ++ case glue(glue(INDEX_op_, x), _i32): ++# define CASE_64(x) \ ++ case glue(glue(INDEX_op_, x), _i64): ++#else ++# define CASE_32_64(x) \ ++ case glue(glue(INDEX_op_, x), _i32): ++# define CASE_64(x) ++#endif ++ ++static void tcg_tci_out_exit_tb(TCGContext *s, uintptr_t arg) ++{ ++ tcg_tci_out_op_p(s, INDEX_op_exit_tb, (void *)arg); ++} ++ ++static void tcg_tci_out_goto_tb(TCGContext *s, int which) ++{ ++ /* indirect jump method. */ ++ tcg_tci_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_nop_fill(tcg_insn_unit *p, int count) ++{ ++ int32_t *p2 = (int32_t*)p; ++ memset(p2, 0, sizeof(*p2) * count); ++} ++ ++static void tcg_out_goto_ptr(TCGContext *s, TCGOpcode opc, TCGReg arg) ++{ ++ tcg_tci_out_op_r(s, opc, arg); ++ tcg_wasm_out_goto_ptr(s, arg); ++} ++static void tcg_out_br(TCGContext *s, TCGOpcode opc, TCGLabel *l) ++{ ++ tcg_tci_out_op_l(s, opc, l); ++ tcg_wasm_out_br(s, l); ++} ++static void tcg_out_setcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret, ++ TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrrc(s, opc, ret, arg1, arg2, cond); ++ tcg_wasm_out_setcond_i32(s, cond, ret, arg1, arg2); ++} ++static void tcg_out_setcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret, ++ TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrrc(s, opc, ret, arg1, arg2, cond); ++ tcg_wasm_out_setcond_i64(s, cond, ret, arg1, arg2); ++} ++static void tcg_out_movcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret, ++ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) ++{ ++ tcg_tci_out_op_rrrrrc(s, opc, ret, c1, c2, v1, v2, cond); ++ tcg_wasm_out_movcond_i32(s, cond, ret, c1, c2, v1, v2); ++} ++static void tcg_out_movcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret, ++ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) ++{ ++ tcg_tci_out_op_rrrrrc(s, opc, ret, c1, c2, v1, v2, cond); ++ tcg_wasm_out_movcond_i64(s, cond, ret, c1, c2, v1, v2); ++} ++static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_tci_out_ldst(s, INDEX_op_ld_i32, val, base, offset); ++ break; ++ case TCG_TYPE_I64: ++ tcg_tci_out_ldst(s, INDEX_op_ld_i64, val, base, offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ tcg_wasm_out_ld(s, type, val, base, offset); ++} ++static void tcg_out_ld8s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld8s(s, type, val, base, offset); ++} ++static void tcg_out_ld8u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld8u(s, type, val, base, offset); ++} ++static void tcg_out_ld16s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld16s(s, type, val, base, offset); ++} ++static void tcg_out_ld16u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld16u(s, type, val, base, offset); ++} ++static void tcg_out_ld32s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld32s(s, type, val, base, offset); ++} ++static void tcg_out_ld32u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_ld32u(s, type, val, base, offset); ++} ++static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ switch (type) { ++ case TCG_TYPE_I32: ++ tcg_tci_out_ldst(s, INDEX_op_st_i32, val, base, offset); ++ break; ++ case TCG_TYPE_I64: ++ tcg_tci_out_ldst(s, INDEX_op_st_i64, val, base, offset); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ tcg_wasm_out_st(s, type, val, base, offset); ++} ++static void tcg_out_st8(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_st8(s, type, val, base, offset); ++} ++static void tcg_out_st16(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_st16(s, type, val, base, offset); ++} ++static void tcg_out_st32(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base, ++ intptr_t offset) ++{ ++ tcg_tci_out_ldst(s, opc, val, base, offset); ++ tcg_wasm_out_st32(s, type, val, base, offset); ++} ++static void tcg_out_i64_calc_add(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_add(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_sub(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_sub(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_mul(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_mul(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_and(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_and(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_or(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_or(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_xor(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_xor(s, ret, arg1, arg2); ++} ++static void tcg_out_shl(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_shl(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_shl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_shl(s, ret, arg1, arg2); ++} ++static void tcg_out_shr_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_shr_u(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_shr_u(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_shr_u(s, ret, arg1, arg2); ++} ++static void tcg_out_shr_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_shr_s(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_shr_s(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_shr_s(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_rotl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_rotl(s, ret, arg1, arg2); ++} ++static void tcg_out_i32_rotl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i32_rotl(s, ret, arg1, arg2); ++} ++static void tcg_out_i32_rotr(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i32_rotr(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_rotr(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_i64_calc_rotr(s, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_div_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_div_s(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_div_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_div_u(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_rem_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_rem_s(s, type, ret, arg1, arg2); ++} ++static void tcg_out_i64_calc_rem_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_rem_u(s, type, ret, arg1, arg2); ++} ++static void tcg_out_andc(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_andc(s, ret, arg1, arg2); ++} ++static void tcg_out_orc(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_orc(s, ret, arg1, arg2); ++} ++static void tcg_out_eqv(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_eqv(s, ret, arg1, arg2); ++} ++static void tcg_out_nand(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_nand(s, ret, arg1, arg2); ++} ++static void tcg_out_nor(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_nor(s, ret, arg1, arg2); ++} ++static void tcg_out_clz32(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_clz32(s, ret, arg1, arg2); ++} ++static void tcg_out_clz64(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_clz64(s, ret, arg1, arg2); ++} ++static void tcg_out_ctz32(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_ctz32(s, ret, arg1, arg2); ++} ++static void tcg_out_ctz64(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2); ++ tcg_wasm_out_ctz64(s, ret, arg1, arg2); ++} ++static void tcg_out_brcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg arg1, ++ TCGReg arg2, TCGLabel *l) ++{ ++ tcg_tci_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), TCG_REG_TMP, arg1, arg2, cond); ++ tcg_tci_out_op_rl(s, opc, TCG_REG_TMP, l); ++ tcg_wasm_out_brcond_i32(s, cond, arg1, arg2, l); ++ ++} ++static void tcg_out_brcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg arg1, ++ TCGReg arg2, TCGLabel *l) ++{ ++ tcg_tci_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), TCG_REG_TMP, arg1, arg2, cond); ++ tcg_tci_out_op_rl(s, opc, TCG_REG_TMP, l); ++ tcg_wasm_out_brcond_i64(s, cond, arg1, arg2, l); ++ ++} ++static void tcg_out_neg(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_op_rr(s, opc, ret, arg); ++ tcg_wasm_out_neg(s, ret, arg); ++} ++static void tcg_out_not(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_op_rr(s, opc, ret, arg); ++ tcg_wasm_out_not(s, ret, arg); ++} ++static void tcg_out_ctpop_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ tcg_wasm_out_ctpop_i32(s, dest, src); ++} ++static void tcg_out_ctpop_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ tcg_wasm_out_ctpop_i64(s, dest, src); ++} ++static void tcg_out_add2(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, ++ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) ++{ ++ tcg_tci_out_op_rrrrrr(s, opc, retl, reth, al, ah, bl, bh); ++ tcg_wasm_out_add2(s, retl, reth, al, ah, bl, bh); ++} ++static void tcg_out_sub2(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, ++ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) ++{ ++ tcg_tci_out_op_rrrrrr(s, opc, retl, reth, al, ah, bl, bh); ++ tcg_wasm_out_sub2(s, retl, reth, al, ah, bl, bh); ++} ++static void tcg_out_mulu2_i32(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrrr(s, opc, retl, reth, arg1, arg2); ++ tcg_wasm_out_mulu2_i32(s, retl, reth, arg1, arg2); ++} ++static void tcg_out_muls2_i32(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2) ++{ ++ tcg_tci_out_op_rrrr(s, opc, retl, reth, arg1, arg2); ++ tcg_wasm_out_muls2_i32(s, retl, reth, arg1, arg2); ++} ++ ++static void tcg_out_bswap16_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i32, dest, dest); ++ } ++ tcg_wasm_out_bswap16(s, dest, src, flags); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_wasm_out_ext16s(s, TCG_TYPE_I32, dest, dest); ++ } ++} ++static void tcg_out_bswap16_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i64, dest, dest); ++ } ++ tcg_wasm_out_bswap16(s, dest, src, flags); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_wasm_out_ext16s(s, TCG_TYPE_I64, dest, dest); ++ } ++} ++ ++static void tcg_out_bswap32_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ tcg_wasm_out_bswap32(s, dest, src, flags); ++} ++static void tcg_out_bswap32_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_tci_out_op_rr(s, INDEX_op_ext32s_i64, dest, dest); ++ } ++ tcg_wasm_out_bswap32(s, dest, src, flags); ++ if (flags & TCG_BSWAP_OS) { ++ tcg_wasm_out_ext32s(s, dest, dest); ++ } ++} ++static void tcg_out_bswap64_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags) ++{ ++ tcg_tci_out_op_rr(s, opc, dest, src); ++ tcg_wasm_out_bswap64(s, dest, src, flags); ++} ++static uint8_t tcg_tci_out_qemu_ldst(TCGContext *s, TCGOpcode opc, const TCGArg *args) ++{ ++ TCGReg addr_reg = args[1]; ++ MemOpIdx oi = args[2]; ++ ++ MemOp mopc = get_memop(oi); ++ TCGAtomAlign aa = atom_and_align_for_opc(s, mopc, MO_ATOM_IFALIGN, false); ++ unsigned a_mask = (1u << aa.align) - 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); ++ ++ new_pool_l8(s, 20, (void*)cur_tci_ptr(s), 0, ++ (TCGReg)args[0], (TCGReg)args[1], (TCGArg)args[2], (int32_t)a_mask, (int32_t)mask_ofs, (uint64_t)s->page_bits, s->page_mask, table_ofs); ++ ++ uint32_t insn = 0; ++ insn = deposit32(insn, 0, 8, opc); ++ tcg_tci_out32(s, insn); ++} ++static void tcg_out_qemu_ld(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64) ++{ ++ tcg_tci_out_qemu_ldst(s, opc, args); ++ tcg_wasm_out_qemu_ld(s, args, is_64); ++} ++static void tcg_out_qemu_st(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64) ++{ ++ tcg_tci_out_qemu_ldst(s, opc, args); ++ tcg_wasm_out_qemu_st(s, args, is_64); ++} ++static void tcg_out_deposit_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len) ++{ ++ TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64; ++ tcg_tci_out_op_rrrbb(s, opc, dest, arg1, arg2, pos, len); ++ tcg_wasm_out_deposit_i32(s, dest, arg1, arg2, pos, len); ++} ++static void tcg_out_deposit_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len) ++{ ++ TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64; ++ tcg_tci_out_op_rrrbb(s, opc, dest, arg1, arg2, pos, len); ++ tcg_wasm_out_deposit_i64(s, dest, arg1, arg2, pos, len); ++} ++static void tcg_out_extract_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len) ++{ ++ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; ++ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len); ++ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I32, false); ++} ++static void tcg_out_extract_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len) ++{ ++ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; ++ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len); ++ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I64, false); ++} ++static void tcg_out_sextract_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len) ++{ ++ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; ++ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len); ++ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I32, true); ++} ++static void tcg_out_sextract_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len) ++{ ++ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; ++ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len); ++ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I64, true); ++} ++static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext8s(s, type, ret, arg); ++ tcg_wasm_out_ext8s(s, type, ret, arg); ++} ++static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext16s(s, type, ret, arg); ++ tcg_wasm_out_ext16s(s, type, ret, arg); ++} ++static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext8u(s, ret, arg); ++ tcg_wasm_out_ext8u(s, ret, arg); ++} ++static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext16u(s, ret, arg); ++ tcg_wasm_out_ext16u(s, ret, arg); ++} ++static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext32s(s, ret, arg); ++ tcg_wasm_out_ext32s(s, ret, arg); ++} ++static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_ext32u(s, ret, arg); ++ tcg_wasm_out_ext32u(s, ret, arg); ++} ++static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_exts_i32_i64(s, ret, arg); ++ tcg_wasm_out_exts_i32_i64(s, ret, arg); ++} ++static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) ++{ ++ tcg_tci_out_extu_i32_i64(s, ret, arg); ++ tcg_wasm_out_extu_i32_i64(s, ret, arg); ++} ++ ++static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) ++{ ++ tcg_tci_out_extrl_i64_i32(s, rd, rs); ++ tcg_wasm_out_extrl_i64_i32(s, rd, rs); ++} ++ ++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, type, ret, arg); ++ return true; ++} ++static void tcg_out_movi(TCGContext *s, TCGType type, ++ TCGReg ret, tcg_target_long arg) ++{ ++ tcg_tci_out_movi(s, type, ret, arg); ++ tcg_wasm_out_movi(s, type, ret, arg); ++} ++static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, ++ tcg_target_long imm) ++{ ++ g_assert_not_reached(); ++} ++static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) ++{ ++ return false; ++} ++static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) ++{ ++ tcg_tci_out_exit_tb(s, arg); ++ tcg_wasm_out_exit_tb(s, arg); ++} ++static void tcg_out_goto_tb(TCGContext *s, int which) ++{ ++ tcg_tci_out_goto_tb(s, which); ++ tcg_wasm_out_goto_tb(s, which); ++} ++static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, ++ TCGReg base, intptr_t ofs) ++{ ++ return false; ++} ++static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, ++ const TCGHelperInfo *info) ++{ ++ tcg_tci_out_call(s, target, info); ++ tcg_wasm_out_call(s, target, info); ++} ++ ++static void tcg_out_op(TCGContext *s, TCGOpcode opc, ++ const TCGArg args[TCG_MAX_OP_ARGS], ++ const int const_args[TCG_MAX_OP_ARGS]) ++{ ++ switch (opc) { ++ case INDEX_op_goto_ptr: ++ tcg_out_goto_ptr(s, opc, args[0]); ++ break; ++ case INDEX_op_br: ++ tcg_out_br(s, opc, arg_label(args[0])); ++ break; ++ case INDEX_op_setcond_i32: ++ tcg_out_setcond_i32(s, opc, args[3], args[0], args[1], args[2]);// ++ break; ++ case INDEX_op_setcond_i64: ++ tcg_out_setcond_i64(s, opc, args[3], args[0], args[1], args[2]);// ++ break; ++ case INDEX_op_movcond_i32: ++ tcg_out_movcond_i32(s, opc, args[5], args[0], args[1], args[2], args[3], args[4]);// ++ break; ++ case INDEX_op_movcond_i64: ++ tcg_out_movcond_i64(s, opc, args[5], args[0], args[1], args[2], args[3], args[4]);// ++ break; ++ case INDEX_op_ld_i64: ++ tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld8s_i32: ++ case INDEX_op_ld8s_i64: ++ tcg_out_ld8s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld8u_i32: ++ case INDEX_op_ld8u_i64: ++ tcg_out_ld8u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld16s_i32: ++ case INDEX_op_ld16s_i64: ++ tcg_out_ld16s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld16u_i32: ++ case INDEX_op_ld16u_i64: ++ tcg_out_ld16u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld32u_i64: ++ tcg_out_ld32u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ld_i32: ++ case INDEX_op_ld32s_i64: ++ tcg_out_ld32s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_st_i64: ++ tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_st8_i32: ++ case INDEX_op_st8_i64: ++ tcg_out_st8(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_st16_i32: ++ case INDEX_op_st16_i64: ++ tcg_out_st16(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_st_i32: ++ case INDEX_op_st32_i64: ++ tcg_out_st32(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_add_i32: ++ case INDEX_op_add_i64: ++ tcg_out_i64_calc_add(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_sub_i32: ++ case INDEX_op_sub_i64: ++ tcg_out_i64_calc_sub(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_mul_i32: ++ case INDEX_op_mul_i64: ++ tcg_out_i64_calc_mul(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_and_i32: ++ case INDEX_op_and_i64: ++ tcg_out_i64_calc_and(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_or_i32: ++ case INDEX_op_or_i64: ++ tcg_out_i64_calc_or(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_xor_i32: ++ case INDEX_op_xor_i64: ++ tcg_out_i64_calc_xor(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_shl_i32: ++ tcg_out_shl(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_shl_i64: ++ tcg_out_shl(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_shr_i32: ++ tcg_out_shr_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_shr_i64: ++ tcg_out_shr_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_sar_i32: ++ tcg_out_shr_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_sar_i64: ++ tcg_out_shr_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rotl_i32: ++ tcg_out_i32_rotl(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rotl_i64: ++ tcg_out_i64_calc_rotl(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rotr_i32: ++ tcg_out_i32_rotr(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rotr_i64: ++ tcg_out_i64_calc_rotr(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_div_i32: ++ tcg_out_i64_calc_div_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_div_i64: ++ tcg_out_i64_calc_div_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_divu_i32: ++ tcg_out_i64_calc_div_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_divu_i64: ++ tcg_out_i64_calc_div_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rem_i32: ++ tcg_out_i64_calc_rem_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_rem_i64: ++ tcg_out_i64_calc_rem_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_remu_i32: ++ tcg_out_i64_calc_rem_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_remu_i64: ++ tcg_out_i64_calc_rem_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_andc_i32: ++ case INDEX_op_andc_i64: ++ tcg_out_andc(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_orc_i32: ++ case INDEX_op_orc_i64: ++ tcg_out_orc(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_eqv_i32: ++ case INDEX_op_eqv_i64: ++ tcg_out_eqv(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_nand_i32: ++ case INDEX_op_nand_i64: ++ tcg_out_nand(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_nor_i32: ++ case INDEX_op_nor_i64: ++ tcg_out_nor(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_clz_i32: ++ tcg_out_clz32(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_clz_i64: ++ tcg_out_clz64(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ctz_i32: ++ tcg_out_ctz32(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_ctz_i64: ++ tcg_out_ctz64(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_brcond_i32: ++ tcg_out_brcond_i32(s, opc, args[2], args[0], args[1], arg_label(args[3])); ++ break; ++ case INDEX_op_brcond_i64: ++ tcg_out_brcond_i64(s, opc, args[2], args[0], args[1], arg_label(args[3])); ++ break; ++ case INDEX_op_neg_i32: ++ case INDEX_op_neg_i64: ++ tcg_out_neg(s, opc, args[0], args[1]); ++ break; ++ case INDEX_op_not_i32: ++ case INDEX_op_not_i64: ++ tcg_out_not(s, opc, args[0], args[1]); ++ break; ++ case INDEX_op_ctpop_i32: ++ tcg_out_ctpop_i32(s, opc, args[1], args[2]); ++ break; ++ case INDEX_op_ctpop_i64: ++ tcg_out_ctpop_i64(s, opc, args[1], args[2]); ++ break; ++ case INDEX_op_add2_i32: ++ case INDEX_op_add2_i64: ++ tcg_out_add2(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]); ++ break; ++ case INDEX_op_sub2_i32: ++ case INDEX_op_sub2_i64: ++ tcg_out_sub2(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]); ++ break; ++ case INDEX_op_qemu_ld_a32_i32: ++ case INDEX_op_qemu_ld_a64_i32: ++ tcg_out_qemu_ld(s, opc, args, false); ++ break; ++ case INDEX_op_qemu_st_a32_i32: ++ case INDEX_op_qemu_st_a64_i32: ++ tcg_out_qemu_st(s, opc, args, false); ++ break; ++ case INDEX_op_qemu_ld_a32_i64: ++ case INDEX_op_qemu_ld_a64_i64: ++ tcg_out_qemu_ld(s, opc, args, true); ++ break; ++ case INDEX_op_qemu_st_a32_i64: ++ case INDEX_op_qemu_st_a64_i64: ++ tcg_out_qemu_st(s, opc, args, true); ++ break; ++ case INDEX_op_extrl_i64_i32: ++ tcg_out_extrl_i64_i32(s, args[0], args[1]); ++ break; ++ case INDEX_op_mb: ++ tcg_tci_out_op_v(s, opc); ++ tcg_wasm_out8(s, 0x01); // nop ++ break; ++ case INDEX_op_extract_i32: ++ tcg_out_extract_i32(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ case INDEX_op_extract_i64: ++ tcg_out_extract_i64(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ case INDEX_op_sextract_i32: ++ tcg_out_sextract_i32(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ case INDEX_op_sextract_i64: ++ tcg_out_sextract_i64(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ case INDEX_op_deposit_i32: ++ tcg_out_deposit_i32(s, opc, args[0], args[1], args[2], args[3], args[4]); ++ break; ++ case INDEX_op_deposit_i64: ++ tcg_out_deposit_i64(s, opc, args[0], args[1], args[2], args[3], args[4]); ++ break; ++ case INDEX_op_bswap16_i32: ++ tcg_out_bswap16_i32(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_bswap16_i64: ++ tcg_out_bswap16_i64(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_bswap32_i32: ++ tcg_out_bswap32_i32(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_bswap32_i64: ++ tcg_out_bswap32_i64(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_bswap64_i64: ++ tcg_out_bswap64_i64(s, opc, args[0], args[1], args[2]); ++ break; ++ case INDEX_op_muls2_i32: ++ tcg_out_muls2_i32(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ case INDEX_op_mulu2_i32: ++ tcg_out_mulu2_i32(s, opc, args[0], args[1], args[2], args[3]); ++ break; ++ default: ++ g_assert_not_reached(); ++ break; ++ } ++ return; ++} ++ ++void tcg_out_init() { ++ current_label_pos = 0; ++ env_cached = 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_tb_start(TCGContext *s) ++{ ++ /* nothing to do */ ++} ++ ++bool tcg_target_has_memory_bswap(MemOp memop) ++{ ++ return false; ++} ++ ++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_I64] = BIT(TCG_TARGET_NB_REGS) - 1; ++ tcg_target_available_regs[TCG_TYPE_I32] = 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 64-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) ++{ ++} +diff --git a/tcg/wasm32/tcg-target.h b/tcg/wasm32/tcg-target.h +new file mode 100644 +index 0000000000..7346c3a76e +--- /dev/null ++++ b/tcg/wasm32/tcg-target.h +@@ -0,0 +1,153 @@ ++/* ++ * Tiny Code Generator for QEMU ++ * ++ * Copyright (c) 2009, 2011 Stefan Weil ++ * ++ * Based on tci/tcg-target.h ++ * ++ * 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. ++ */ ++ ++#ifndef TCG_TARGET_H ++#define TCG_TARGET_H ++ ++#define TCG_TARGET_INSN_UNIT_SIZE 1 ++#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 ++#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) ++ ++/* Optional instructions. */ ++ ++#define TCG_TARGET_HAS_bswap16_i32 1 ++#define TCG_TARGET_HAS_bswap32_i32 1 ++#define TCG_TARGET_HAS_div_i32 1 ++#define TCG_TARGET_HAS_rem_i32 1 ++#define TCG_TARGET_HAS_ext8s_i32 1 ++#define TCG_TARGET_HAS_ext16s_i32 1 ++#define TCG_TARGET_HAS_ext8u_i32 1 ++#define TCG_TARGET_HAS_ext16u_i32 1 ++#define TCG_TARGET_HAS_andc_i32 1 ++#define TCG_TARGET_HAS_deposit_i32 1 ++#define TCG_TARGET_HAS_extract_i32 1 ++#define TCG_TARGET_HAS_sextract_i32 1 ++#define TCG_TARGET_HAS_extract2_i32 0 ++#define TCG_TARGET_HAS_eqv_i32 1 ++#define TCG_TARGET_HAS_nand_i32 1 ++#define TCG_TARGET_HAS_nor_i32 1 ++#define TCG_TARGET_HAS_clz_i32 1 ++#define TCG_TARGET_HAS_ctz_i32 1 ++#define TCG_TARGET_HAS_ctpop_i32 1 ++#define TCG_TARGET_HAS_neg_i32 1 ++#define TCG_TARGET_HAS_not_i32 1 ++#define TCG_TARGET_HAS_orc_i32 1 ++#define TCG_TARGET_HAS_rot_i32 1 ++#define TCG_TARGET_HAS_movcond_i32 1 ++#define TCG_TARGET_HAS_negsetcond_i32 0 ++#define TCG_TARGET_HAS_muls2_i32 1 ++#define TCG_TARGET_HAS_muluh_i32 0 ++#define TCG_TARGET_HAS_mulsh_i32 0 ++#define TCG_TARGET_HAS_qemu_st8_i32 0 ++ ++#define TCG_TARGET_HAS_extr_i64_i32 0 ++#define TCG_TARGET_HAS_extrl_i64_i32 1 ++#define TCG_TARGET_HAS_extrh_i64_i32 0 ++#define TCG_TARGET_HAS_bswap16_i64 1 ++#define TCG_TARGET_HAS_bswap32_i64 1 ++#define TCG_TARGET_HAS_bswap64_i64 1 ++#define TCG_TARGET_HAS_deposit_i64 1 ++#define TCG_TARGET_HAS_extract_i64 1 ++#define TCG_TARGET_HAS_sextract_i64 1 ++#define TCG_TARGET_HAS_extract2_i64 0 ++#define TCG_TARGET_HAS_div_i64 1 ++#define TCG_TARGET_HAS_rem_i64 1 ++#define TCG_TARGET_HAS_ext8s_i64 1 ++#define TCG_TARGET_HAS_ext16s_i64 1 ++#define TCG_TARGET_HAS_ext32s_i64 1 ++#define TCG_TARGET_HAS_ext8u_i64 1 ++#define TCG_TARGET_HAS_ext16u_i64 1 ++#define TCG_TARGET_HAS_ext32u_i64 1 ++#define TCG_TARGET_HAS_andc_i64 1 ++#define TCG_TARGET_HAS_eqv_i64 1 ++#define TCG_TARGET_HAS_nand_i64 1 ++#define TCG_TARGET_HAS_nor_i64 1 ++#define TCG_TARGET_HAS_clz_i64 1 ++#define TCG_TARGET_HAS_ctz_i64 1 ++#define TCG_TARGET_HAS_ctpop_i64 1 ++#define TCG_TARGET_HAS_neg_i64 1 ++#define TCG_TARGET_HAS_not_i64 1 ++#define TCG_TARGET_HAS_orc_i64 1 ++#define TCG_TARGET_HAS_rot_i64 1 ++#define TCG_TARGET_HAS_negsetcond_i64 0 ++#define TCG_TARGET_HAS_tst 0 ++#define TCG_TARGET_HAS_tst_vec 0 ++#define TCG_TARGET_HAS_movcond_i64 1 ++#define TCG_TARGET_HAS_muls2_i64 0 ++#define TCG_TARGET_HAS_add2_i32 1 ++#define TCG_TARGET_HAS_sub2_i32 1 ++#define TCG_TARGET_HAS_mulu2_i32 1 ++#define TCG_TARGET_HAS_add2_i64 1 ++#define TCG_TARGET_HAS_sub2_i64 1 ++#define TCG_TARGET_HAS_mulu2_i64 0 ++#define TCG_TARGET_HAS_muluh_i64 0 ++#define TCG_TARGET_HAS_mulsh_i64 0 ++ ++#define TCG_TARGET_HAS_qemu_ldst_i128 0 ++ ++/* 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; ++ ++/* Used for function call generation. */ ++#define TCG_TARGET_CALL_STACK_OFFSET 0 ++#define TCG_TARGET_STACK_ALIGN 8 ++#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 ++ ++#define HAVE_TCG_QEMU_TB_EXEC ++#define TCG_TARGET_NEED_POOL_LABELS ++ ++#define TCG_TARGET_DEFAULT_MO (0) ++ ++#define TCG_TARGET_HAS_MEMORY_BSWAP 0 ++ ++#endif /* TCG_TARGET_H */ +diff --git a/util/cacheflush.c b/util/cacheflush.c +index a08906155a..babbb644e5 100644 +--- a/util/cacheflush.c ++++ b/util/cacheflush.c +@@ -225,7 +225,7 @@ static void __attribute__((constructor)) init_cache_info(void) + * Architecture (+ OS) specific cache flushing mechanisms. + */ + +-#if defined(__i386__) || defined(__x86_64__) || defined(__s390__) ++#if defined(__i386__) || defined(__x86_64__) || defined(__s390__) || defined(EMSCRIPTEN) + + /* Caches are coherent and do not require flushing; symbol inline. */ + +diff --git a/util/coroutine-fiber.c b/util/coroutine-fiber.c +new file mode 100644 +index 0000000000..2ce1575362 +--- /dev/null ++++ b/util/coroutine-fiber.c +@@ -0,0 +1,123 @@ ++/* ++ * emscripten fiber coroutine initialization code ++ * based on coroutine-ucontext.c ++ * ++ * Copyright (C) 2006 Anthony Liguori ++ * Copyright (C) 2011 Kevin Wolf ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.0 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++ ++#include ++ ++typedef struct { ++ Coroutine base; ++ ++ void *asyncify_stack; ++ size_t asyncify_stack_size; ++ ++ CoroutineAction action; ++ ++ emscripten_fiber_t fiber; ++} CoroutineEmscripten; ++ ++/** ++ * Per-thread coroutine bookkeeping ++ */ ++QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); ++QEMU_DEFINE_STATIC_CO_TLS(CoroutineEmscripten *, leader); ++size_t leader_asyncify_stack_size = COROUTINE_STACK_SIZE; ++ ++static void coroutine_trampoline(void *co_) ++{ ++ Coroutine *co = co_; ++ ++ while (true) { ++ co->entry(co->entry_arg); ++ qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); ++ } ++} ++ ++Coroutine *qemu_coroutine_new(void) ++{ ++ CoroutineEmscripten *co; ++ ++ co = g_malloc0(sizeof(*co)); ++ ++ size_t stack_size = COROUTINE_STACK_SIZE; ++ char *stack = qemu_alloc_stack(&stack_size); ++ ++ co->asyncify_stack_size = COROUTINE_STACK_SIZE; ++ co->asyncify_stack = g_malloc0(co->asyncify_stack_size); ++ emscripten_fiber_init(&co->fiber, coroutine_trampoline, &co->base, ++ stack, stack_size, co->asyncify_stack, co->asyncify_stack_size); ++ ++ return &co->base; ++} ++ ++void qemu_coroutine_delete(Coroutine *co_) ++{ ++ CoroutineEmscripten *co = DO_UPCAST(CoroutineEmscripten, base, co_); ++ ++ g_free(co->asyncify_stack); ++ g_free(co); ++} ++ ++#include "../tcg/wasm32.h" ++ ++CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, ++ CoroutineAction action) ++{ ++ CoroutineEmscripten *from = DO_UPCAST(CoroutineEmscripten, base, from_); ++ CoroutineEmscripten *to = DO_UPCAST(CoroutineEmscripten, base, to_); ++ ++#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) ++ set_unwinding_flag(); ++#endif ++ ++ set_current(to_); ++ to->action = action; ++ emscripten_fiber_swap(&from->fiber, &to->fiber); ++ return from->action; ++} ++ ++Coroutine *qemu_coroutine_self(void) ++{ ++ Coroutine *self = get_current(); ++ ++ if (!self) { ++ CoroutineEmscripten *leaderp = get_leader(); ++ if (!leaderp) { ++ leaderp = g_malloc0(sizeof(*leaderp)); ++ leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size); ++ leaderp->asyncify_stack_size = leader_asyncify_stack_size; ++ emscripten_fiber_init_from_current_context(&leaderp->fiber, leaderp->asyncify_stack, leaderp->asyncify_stack_size); ++ set_leader(leaderp); ++ } ++ self = &leaderp->base; ++ set_current(self); ++ } ++ return self; ++} ++ ++bool qemu_in_coroutine(void) ++{ ++ Coroutine *self = get_current(); ++ ++ return self && self->caller; ++} +diff --git a/util/int128.c b/util/int128.c +index df6c6331bd..c442ff4da8 100644 +--- a/util/int128.c ++++ b/util/int128.c +@@ -144,7 +144,7 @@ Int128 int128_rems(Int128 a, Int128 b) + return r; + } + +-#elif defined(CONFIG_TCG_INTERPRETER) ++#elif defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN) + + Int128 int128_divu(Int128 a_s, Int128 b_s) + { +diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c +index ed14f9c64d..8c8708ca4d 100644 +--- a/util/mmap-alloc.c ++++ b/util/mmap-alloc.c +@@ -250,6 +250,11 @@ void *qemu_ram_mmap(int fd, + uint32_t qemu_map_flags, + off_t map_offset) + { ++#if defined(EMSCRIPTEN) ++ void *ptr; ++ ptr = mmap_activate(0, size + align, fd, qemu_map_flags, map_offset); ++ return (void *)QEMU_ALIGN_UP((uintptr_t)ptr, align); ++#else + const size_t guard_pagesize = mmap_guard_pagesize(fd); + size_t offset, total; + void *ptr, *guardptr; +@@ -292,6 +297,7 @@ void *qemu_ram_mmap(int fd, + } + + return ptr; ++#endif + } + + void qemu_ram_munmap(int fd, void *ptr, size_t size) diff --git a/qemu/wasm/Dockerfile b/qemu/wasm/Dockerfile new file mode 100644 index 0000000..93754e2 --- /dev/null +++ b/qemu/wasm/Dockerfile @@ -0,0 +1,92 @@ +ARG EMSDK_VERSION=3.1.50 +ARG ZLIB_VERSION=1.3.1 +ARG GLIB_SERIES=2.75 +ARG GLIB_VERSION=2.75.0 +ARG FFI_COMMIT=adbcf2b247696dde2667ab552cb93e0c79455c84 + +FROM emscripten/emsdk:${EMSDK_VERSION} AS base +ENV TARGET=/build/target +ENV CFLAGS="-O2 -matomics -mbulk-memory -DNDEBUG -sWASM_BIGINT -DWASM_BIGINT -pthread -sMALLOC=mimalloc -sASYNCIFY=1" +ENV CXXFLAGS="$CFLAGS" +ENV LDFLAGS="-L$TARGET/lib -O2" +ENV CPATH="$TARGET/include" +ENV PKG_CONFIG_PATH="$TARGET/lib/pkgconfig" +ENV EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" +ENV CHOST=wasm32-unknown-linux +ENV MAKEFLAGS=-j2 +RUN apt-get update && apt-get install -y \ + autoconf build-essential libglib2.0-dev libtool ninja-build pkgconf python3-pip +RUN pip3 install meson==1.5.0 tomli +WORKDIR /build +RUN mkdir -p "$TARGET" + +FROM base AS zlib +ARG ZLIB_VERSION +RUN mkdir /zlib \ + && curl -Ls "https://zlib.net/fossils/zlib-${ZLIB_VERSION}.tar.gz" \ + | tar xzC /zlib --strip-components=1 +WORKDIR /zlib +RUN emconfigure ./configure --prefix="$TARGET" --static && make install + +FROM base AS libffi +ARG FFI_COMMIT +RUN git clone https://github.com/libffi/libffi /libffi \ + && git -C /libffi checkout "$FFI_COMMIT" +WORKDIR /libffi +RUN autoreconf -fiv \ + && emconfigure ./configure --host="$CHOST" --prefix="$TARGET" \ + --enable-static --disable-shared --disable-dependency-tracking \ + --disable-builddir --disable-multi-os-directory --disable-raw-api \ + --disable-structs --disable-docs \ + && emmake make install SUBDIRS=include + +FROM base AS glib +ARG GLIB_SERIES +ARG GLIB_VERSION +COPY --from=zlib /build/ /build/ +COPY --from=libffi /build/ /build/ +RUN mkdir /stub \ + && printf '%s\n' '#include ' \ + 'int res_query(const char *n, int c, int t, unsigned char *d, int l) { h_errno = HOST_NOT_FOUND; return -1; }' \ + >/stub/res_query.c \ + && emcc $CFLAGS -c /stub/res_query.c -fPIC -o /stub/libresolv.o \ + && ar rcs "$TARGET/lib/libresolv.a" /stub/libresolv.o +RUN mkdir /glib \ + && curl -Lks "https://download.gnome.org/sources/glib/${GLIB_SERIES}/glib-${GLIB_VERSION}.tar.xz" \ + | tar xJC /glib --strip-components=1 +WORKDIR /glib +ENV CFLAGS="-Wno-error=incompatible-function-pointer-types -O2 -matomics -mbulk-memory -DNDEBUG -pthread -sWASM_BIGINT -sMALLOC=mimalloc -sASYNCIFY=1" +ENV CXXFLAGS="$CFLAGS" +RUN printf '%s\n' \ + '#!/bin/bash' \ + 'set -euo pipefail' \ + 'cmd="$1"; shift' \ + 'args=()' \ + 'for arg in "$@"; do args+=("${arg/-Werror=unused-command-line-argument/-Wno-error=unused-command-line-argument}"); done' \ + '"$cmd" "${args[@]}"' \ + >/emcc-meson-wrap.sh && chmod +x /emcc-meson-wrap.sh +RUN printf '%s\n' \ + '[host_machine]' \ + "system = 'emscripten'" \ + "cpu_family = 'wasm32'" \ + "cpu = 'wasm32'" \ + "endian = 'little'" \ + '' \ + '[binaries]' \ + "c = ['bash', '/emcc-meson-wrap.sh', 'emcc']" \ + "cpp = ['bash', '/emcc-meson-wrap.sh', 'em++']" \ + "ar = 'emar'" \ + "ranlib = 'emranlib'" \ + "pkgconfig = ['pkg-config', '--static']" \ + >/cross.meson +RUN meson setup _build --prefix="$TARGET" --cross-file=/cross.meson \ + --default-library=static --buildtype=release --force-fallback-for=pcre2,gvdb \ + -Dselinux=disabled -Dxattr=false -Dlibmount=disabled -Dnls=disabled \ + -Dtests=false -Dglib_assert=false -Dglib_checks=false \ + && sed -i -E '/#define HAVE_(CLOSE_RANGE|EPOLL_CREATE|KQUEUE|POSIX_SPAWN|FALLOCATE) 1/d' _build/config.h \ + && meson install -C _build + +FROM base +COPY --from=glib /build/ /build/ +WORKDIR /build +CMD ["sleep", "infinity"] diff --git a/scripts/build-qemu-wasm.sh b/scripts/build-qemu-wasm.sh new file mode 100755 index 0000000..4b50465 --- /dev/null +++ b/scripts/build-qemu-wasm.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SRC="${QEMU_WASM_SRC:-$ROOT/_scratch/qemu-wasm-src}" +OUT="${WASM_OUT:-$ROOT/dist/wasm}" +[[ "$SRC" = /* ]] || SRC="$ROOT/$SRC" +[[ "$OUT" = /* ]] || OUT="$ROOT/$OUT" +TAG="esp-develop-9.2.2-20260417" +COMMIT="40edccac415693c5130f91c01d84176ae6008566" +QEMU_WASM_COMMIT="0ef7b4e2814b231705d8371dd7997f5b72e70baf" +IMAGE="xteink-qemu-wasm-build" +JOBS="${JOBS:-2}" + +if [ -n "${CONTAINER_ENGINE:-}" ]; then + ENGINE="$CONTAINER_ENGINE" +elif command -v podman >/dev/null; then + ENGINE=podman +else + ENGINE=docker +fi + +if [ ! -d "$SRC/.git" ]; then + git clone --depth 1 --branch "$TAG" https://github.com/espressif/qemu "$SRC" +fi + +cd "$SRC" +if [ "$(git rev-parse HEAD)" != "$COMMIT" ]; then + echo "QEMU source must be $COMMIT ($TAG)" >&2 + exit 1 +fi + +if [ ! -f hw/display/xteink_x3_eink.c ] || [ ! -f tcg/wasm32.c ]; then + if ! git diff --quiet || ! git diff --cached --quiet; then + echo "QEMU source has changes that conflict with the WASM patches" >&2 + exit 1 + fi + git apply "$ROOT/qemu/patches/0001-xteink-machine.patch" + git apply --whitespace=nowarn "$ROOT/qemu/patches/0002-qemu-wasm.patch" +fi + +printf 'Building Emscripten environment (qemu-wasm %s)\n' "$QEMU_WASM_COMMIT" +"$ENGINE" build -t "$IMAGE" -f "$ROOT/qemu/wasm/Dockerfile" "$ROOT/qemu/wasm" + +FLAGS="-O3 -Wno-error=unused-command-line-argument -matomics -mbulk-memory -DNDEBUG -DG_DISABLE_ASSERT -D_GNU_SOURCE -sASYNCIFY=1 -pthread -sPROXY_TO_PTHREAD=1 -sFORCE_FILESYSTEM -sALLOW_TABLE_GROWTH -sEMULATE_FUNCTION_POINTER_CASTS=1 -sINITIAL_MEMORY=512MB -sWASM_BIGINT -sMALLOC=mimalloc -sEXPORT_ES6=1 -sASYNCIFY_IMPORTS=ffi_call_js" + +if [ ! -f build/build.ninja ]; then + "$ENGINE" run --rm -v "$SRC:/qemu" "$IMAGE" bash -lc " + cd /qemu + emconfigure ./configure \\ + --static \\ + --target-list=riscv32-softmmu \\ + --cpu=wasm32 \\ + --cross-prefix= \\ + --without-default-features \\ + --without-default-devices \\ + --with-devices-riscv32=xteink \\ + --enable-system \\ + --with-coroutine=fiber \\ + --disable-werror \\ + --disable-docs \\ + --disable-tools \\ + --extra-cflags='$FLAGS' \\ + --extra-cxxflags='$FLAGS' \\ + --extra-ldflags='-sEXPORTED_RUNTIME_METHODS=getTempRet0,setTempRet0,addFunction,removeFunction,TTY,FS' + " +fi + +"$ENGINE" run --rm -v "$SRC:/qemu" "$IMAGE" \ + bash -lc "cd /qemu && emmake ninja -C build -j$JOBS qemu-system-riscv32.js" + +mkdir -p "$OUT" +install -m 0644 build/qemu-system-riscv32.js "$OUT/" +install -m 0644 build/qemu-system-riscv32.wasm "$OUT/" +install -m 0644 build/qemu-system-riscv32.worker.js "$OUT/" +if command -v node >/dev/null; then + "$ROOT/scripts/smoke-qemu-wasm.sh" "$OUT/qemu-system-riscv32.js" +fi +printf '%s\n' "$OUT" diff --git a/scripts/smoke-qemu-wasm.mjs b/scripts/smoke-qemu-wasm.mjs new file mode 100644 index 0000000..d07cccd --- /dev/null +++ b/scripts/smoke-qemu-wasm.mjs @@ -0,0 +1,9 @@ +import { pathToFileURL } from 'node:url'; + +const modulePath = process.argv[2]; +if (!modulePath) { + throw new Error('usage: node scripts/smoke-qemu-wasm.mjs '); +} + +const { default: createQemu } = await import(pathToFileURL(modulePath)); +await createQemu({ arguments: ['-machine', 'help'] }); diff --git a/scripts/smoke-qemu-wasm.sh b/scripts/smoke-qemu-wasm.sh new file mode 100755 index 0000000..cd37016 --- /dev/null +++ b/scripts/smoke-qemu-wasm.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +MODULE="${1:?usage: scripts/smoke-qemu-wasm.sh }" +OUTPUT="$(mktemp)" +trap 'rm -f "$OUTPUT"' EXIT + +timeout 15 node "$(dirname "$0")/smoke-qemu-wasm.mjs" "$MODULE" >"$OUTPUT" 2>&1 || status=$? +status="${status:-0}" +if [ "$status" -ne 0 ] && [ "$status" -ne 1 ] && [ "$status" -ne 124 ]; then + cat "$OUTPUT" >&2 + exit "$status" +fi + +grep -F 'xteink xteink X3/X4 (ESP32-C3); variant=x3|x4' "$OUTPUT" >/dev/null +if grep -Eq 'RuntimeError|table index is out of bounds' "$OUTPUT"; then + cat "$OUTPUT" >&2 + exit 1 +fi