TCG: add wasm backend

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
Kohei Tokunaga
2024-08-28 10:36:46 +09:00
committed by Evan Reichard
parent 40edccac41
commit 2816a551ef
29 changed files with 3656 additions and 38 deletions
+47 -5
View File
@@ -12,32 +12,52 @@
* Load helpers for tcg-ldst.h * 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, tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); 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, tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); 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, tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); 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, uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); 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) MemOpIdx oi, uintptr_t retaddr)
{ {
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); 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) 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); tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8);
do_st1_mmu(env_cpu(env), addr, val, oi, ra); 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, 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); tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
do_st2_mmu(env_cpu(env), addr, val, oi, retaddr); 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, 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); tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
do_st4_mmu(env_cpu(env), addr, val, oi, retaddr); 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, 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); tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
do_st8_mmu(env_cpu(env), addr, val, oi, retaddr); 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, 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); tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); 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) void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi)
{ {
helper_st16_mmu(env, addr, val, oi, GETPC()); helper_st16_mmu(env, addr, val, oi, GETPC());
#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
set_done_flag();
#endif
} }
/* /*
+4
View File
@@ -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) void tb_reset_jump(TranslationBlock *tb, int n)
{ {
uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[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); tb_set_jmp_target(tb, n, addr);
#endif
} }
/* remove any jumps to the TB */ /* remove any jumps to the TB */
+12
View File
@@ -110,6 +110,10 @@
#include <sys/diskslice.h> #include <sys/diskslice.h>
#endif #endif
#ifdef EMSCRIPTEN
#include <sys/ioctl.h>
#endif
/* OS X does not have O_DSYNC */ /* OS X does not have O_DSYNC */
#ifndef O_DSYNC #ifndef O_DSYNC
#ifdef O_SYNC #ifdef O_SYNC
@@ -2002,6 +2006,13 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque)
return handle_aiocb_write_zeroes(aiocb); 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 #ifndef HAVE_COPY_FILE_RANGE
static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, 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) 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 #endif
#endif
/* /*
* parse_zone - Fill a zone descriptor * parse_zone - Fill a zone descriptor
Vendored
+7 -1
View File
@@ -340,7 +340,9 @@ int main(void) { return 0; }
EOF EOF
} }
if check_define __linux__ ; then if check_define __EMSCRIPTEN__ ; then
host_os=linux
elif check_define __linux__ ; then
host_os=linux host_os=linux
elif check_define _WIN32 ; then elif check_define _WIN32 ; then
host_os=windows host_os=windows
@@ -526,6 +528,10 @@ case "$cpu" in
linux_arch=x86 linux_arch=x86
CPU_CFLAGS="-m64" CPU_CFLAGS="-m64"
;; ;;
wasm32)
host_arch=wasm32
CPU_CFLAGS="-m32"
;;
esac esac
# Now we have our CPU_CFLAGS we can check if we are targeting a 32 or # Now we have our CPU_CFLAGS we can check if we are targeting a 32 or
+1 -1
View File
@@ -42,7 +42,7 @@ static void initialize_debug_host(CPUDebug *s)
#else #else
s->info.endian = BFD_ENDIAN_LITTLE; s->info.endian = BFD_ENDIAN_LITTLE;
#endif #endif
#if defined(CONFIG_TCG_INTERPRETER) #if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
s->info.print_insn = print_insn_tci; s->info.print_insn = print_insn_tci;
#elif defined(__i386__) #elif defined(__i386__)
s->info.mach = bfd_mach_i386_i386; s->info.mach = bfd_mach_i386_i386;
+1 -1
View File
@@ -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); 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. */ /* 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; extern __thread uintptr_t tci_tb_ptr;
# define GETPC() tci_tb_ptr # define GETPC() tci_tb_ptr
#else #else
+8 -9
View File
@@ -75,7 +75,6 @@
#else #else
# define ATOMIC_REG_SIZE sizeof(void *) # define ATOMIC_REG_SIZE sizeof(void *)
#endif #endif
/* Weak atomic operations prevent the compiler moving other /* Weak atomic operations prevent the compiler moving other
* loads/stores past the atomic operation load/store. However there is * loads/stores past the atomic operation load/store. However there is
* no explicit memory barrier for the processor. * no explicit memory barrier for the processor.
@@ -91,7 +90,7 @@
#define qatomic_read(ptr) \ #define qatomic_read(ptr) \
({ \ ({ \
qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ /*qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE);*/ \
qatomic_read__nocheck(ptr); \ qatomic_read__nocheck(ptr); \
}) })
@@ -99,7 +98,7 @@
__atomic_store_n(ptr, i, __ATOMIC_RELAXED) __atomic_store_n(ptr, i, __ATOMIC_RELAXED)
#define qatomic_set(ptr, i) do { \ #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); \ qatomic_set__nocheck(ptr, i); \
} while(0) } while(0)
@@ -122,7 +121,7 @@
*/ */
#define qatomic_rcu_read_internal(ptr, _val) \ #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; \ typeof_strip_qual(*ptr) _val; \
qatomic_rcu_read__nocheck(ptr, &_val); \ qatomic_rcu_read__nocheck(ptr, &_val); \
_val; \ _val; \
@@ -131,20 +130,20 @@
qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val))
#define qatomic_rcu_set(ptr, i) do { \ #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); \ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \
} while(0) } while(0)
#define qatomic_load_acquire(ptr) \ #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; \ typeof_strip_qual(*ptr) _val; \
__atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \
_val; \ _val; \
}) })
#define qatomic_store_release(ptr, i) do { \ #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); \ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \
} while(0) } while(0)
@@ -156,7 +155,7 @@
}) })
#define qatomic_xchg(ptr, i) ({ \ #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); \ qatomic_xchg__nocheck(ptr, i); \
}) })
@@ -169,7 +168,7 @@
}) })
#define qatomic_cmpxchg(ptr, old, new) ({ \ #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); \ qatomic_cmpxchg__nocheck(ptr, old, new); \
}) })
+1 -1
View File
@@ -19,7 +19,7 @@
* mappings of the same physical page(s). * 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) static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len)
{ {
+1 -1
View File
@@ -8,7 +8,7 @@
* But libffi does not support __int128_t, and therefore cannot pass * But libffi does not support __int128_t, and therefore cannot pass
* or return values of this type, force use of the Int128 struct. * 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 Int128;
typedef __int128_t __attribute__((aligned(16))) Int128Aligned; typedef __int128_t __attribute__((aligned(16))) Int128Aligned;
+4 -2
View File
@@ -133,7 +133,7 @@ QEMU_EXTERN_C int daemon(int, int);
#include <setjmp.h> #include <setjmp.h>
#include <signal.h> #include <signal.h>
#ifdef CONFIG_IOVEC #if defined(CONFIG_IOVEC) || defined(EMSCRIPTEN)
#include <sys/uio.h> #include <sys/uio.h>
#endif #endif
@@ -281,6 +281,7 @@ void QEMU_ERROR("code path is reachable")
#ifndef WCOREDUMP #ifndef WCOREDUMP
#define WCOREDUMP(status) 0 #define WCOREDUMP(status) 0
#endif #endif
#ifndef EMSCRIPTEN
/* /*
* We have a lot of unaudited code that may fail in strange ways, or * 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 * 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 #ifdef G_DISABLE_ASSERT
#error building with G_DISABLE_ASSERT is not supported #error building with G_DISABLE_ASSERT is not supported
#endif #endif
#endif
#ifndef OFF_MAX #ifndef OFF_MAX
#define OFF_MAX (sizeof (off_t) == 8 ? INT64_MAX : INT32_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); int qemu_get_thread_id(void);
#ifndef CONFIG_IOVEC #if !defined(CONFIG_IOVEC) && !defined(EMSCRIPTEN)
struct iovec { struct iovec {
void *iov_base; void *iov_base;
size_t iov_len; size_t iov_len;
+1 -1
View File
@@ -1010,7 +1010,7 @@ static inline size_t tcg_current_code_size(TCGContext *s)
#define TB_EXIT_IDXMAX 1 #define TB_EXIT_IDXMAX 1
#define TB_EXIT_REQUESTED 3 #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); uintptr_t tcg_qemu_tb_exec(CPUArchState *env, const void *tb_ptr);
#else #else
typedef uintptr_t tcg_prologue_fn(CPUArchState *env, const void *tb_ptr); typedef uintptr_t tcg_prologue_fn(CPUArchState *env, const void *tb_ptr);
+4 -2
View File
@@ -47,7 +47,7 @@ qapi_trace_events = []
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin'] bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', 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() cpu = host_machine.cpu_family()
@@ -482,7 +482,7 @@ ucontext_probe = '''
# On Windows the only valid backend is the Windows specific one. # On Windows the only valid backend is the Windows specific one.
# For POSIX prefer ucontext, but it's not always possible. The fallback # For POSIX prefer ucontext, but it's not always possible. The fallback
# is sigcontext. # is sigcontext.
supported_backends = [] supported_backends = ['fiber']
if host_os == 'windows' if host_os == 'windows'
supported_backends += ['windows'] supported_backends += ['windows']
else else
@@ -884,6 +884,8 @@ if get_option('tcg').allowed()
tcg_arch = 'i386' tcg_arch = 'i386'
elif host_arch == 'ppc64' elif host_arch == 'ppc64'
tcg_arch = 'ppc' tcg_arch = 'ppc'
elif host_arch == 'wasm32'
tcg_arch = 'wasm32'
endif endif
add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch, add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch,
language: all_languages) language: all_languages)
+1 -1
View File
@@ -34,7 +34,7 @@ option('fuzzing_engine', type : 'string', value : '',
option('trace_file', type: 'string', value: 'trace', option('trace_file', type: 'string', value: 'trace',
description: 'Trace file prefix for simple backend') description: 'Trace file prefix for simple backend')
option('coroutine_backend', type: 'combo', option('coroutine_backend', type: 'combo',
choices: ['ucontext', 'sigaltstack', 'windows', 'auto'], choices: ['ucontext', 'sigaltstack', 'windows', 'auto', 'fiber'],
value: 'auto', description: 'coroutine backend to use') value: 'auto', description: 'coroutine backend to use')
# Everything else can be set via --enable/--disable-* option # Everything else can be set via --enable/--disable-* option
+2
View File
@@ -148,11 +148,13 @@ static void change_process_uid(void)
exit(1); exit(1);
} }
if (user_pwd) { if (user_pwd) {
#ifndef EMSCRIPTEN
if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) {
error_report("Failed to initgroups(\"%s\", %d)", error_report("Failed to initgroups(\"%s\", %d)",
user_pwd->pw_name, user_pwd->pw_gid); user_pwd->pw_name, user_pwd->pw_gid);
exit(1); exit(1);
} }
#endif
} else { } else {
if (setgroups(1, &user_gid) < 0) { if (setgroups(1, &user_gid) < 0) {
error_report("Failed to setgroups(1, [%d])", error_report("Failed to setgroups(1, [%d])",
+1 -1
View File
@@ -80,7 +80,7 @@ meson_options_help() {
printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string' printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string'
printf "%s\n" ' [NORMAL]' printf "%s\n" ' [NORMAL]'
printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:' 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" ' --with-pkgversion=VALUE use specified string as sub-version of the'
printf "%s\n" ' package' printf "%s\n" ' package'
printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories' printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories'
+6
View File
@@ -15,6 +15,8 @@ tcg_ss.add(files(
'tcg-op-vec.c', 'tcg-op-vec.c',
)) ))
cpu = host_machine.cpu_family()
if get_option('tcg_interpreter') if get_option('tcg_interpreter')
libffi = dependency('libffi', version: '>=3.0', required: true, libffi = dependency('libffi', version: '>=3.0', required: true,
method: 'pkg-config') method: 'pkg-config')
@@ -29,6 +31,10 @@ endif
tcg_ss = tcg_ss.apply({}) tcg_ss = tcg_ss.apply({})
if cpu == 'wasm32'
specific_ss.add(files('wasm32.c'))
endif
libtcg_user = static_library('tcg_user', libtcg_user = static_library('tcg_user',
tcg_ss.sources() + genh, tcg_ss.sources() + genh,
dependencies: tcg_ss.dependencies(), dependencies: tcg_ss.dependencies(),
+3 -3
View File
@@ -568,7 +568,7 @@ static int alloc_code_gen_buffer_anon(size_t size, int prot,
return prot; return prot;
} }
#ifndef CONFIG_TCG_INTERPRETER #if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
#ifdef CONFIG_POSIX #ifdef CONFIG_POSIX
#include "qemu/memfd.h" #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) 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 # ifdef CONFIG_DARWIN
return alloc_code_gen_buffer_splitwx_vmremap(size, errp); return alloc_code_gen_buffer_splitwx_vmremap(size, errp);
# endif # 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. * Work with the page protections set up with the initial mapping.
*/ */
need_prot = PROT_READ | PROT_WRITE; need_prot = PROT_READ | PROT_WRITE;
#ifndef CONFIG_TCG_INTERPRETER #if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
if (tcg_splitwx_diff == 0) { if (tcg_splitwx_diff == 0) {
need_prot |= host_prot_read_exec(); need_prot |= host_prot_read_exec();
} }
+352 -7
View File
@@ -131,6 +131,10 @@ static void tcg_out_goto_tb(TCGContext *s, int which);
static void tcg_out_op(TCGContext *s, TCGOpcode opc, static void tcg_out_op(TCGContext *s, TCGOpcode opc,
const TCGArg args[TCG_MAX_OP_ARGS], const TCGArg args[TCG_MAX_OP_ARGS],
const int const_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 #if TCG_TARGET_MAYBE_vec
static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
TCGReg dst, TCGReg src); TCGReg dst, TCGReg src);
@@ -245,7 +249,7 @@ TCGv_env tcg_env;
const void *tcg_code_gen_epilogue; const void *tcg_code_gen_epilogue;
uintptr_t tcg_splitwx_diff; uintptr_t tcg_splitwx_diff;
#ifndef CONFIG_TCG_INTERPRETER #if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
tcg_prologue_fn *tcg_qemu_tb_exec; tcg_prologue_fn *tcg_qemu_tb_exec;
#endif #endif
@@ -352,6 +356,9 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l)
tcg_debug_assert(!l->has_value); tcg_debug_assert(!l->has_value);
l->has_value = 1; l->has_value = 1;
l->u.value_ptr = tcg_splitwx_to_rx(s->code_ptr); 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) TCGLabel *gen_new_label(void)
@@ -420,6 +427,114 @@ tlb_mask_table_ofs(TCGContext *s, int which)
sizeof(CPUNegativeOffsetState)); 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. */ /* Signal overflow, starting over with fewer guest insns. */
static G_NORETURN static G_NORETURN
void tcg_raise_tb_overflow(TCGContext *s) void tcg_raise_tb_overflow(TCGContext *s)
@@ -937,7 +1052,7 @@ static TCGHelperInfo info_helper_st128_mmu = {
| dh_typemask(ptr, 5) /* uintptr_t ra */ | dh_typemask(ptr, 5) /* uintptr_t ra */
}; };
#ifdef CONFIG_TCG_INTERPRETER #if defined(CONFIG_TCG_INTERPRETER)
static ffi_type *typecode_to_ffi(int argmask) static ffi_type *typecode_to_ffi(int argmask)
{ {
/* /*
@@ -1406,12 +1521,11 @@ void tcg_prologue_init(void)
{ {
TCGContext *s = tcg_ctx; TCGContext *s = tcg_ctx;
size_t prologue_size; size_t prologue_size;
s->code_ptr = s->code_gen_ptr; s->code_ptr = s->code_gen_ptr;
s->code_buf = s->code_gen_ptr; s->code_buf = s->code_gen_ptr;
s->data_gen_ptr = NULL; 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); tcg_qemu_tb_exec = (tcg_prologue_fn *)tcg_splitwx_to_rx(s->code_ptr);
#endif #endif
@@ -1434,7 +1548,7 @@ void tcg_prologue_init(void)
prologue_size = tcg_current_code_size(s); prologue_size = tcg_current_code_size(s);
perf_report_prologue(s->code_gen_ptr, prologue_size); 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), flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf),
(uintptr_t)s->code_buf, prologue_size); (uintptr_t)s->code_buf, prologue_size);
#endif #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. * Assert that goto_ptr is implemented completely, setting an epilogue.
* For tci, we use NULL as the signal to return from the interpreter, * 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); 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, static void tcg_out_ld_helper_ret(TCGContext *s, const TCGLabelQemuLdst *ldst,
bool load_sign, bool load_sign,
const TCGLdstHelperParam *parm) 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->code_ptr = s->code_buf;
s->data_gen_ptr = NULL; 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 #ifdef TCG_TARGET_NEED_LDST_LABELS
QSIMPLEQ_INIT(&s->ldst_labels); QSIMPLEQ_INIT(&s->ldst_labels);
#endif #endif
@@ -6286,13 +6559,85 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start)
return -2; return -2;
} }
#ifndef CONFIG_TCG_INTERPRETER #if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
/* flush instruction cache */ /* flush instruction cache */
flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf), flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf),
(uintptr_t)s->code_buf, (uintptr_t)s->code_buf,
tcg_ptr_byte_diff(s->code_ptr, s->code_buf)); tcg_ptr_byte_diff(s->code_ptr, s->code_buf));
#endif #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); return tcg_current_code_size(s);
} }
+172
View File
@@ -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 <string.h>
#include <emscripten.h>
#include <emscripten/threading.h>
#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
+38
View File
@@ -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
+15
View File
@@ -0,0 +1,15 @@
/*
* C_On_Im(...) defines a constraint set with <n> outputs and <m> inputs.
* Each operand should be a sequence of constraint letters as defined by
* tcg-target-con-str.h; the constraint combination is inclusive or.
*/
C_O0_I1(r)
C_O0_I2(r, r)
C_O0_I3(r, r, r)
C_O0_I4(r, r, r, r)
C_O1_I1(r, r)
C_O1_I2(r, r, r)
C_O1_I4(r, r, r, r, r)
C_O2_I1(r, r, r)
C_O2_I2(r, r, r, r)
C_O2_I4(r, r, r, r, r, r)
+5
View File
@@ -0,0 +1,5 @@
/*
* Define constraint letters for register sets:
* REGS(letter, register_mask)
*/
REGS('r', MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS))
+6
View File
@@ -0,0 +1,6 @@
#ifndef TCG_TARGET_REG_BITS_H
#define TCG_TARGET_REG_BITS_H
#define TCG_TARGET_REG_BITS 64
#endif
File diff suppressed because it is too large Load Diff
+158
View File
@@ -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 */
+1 -1
View File
@@ -225,7 +225,7 @@ static void __attribute__((constructor)) init_cache_info(void)
* Architecture (+ OS) specific cache flushing mechanisms. * 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. */ /* Caches are coherent and do not require flushing; symbol inline. */
+121
View File
@@ -0,0 +1,121 @@
/*
* emscripten fiber coroutine initialization code
* based on coroutine-ucontext.c
*
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
* Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/coroutine_int.h"
#include "qemu/coroutine-tls.h"
#include <emscripten/fiber.h>
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;
}
+1 -1
View File
@@ -144,7 +144,7 @@ Int128 int128_rems(Int128 a, Int128 b)
return r; return r;
} }
#elif defined(CONFIG_TCG_INTERPRETER) #elif defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
Int128 int128_divu(Int128 a_s, Int128 b_s) Int128 int128_divu(Int128 a_s, Int128 b_s)
{ {
+6
View File
@@ -250,6 +250,11 @@ void *qemu_ram_mmap(int fd,
uint32_t qemu_map_flags, uint32_t qemu_map_flags,
off_t map_offset) 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); const size_t guard_pagesize = mmap_guard_pagesize(fd);
size_t offset, total; size_t offset, total;
void *ptr, *guardptr; void *ptr, *guardptr;
@@ -292,6 +297,7 @@ void *qemu_ram_mmap(int fd,
} }
return ptr; return ptr;
#endif
} }
void qemu_ram_munmap(int fd, void *ptr, size_t size) void qemu_ram_munmap(int fd, void *ptr, size_t size)