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..440cb211d8 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 */ 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/configure b/configure index 18336376bf..0827ad22a2 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=linux +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 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/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/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..2dda2e1e14 100644 --- a/meson.build +++ b/meson.build @@ -47,7 +47,7 @@ qapi_trace_events = [] bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin'] supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] 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() @@ -482,7 +482,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 @@ -884,6 +884,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) 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..7eb99c807e 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -15,6 +15,8 @@ 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') @@ -29,6 +31,10 @@ endif tcg_ss = tcg_ss.apply({}) +if cpu == 'wasm32' + specific_ss.add(files('wasm32.c')) +endif + libtcg_user = static_library('tcg_user', tcg_ss.sources() + genh, dependencies: tcg_ss.dependencies(), 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..d470dfc321 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,114 @@ tlb_mask_table_ofs(TCGContext *s, int which) sizeof(CPUNegativeOffsetState)); } +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + +#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(TCGContext *s, int label) +{ + int i = block_ptr_placeholder_idx_pos++; + tcg_debug_assert(i <= LABEL_MAX); + block_ptr_placeholder[i].label = label; + block_ptr_placeholder[i].ptr = (uintptr_t)s->code_ptr;; +} + +#endif + /* Signal overflow, starting over with fewer guest insns. */ static G_NORETURN void tcg_raise_tb_overflow(TCGContext *s) @@ -937,7 +1052,7 @@ static TCGHelperInfo info_helper_st128_mmu = { | dh_typemask(ptr, 5) /* uintptr_t ra */ }; -#ifdef CONFIG_TCG_INTERPRETER +#if defined(CONFIG_TCG_INTERPRETER) static ffi_type *typecode_to_ffi(int argmask) { /* @@ -1406,12 +1521,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 +1548,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 +1585,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 +6018,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, 24, // global.set $block_ptr + 0x0b, // end + + 0x03, 0x40, // loop + 0x23, 24, // 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 +6444,20 @@ 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) + 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); + + s->code_ptr += 4; // placeholder for export vector offset + uint8_t *code_begin = s->code_ptr; + s->code_ptr += 4; // placeholder for size +#endif + #ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_INIT(&s->ldst_labels); #endif @@ -6286,13 +6559,85 @@ 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_out8(s, 0x0b); //end if + tcg_out8(s, 0x0b); //end loop + tcg_out8(s, 0x0); // unreachable + tcg_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); + 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; + + // 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, code_size, 1); + + // write header size + *(uint32_t *)wasm_blob_ptr_base = wasm_blob_ptr - wasm_blob_ptr_base - 4; + s->code_ptr = wasm_blob_ptr; + + // record importing helper functions + uint32_t *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; + + // init exporting functions with zeros + size_base = (uint32_t*)s->code_ptr; + s->code_ptr += 4; + *(uint32_t*)(s->code_buf) = (uint32_t)(s->code_ptr - s->code_buf); + int export_size = get_core_nums() * 4; + memset(s->code_ptr, 0, export_size); + s->code_ptr += export_size; + *size_base = export_size; + + 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..45136035ee --- /dev/null +++ b/tcg/wasm32.c @@ -0,0 +1,172 @@ +#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, (int cur_core_num, int all_cores_num, int wasm_body_begin, int wasm_body_size, int wasm_header_begin, int wasm_header_size, int import_vec_begin, int import_vec_size, int export_vec_begin, int export_vec_size, int *to_remove_ptr, int *to_remove_num), { + const memory_v = new DataView(HEAP8.buffer); + var wasm = new Uint8Array(wasm_header_size + wasm_body_size); + wasm.set(HEAP8.subarray(wasm_header_begin, wasm_header_begin + wasm_header_size)); + wasm.set(HEAP8.subarray(wasm_body_begin, wasm_body_begin + wasm_body_size), wasm_header_size); + const mod = new WebAssembly.Module(wasm); + 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 inst = new WebAssembly.Instance(mod, { + "env": { + "buffer": wasmMemory, + }, + "helper": helper, + }); + var ptr = export_vec_begin + 4 * cur_core_num; + const fidx = addFunction(inst.exports.start, 'ii'); + memory_v.setUint32(ptr, fidx, true); + ptr += 4 * all_cores_num; + + const remove_n = memory_v.getInt32(to_remove_num, true); + if (remove_n > 500) { + for (var i = 0; i < remove_n * 4; i += 4) { + removeFunction(memory_v.getInt32(to_remove_ptr + i, true)); + } + memory_v.setInt32(to_remove_num, 0, true); + } + + return fidx; +}); + +#define TO_REMOVE_INSTANCE_SIZE 50000 +__thread static int to_remove_instance[TO_REMOVE_INSTANCE_SIZE]; +__thread static int to_remove_instance_idx = 0; + +#define ACTIVE_TBS_MAX 60000 +#define REMOVE_TBS_NUM 10000 +__thread static int active_tbs[ACTIVE_TBS_MAX]; +__thread static int active_tbs_begin = 0; +__thread static int active_tbs_end = 0; +__thread static int active_tbs_lim; +__thread static int active_tbs_rmv; + +static int active_tbs_len() +{ + if (active_tbs_begin == active_tbs_end) { + return 0; + } + if (active_tbs_begin < active_tbs_end) { + return active_tbs_end - active_tbs_begin; + } + if (active_tbs_begin > active_tbs_end) { + return (active_tbs_lim - active_tbs_begin) + active_tbs_end; + } + g_assert_not_reached(); +} + +static void prepare_wasm(void *tb_ptr, int cur_core_num, int all_cores_num) +{ + uint32_t code_size = *(uint32_t*)((uint32_t)tb_ptr + 4); + uint32_t code_begin = (uint32_t)tb_ptr + 4 + 4; + uint32_t header_size = *(uint32_t*)(code_begin + code_size); + uint32_t header_begin = code_begin + code_size + 4; + uint32_t import_vec_size = *(uint32_t*)(header_begin + header_size); + uint32_t import_vec_begin = header_begin + header_size + 4; + uint32_t export_vec_size = *(uint32_t*)(import_vec_begin + import_vec_size); + uint32_t export_vec_begin = import_vec_begin + import_vec_size + 4; + + if (active_tbs_len() == (active_tbs_lim-1)) { + int idx = active_tbs_begin; + for (int i = 0; i < active_tbs_rmv; i++) { + uint8_t *p = (uint8_t*)active_tbs[idx]; + int f = *(uint32_t*)((uint8_t*)p + *(uint32_t*)p + cur_core_num * 4); + *(uint32_t*)((uint8_t*)p + *(uint32_t*)p + cur_core_num * 4) = 0; + to_remove_instance[to_remove_instance_idx++] = f; + idx++; + if (idx >= active_tbs_lim) { + idx = 0; + } + } + active_tbs_begin = idx; + } + + int fidx = instantiate_wasm(cur_core_num, all_cores_num, code_begin, code_size, header_begin, header_size, import_vec_begin, import_vec_size, export_vec_begin, export_vec_size, to_remove_instance, &to_remove_instance_idx); + active_tbs[active_tbs_end++] = (int)tb_ptr; + if (active_tbs_end >= active_tbs_lim) { + active_tbs_end = 0; + } +} + +__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*); + +__thread bool initdone = false; +__thread int cur_core_num = -1; +__thread int all_cores_num = -1; +int cur_core_num_max = 0; + +int get_core_nums() +{ + return emscripten_num_logical_cores(); +} + +extern unsigned int tcg_max_ctxs; + +uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, const void *v_tb_ptr) +{ + if (!initdone) { + active_tbs_lim = ACTIVE_TBS_MAX / tcg_max_ctxs; + active_tbs_rmv = REMOVE_TBS_NUM / tcg_max_ctxs; + cur_core_num = qatomic_fetch_inc(&cur_core_num_max); + all_cores_num = get_core_nums(); + ctx.stack = (uint64_t*)malloc((TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE) / sizeof(uint64_t)); + ctx.stack128 = (uint64_t*)malloc((TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE) / sizeof(uint64_t)); + initdone = true; + } + ctx.env = env; + ctx.tb_ptr = (uint32_t*)v_tb_ptr; + ctx.do_init = 1; + ctx.tci_tb_ptr = (uint32_t*)&tci_tb_ptr; + uint32_t prev_tb_ptr = 0; + while (true) { + int tb_entry_ptr = (uint32_t)ctx.tb_ptr + *(uint32_t*)ctx.tb_ptr + cur_core_num * 4; + if (*(uint32_t*)tb_entry_ptr == 0) { + prepare_wasm(ctx.tb_ptr, cur_core_num, all_cores_num); + } + uint32_t 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..3278f2541f --- /dev/null +++ b/tcg/wasm32.h @@ -0,0 +1,38 @@ +#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(); + +#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..7217d872c0 --- /dev/null +++ b/tcg/wasm32/tcg-target.c.inc @@ -0,0 +1,2677 @@ +/* + * 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 + +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_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, + + // Arguments + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, +}; + +#define NUM_OF_IARG_REGS 8 +static const int tcg_target_call_iarg_regs[NUM_OF_IARG_REGS] = { + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, +}; + +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_A0 + 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", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", +}; +#endif + +#define REG_INDEX_IARG_BASE 16 +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 + + 16, // TCG_REG_A0 + 17, // TCG_REG_A1 + 18, // TCG_REG_A2 + 19, // TCG_REG_A3 + 20, // TCG_REG_A4 + 21, // TCG_REG_A5 + 22, // TCG_REG_A6 + 23, // TCG_REG_A7 +}; + +#define BLOCK_PTR_IDX 24 + +#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 void tcg_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_out8(s, b); + } +} + +static void tcg_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_out8(s, b); + } +} + +static void tcg_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_out8(s, b); + } while (v != 0); +} + +static void tcg_out_op_br(TCGContext *s, int i) +{ + tcg_out8(s, 0x0c); + tcg_out8(s, i); +} + +static void tcg_out_op_if_noret(TCGContext *s) +{ + tcg_out8(s, 0x04); + tcg_out8(s, 0x40); +} + +static void tcg_out_op_if_ret_i64(TCGContext *s) +{ + tcg_out8(s, 0x04); + tcg_out8(s, 0x7e); +} + +static void tcg_out_op_if_ret_i32(TCGContext *s) +{ + tcg_out8(s, 0x04); + tcg_out8(s, 0x7f); +} + +static void tcg_out_op_else(TCGContext *s) +{ + tcg_out8(s, 0x05); +} + +static void tcg_out_op_end(TCGContext *s) +{ + tcg_out8(s, 0x0b); +} + +static void tcg_out_op_i32_eqz(TCGContext *s){ tcg_out8(s, 0x45); } +static void tcg_out_op_i32_eq(TCGContext *s){ tcg_out8(s, 0x46); } +static void tcg_out_op_i32_and(TCGContext *s){ tcg_out8(s, 0x71); } +static void tcg_out_op_i32_or(TCGContext *s){ tcg_out8(s, 0x72); } +//static void tcg_out_op_i32_xor(TCGContext *s){ tcg_out8(s, 0x73); } +static void tcg_out_op_i32_shl(TCGContext *s){ tcg_out8(s, 0x74); } +static void tcg_out_op_i32_shr_s(TCGContext *s){ tcg_out8(s, 0x75); } +static void tcg_out_op_i32_shr_u(TCGContext *s){ tcg_out8(s, 0x76); } +static void tcg_out_op_i32_rotl(TCGContext *s){ tcg_out8(s, 0x77); } +static void tcg_out_op_i32_rotr(TCGContext *s){ tcg_out8(s, 0x78); } +static void tcg_out_op_i32_clz(TCGContext *s){ tcg_out8(s, 0x67); } +static void tcg_out_op_i32_ctz(TCGContext *s){ tcg_out8(s, 0x68); } +//static void tcg_out_op_i32_popcnt(TCGContext *s){ tcg_out8(s, 0x69); } +static void tcg_out_op_i32_add(TCGContext *s){ tcg_out8(s, 0x6a); } +//static void tcg_out_op_i32_sub(TCGContext *s){ tcg_out8(s, 0x6b); } +//static void tcg_out_op_i32_mul(TCGContext *s){ tcg_out8(s, 0x6c); } +//static void tcg_out_op_i32_div_s(TCGContext *s){ tcg_out8(s, 0x6d); } +//static void tcg_out_op_i32_div_u(TCGContext *s){ tcg_out8(s, 0x6e); } +//static void tcg_out_op_i32_rem_s(TCGContext *s){ tcg_out8(s, 0x6f); } +//static void tcg_out_op_i32_rem_u(TCGContext *s){ tcg_out8(s, 0x70); } +static void tcg_out_op_i32_ne(TCGContext *s){ tcg_out8(s, 0x47); } +//static void tcg_out_op_i32_le_u(TCGContext *s){ tcg_out8(s, 0x4d); } + +static void tcg_out_op_i64_eqz(TCGContext *s){ tcg_out8(s, 0x50); } +static void tcg_out_op_i64_eq(TCGContext *s){ tcg_out8(s, 0x51); } +static void tcg_out_op_i64_and(TCGContext *s){ tcg_out8(s, 0x83); } +static void tcg_out_op_i64_or(TCGContext *s){ tcg_out8(s, 0x84); } +static void tcg_out_op_i64_xor(TCGContext *s){ tcg_out8(s, 0x85); } +static void tcg_out_op_i64_shl(TCGContext *s){ tcg_out8(s, 0x86); } +static void tcg_out_op_i64_shr_s(TCGContext *s){ tcg_out8(s, 0x87); } +static void tcg_out_op_i64_shr_u(TCGContext *s){ tcg_out8(s, 0x88); } +static void tcg_out_op_i64_rotl(TCGContext *s){ tcg_out8(s, 0x89); } +static void tcg_out_op_i64_rotr(TCGContext *s){ tcg_out8(s, 0x8a); } +static void tcg_out_op_i64_clz(TCGContext *s){ tcg_out8(s, 0x79); } +static void tcg_out_op_i64_ctz(TCGContext *s){ tcg_out8(s, 0x7a); } +static void tcg_out_op_i64_popcnt(TCGContext *s){ tcg_out8(s, 0x7b); } +static void tcg_out_op_i64_add(TCGContext *s){ tcg_out8(s, 0x7c); } +static void tcg_out_op_i64_sub(TCGContext *s){ tcg_out8(s, 0x7d); } +static void tcg_out_op_i64_mul(TCGContext *s){ tcg_out8(s, 0x7e); } +static void tcg_out_op_i64_div_s(TCGContext *s){ tcg_out8(s, 0x7f); } +static void tcg_out_op_i64_div_u(TCGContext *s){ tcg_out8(s, 0x80); } +static void tcg_out_op_i64_rem_s(TCGContext *s){ tcg_out8(s, 0x81); } +static void tcg_out_op_i64_rem_u(TCGContext *s){ tcg_out8(s, 0x82); } +//static void tcg_out_op_i64_ne(TCGContext *s){ tcg_out8(s, 0x52); } +static void tcg_out_op_i64_le_u(TCGContext *s){ tcg_out8(s, 0x58); } +static void tcg_out_op_i64_lt_u(TCGContext *s){ tcg_out8(s, 0x54); } +static void tcg_out_op_i64_gt_u(TCGContext *s){ tcg_out8(s, 0x56); } + +static void tcg_out_op_i32_wrap_i64(TCGContext *s){ tcg_out8(s, 0xa7); } + +static void tcg_out_op_var(TCGContext *s, uint8_t instr, uint8_t i) +{ + tcg_out8(s, instr); + tcg_out8(s, i); +} + +static void tcg_out_op_local_get(TCGContext *s, uint8_t i) +{ + tcg_out_op_var(s, 0x20, i); +} + +static void tcg_out_op_local_set(TCGContext *s, uint8_t i) +{ + tcg_out_op_var(s, 0x21, i); +} + +static void tcg_out_op_local_tee(TCGContext *s, uint8_t i) +{ + tcg_out_op_var(s, 0x22, i); +} + +static void tcg_out_op_global_get(TCGContext *s, uint8_t i) +{ + tcg_out_op_var(s, 0x23, i); +} + +static void tcg_out_op_global_set(TCGContext *s, uint8_t i) +{ + if (i == tcg_target_reg_index[TCG_REG_R14]) { + env_cached = false; + } + tcg_out_op_var(s, 0x24, i); +} + +static void tcg_out_op_global_get_r_i32(TCGContext *s, TCGReg r0) +{ + if (r0 == TCG_REG_R14) { + if (!env_cached) { + tcg_out_op_global_get(s, tcg_target_reg_index[r0]); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_local_tee(s, TMP32_LOCAL_ENV_IDX); + env_cached = true; + } else { + tcg_out_op_local_get(s, TMP32_LOCAL_ENV_IDX); + } + return; + } + tcg_out_op_global_get(s, tcg_target_reg_index[r0]); + tcg_out_op_i32_wrap_i64(s); +} + +static void tcg_out_op_global_get_r(TCGContext *s, TCGReg r0) +{ + tcg_out_op_global_get(s, tcg_target_reg_index[r0]); +} + +static void tcg_out_op_global_set_r(TCGContext *s, TCGReg r0) +{ + tcg_out_op_global_set(s, tcg_target_reg_index[r0]); +} + +static void tcg_out_op_i32_const(TCGContext *s, int32_t v) +{ + tcg_out8(s, 0x41); + tcg_out_leb128_sint32_t(s, v); +} + +static void tcg_out_op_i64_const(TCGContext *s, int64_t v) +{ + tcg_out8(s, 0x42); + tcg_out_leb128_sint64_t(s, v); +} + +static void tcg_out_op_loadstore(TCGContext *s, uint8_t instr, uint32_t a, uint32_t o) +{ + tcg_out8(s, instr); + tcg_out_leb128_uint32_t(s, a); + tcg_out_leb128_uint32_t(s, o); +} + +static void tcg_out_op_i64_store(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x37, a, o); +} + +static void tcg_out_op_i32_store(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x36, a, o); +} + +static void tcg_out_op_i64_store8(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x3c, a, o); +} + +//static void tcg_out_op_i32_store8(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x3a, a, o); +//} + +static void tcg_out_op_i64_store16(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x3d, a, o); +} + +//static void tcg_out_op_i32_store16(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x3b, a, o); +//} + +static void tcg_out_op_i64_store32(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x3e, a, o); +} + +static void tcg_out_op_i64_load(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x29, a, o); +} + +static void tcg_out_op_i32_load(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x28, a, o); +} + + static void tcg_out_op_i64_load8_s(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x30, a, o); +} + +//static void tcg_out_op_i32_load8_s(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x2c, a, o); +//} +// +static void tcg_out_op_i64_load8_u(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x31, a, o); +} + +//static void tcg_out_op_i32_load8_u(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x2d, a, o); +//} + +static void tcg_out_op_i64_load16_s(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x32, a, o); +} + +//static void tcg_out_op_i32_load16_s(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x2e, a, o); +//} + +static void tcg_out_op_i64_load16_u(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x33, a, o); +} + +//static void tcg_out_op_i32_load16_u(TCGContext *s, uint32_t a, uint32_t o) +//{ +// tcg_out_op_loadstore(s, 0x2f, a, o); +//} + +static void tcg_out_op_i64_load32_u(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x35, a, o); +} + +static void tcg_out_op_i64_load32_s(TCGContext *s, uint32_t a, uint32_t o) +{ + tcg_out_op_loadstore(s, 0x34, a, o); +} + +static void tcg_out_op_return(TCGContext *s) +{ + tcg_out8(s, 0x0f); +} + +static void tcg_out_op_call(TCGContext *s, uint32_t func_idx) +{ + tcg_out8(s, 0x10); + tcg_out_leb128_uint32_t(s, func_idx); +} + +static void tcg_out_op_i64_extend_i32_u(TCGContext *s) +{ + tcg_out8(s, 0xad); +} + +static void tcg_out_op_i64_extend_i32_s(TCGContext *s) +{ + tcg_out8(s, 0xac); +} + +static void tcg_out_op_i64_extend8_s(TCGContext *s) +{ + tcg_out8(s, 0xc2); +} + +//static void tcg_out_op_i32_extend8_s(TCGContext *s) +//{ +// tcg_out8(s, 0xc0); +//} + +static void tcg_out_op_i64_extend16_s(TCGContext *s) +{ + tcg_out8(s, 0xc3); +} + +//static void tcg_out_op_i32_extend16_s(TCGContext *s) +//{ +// tcg_out8(s, 0xc1); +//} + +static void tcg_out_op_not(TCGContext *s){ + tcg_out_op_i64_const(s, -1); + tcg_out_op_i64_xor(s); +} + +static void tcg_out_op_set_r_as_i64(TCGContext *s, TCGReg al, TCGReg ah) +{ + tcg_out_op_local_set(s, TMP64_4_IDX); + + // set lower bits + tcg_out_op_local_get(s, TMP64_4_IDX); + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, al); + + // set higher bits + tcg_out_op_local_get(s, TMP64_4_IDX); + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_shr_u(s); + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_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_out_op_cond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2) +{ + uint8_t op = tcg_cond_to_inst[cond].i64; + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out8(s, op); +} + +#define tcg_out_i64_calc(op) \ + static void tcg_out_i64_calc_##op(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ \ + tcg_out_op_global_get_r(s, arg1); \ + tcg_out_op_global_get_r(s, arg2); \ + tcg_out_op_i64_##op(s); \ + tcg_out_op_global_set_r(s, ret); \ + } +tcg_out_i64_calc(and); +tcg_out_i64_calc(or); +tcg_out_i64_calc(xor); +tcg_out_i64_calc(shl); +tcg_out_i64_calc(shr_s); +tcg_out_i64_calc(shr_u); +tcg_out_i64_calc(rotl); +tcg_out_i64_calc(rotr); +tcg_out_i64_calc(add); +tcg_out_i64_calc(sub); +tcg_out_i64_calc(mul); +tcg_out_i64_calc(div_s); +tcg_out_i64_calc(div_u); +tcg_out_i64_calc(rem_s); +tcg_out_i64_calc(rem_u); + +static void tcg_out_i32_rotl(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i32_rotl(s); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_i32_rotr(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i32_rotr(s); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_clz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_ret_i64(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_else(s); + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_clz(s); + tcg_out_op_end(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_clz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_ret_i32(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_else(s); + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i32_clz(s); + tcg_out_op_end(s); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_ctz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_ret_i64(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_else(s); + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_ctz(s); + tcg_out_op_end(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_ctz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_ret_i32(s); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_else(s); + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i32_ctz(s); + tcg_out_op_end(s); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_not(TCGContext *s, TCGReg ret, TCGReg arg){ + tcg_out_op_global_get_r(s, arg); + tcg_out_op_not(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_andc(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_not(s); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_orc(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_not(s); + tcg_out_op_i64_or(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_eqv(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_xor(s); + tcg_out_op_not(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_nand(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_and(s); + tcg_out_op_not(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_nor(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_or(s); + tcg_out_op_not(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_neg(TCGContext *s, TCGReg ret, TCGReg arg){ + tcg_out_op_global_get_r(s, arg); + tcg_out_op_not(s); + tcg_out_op_i64_const(s, 1); + tcg_out_op_i64_add(s); + tcg_out_op_global_set_r(s, ret); +} + +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + return false; +} + +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load32_u(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld8s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load8_s(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld8u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load8_u(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld16s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load16_s(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld16u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load16_u(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld32s(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load32_s(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld32u(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_i64_load32_u(s, 0, (uint32_t)offset); + tcg_out_op_global_set_r(s, val); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_global_get_r(s, val); + tcg_out_op_i64_store32(s, 0, (uint32_t)offset); + break; + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_global_get_r(s, val); + tcg_out_op_i64_store(s, 0, (uint32_t)offset); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_st8(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_global_get_r(s, val); + tcg_out_op_i64_store8(s, 0, (uint32_t)offset); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_st16(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_global_get_r(s, val); + tcg_out_op_i64_store16(s, 0, (uint32_t)offset); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_st32(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + tcg_out_op_global_get_r(s, val); + tcg_out_op_i64_store32(s, 0, (uint32_t)offset); + break; + default: + g_assert_not_reached(); + } +} + +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t offset) +{ + tcg_out_op_global_get_r_i32(s, base); + if ((int32_t)offset < 0) { + tcg_out_op_i32_const(s, (int32_t)offset); + tcg_out_op_i32_add(s); + offset = 0; + } + switch (type) { + case TCG_TYPE_I32: + tcg_out_op_i64_const(s, (int32_t)val); + tcg_out_op_i64_store32(s, 0, (uint32_t)offset); + break; + case TCG_TYPE_I64: + tcg_out_op_i64_const(s, val); + tcg_out_op_i64_store(s, 0, (uint32_t)offset); + break; + default: + g_assert_not_reached(); + } + return true; +} + +static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r(s, arg); + tcg_out_op_global_set_r(s, ret); + break; + default: + g_assert_not_reached(); + } + return true; +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long arg) +{ + switch (type) { + case TCG_TYPE_I32: + tcg_out_op_i64_const(s, (int32_t)arg); + break; + case TCG_TYPE_I64: + tcg_out_op_i64_const(s, arg); + break; + default: + g_assert_not_reached(); + } + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_extend8_s(s); + tcg_out_op_global_set_r(s, rd); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_const(s, 0xff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, rd); +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_extend16_s(s); + tcg_out_op_global_set_r(s, rd); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_const(s, 0xffff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, rd); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, rd); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, rd); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32s(s, rd, rs); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, rd); +} + +static void tcg_out_extrh_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_op_global_get_r(s, rs); + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_shr_u(s); + tcg_out_op_global_set_r(s, rd); +} + +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + tcg_out_op_cond_i64(s, cond, arg1, arg2); + tcg_out_op_i64_extend_i32_u(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) +{ + tcg_out_op_cond_i64(s, cond, c1, c2); + tcg_out_op_if_ret_i64(s); + tcg_out_op_global_get_r(s, v1); + tcg_out_op_else(s); + tcg_out_op_global_get_r(s, v2); + tcg_out_op_end(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_add2(TCGContext *s, TCGReg retl, TCGReg reth, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +{ + // add higer + tcg_out_op_global_get_r(s, ah); + tcg_out_op_global_get_r(s, bh); + tcg_out_op_i64_add(s); + + // add lower + tcg_out_op_global_get_r(s, al); + tcg_out_op_global_get_r(s, bl); + tcg_out_op_i64_add(s); + + // get carry + if ((al == retl) && (bl == retl)) { + tcg_out_op_local_set(s, TMP64_0_IDX); + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_global_get_r(s, al); + tcg_out_op_i64_lt_u(s); + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_global_set_r(s, retl); + } else { + tcg_out_op_global_set_r(s, retl); + tcg_out_op_global_get_r(s, retl); + if (al == retl) { + tcg_out_op_global_get_r(s, bl); + } else { + tcg_out_op_global_get_r(s, al); + } + tcg_out_op_i64_lt_u(s); + } + + // add carry to higher + tcg_out_op_i64_add(s); + tcg_out_op_global_set_r(s, reth); +} + +static void tcg_out_sub2(TCGContext *s, TCGReg retl, TCGReg reth, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +{ + // sub higher + tcg_out_op_global_get_r(s, ah); + tcg_out_op_global_get_r(s, bh); + tcg_out_op_i64_sub(s); + + // sub lower + tcg_out_op_global_get_r(s, al); + tcg_out_op_global_get_r(s, bl); + tcg_out_op_i64_sub(s); + + // get underflow + if (al == retl) { + tcg_out_op_local_set(s, TMP64_0_IDX); + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_global_get_r(s, al); + tcg_out_op_i64_gt_u(s); + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_global_set_r(s, retl); + } else { + tcg_out_op_global_set_r(s, retl); + + tcg_out_op_global_get_r(s, retl); + tcg_out_op_global_get_r(s, al); + tcg_out_op_i64_gt_u(s); + } + + tcg_out_op_i64_sub(s); + tcg_out_op_global_set_r(s, reth); +} + +static void tcg_out_mulu2_i32(TCGContext *s, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2) +{ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_mul(s); + tcg_out_op_set_r_as_i64(s, retl, reth); +} + +static void tcg_out_muls2_i32(TCGContext *s, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2) +{ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_mul(s); + tcg_out_op_set_r_as_i64(s, retl, reth); +} + +static void tcg_out_muluh_i32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2) +{ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_mul(s); + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_shr_u(s); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_mulsh_i32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2) +{ + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_mul(s); + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_shr_u(s); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_set_r(s, ret); +} + +static void tcg_out_ctpop(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_op_global_get_r(s, src); + tcg_out_op_i64_popcnt(s); + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_deposit(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len) +{ + int mask = ((1< 0) { + tcg_out_op_i64_const(s, sl); + tcg_out_op_i64_shl(s); + } + tcg_out_op_i64_const(s, rs); + if (sign) { + tcg_out_op_i64_shr_s(s); + } else { + tcg_out_op_i64_shr_u(s); + } + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_extract2_i32(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos) +{ + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_const(s, 32-pos); + tcg_out_op_i64_shl(s); + + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_out_op_i64_const(s, pos); + tcg_out_op_i64_shr_u(s); + + tcg_out_op_i64_or(s); + + tcg_out_op_i64_const(s, 0xffffffff); + tcg_out_op_i64_and(s); + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_extract2_i64(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos) +{ + tcg_out_op_global_get_r(s, arg2); + tcg_out_op_i64_const(s, 64-pos); + tcg_out_op_i64_shl(s); + + tcg_out_op_global_get_r(s, arg1); + tcg_out_op_i64_const(s, pos); + tcg_out_op_i64_shr_u(s); + + tcg_out_op_i64_or(s); + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_bswap64(TCGContext *s, TCGReg dest, TCGReg src, int flags) +{ + tcg_out_op_global_get_r(s, src); // ABCDEFGH + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_rotr(s); + tcg_out_op_local_set(s, TMP64_0_IDX); // EFGHABCD + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i64_const(s, 0xf000f000); + tcg_out_op_i64_and(s); + tcg_out_op_i64_const(s, 12); + tcg_out_op_i64_shr_u(s); // ___E___A + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i64_const(s, 0x0f000f00); + tcg_out_op_i64_and(s); + tcg_out_op_i64_const(s, 4); + tcg_out_op_i64_shr_u(s); // __F___B_ + + tcg_out_op_i64_or(s); + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i64_const(s, 0x00f000f0); + tcg_out_op_i64_and(s); + tcg_out_op_i64_const(s, 4); + tcg_out_op_i64_shl(s); // _G___C__ + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i64_const(s, 0x000f000f); + tcg_out_op_i64_and(s); + tcg_out_op_i64_const(s, 12); + tcg_out_op_i64_shl(s); // H___D___ + + tcg_out_op_i64_or(s); + + tcg_out_op_i64_or(s); // HGFEDCBA + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_bswap32(TCGContext *s, TCGReg dest, TCGReg src, int flags) +{ + tcg_out_op_global_get_r(s, src); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_local_set(s, TMP32_LOCAL_0_IDX); + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); // ABCD + tcg_out_op_i32_const(s, 16); + tcg_out_op_i32_rotr(s); + tcg_out_op_local_set(s, TMP32_LOCAL_0_IDX); // CDAB + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_const(s, 0xff00ff00); + tcg_out_op_i32_and(s); + tcg_out_op_i32_const(s, 8); + tcg_out_op_i32_shr_u(s); // _C_A + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_const(s, 0x00ff00ff); + tcg_out_op_i32_and(s); + tcg_out_op_i32_const(s, 8); + tcg_out_op_i32_shl(s); // D_B_ + + tcg_out_op_i32_or(s); // DCBA + tcg_out_op_i64_extend_i32_u(s); + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_bswap16(TCGContext *s, TCGReg dest, TCGReg src, int flags) +{ + tcg_out_op_global_get_r(s, src); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_local_set(s, TMP32_LOCAL_0_IDX); + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); // __AB + tcg_out_op_i32_const(s, 8); + tcg_out_op_i32_rotr(s); + tcg_out_op_local_set(s, TMP32_LOCAL_0_IDX); // B__A + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_const(s, 0x000000ff); + tcg_out_op_i32_and(s); // ___A + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_const(s, 0xff000000); + tcg_out_op_i32_and(s); + tcg_out_op_i32_const(s, 24); + if (flags & TCG_BSWAP_OS) { + tcg_out_op_i32_shr_s(s); // SSB_ + } else { + tcg_out_op_i32_shr_u(s); // 00B_ + } + + tcg_out_op_i32_or(s); // **BA + tcg_out_op_i64_extend_i32_u(s); + tcg_out_op_global_set_r(s, dest); +} + +static void tcg_out_ctx_i32_store_const(TCGContext *s, int off, int32_t v) +{ + tcg_out_op_local_get(s, CTX_IDX); + tcg_out_op_i32_const(s, v); + tcg_out_op_i32_store(s, 0, off); +} + +static void tcg_out_ctx_i32_store_r(TCGContext *s, int off, TCGReg r0) +{ + tcg_out_op_local_get(s, CTX_IDX); + tcg_out_op_global_get_r(s, r0); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i32_store(s, 0, off); +} + +static void tcg_out_ctx_i32_load(TCGContext *s, int off) +{ + tcg_out_op_local_get(s, CTX_IDX); + tcg_out_op_i32_load(s, 0, off); +} + +static void tcg_out_label_idx(TCGContext *s, int label) +{ + int block_idx = wasm_alloc_block_idx(s); + wasm_add_label_context(s, label, block_idx); + + tcg_out_op_end(s); // end if of the previous block + + // following block + tcg_out_op_global_get(s, BLOCK_PTR_IDX); + tcg_out_op_i64_const(s, block_idx); + tcg_out_op_i64_le_u(s); + tcg_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_out_label_idx(s, l->id + 1); +} + +static void tcg_out_op_br_to_label(TCGContext *s, TCGLabel *l, bool br_if) +{ + int toploop_depth = 1; + if (br_if) { + tcg_out_op_if_noret(s); + toploop_depth++; + } + tcg_out8(s, 0x42); // i64.const + wasm_add_label_block_ptr_placeholder(s, l->id + 1); + tcg_out8(s, 0x80); // filled before instantiation + tcg_out8(s, 0x80); + tcg_out8(s, 0x80); + tcg_out8(s, 0x80); + tcg_out8(s, 0x00); + tcg_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_out_op_br(s, toploop_depth); // br to the top of loop + } else { + tcg_out_op_br(s, toploop_depth - 1); // br to the end of the current block + } + if (br_if) { + tcg_out_op_end(s); + } +} + +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_op_br_to_label(s, l, false); +} + +static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, + TCGReg arg2, TCGLabel *l) +{ + tcg_out_op_cond_i64(s, cond, arg1, arg2); + tcg_out_op_br_to_label(s, l, true); +} + +static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) +{ + tcg_out_ctx_i32_store_const(s, TB_PTR_OFF, 0); + tcg_out_op_i32_const(s, (int32_t)arg); + tcg_out_op_return(s); +} + +static void tcg_out_goto_ptr(TCGContext *s, TCGReg arg) +{ + tcg_out_op_global_get_r(s, arg); + tcg_out_op_i32_wrap_i64(s); + tcg_out_ctx_i32_load(s, TB_PTR_OFF); + tcg_out_op_i32_eq(s); + tcg_out_op_if_noret(s); + tcg_out_op_i64_const(s, 0); + tcg_out_op_global_set(s, BLOCK_PTR_IDX); + tcg_out_op_br(s, 2); // br to the top of loop + tcg_out_op_end(s); + + tcg_out_ctx_i32_store_r(s, TB_PTR_OFF, arg); + tcg_out_ctx_i32_store_const(s, DO_INIT_OFF, 1); + tcg_out_op_i32_const(s, 0); + tcg_out_op_return(s); +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + tcg_out_op_i32_const(s, (int32_t)get_jmp_target_addr(s, which)); + tcg_out_op_i32_load(s, 0, 0); + tcg_out_op_local_set(s, TMP32_LOCAL_0_IDX); + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_const(s, 0); + tcg_out_op_i32_ne(s); + tcg_out_op_if_noret(s); + + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_ctx_i32_load(s, TB_PTR_OFF); + tcg_out_op_i32_eq(s); + tcg_out_op_if_noret(s); + tcg_out_op_i64_const(s, 0); + tcg_out_op_global_set(s, BLOCK_PTR_IDX); + tcg_out_op_br(s, 3); // br to the top of loop + tcg_out_op_end(s); + + // store jmp target address to buf + tcg_out_op_local_get(s, CTX_IDX); + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + tcg_out_op_i32_store(s, 0, TB_PTR_OFF); + tcg_out_ctx_i32_store_const(s, DO_INIT_OFF, 1); + + tcg_out_op_i32_const(s, 0); + tcg_out_op_return(s); + tcg_out_op_end(s); + + set_jmp_reset_offset(s, which); +} + +static void push_arg_i64(TCGContext *s, int *reg_idx, int *stack_offset) { + if (*reg_idx < NUM_OF_IARG_REGS) { + tcg_out_op_global_get_r(s, REG_INDEX_IARG_BASE + *reg_idx); // arg register + int addend = 1; + *reg_idx = *reg_idx + addend; + } else { + tcg_out_op_global_get_r(s, TCG_REG_CALL_STACK); + tcg_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_out_op_global_get_r(s, TCG_REG_CALL_STACK); + } + + 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 = true; + 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_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: + // push current 128stack pointer + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i32_const(s, stack128_base); + tcg_out_op_i32_add(s); + + // copy data to 128stack + if (!cached_128base) { + tcg_out_ctx_i32_load(s, STACK128_OFF); + tcg_out_op_i64_extend_i32_s(s); + tcg_out_op_local_set(s, TMP64_0_IDX); + cached_128base = true; + } + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i32_wrap_i64(s); + push_arg_i64(s, ®_idx, &stack_offset); + tcg_out_op_i64_store(s, 0, stack128_base); + stack128_base += 8; + + tcg_out_op_local_get(s, TMP64_0_IDX); + tcg_out_op_i32_wrap_i64(s); + push_arg_i64(s, ®_idx, &stack_offset); + tcg_out_op_i64_store(s, 0, stack128_base); + stack128_base += 8; + break; + default: + g_assert_not_reached(); + } + } + + tcg_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_out_op_i64_extend_i32_s(s); + tcg_out_op_global_set_r(s, TCG_REG_A0); + break; + case dh_typecode_i64: + case dh_typecode_s64: + tcg_out_op_global_set_r(s, TCG_REG_A0); + break; + case dh_typecode_i128: + tcg_out_op_global_get_r(s, TCG_REG_CALL_STACK); + tcg_out_op_i64_load(s, 0, stack_offset); + tcg_out_op_global_set_r(s, TCG_REG_A0); + stack_offset += 8; + + tcg_out_op_global_get_r(s, TCG_REG_CALL_STACK); + tcg_out_op_i64_load(s, 0, stack_offset); + tcg_out_op_global_set_r(s, TCG_REG_A1); + 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_out_call(TCGContext *s, const tcg_insn_unit *func, + const TCGHelperInfo *info) +{ + // set return position + tcg_out_ctx_i32_load(s, HELPER_RET_TB_PTR_OFF); + tcg_out_op_i32_const(s, (int32_t)s->code_buf + tcg_current_code_size(s)); + tcg_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_out_op_i64_const(s, target_block_idx); + tcg_out_op_global_set(s, BLOCK_PTR_IDX); + tcg_out_op_end(s); // close if of this block (rewind skips this) + env_cached = false; + + int block_idx = wasm_alloc_block_idx(s); + tcg_out_op_global_get(s, BLOCK_PTR_IDX); + tcg_out_op_i64_const(s, block_idx); + tcg_out_op_i64_le_u(s); + tcg_out_op_if_noret(s); + tcg_out_ctx_i32_store_const(s, UNWINDING_OFF, 0); + gen_func_wrapper_code(s, func, info, func_idx); + + tcg_out_op_i32_const(s, 1); + tcg_out_ctx_i32_load(s, UNWINDING_OFF); + tcg_out_op_i32_eq(s); + tcg_out_op_if_noret(s); + tcg_out_op_i32_const(s, 0); + tcg_out_op_return(s); + tcg_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_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 add_off = offsetof(CPUTLBEntry, addend); + + tcg_out_op_global_get_r(s, addr); + tcg_out_op_i64_const(s, s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_op_i64_shr_u(s); + + tcg_out_op_global_get_r_i32(s, TCG_AREG0); + int off = mask_ofs; + if ((int64_t)off < 0) { + tcg_out_op_i32_const(s, (int64_t)off); + tcg_out_op_i32_add(s); + off = 0; + } + tcg_out_op_i64_load(s, 0, (uint64_t)off); + tcg_out_op_local_set(s, TMP64_2_IDX); + + tcg_out_op_local_get(s, TMP64_2_IDX); + + tcg_out_op_i64_and(s); + + tcg_out_op_local_get(s, TMP64_2_IDX); + tcg_out_op_i64_const(s, 32); + tcg_out_op_i64_shr_u(s); + tcg_out_op_i64_add(s); + + tcg_out_op_i32_wrap_i64(s); + tcg_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_out_op_i32_const(s, (int64_t)off); + tcg_out_op_i32_add(s); + off = 0; + } + tcg_out_op_i64_load(s, 0, (uint64_t)off); + + tcg_out_op_global_get_r(s, addr); + if (a_mask < s_mask) { + tcg_out_op_i64_const(s, s_mask - a_mask); + tcg_out_op_i64_add(s); + } + compare_mask = (uint64_t)s->page_mask | a_mask; + tcg_out_op_i64_const(s, compare_mask); + tcg_out_op_i64_and(s); + + tcg_out_op_i64_eq(s); + + tcg_out_op_i64_const(s, 0); + tcg_out_op_local_set(s, TMP64_0_IDX); + + tcg_out_op_if_noret(s); + tcg_out_op_local_get(s, TMP32_LOCAL_0_IDX); + off = add_off; + if ((int64_t)off < 0) { + tcg_out_op_i32_const(s, (int64_t)off); + tcg_out_op_i32_add(s); + off = 0; + } + tcg_out_op_i32_load(s, 0, (uint64_t)off); + tcg_out_op_i64_extend_i32_u(s); + tcg_out_op_global_get_r(s, addr); + tcg_out_op_i64_add(s); + tcg_out_op_local_set(s, TMP64_0_IDX); + + tcg_out_op_end(s); + + + return TMP64_0_IDX; +} + +static void tcg_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_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load8_u(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_SB: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load8_s(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_UW: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load16_u(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_SW: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load16_s(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_UL: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load32_u(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_SL: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load32_s(s, 0, 0); + tcg_out_op_global_set_r(s, r); + break; + case MO_UQ: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_i64_load(s, 0, 0); + tcg_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_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_out_tlb_load(s, addr_reg, oi, true); + + tcg_out_op_local_get(s, base); + tcg_out_op_i64_eqz(s); + tcg_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_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0); + + tcg_out_op_else(s); + + // fast path + tcg_out_qemu_ld_direct(s, data_reg, base, opc); + + tcg_out_op_end(s); + + int target_block_idx = wasm_block_current_idx(s) + 1; + tcg_out_op_i64_const(s, target_block_idx); + tcg_out_op_global_set(s, BLOCK_PTR_IDX); + + tcg_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_out_op_global_get(s, BLOCK_PTR_IDX); + tcg_out_op_i64_const(s, block_idx); + tcg_out_op_i64_le_u(s); + tcg_out_op_if_noret(s); + + tcg_out_op_local_get(s, base); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_noret(s); + + // call helper + tcg_out_op_global_get_r(s, TCG_AREG0); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, addr_reg); + tcg_out_op_i32_const(s, oi); + tcg_out_op_i32_const(s, (int32_t)s->code_buf + tcg_current_code_size(s)); + tcg_out_op_call(s, func_idx); + tcg_out_op_global_set_r(s, data_reg); + tcg_out_ctx_i32_load(s, DONE_FLAG_OFF); + tcg_out_op_i32_eqz(s); + + tcg_out_op_if_noret(s); + tcg_out_op_i32_const(s, 0); + tcg_out_op_return(s); + tcg_out_op_end(s); + tcg_out_op_end(s); +} + +static void tcg_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_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, lo); + tcg_out_op_i64_store8(s, 0, 0); + break; + case MO_16: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, lo); + tcg_out_op_i64_store16(s, 0, 0); + break; + case MO_32: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, lo); + tcg_out_op_i64_store32(s, 0, 0); + break; + case MO_64: + tcg_out_op_local_get(s, base); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, lo); + tcg_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_UB: + return helper_stb_mmu; + case MO_UW: + return helper_stw_mmu; + case MO_UL: + return helper_stl_mmu; + case MO_UQ: + return helper_stq_mmu; + default: + g_assert_not_reached(); + } +} + +static void tcg_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_out_tlb_load(s, addr_reg, oi, false); + + tcg_out_op_local_get(s, base); + tcg_out_op_i64_eqz(s); + tcg_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_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0); + + tcg_out_op_else(s); + + // fast path + tcg_out_qemu_st_direct(s, data_reg, base, opc); + + tcg_out_op_end(s); + + int target_block_idx = wasm_block_current_idx(s) + 1; + tcg_out_op_i64_const(s, target_block_idx); + tcg_out_op_global_set(s, BLOCK_PTR_IDX); + + tcg_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_out_op_global_get(s, BLOCK_PTR_IDX); + tcg_out_op_i64_const(s, block_idx); + tcg_out_op_i64_le_u(s); + tcg_out_op_if_noret(s); + + tcg_out_op_local_get(s, base); + tcg_out_op_i64_eqz(s); + tcg_out_op_if_noret(s); + + // call helper + tcg_out_op_global_get_r(s, TCG_AREG0); + tcg_out_op_i32_wrap_i64(s); + tcg_out_op_global_get_r(s, addr_reg); + MemOp mop = get_memop(oi); + switch (mop & MO_SSIZE) { + case MO_UQ: + tcg_out_op_global_get_r(s, data_reg); + break; + default: + tcg_out_op_global_get_r(s, data_reg); + tcg_out_op_i32_wrap_i64(s); + break; + } + tcg_out_op_i32_const(s, oi); + tcg_out_op_i32_const(s, (int32_t)s->code_buf + tcg_current_code_size(s)); + tcg_out_op_call(s, func_idx); + + tcg_out_ctx_i32_load(s, DONE_FLAG_OFF); + tcg_out_op_i32_eqz(s); + tcg_out_op_if_noret(s); + tcg_out_op_i32_const(s, 0); + tcg_out_op_return(s); + tcg_out_op_end(s); + tcg_out_op_end(s); +} + +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, args[0]); + break; + case INDEX_op_br: + tcg_out_br(s, arg_label(args[0])); + break; + case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + tcg_out_setcond(s, args[3], args[0], args[1], args[2]);// + break; + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + tcg_out_movcond(s, 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, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ld8u(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ld16s(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ld16u(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_ld32u_i64: + tcg_out_ld32u(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32s_i64: + tcg_out_ld32s(s, 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, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_st16(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_st32(s, 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, args[0], args[1], args[2]); + break; + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + tcg_out_i64_calc_sub(s, args[0], args[1], args[2]); + break; + case INDEX_op_mul_i32: + case INDEX_op_mul_i64: + tcg_out_i64_calc_mul(s, args[0], args[1], args[2]); + break; + case INDEX_op_and_i32: + case INDEX_op_and_i64: + tcg_out_i64_calc_and(s, args[0], args[1], args[2]); + break; + case INDEX_op_or_i32: + case INDEX_op_or_i64: + tcg_out_i64_calc_or(s, args[0], args[1], args[2]); + break; + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: + tcg_out_i64_calc_xor(s, args[0], args[1], args[2]); + break; + case INDEX_op_shl_i32: + case INDEX_op_shl_i64: + tcg_out_i64_calc_shl(s, args[0], args[1], args[2]); + break; + case INDEX_op_shr_i32: + case INDEX_op_shr_i64: + tcg_out_i64_calc_shr_u(s, args[0], args[1], args[2]); + break; + case INDEX_op_sar_i32: + case INDEX_op_sar_i64: + tcg_out_i64_calc_shr_s(s, args[0], args[1], args[2]); + break; + case INDEX_op_rotl_i32: + tcg_out_i32_rotl(s, args[0], args[1], args[2]); + break; + case INDEX_op_rotl_i64: + tcg_out_i64_calc_rotl(s, args[0], args[1], args[2]); + break; + case INDEX_op_rotr_i32: + tcg_out_i32_rotr(s, args[0], args[1], args[2]); + break; + case INDEX_op_rotr_i64: + tcg_out_i64_calc_rotr(s, args[0], args[1], args[2]); + break; + case INDEX_op_div_i32: + case INDEX_op_div_i64: + tcg_out_i64_calc_div_s(s, args[0], args[1], args[2]); + break; + case INDEX_op_divu_i32: + case INDEX_op_divu_i64: + tcg_out_i64_calc_div_u(s, args[0], args[1], args[2]); + break; + case INDEX_op_rem_i32: + case INDEX_op_rem_i64: + tcg_out_i64_calc_rem_s(s, args[0], args[1], args[2]); + break; + case INDEX_op_remu_i32: + case INDEX_op_remu_i64: + tcg_out_i64_calc_rem_u(s, args[0], args[1], args[2]); + break; + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + tcg_out_andc(s, args[0], args[1], args[2]); + break; + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + tcg_out_orc(s, args[0], args[1], args[2]); + break; + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + tcg_out_eqv(s, args[0], args[1], args[2]); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out_nand(s, args[0], args[1], args[2]); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out_nor(s, args[0], args[1], args[2]); + break; + case INDEX_op_clz_i32: + tcg_out_clz32(s, args[0], args[1], args[2]); + break; + case INDEX_op_clz_i64: + tcg_out_clz64(s, args[0], args[1], args[2]); + break; + case INDEX_op_ctz_i32: + tcg_out_ctz32(s, args[0], args[1], args[2]); + break; + case INDEX_op_ctz_i64: + tcg_out_ctz64(s, args[0], args[1], args[2]); + break; + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + tcg_out_brcond(s, 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, args[0], args[1]); + break; + case INDEX_op_not_i32: + case INDEX_op_not_i64: + tcg_out_not(s, args[0], args[1]); + break; + case INDEX_op_ctpop_i32: + case INDEX_op_ctpop_i64: + tcg_out_ctpop(s, args[1], args[2]); + break; + case INDEX_op_add2_i32: + case INDEX_op_add2_i64: + tcg_out_add2(s, 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, args[0], args[1], args[2], args[3], args[4], args[5]); + break; + case INDEX_op_muls2_i32: + tcg_out_muls2_i32(s, args[0], args[1], args[2], args[3]); + break; + case INDEX_op_mulu2_i32: + tcg_out_mulu2_i32(s, args[0], args[1], args[2], args[3]); + break; + case INDEX_op_mulsh_i32: + tcg_out_mulsh_i32(s, args[0], args[1], args[2]); + break; + case INDEX_op_muluh_i32: + tcg_out_muluh_i32(s, args[0], args[1], args[2]); + break; + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + tcg_out_bswap16(s, args[0], args[1], args[2]); + break; + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + tcg_out_bswap32(s, args[0], args[1], args[2]); + break; + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, args, false); + break; + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, args, false); + break; + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, args, true); + break; + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, args, true); + break; + case INDEX_op_mb: + break; + case INDEX_op_deposit_i32: + case INDEX_op_deposit_i64: + tcg_out_deposit(s, args[0], args[1], args[2], args[3], args[4]); + break; + case INDEX_op_extract_i32: + tcg_out_extract(s, args[0], args[1], args[2], args[3], TCG_TYPE_I32, false); + break; + case INDEX_op_extract_i64: + tcg_out_extract(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64, false); + break; + case INDEX_op_sextract_i32: + tcg_out_extract(s, args[0], args[1], args[2], args[3], TCG_TYPE_I32, true); + break; + case INDEX_op_sextract_i64: + tcg_out_extract(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64, true); + break; + case INDEX_op_extrl_i64_i32: + tcg_out_extrl_i64_i32(s, args[0], args[1]); + break; + case INDEX_op_extrh_i64_i32: + tcg_out_extrh_i64_i32(s, args[0], args[1]); + break; + case INDEX_op_extract2_i32: + tcg_out_extract2_i32(s, args[0], args[1], args[2], args[3]); + break; + case INDEX_op_extract2_i64: + tcg_out_extract2_i64(s, args[0], args[1], args[2], args[3]); + break; + default: + g_assert_not_reached(); + } +} + +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, TCGType type, int ct) +{ + return ct & TCG_CT_CONST; +} + +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] = 0xffffff; + tcg_target_available_regs[TCG_TYPE_I32] = 0xffffff; + /* + * 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_A0, 64 / 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..b6c9f395be --- /dev/null +++ b/tcg/wasm32/tcg-target.h @@ -0,0 +1,158 @@ +/* + * 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 1 +#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_muls2_i32 1 +#define TCG_TARGET_HAS_muluh_i32 1 +#define TCG_TARGET_HAS_mulsh_i32 1 +#define TCG_TARGET_HAS_qemu_st8_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 + +#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_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_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_andc_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 1 +#define TCG_TARGET_HAS_extrl_i64_i32 1 +#define TCG_TARGET_HAS_extrh_i64_i32 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_movcond_i64 1 +#define TCG_TARGET_HAS_muls2_i64 0 +#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_st8_i64 0 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +/* Number of registers available. */ +#define TCG_TARGET_NB_REGS 24 + +/* 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, + + // Arguments + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, + + 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_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..868b9a2d2b --- /dev/null +++ b/util/coroutine-fiber.c @@ -0,0 +1,121 @@ +/* + * 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_); + + set_unwinding_flag(); + + 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)