7874 lines
260 KiB
Diff
7874 lines
260 KiB
Diff
diff --git c/accel/tcg/cputlb.c w/accel/tcg/cputlb.c
|
|
index b76a4eac4e..a673eb2eb7 100644
|
|
--- c/accel/tcg/cputlb.c
|
|
+++ w/accel/tcg/cputlb.c
|
|
@@ -225,7 +225,7 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
|
|
rate = desc->window_max_entries * 100 / old_size;
|
|
|
|
if (rate > 70) {
|
|
- new_size = MIN(old_size << 1, 1 << CPU_TLB_DYN_MAX_BITS);
|
|
+ new_size = MIN(old_size << 1, (uint64_t)1 << CPU_TLB_DYN_MAX_BITS);
|
|
} else if (rate < 30 && window_expired) {
|
|
size_t ceil = pow2ceil(desc->window_max_entries);
|
|
size_t expected_rate = desc->window_max_entries * 100 / ceil;
|
|
diff --git c/accel/tcg/ldst_common.c.inc w/accel/tcg/ldst_common.c.inc
|
|
index ebbf380d76..3ff0b97f27 100644
|
|
--- c/accel/tcg/ldst_common.c.inc
|
|
+++ w/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 c/accel/tcg/tb-maint.c w/accel/tcg/tb-maint.c
|
|
index cc0f5afd47..1095dfd912 100644
|
|
--- c/accel/tcg/tb-maint.c
|
|
+++ w/accel/tcg/tb-maint.c
|
|
@@ -755,6 +755,18 @@ static void tb_remove(TranslationBlock *tb)
|
|
}
|
|
#endif /* CONFIG_USER_ONLY */
|
|
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN)
|
|
+#include "../../tcg/wasm32.h"
|
|
+
|
|
+static void wasm_remove_tb_instance(void *p, uint32_t hash, void *userp)
|
|
+{
|
|
+ TranslationBlock *tb = p;
|
|
+ (void)hash;
|
|
+ (void)userp;
|
|
+ remove_tb(tb->tc.ptr);
|
|
+}
|
|
+#endif
|
|
+
|
|
/* flush all the translation blocks */
|
|
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
|
{
|
|
@@ -771,6 +783,10 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
|
tcg_flush_jmp_cache(cpu);
|
|
}
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ qht_iter(&tb_ctx.htable, wasm_remove_tb_instance, NULL);
|
|
+ flush_tb_instances();
|
|
+#endif
|
|
qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
|
|
tb_remove_all();
|
|
|
|
@@ -854,7 +870,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 */
|
|
@@ -879,6 +899,10 @@ static void tb_jmp_cache_inval_tb(TranslationBlock *tb)
|
|
{
|
|
CPUState *cpu;
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ remove_tb(tb->tc.ptr);
|
|
+#endif
|
|
+
|
|
if (tb_cflags(tb) & CF_PCREL) {
|
|
/* A TB may be at any virtual address */
|
|
CPU_FOREACH(cpu) {
|
|
diff --git c/accel/tcg/tcg-accel-ops-mttcg.c w/accel/tcg/tcg-accel-ops-mttcg.c
|
|
index 49814ec4af..0715610d37 100644
|
|
--- c/accel/tcg/tcg-accel-ops-mttcg.c
|
|
+++ w/accel/tcg/tcg-accel-ops-mttcg.c
|
|
@@ -61,12 +61,18 @@ static void mttcg_force_rcu(Notifier *notify, void *data)
|
|
* variable current_cpu can be used deep in the code to find the
|
|
* current CPUState for a given thread.
|
|
*/
|
|
-
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+#include "../../tcg/wasm32.h"
|
|
+#endif
|
|
static void *mttcg_cpu_thread_fn(void *arg)
|
|
{
|
|
MttcgForceRcuNotifier force_rcu;
|
|
CPUState *cpu = arg;
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ init_wasm32();
|
|
+#endif
|
|
+
|
|
assert(tcg_enabled());
|
|
g_assert(!icount_enabled());
|
|
|
|
diff --git c/accel/tcg/tcg-accel-ops-rr.c w/accel/tcg/tcg-accel-ops-rr.c
|
|
index 8ebadf8e9e..1e38df86e8 100644
|
|
--- c/accel/tcg/tcg-accel-ops-rr.c
|
|
+++ w/accel/tcg/tcg-accel-ops-rr.c
|
|
@@ -177,11 +177,19 @@ static int rr_cpu_count(void)
|
|
* elsewhere.
|
|
*/
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+#include "../../tcg/wasm32.h"
|
|
+#endif
|
|
+
|
|
static void *rr_cpu_thread_fn(void *arg)
|
|
{
|
|
Notifier force_rcu;
|
|
CPUState *cpu = arg;
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ init_wasm32();
|
|
+#endif
|
|
+
|
|
assert(tcg_enabled());
|
|
rcu_register_thread();
|
|
force_rcu.notify = rr_force_rcu;
|
|
diff --git c/backends/meson.build w/backends/meson.build
|
|
index da714b93d1..9b88d22685 100644
|
|
--- c/backends/meson.build
|
|
+++ w/backends/meson.build
|
|
@@ -12,8 +12,10 @@ system_ss.add([files(
|
|
|
|
if host_os != 'windows'
|
|
system_ss.add(files('rng-random.c'))
|
|
- system_ss.add(files('hostmem-file.c'))
|
|
- system_ss.add([files('hostmem-shm.c'), rt])
|
|
+ if host_os != 'emscripten'
|
|
+ system_ss.add(files('hostmem-file.c'))
|
|
+ system_ss.add([files('hostmem-shm.c'), rt])
|
|
+ endif
|
|
endif
|
|
if host_os == 'linux'
|
|
system_ss.add(files('hostmem-memfd.c'))
|
|
diff --git c/block/file-posix.c w/block/file-posix.c
|
|
index 90fa54352c..0fa9cc31b6 100644
|
|
--- c/block/file-posix.c
|
|
+++ w/block/file-posix.c
|
|
@@ -110,6 +110,10 @@
|
|
#include <sys/diskslice.h>
|
|
#endif
|
|
|
|
+#ifdef EMSCRIPTEN
|
|
+#include <sys/ioctl.h>
|
|
+#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 c/block/vvfat.c w/block/vvfat.c
|
|
index 8ffe8b3b9b..3c0ab6c228 100644
|
|
--- c/block/vvfat.c
|
|
+++ w/block/vvfat.c
|
|
@@ -749,6 +749,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|
const char* dirname = mapping->path;
|
|
int first_cluster = mapping->begin;
|
|
int parent_index = mapping->info.dir.parent_mapping_index;
|
|
+ bool is_root = parent_index < 0;
|
|
mapping_t* parent_mapping = (mapping_t*)
|
|
(parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
|
|
int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
|
|
@@ -765,9 +766,9 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|
}
|
|
|
|
i = mapping->info.dir.first_dir_index =
|
|
- first_cluster == 0 ? 0 : s->directory.next;
|
|
+ is_root ? 0 : s->directory.next;
|
|
|
|
- if (first_cluster != 0) {
|
|
+ if (!is_root) {
|
|
/* create the top entries of a subdirectory */
|
|
(void)create_short_and_long_name(s, i, ".", 1);
|
|
(void)create_short_and_long_name(s, i, "..", 1);
|
|
@@ -781,13 +782,14 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|
int is_dot=!strcmp(entry->d_name,".");
|
|
int is_dotdot=!strcmp(entry->d_name,"..");
|
|
|
|
- if (first_cluster == 0 && s->directory.next >= s->root_entries - 1) {
|
|
+ if (is_root && s->fat_type != 32 &&
|
|
+ s->directory.next >= s->root_entries - 1) {
|
|
fprintf(stderr, "Too many entries in root directory\n");
|
|
closedir(dir);
|
|
return -2;
|
|
}
|
|
|
|
- if(first_cluster == 0 && (is_dotdot || is_dot))
|
|
+ if(is_root && (is_dotdot || is_dot))
|
|
continue;
|
|
|
|
buffer = g_malloc(length);
|
|
@@ -860,8 +862,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|
memset(direntry,0,sizeof(direntry_t));
|
|
}
|
|
|
|
- if (s->fat_type != 32 &&
|
|
- mapping_index == 0 &&
|
|
+ if (s->fat_type != 32 && is_root &&
|
|
s->directory.next < s->root_entries) {
|
|
/* root directory */
|
|
int cur = s->directory.next;
|
|
@@ -877,20 +878,29 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|
* 0x20 / s->cluster_size;
|
|
mapping->end = first_cluster;
|
|
|
|
- direntry = array_get(&(s->directory), mapping->dir_index);
|
|
- set_begin_of_direntry(direntry, mapping->begin);
|
|
+ if (!is_root) {
|
|
+ direntry = array_get(&(s->directory), mapping->dir_index);
|
|
+ set_begin_of_direntry(direntry, mapping->begin);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
|
|
{
|
|
- return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
|
|
+ off_t relative_sector = sector_num - s->offset_to_root_dir;
|
|
+
|
|
+ if (relative_sector < 0) {
|
|
+ return -1 - (-relative_sector - 1) / s->sectors_per_cluster;
|
|
+ }
|
|
+ return relative_sector / s->sectors_per_cluster
|
|
+ + (s->fat_type == 32 ? 2 : 0);
|
|
}
|
|
|
|
static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
|
|
{
|
|
- return s->offset_to_root_dir + s->sectors_per_cluster * cluster_num;
|
|
+ return s->offset_to_root_dir + s->sectors_per_cluster
|
|
+ * (cluster_num - (s->fat_type == 32 ? 2 : 0));
|
|
}
|
|
|
|
static int init_directories(BDRVVVFATState* s,
|
|
@@ -934,11 +944,12 @@ static int init_directories(BDRVVVFATState* s,
|
|
init_fat(s);
|
|
|
|
/* TODO: if there are more entries, bootsector has to be adjusted! */
|
|
- s->root_entries = 0x02 * 0x10 * s->sectors_per_cluster;
|
|
+ s->root_entries = s->fat_type == 32 ? 0 :
|
|
+ 0x02 * 0x10 * s->sectors_per_cluster;
|
|
s->cluster_count=sector2cluster(s, s->sector_count);
|
|
|
|
mapping = array_get_next(&(s->mapping));
|
|
- mapping->begin = 0;
|
|
+ mapping->begin = s->fat_type == 32 ? 2 : 0;
|
|
mapping->dir_index = 0;
|
|
mapping->info.dir.parent_mapping_index = -1;
|
|
mapping->first_mapping_index = -1;
|
|
@@ -950,11 +961,10 @@ static int init_directories(BDRVVVFATState* s,
|
|
mapping->read_only = 0;
|
|
s->path = mapping->path;
|
|
|
|
- for (i = 0, cluster = 0; i < s->mapping.next; i++) {
|
|
- /* MS-DOS expects the FAT to be 0 for the root directory
|
|
- * (except for the media byte). */
|
|
- /* LATER TODO: still true for FAT32? */
|
|
- int fix_fat = (i != 0);
|
|
+ for (i = 0, cluster = s->fat_type == 32 ? 2 : 0;
|
|
+ i < s->mapping.next; i++) {
|
|
+ /* FAT12/16 stores the root directory outside the cluster chain. */
|
|
+ int fix_fat = (i != 0 || s->fat_type == 32);
|
|
mapping = array_get(&(s->mapping), i);
|
|
|
|
if (mapping->mode & MODE_DIRECTORY) {
|
|
@@ -1026,22 +1036,35 @@ static int init_directories(BDRVVVFATState* s,
|
|
/* media descriptor: hard disk=0xf8, floppy=0xf0 */
|
|
bootsector->media_type = (s->offset_to_bootsector > 0 ? 0xf8 : 0xf0);
|
|
s->fat.pointer[0] = bootsector->media_type;
|
|
- bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
|
|
+ bootsector->sectors_per_fat = s->fat_type == 32 ? 0 :
|
|
+ cpu_to_le16(s->sectors_per_fat);
|
|
bootsector->sectors_per_track = cpu_to_le16(secs);
|
|
bootsector->number_of_heads = cpu_to_le16(heads);
|
|
bootsector->hidden_sectors = cpu_to_le32(s->offset_to_bootsector);
|
|
bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
|
|
|
|
- /* LATER TODO: if FAT32, this is wrong */
|
|
- /* drive_number: fda=0, hda=0x80 */
|
|
- bootsector->u.fat16.drive_number = s->offset_to_bootsector == 0 ? 0 : 0x80;
|
|
- bootsector->u.fat16.signature=0x29;
|
|
- bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
|
|
-
|
|
- memcpy(bootsector->u.fat16.volume_label, s->volume_label,
|
|
- sizeof(bootsector->u.fat16.volume_label));
|
|
- memcpy(bootsector->u.fat16.fat_type,
|
|
- s->fat_type == 12 ? "FAT12 " : "FAT16 ", 8);
|
|
+ if (s->fat_type == 32) {
|
|
+ bootsector->u.fat32.sectors_per_fat = cpu_to_le32(s->sectors_per_fat);
|
|
+ bootsector->u.fat32.first_cluster_of_root_dir = cpu_to_le32(2);
|
|
+ bootsector->u.fat32.info_sector = cpu_to_le16(0xffff);
|
|
+ bootsector->u.fat32.backup_boot_sector = cpu_to_le16(0xffff);
|
|
+ bootsector->u.fat32.drive_number = 0x80;
|
|
+ bootsector->u.fat32.signature = 0x29;
|
|
+ bootsector->u.fat32.id = cpu_to_le32(0xfabe1afd);
|
|
+ memcpy(bootsector->u.fat32.volume_label, s->volume_label,
|
|
+ sizeof(bootsector->u.fat32.volume_label));
|
|
+ memcpy(bootsector->u.fat32.fat_type, "FAT32 ", 8);
|
|
+ } else {
|
|
+ /* drive_number: fda=0, hda=0x80 */
|
|
+ bootsector->u.fat16.drive_number =
|
|
+ s->offset_to_bootsector == 0 ? 0 : 0x80;
|
|
+ bootsector->u.fat16.signature=0x29;
|
|
+ bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
|
|
+ memcpy(bootsector->u.fat16.volume_label, s->volume_label,
|
|
+ sizeof(bootsector->u.fat16.volume_label));
|
|
+ memcpy(bootsector->u.fat16.fat_type,
|
|
+ s->fat_type == 12 ? "FAT12 " : "FAT16 ", 8);
|
|
+ }
|
|
bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
|
|
|
|
return 0;
|
|
@@ -1139,6 +1162,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|
{
|
|
BDRVVVFATState *s = bs->opaque;
|
|
int cyls, heads, secs;
|
|
+ uint64_t total_sectors;
|
|
bool floppy;
|
|
const char *dirname, *label;
|
|
QemuOpts *opts;
|
|
@@ -1205,7 +1229,9 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
switch (s->fat_type) {
|
|
case 32:
|
|
+#ifndef EMSCRIPTEN
|
|
warn_report("FAT32 has not been tested. You are welcome to do so!");
|
|
+#endif
|
|
break;
|
|
case 16:
|
|
case 12:
|
|
@@ -1219,8 +1245,17 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
s->bs = bs;
|
|
|
|
- /* LATER TODO: if FAT32, adjust */
|
|
- s->sectors_per_cluster=0x10;
|
|
+#ifdef EMSCRIPTEN
|
|
+ if (!floppy && s->fat_type == 32) {
|
|
+ /* Browser SD Geometry - A larger cluster keeps the synthesized 32 GiB FAT near 4 MiB. */
|
|
+ s->sectors_per_cluster = 0x40;
|
|
+ total_sectors = 32ULL * 1024 * 1024 * 1024 / BDRV_SECTOR_SIZE;
|
|
+ } else
|
|
+#endif
|
|
+ {
|
|
+ s->sectors_per_cluster = 0x10;
|
|
+ total_sectors = (uint64_t)cyls * heads * secs;
|
|
+ }
|
|
|
|
s->current_cluster=0xffffffff;
|
|
|
|
@@ -1232,8 +1267,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|
DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
|
|
dirname, cyls, heads, secs));
|
|
|
|
- s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
|
|
- bs->total_sectors = cyls * heads * secs;
|
|
+ s->sector_count = total_sectors - s->offset_to_bootsector;
|
|
+ bs->total_sectors = total_sectors;
|
|
|
|
if (qemu_opt_get_bool(opts, "rw", false)) {
|
|
if (!bdrv_is_read_only(bs)) {
|
|
@@ -1259,8 +1294,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|
goto fail;
|
|
}
|
|
|
|
- s->sector_count = s->offset_to_root_dir
|
|
- + s->sectors_per_cluster * s->cluster_count;
|
|
+ s->sector_count = cluster2sector(s, s->cluster_count);
|
|
|
|
/* Disable migration when vvfat is used rw */
|
|
if (s->qcow) {
|
|
@@ -1415,6 +1449,7 @@ read_cluster_directory:
|
|
if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
|
|
return -3;
|
|
s->cluster=s->cluster_buffer;
|
|
+ memset(s->cluster, 0, s->cluster_size);
|
|
result=read(s->current_fd,s->cluster,s->cluster_size);
|
|
if(result<0) {
|
|
s->current_cluster = -1;
|
|
@@ -1523,8 +1558,9 @@ vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sector
|
|
} else {
|
|
uint32_t sector = sector_num - s->offset_to_root_dir,
|
|
sector_offset_in_cluster=(sector%s->sectors_per_cluster),
|
|
- cluster_num=sector/s->sectors_per_cluster;
|
|
- if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
|
|
+ cluster_num=sector/s->sectors_per_cluster
|
|
+ + (s->fat_type == 32 ? 2 : 0);
|
|
+ if(cluster_num >= s->cluster_count || read_cluster(s, cluster_num) != 0) {
|
|
/* LATER TODO: strict: return -1; */
|
|
memset(buf+i*0x200,0,0x200);
|
|
continue;
|
|
@@ -1780,7 +1816,8 @@ static int parse_short_name(BDRVVVFATState* s,
|
|
static inline uint32_t modified_fat_get(BDRVVVFATState* s,
|
|
unsigned int cluster)
|
|
{
|
|
- if (cluster < s->last_cluster_of_root_directory) {
|
|
+ if (s->fat_type != 32 &&
|
|
+ cluster < s->last_cluster_of_root_directory) {
|
|
if (cluster + 1 == s->last_cluster_of_root_directory)
|
|
return s->max_fat_value;
|
|
else
|
|
@@ -2182,14 +2219,17 @@ DLOG(checkpoint());
|
|
mapping->mode |= MODE_DELETED;
|
|
}
|
|
|
|
- used_clusters_count = check_directory_consistency(s, 0, s->path);
|
|
+ mapping_t *root_mapping = array_get(&(s->mapping), 0);
|
|
+ used_clusters_count = check_directory_consistency(s,
|
|
+ root_mapping->begin, s->path);
|
|
if (used_clusters_count <= 0) {
|
|
DLOG(fprintf(stderr, "problem in directory\n"));
|
|
return 0;
|
|
}
|
|
|
|
- check = s->last_cluster_of_root_directory;
|
|
- for (i = check; i < sector2cluster(s, s->sector_count); i++) {
|
|
+ check = s->fat_type == 32 ? 0 : s->last_cluster_of_root_directory;
|
|
+ for (i = s->fat_type == 32 ? 2 : check;
|
|
+ i < sector2cluster(s, s->sector_count); i++) {
|
|
if (modified_fat_get(s, i)) {
|
|
if(!s->used_clusters[i]) {
|
|
DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
|
|
@@ -2417,7 +2457,10 @@ static int coroutine_fn GRAPH_RDLOCK
|
|
commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index)
|
|
{
|
|
direntry_t* direntry = array_get(&(s->directory), dir_index);
|
|
- uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
|
|
+ bool is_root = dir_index == 0;
|
|
+ mapping_t *root_mapping = array_get(&(s->mapping), 0);
|
|
+ uint32_t first_cluster = is_root ? root_mapping->begin :
|
|
+ begin_of_direntry(direntry);
|
|
mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
|
|
int factor = 0x10 * s->sectors_per_cluster;
|
|
int old_cluster_count, new_cluster_count;
|
|
@@ -2440,7 +2483,7 @@ commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index)
|
|
first_dir_index = current_dir_index;
|
|
mapping->info.dir.parent_mapping_index = parent_mapping_index;
|
|
|
|
- if (first_cluster == 0) {
|
|
+ if (is_root && s->fat_type != 32) {
|
|
old_cluster_count = new_cluster_count =
|
|
s->last_cluster_of_root_directory;
|
|
} else {
|
|
@@ -2998,11 +3041,9 @@ DLOG(checkpoint());
|
|
*/
|
|
unsigned char *bootsector = s->first_sectors
|
|
+ s->offset_to_bootsector * 0x200;
|
|
- /*
|
|
- * LATER TODO: if FAT32, this is wrong (see init_directories(),
|
|
- * which always creates a FAT16 bootsector)
|
|
- */
|
|
- const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1);
|
|
+ const int reserved1_offset = s->fat_type == 32 ?
|
|
+ offsetof(bootsector_t, u.fat32.reserved1) :
|
|
+ offsetof(bootsector_t, u.fat16.reserved1);
|
|
|
|
for (i = 0; i < 0x200; i++) {
|
|
if (i != reserved1_offset && bootsector[i] != buf[i]) {
|
|
@@ -3057,8 +3098,8 @@ DLOG(checkpoint());
|
|
begin = sector_num;
|
|
if (end > sector_num + nb_sectors)
|
|
end = sector_num + nb_sectors;
|
|
- dir_index = mapping->dir_index +
|
|
- 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
|
|
+ dir_index = mapping->info.dir.first_dir_index +
|
|
+ 0x10 * (begin - cluster2sector(s, mapping->begin));
|
|
direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
|
|
|
|
for (k = 0; k < (end - begin) * 0x10; k++) {
|
|
@@ -3093,7 +3134,7 @@ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sec
|
|
}
|
|
|
|
for (i = first_cluster; i <= last_cluster; i++) {
|
|
- if (i >= 0) {
|
|
+ if (i >= 0 && i < sector2cluster(s, s->sector_count)) {
|
|
s->used_clusters[i] |= USED_ALLOCATED;
|
|
}
|
|
}
|
|
diff --git c/configs/devices/riscv32-softmmu/xteink.mak w/configs/devices/riscv32-softmmu/xteink.mak
|
|
new file mode 100644
|
|
index 0000000000..a9179b4119
|
|
--- /dev/null
|
|
+++ w/configs/devices/riscv32-softmmu/xteink.mak
|
|
@@ -0,0 +1 @@
|
|
+CONFIG_RISCV_ESP32C3=y
|
|
diff --git c/configure w/configure
|
|
index 18336376bf..4a4b9e7754 100755
|
|
--- c/configure
|
|
+++ w/configure
|
|
@@ -340,7 +340,9 @@ int main(void) { return 0; }
|
|
EOF
|
|
}
|
|
|
|
-if check_define __linux__ ; then
|
|
+if check_define __EMSCRIPTEN__ ; then
|
|
+ host_os=emscripten
|
|
+elif check_define __linux__ ; then
|
|
host_os=linux
|
|
elif check_define _WIN32 ; then
|
|
host_os=windows
|
|
@@ -526,6 +528,10 @@ case "$cpu" in
|
|
linux_arch=x86
|
|
CPU_CFLAGS="-m64"
|
|
;;
|
|
+ wasm32)
|
|
+ host_arch=wasm32
|
|
+ CPU_CFLAGS="-m32"
|
|
+ ;;
|
|
esac
|
|
|
|
# Now we have our CPU_CFLAGS we can check if we are targeting a 32 or
|
|
@@ -547,7 +553,7 @@ else
|
|
host_bits=32
|
|
fi
|
|
|
|
-if test -n "$host_arch" && {
|
|
+if test -n "$host_arch" && test "$host_arch" != wasm32 && {
|
|
! test -d "$source_path/linux-user/include/host/$host_arch" ||
|
|
! test -d "$source_path/common-user/host/$host_arch"; }; then
|
|
error_exit "linux-user/include/host/$host_arch does not exist." \
|
|
diff --git c/disas/disas-host.c w/disas/disas-host.c
|
|
index 8146fafe80..2ee0605dba 100644
|
|
--- c/disas/disas-host.c
|
|
+++ w/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 c/hw/adc/esp32c3_adc.c w/hw/adc/esp32c3_adc.c
|
|
index 89087429c7..4d38b3e13c 100644
|
|
--- c/hw/adc/esp32c3_adc.c
|
|
+++ w/hw/adc/esp32c3_adc.c
|
|
@@ -27,7 +27,7 @@ static uint64_t esp32c3_adc_read(void *opaque, hwaddr addr, unsigned size)
|
|
}
|
|
if (addr == ADC_DATA1 || addr == ADC_DATA2) {
|
|
return s->channel < ESP32C3_ADC_CHANNELS
|
|
- ? MIN(s->input[s->channel], ADC_MAX)
|
|
+ ? MIN(qatomic_read(&s->input[s->channel]), ADC_MAX)
|
|
: ADC_MAX;
|
|
}
|
|
return s->regs[addr / 4];
|
|
diff --git c/hw/block/m25p80.c w/hw/block/m25p80.c
|
|
index 46dbe7c521..1ee6f8ce7e 100644
|
|
--- c/hw/block/m25p80.c
|
|
+++ w/hw/block/m25p80.c
|
|
@@ -222,7 +222,8 @@ static const FlashPartInfo known_devices[] = {
|
|
{ INFO("is25lp016d", 0x9d6015, 0, 64 << 10, 32, ER_4K) },
|
|
{ INFO("is25lp032", 0x9d6016, 0, 64 << 10, 64, ER_4K) },
|
|
{ INFO("is25lp064", 0x9d6017, 0, 64 << 10, 128, ER_4K) },
|
|
- { INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K) },
|
|
+ { INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K),
|
|
+ .sfdp_read = m25p80_sfdp_is25lp128 },
|
|
{ INFO("is25lp256", 0x9d6019, 0, 64 << 10, 512, ER_4K) },
|
|
{ INFO("is25wp032", 0x9d7016, 0, 64 << 10, 64, ER_4K) },
|
|
{ INFO("is25wp064", 0x9d7017, 0, 64 << 10, 128, ER_4K) },
|
|
@@ -385,6 +386,8 @@ typedef enum {
|
|
READ_FSR = 0x70,
|
|
RDCR = 0x15,
|
|
RDSFDP = 0x5a,
|
|
+ PROGRAM_ERASE_SUSPEND = 0x75,
|
|
+ PROGRAM_ERASE_RESUME = 0x7a,
|
|
|
|
READ = 0x03,
|
|
READ4 = 0x13,
|
|
@@ -856,28 +859,18 @@ static void complete_collecting_data(Flash *s)
|
|
break;
|
|
case RDID_90:
|
|
case RDID_AB:
|
|
- if (get_man(s) == MAN_SST) {
|
|
- if (s->cur_addr <= 1) {
|
|
- if (s->cur_addr) {
|
|
- s->data[0] = s->pi->id[2];
|
|
- s->data[1] = s->pi->id[0];
|
|
- } else {
|
|
- s->data[0] = s->pi->id[0];
|
|
- s->data[1] = s->pi->id[2];
|
|
- }
|
|
- s->pos = 0;
|
|
- s->len = 2;
|
|
- s->data_read_loop = true;
|
|
- s->state = STATE_READING_DATA;
|
|
- } else {
|
|
- qemu_log_mask(LOG_GUEST_ERROR,
|
|
- "M25P80: Invalid read id address\n");
|
|
- }
|
|
+ // Read Manufacturer/Device ID - The address low bit selects which of the manufacturer/device pair comes first; real chips answer regardless of vendor, so this is not gated on MAN_SST. Reused for 0xAB (Release Power-Down + Device ID), whose trailing byte reads back the device id.
|
|
+ if (s->cur_addr) {
|
|
+ s->data[0] = s->pi->id[2];
|
|
+ s->data[1] = s->pi->id[0];
|
|
} else {
|
|
- qemu_log_mask(LOG_GUEST_ERROR,
|
|
- "M25P80: Read id (command 0x90/0xAB) is not supported"
|
|
- " by device\n");
|
|
+ s->data[0] = s->pi->id[0];
|
|
+ s->data[1] = s->pi->id[2];
|
|
}
|
|
+ s->pos = 0;
|
|
+ s->len = 2;
|
|
+ s->data_read_loop = true;
|
|
+ s->state = STATE_READING_DATA;
|
|
break;
|
|
|
|
case RDSFDP:
|
|
@@ -1414,6 +1407,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
|
}
|
|
break;
|
|
case NOP:
|
|
+ case PROGRAM_ERASE_SUSPEND:
|
|
+ case PROGRAM_ERASE_RESUME:
|
|
+ /* Program and erase complete synchronously in this model. */
|
|
break;
|
|
case EN_4BYTE_ADDR:
|
|
s->four_bytes_address_mode = true;
|
|
diff --git c/hw/block/m25p80_sfdp.c w/hw/block/m25p80_sfdp.c
|
|
index a03a291a09..6e01178299 100644
|
|
--- c/hw/block/m25p80_sfdp.c
|
|
+++ w/hw/block/m25p80_sfdp.c
|
|
@@ -479,3 +479,12 @@ static const uint8_t sfdp_is25wp256[] = {
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
};
|
|
define_sfdp_read(is25wp256);
|
|
+
|
|
+uint8_t m25p80_sfdp_is25lp128(uint32_t addr)
|
|
+{
|
|
+ /* The sibling parts share SFDP data except for the density field. */
|
|
+ if (addr == 0x37) {
|
|
+ return 0x07;
|
|
+ }
|
|
+ return m25p80_sfdp_is25wp256(addr);
|
|
+}
|
|
diff --git c/hw/block/m25p80_sfdp.h w/hw/block/m25p80_sfdp.h
|
|
index 35785686a0..e6bfdfa7e8 100644
|
|
--- c/hw/block/m25p80_sfdp.h
|
|
+++ w/hw/block/m25p80_sfdp.h
|
|
@@ -28,6 +28,7 @@ uint8_t m25p80_sfdp_w25q512jv(uint32_t addr);
|
|
uint8_t m25p80_sfdp_w25q80bl(uint32_t addr);
|
|
uint8_t m25p80_sfdp_w25q01jvq(uint32_t addr);
|
|
|
|
+uint8_t m25p80_sfdp_is25lp128(uint32_t addr);
|
|
uint8_t m25p80_sfdp_is25wp256(uint32_t addr);
|
|
|
|
#endif
|
|
diff --git c/hw/display/xteink_x3_eink.c w/hw/display/xteink_x3_eink.c
|
|
index b699abae4e..8a177e9159 100644
|
|
--- c/hw/display/xteink_x3_eink.c
|
|
+++ w/hw/display/xteink_x3_eink.c
|
|
@@ -9,6 +9,60 @@
|
|
#include "hw/irq.h"
|
|
#include "hw/display/xteink_x3_eink.h"
|
|
|
|
+#ifdef __EMSCRIPTEN__
|
|
+#include <emscripten/emscripten.h>
|
|
+
|
|
+static QemuConsole *xteink_wasm_console;
|
|
+static uint32_t xteink_wasm_generation;
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE uintptr_t xteink_wasm_framebuffer(void)
|
|
+{
|
|
+ DisplaySurface *surface = xteink_wasm_console
|
|
+ ? qemu_console_surface(xteink_wasm_console)
|
|
+ : NULL;
|
|
+ return surface ? (uintptr_t)surface_data(surface) : 0;
|
|
+}
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE int xteink_wasm_width(void)
|
|
+{
|
|
+ DisplaySurface *surface = xteink_wasm_console
|
|
+ ? qemu_console_surface(xteink_wasm_console)
|
|
+ : NULL;
|
|
+ return surface ? surface_width(surface) : 0;
|
|
+}
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE int xteink_wasm_height(void)
|
|
+{
|
|
+ DisplaySurface *surface = xteink_wasm_console
|
|
+ ? qemu_console_surface(xteink_wasm_console)
|
|
+ : NULL;
|
|
+ return surface ? surface_height(surface) : 0;
|
|
+}
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE int xteink_wasm_stride(void)
|
|
+{
|
|
+ DisplaySurface *surface = xteink_wasm_console
|
|
+ ? qemu_console_surface(xteink_wasm_console)
|
|
+ : NULL;
|
|
+ return surface ? surface_stride(surface) : 0;
|
|
+}
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE uint32_t xteink_wasm_frame_generation(void)
|
|
+{
|
|
+ return qatomic_read(&xteink_wasm_generation);
|
|
+}
|
|
+
|
|
+static void xteink_wasm_frame_updated(QemuConsole *console)
|
|
+{
|
|
+ xteink_wasm_console = console;
|
|
+ qatomic_inc(&xteink_wasm_generation);
|
|
+}
|
|
+#else
|
|
+static void xteink_wasm_frame_updated(QemuConsole *console)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
#define CMD_POWER_OFF 0x02
|
|
#define CMD_POWER_ON 0x04
|
|
#define CMD_DTM1 0x10
|
|
@@ -33,6 +87,7 @@ static void xteink_x3_eink_render(XteinkX3EinkState *s)
|
|
}
|
|
}
|
|
|
|
+ xteink_wasm_frame_updated(s->console);
|
|
dpy_gfx_update(s->console, 0, 0, XTEINK_X3_HEIGHT, XTEINK_X3_WIDTH);
|
|
}
|
|
|
|
@@ -171,6 +226,7 @@ static void xteink_x4_eink_render(XteinkX4EinkState *s)
|
|
DisplaySurface *surface = qemu_console_surface(s->console);
|
|
memset(surface_data(surface), 0xff,
|
|
surface_stride(surface) * surface_height(surface));
|
|
+ xteink_wasm_frame_updated(s->console);
|
|
dpy_gfx_update(s->console, 0, 0, 480, 800);
|
|
}
|
|
|
|
diff --git c/hw/gpio/esp32_gpio.c w/hw/gpio/esp32_gpio.c
|
|
index 9ec8bd9244..754990968c 100644
|
|
--- c/hw/gpio/esp32_gpio.c
|
|
+++ w/hw/gpio/esp32_gpio.c
|
|
@@ -38,10 +38,20 @@ static void esp32_gpio_drive_outputs(Esp32GpioState *s)
|
|
}
|
|
}
|
|
|
|
+void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level)
|
|
+{
|
|
+ uint32_t old_level;
|
|
+ uint32_t new_level;
|
|
+
|
|
+ do {
|
|
+ old_level = qatomic_read(&s->input_level);
|
|
+ new_level = deposit32(old_level, pin, 1, level);
|
|
+ } while (qatomic_cmpxchg(&s->input_level, old_level, new_level) != old_level);
|
|
+}
|
|
+
|
|
static void esp32_gpio_set_input(void *opaque, int pin, int level)
|
|
{
|
|
- Esp32GpioState *s = ESP32_GPIO(opaque);
|
|
- s->input_level = deposit32(s->input_level, pin, 1, !!level);
|
|
+ esp32_gpio_set_input_level(ESP32_GPIO(opaque), pin, level);
|
|
}
|
|
|
|
static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name,
|
|
@@ -49,7 +59,7 @@ static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name,
|
|
{
|
|
Esp32GpioState *s = ESP32_GPIO(obj);
|
|
int pin = GPOINTER_TO_INT(opaque);
|
|
- bool level = extract32(s->input_level, pin, 1);
|
|
+ bool level = extract32(qatomic_read(&s->input_level), pin, 1);
|
|
visit_type_bool(v, name, &level, errp);
|
|
}
|
|
|
|
@@ -78,7 +88,7 @@ static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size)
|
|
case A_GPIO_STRAP:
|
|
return s->strap_mode;
|
|
case GPIO_IN:
|
|
- return s->input_level;
|
|
+ return qatomic_read(&s->input_level);
|
|
default:
|
|
return 0;
|
|
}
|
|
diff --git c/hw/misc/esp32c3_cache.c w/hw/misc/esp32c3_cache.c
|
|
index 1879b5c2df..e3f09cc51b 100644
|
|
--- c/hw/misc/esp32c3_cache.c
|
|
+++ w/hw/misc/esp32c3_cache.c
|
|
@@ -58,7 +58,6 @@ static inline uint32_t esp32c3_read_mmu_value(ESP32C3CacheState *s, hwaddr reg_a
|
|
|
|
static inline void esp32c3_write_mmu_value(ESP32C3CacheState *s, hwaddr reg_addr, uint32_t value)
|
|
{
|
|
- ESP32C3XtsAesClass *xts_aes_class = ESP32C3_XTS_AES_GET_CLASS(s->xts_aes);
|
|
/* Make the assumption that the address is aligned on sizeof(uint32_t) */
|
|
const uint32_t index = reg_addr / sizeof(uint32_t);
|
|
/* Reserved bits shall always be 0 */
|
|
@@ -82,8 +81,12 @@ static inline void esp32c3_write_mmu_value(ESP32C3CacheState *s, hwaddr reg_addr
|
|
if (s->flash_blk != NULL) {
|
|
blk_pread(s->flash_blk, physical_address, ESP32C3_PAGE_SIZE, cache_data, 0);
|
|
}
|
|
- if (xts_aes_class->is_flash_enc_enabled(s->xts_aes)) {
|
|
- xts_aes_class->decrypt(s->xts_aes, physical_address, cache_data, ESP32C3_PAGE_SIZE);
|
|
+ // XTS-AES Flash Decryption - Only wired when built with CONFIG_GCRYPT; absent here means flash is treated as unencrypted, matching hardware with flash encryption disabled. Encrypted-flash firmware needs the gcrypt build.
|
|
+ if (s->xts_aes != NULL) {
|
|
+ ESP32C3XtsAesClass *xts_aes_class = ESP32C3_XTS_AES_GET_CLASS(s->xts_aes);
|
|
+ if (xts_aes_class->is_flash_enc_enabled(s->xts_aes)) {
|
|
+ xts_aes_class->decrypt(s->xts_aes, physical_address, cache_data, ESP32C3_PAGE_SIZE);
|
|
+ }
|
|
}
|
|
}
|
|
s->mmu[index].val = e.val;
|
|
@@ -225,12 +228,6 @@ static void esp32c3_cache_realize(DeviceState *dev, Error **errp)
|
|
/* Initialize the registers */
|
|
esp32c3_cache_reset_hold(OBJECT(dev), RESET_TYPE_COLD);
|
|
|
|
- ESP32C3CacheState *s = ESP32C3_CACHE(dev);
|
|
-
|
|
- /* Make sure XTS_AES was set or issue an error */
|
|
- if (s->xts_aes == NULL) {
|
|
- error_report("[CACHE] XTS_AES controller must be set!");
|
|
- }
|
|
}
|
|
|
|
static void esp32c3_cache_init(Object *obj)
|
|
diff --git c/hw/misc/esp32c3_jtag.c w/hw/misc/esp32c3_jtag.c
|
|
index 82ee69717b..64a971f183 100644
|
|
--- c/hw/misc/esp32c3_jtag.c
|
|
+++ w/hw/misc/esp32c3_jtag.c
|
|
@@ -16,10 +16,18 @@
|
|
#include "hw/misc/esp32c3_jtag.h"
|
|
|
|
|
|
+/* USB Serial JTAG Console Tap - The firmware routes ESP-IDF console output (incl. panic dumps) through this peripheral. EP1 is the byte FIFO; EP1_CONF bit1 (SERIAL_IN_EP_DATA_FREE) must read set so the driver believes the TX FIFO can always accept a byte. */
|
|
+#define ESP32C3_JTAG_EP1_REG 0x00
|
|
+#define ESP32C3_JTAG_EP1_CONF_REG 0x04
|
|
+#define ESP32C3_JTAG_IN_EP_DATA_FREE (1u << 1)
|
|
+
|
|
static uint64_t esp32c3_jtag_read(void *opaque, hwaddr addr, unsigned int size)
|
|
{
|
|
ESP32C3UsbJtagState *s = ESP32C3_JTAG(opaque);
|
|
(void) s;
|
|
+ if (addr == ESP32C3_JTAG_EP1_CONF_REG) {
|
|
+ return ESP32C3_JTAG_IN_EP_DATA_FREE;
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -27,6 +35,9 @@ static void esp32c3_jtag_write(void *opaque, hwaddr addr, uint64_t value, unsign
|
|
{
|
|
ESP32C3UsbJtagState *s = ESP32C3_JTAG(opaque);
|
|
(void) s;
|
|
+ if (addr == ESP32C3_JTAG_EP1_REG) {
|
|
+ fputc((int)(value & 0xff), stderr);
|
|
+ }
|
|
}
|
|
|
|
static const MemoryRegionOps esp32c3_jtag_ops = {
|
|
diff --git c/hw/misc/esp_sha.c w/hw/misc/esp_sha.c
|
|
index c9fa2e610a..cc186595eb 100644
|
|
--- c/hw/misc/esp_sha.c
|
|
+++ w/hw/misc/esp_sha.c
|
|
@@ -22,46 +22,71 @@
|
|
#define SHA_WARNING 0
|
|
#define SHA_DEBUG 0
|
|
|
|
+static void esp_sha1_init(void *context) { sha1_init(context); }
|
|
+static void esp_sha224_init(void *context) { sha224_init(context); }
|
|
+static void esp_sha256_init(void *context) { sha256_init(context); }
|
|
+static void esp_sha384_init(void *context) { sha384_init(context); }
|
|
+static void esp_sha512_init(void *context) { sha512_init(context); }
|
|
+static void esp_sha512_224_init(void *context) { sha512_224_init(context); }
|
|
+static void esp_sha512_256_init(void *context) { sha512_256_init(context); }
|
|
+static void esp_sha512_t_init(void *context) { sha512_t_init(context); }
|
|
+static void esp_sha1_compress(void *context, const uint8_t *message)
|
|
+{
|
|
+ sha1_compress(context, message);
|
|
+}
|
|
+static void esp_sha224_compress(void *context, const uint8_t *message)
|
|
+{
|
|
+ sha224_compress(context, (uint8_t *)message);
|
|
+}
|
|
+static void esp_sha256_compress(void *context, const uint8_t *message)
|
|
+{
|
|
+ sha256_compress(context, (uint8_t *)message);
|
|
+}
|
|
+static void esp_sha512_compress(void *context, const uint8_t *message)
|
|
+{
|
|
+ sha512_compress(context, (uint8_t *)message);
|
|
+}
|
|
+
|
|
static ESPHashAlg esp_sha_algs[] = {
|
|
[ESP_SHA_1_MODE] = {
|
|
- .init = (hash_init) sha1_init,
|
|
- .compress = (hash_compress) sha1_compress,
|
|
+ .init = esp_sha1_init,
|
|
+ .compress = esp_sha1_compress,
|
|
.len = sizeof(struct sha1_state)
|
|
},
|
|
[ESP_SHA_224_MODE] = {
|
|
- .init = (hash_init) sha224_init,
|
|
- .compress = (hash_compress) sha224_compress,
|
|
+ .init = esp_sha224_init,
|
|
+ .compress = esp_sha224_compress,
|
|
.len = SHA224_HASH_SIZE
|
|
},
|
|
[ESP_SHA_256_MODE] = {
|
|
- .init = (hash_init) sha256_init,
|
|
- .compress = (hash_compress) sha256_compress,
|
|
+ .init = esp_sha256_init,
|
|
+ .compress = esp_sha256_compress,
|
|
.len = sizeof(struct sha256_state)
|
|
},
|
|
[ESP_SHA_384_MODE] = {
|
|
- .init = (hash_init) sha384_init,
|
|
- .compress = (hash_compress) sha512_compress,
|
|
+ .init = esp_sha384_init,
|
|
+ .compress = esp_sha512_compress,
|
|
.len = SHA384_HASH_SIZE
|
|
},
|
|
[ESP_SHA_512_MODE] = {
|
|
- .init = (hash_init) sha512_init,
|
|
- .compress = (hash_compress) sha512_compress,
|
|
+ .init = esp_sha512_init,
|
|
+ .compress = esp_sha512_compress,
|
|
.len = sizeof(struct sha512_state)
|
|
},
|
|
[ESP_SHA_512_224_MODE] = {
|
|
- .init = (hash_init) sha512_224_init,
|
|
- .compress = (hash_compress) sha512_compress,
|
|
+ .init = esp_sha512_224_init,
|
|
+ .compress = esp_sha512_compress,
|
|
.len = sizeof(struct sha512_state)
|
|
},
|
|
[ESP_SHA_512_256_MODE] = {
|
|
- .init = (hash_init) sha512_256_init,
|
|
- .compress = (hash_compress) sha512_compress,
|
|
+ .init = esp_sha512_256_init,
|
|
+ .compress = esp_sha512_compress,
|
|
.len = sizeof(struct sha512_state)
|
|
},
|
|
[ESP_SHA_512_t_MODE] = {
|
|
- .init = (hash_init) sha512_t_init,
|
|
- .init_message = (hash_init_message) sha512_t_init_message,
|
|
- .compress = (hash_compress) sha512_compress,
|
|
+ .init = esp_sha512_t_init,
|
|
+ .init_message = sha512_t_init_message,
|
|
+ .compress = esp_sha512_compress,
|
|
.len = sizeof(struct sha512_state)
|
|
},
|
|
};
|
|
diff --git c/hw/riscv/esp32c3.c w/hw/riscv/esp32c3.c
|
|
index 2f1ccbfbb0..af8bb86c97 100644
|
|
--- c/hw/riscv/esp32c3.c
|
|
+++ w/hw/riscv/esp32c3.c
|
|
@@ -57,6 +57,10 @@
|
|
#include "hw/sd/sd.h"
|
|
#include "hw/net/can/esp32c3_twai.h"
|
|
|
|
+#ifdef __EMSCRIPTEN__
|
|
+#include <emscripten/emscripten.h>
|
|
+#endif
|
|
+
|
|
#define ESP32C3_IO_WARNING 0
|
|
|
|
#define ESP32C3_RESET_ADDRESS 0x40000000
|
|
@@ -103,6 +107,29 @@ struct Esp32C3MachineState {
|
|
Esp32C3TWAIState twai;
|
|
};
|
|
|
|
+#ifdef __EMSCRIPTEN__
|
|
+static struct Esp32C3MachineState *xteink_wasm_machine;
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_adc(int channel, uint32_t value)
|
|
+{
|
|
+ if (xteink_wasm_machine && channel >= 0 &&
|
|
+ channel < ESP32C3_ADC_CHANNELS) {
|
|
+ fprintf(stderr, "[button] adc channel=%d value=%u\n", channel, value);
|
|
+ qatomic_set(&xteink_wasm_machine->adc.input[channel],
|
|
+ MIN(value, UINT32_C(0xfff)));
|
|
+ }
|
|
+}
|
|
+
|
|
+EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_gpio(int pin, int level)
|
|
+{
|
|
+ if (xteink_wasm_machine && pin >= 0 && pin < ESP32_GPIO_COUNT) {
|
|
+ fprintf(stderr, "[button] gpio pin=%d level=%d\n", pin, level);
|
|
+ esp32_gpio_set_input_level(&xteink_wasm_machine->gpio.parent,
|
|
+ pin, level);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
/* Fake register used by ESP-IDF application to determine whether the code is running on real hardware or on QEMU */
|
|
#define A_SYSCON_ORIGIN_REG 0x3F8
|
|
/* Temporary macro for generating a random value from register SYSCON_RND_DATA_REG */
|
|
@@ -421,12 +448,18 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
object_initialize_child(OBJECT(machine), "efuse", &ms->efuse, TYPE_ESP32C3_EFUSE);
|
|
object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK);
|
|
object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA);
|
|
+#ifdef CONFIG_GCRYPT
|
|
object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES);
|
|
+#endif
|
|
object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA);
|
|
+#ifdef CONFIG_GCRYPT
|
|
object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA);
|
|
+#endif
|
|
object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC);
|
|
+#ifdef CONFIG_GCRYPT
|
|
object_initialize_child(OBJECT(machine), "ds", &ms->ds, TYPE_ESP32C3_DS);
|
|
object_initialize_child(OBJECT(machine), "xts_aes", &ms->xts_aes, TYPE_ESP32C3_XTS_AES);
|
|
+#endif
|
|
object_initialize_child(OBJECT(machine), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG);
|
|
object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], TYPE_ESP32C3_TIMG);
|
|
object_initialize_child(OBJECT(machine), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER);
|
|
@@ -484,7 +517,9 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
|
|
/* SPI1 controller (SPI Flash) */
|
|
{
|
|
+#ifdef CONFIG_GCRYPT
|
|
ms->spi1.xts_aes = &ms->xts_aes;
|
|
+#endif
|
|
sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal);
|
|
MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi1), 0);
|
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI1_BASE, mr, 0);
|
|
@@ -539,7 +574,9 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
if (blk) {
|
|
ms->cache.flash_blk = blk;
|
|
}
|
|
+#ifdef CONFIG_GCRYPT
|
|
ms->cache.xts_aes = &ms->xts_aes;
|
|
+#endif
|
|
sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal);
|
|
MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->cache), 0);
|
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_EXTMEM_BASE, mr, 0);
|
|
@@ -635,6 +672,7 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
qdev_get_gpio_in(intmatrix_dev, ETS_SHA_INTR_SOURCE));
|
|
}
|
|
|
|
+#ifdef CONFIG_GCRYPT
|
|
/* AES realization */
|
|
{
|
|
ms->aes.parent.gdma = ESP_GDMA(&ms->gdma);
|
|
@@ -654,6 +692,8 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
qdev_get_gpio_in(intmatrix_dev, ETS_RSA_INTR_SOURCE));
|
|
}
|
|
|
|
+#endif
|
|
+
|
|
/* HMAC realization */
|
|
{
|
|
ms->hmac.parent.efuse = ESP_EFUSE(&ms->efuse);
|
|
@@ -662,6 +702,7 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_HMAC_BASE, mr, 0);
|
|
}
|
|
|
|
+#ifdef CONFIG_GCRYPT
|
|
/* Digital Signature realization */
|
|
{
|
|
ms->ds.parent.hmac = ESP_HMAC(&ms->hmac);
|
|
@@ -682,6 +723,8 @@ static void esp32c3_machine_init(MachineState *machine)
|
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_XTS_BASE, mr, 0);
|
|
}
|
|
|
|
+#endif
|
|
+
|
|
/* RGB display realization */
|
|
if (!ms->xteink) {
|
|
/* Give the internal RAM memory region to the display */
|
|
@@ -706,6 +749,9 @@ static void xteink_machine_init(MachineState *machine)
|
|
Esp32C3MachineState *ms = ESP32C3_MACHINE(machine);
|
|
ms->xteink = true;
|
|
esp32c3_machine_init(machine);
|
|
+#ifdef __EMSCRIPTEN__
|
|
+ xteink_wasm_machine = ms;
|
|
+#endif
|
|
|
|
/* X3 exposes the fingerprint chips; X4 omits them so stock firmware's
|
|
* all-NAK probe selects X4. The X4 panel is a blank protocol stub. */
|
|
diff --git c/hw/riscv/esp32c3_intmatrix.c w/hw/riscv/esp32c3_intmatrix.c
|
|
index c1376b7299..1275808dc0 100644
|
|
--- c/hw/riscv/esp32c3_intmatrix.c
|
|
+++ w/hw/riscv/esp32c3_intmatrix.c
|
|
@@ -25,9 +25,10 @@
|
|
#define INTMATRIX_DEBUG 0
|
|
#define INTMATRIX_WARNING 0
|
|
|
|
-#define BIT_SET(reg, bit) ((reg) & BIT(bit))
|
|
-#define CLEAR_BIT(reg, bit) do { (reg) &= ~BIT(bit); } while(0)
|
|
-#define SET_BIT(reg, bit) do { (reg) |= BIT(bit); } while(0)
|
|
+/* 64-bit Shifts For irq_levels - There are up to 62 interrupt sources, so bit ops on the uint64_t level mask must shift in 64-bit. QEMU's BIT() is `1UL << nr`, which is only 32-bit on wasm32 and silently aliases source N to N-32. */
|
|
+#define BIT_SET(reg, bit) ((reg) & (1ULL << (bit)))
|
|
+#define CLEAR_BIT(reg, bit) do { (reg) &= ~(1ULL << (bit)); } while(0)
|
|
+#define SET_BIT(reg, bit) do { (reg) |= (1ULL << (bit)); } while(0)
|
|
|
|
|
|
static int esp32c3_get_output_line_level(ESP32C3IntMatrixState *s, int line)
|
|
@@ -36,7 +37,7 @@ static int esp32c3_get_output_line_level(ESP32C3IntMatrixState *s, int line)
|
|
|
|
for (int i = 0; level_shared == 0 && i < ESP32C3_INT_MATRIX_INPUTS; i++) {
|
|
const uint_fast8_t mapped = s->irq_map[i];
|
|
- if (mapped == line && (s->irq_levels & BIT(i)))
|
|
+ if (mapped == line && (s->irq_levels & (1ULL << i)))
|
|
{
|
|
level_shared |= 1;
|
|
}
|
|
diff --git c/hw/sd/ssi-sd.c w/hw/sd/ssi-sd.c
|
|
index 552dc2c8ea..eb12207897 100644
|
|
--- c/hw/sd/ssi-sd.c
|
|
+++ w/hw/sd/ssi-sd.c
|
|
@@ -162,6 +162,10 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
|
return SSI_DUMMY;
|
|
}
|
|
|
|
+ if ((val & 0xc0) != 0x40) {
|
|
+ DPRINTF("Ignore non-command byte 0x%02x\n", val);
|
|
+ return SSI_DUMMY;
|
|
+ }
|
|
s->cmd = val & 0x3f;
|
|
s->mode = SSI_SD_CMDARG;
|
|
s->arglen = 0;
|
|
diff --git c/include/exec/exec-all.h w/include/exec/exec-all.h
|
|
index 2e4c4cc4b4..99e6da08c9 100644
|
|
--- c/include/exec/exec-all.h
|
|
+++ w/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 c/include/hw/gpio/esp32_gpio.h w/include/hw/gpio/esp32_gpio.h
|
|
index e2f52ad702..721f5de5df 100644
|
|
--- c/include/hw/gpio/esp32_gpio.h
|
|
+++ w/include/hw/gpio/esp32_gpio.h
|
|
@@ -33,3 +33,5 @@ typedef struct Esp32GpioState {
|
|
typedef struct Esp32GpioClass {
|
|
SysBusDeviceClass parent_class;
|
|
} Esp32GpioClass;
|
|
+
|
|
+void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level);
|
|
diff --git c/include/qemu/atomic.h w/include/qemu/atomic.h
|
|
index 7a3f2e6576..50dc235043 100644
|
|
--- c/include/qemu/atomic.h
|
|
+++ w/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 c/include/qemu/cacheflush.h w/include/qemu/cacheflush.h
|
|
index ae20bcda73..3f414fb7c9 100644
|
|
--- c/include/qemu/cacheflush.h
|
|
+++ w/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 c/include/qemu/int128.h w/include/qemu/int128.h
|
|
index 174bd7dafb..0305e262ef 100644
|
|
--- c/include/qemu/int128.h
|
|
+++ w/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 c/include/qemu/osdep.h w/include/qemu/osdep.h
|
|
index fdff07fd99..e95faa4478 100644
|
|
--- c/include/qemu/osdep.h
|
|
+++ w/include/qemu/osdep.h
|
|
@@ -133,7 +133,7 @@ QEMU_EXTERN_C int daemon(int, int);
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
|
|
-#ifdef CONFIG_IOVEC
|
|
+#if defined(CONFIG_IOVEC) || defined(EMSCRIPTEN)
|
|
#include <sys/uio.h>
|
|
#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 c/include/tcg/helper-info.h w/include/tcg/helper-info.h
|
|
index 909fe73afa..c5064473ac 100644
|
|
--- c/include/tcg/helper-info.h
|
|
+++ w/include/tcg/helper-info.h
|
|
@@ -9,7 +9,7 @@
|
|
#ifndef TCG_HELPER_INFO_H
|
|
#define TCG_HELPER_INFO_H
|
|
|
|
-#ifdef CONFIG_TCG_INTERPRETER
|
|
+#if defined(CONFIG_TCG_INTERPRETER) || defined(EMSCRIPTEN)
|
|
#include <ffi.h>
|
|
#endif
|
|
#include "tcg-target-reg-bits.h"
|
|
@@ -48,11 +48,14 @@ struct TCGHelperInfo {
|
|
const char *name;
|
|
|
|
/* Used with g_once_init_enter. */
|
|
-#ifdef CONFIG_TCG_INTERPRETER
|
|
+#if defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
ffi_cif *cif;
|
|
#else
|
|
uintptr_t init;
|
|
#endif
|
|
+#if defined(EMSCRIPTEN)
|
|
+ ffi_cif *cif;
|
|
+#endif
|
|
|
|
unsigned typemask : 32;
|
|
unsigned flags : 8;
|
|
diff --git c/include/tcg/tcg-opc.h w/include/tcg/tcg-opc.h
|
|
index 546eb49c11..834757c3a4 100644
|
|
--- c/include/tcg/tcg-opc.h
|
|
+++ w/include/tcg/tcg-opc.h
|
|
@@ -305,7 +305,7 @@ DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT)
|
|
#include "tcg-target.opc.h"
|
|
#endif
|
|
|
|
-#ifdef TCG_TARGET_INTERPRETER
|
|
+#if defined(TCG_TARGET_INTERPRETER) || defined(EMSCRIPTEN)
|
|
/* These opcodes are only for use between the tci generator and interpreter. */
|
|
DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT)
|
|
DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT)
|
|
diff --git c/include/tcg/tcg.h w/include/tcg/tcg.h
|
|
index 9b82844dab..2cdfe0617b 100644
|
|
--- c/include/tcg/tcg.h
|
|
+++ w/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 c/meson.build w/meson.build
|
|
index 7f28871776..6e558969d4 100644
|
|
--- c/meson.build
|
|
+++ w/meson.build
|
|
@@ -45,9 +45,9 @@ genh = []
|
|
qapi_trace_events = []
|
|
|
|
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
|
|
-supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
|
|
+supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux', 'emscripten']
|
|
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
|
|
- 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64']
|
|
+ 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64', 'wasm32']
|
|
|
|
cpu = host_machine.cpu_family()
|
|
|
|
@@ -309,6 +309,7 @@ endif
|
|
# Compiler flags #
|
|
##################
|
|
|
|
+if host_arch != 'wasm32'
|
|
foreach lang : all_languages
|
|
compiler = meson.get_compiler(lang)
|
|
if compiler.get_id() == 'gcc' and compiler.version().version_compare('>=7.4')
|
|
@@ -328,6 +329,7 @@ foreach lang : all_languages
|
|
error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v15.0) to compile QEMU')
|
|
endif
|
|
endforeach
|
|
+endif
|
|
|
|
# default flags for all hosts
|
|
# We use -fwrapv to tell the compiler that we require a C dialect where
|
|
@@ -482,7 +484,7 @@ ucontext_probe = '''
|
|
# On Windows the only valid backend is the Windows specific one.
|
|
# For POSIX prefer ucontext, but it's not always possible. The fallback
|
|
# is sigcontext.
|
|
-supported_backends = []
|
|
+supported_backends = ['fiber']
|
|
if host_os == 'windows'
|
|
supported_backends += ['windows']
|
|
else
|
|
@@ -510,7 +512,7 @@ safe_stack_probe = '''
|
|
#endif
|
|
return 0;
|
|
}'''
|
|
-if get_option('safe_stack') != not cc.compiles(safe_stack_probe)
|
|
+if host_arch != 'wasm32' and get_option('safe_stack') != not cc.compiles(safe_stack_probe)
|
|
safe_stack_arg = get_option('safe_stack') ? '-fsanitize=safe-stack' : '-fno-sanitize=safe-stack'
|
|
if get_option('safe_stack') != not cc.compiles(safe_stack_probe, args: safe_stack_arg)
|
|
error(get_option('safe_stack') \
|
|
@@ -520,7 +522,7 @@ if get_option('safe_stack') != not cc.compiles(safe_stack_probe)
|
|
qemu_cflags += safe_stack_arg
|
|
qemu_ldflags += safe_stack_arg
|
|
endif
|
|
-if get_option('safe_stack') and coroutine_backend != 'ucontext'
|
|
+if host_arch != 'wasm32' and get_option('safe_stack') and coroutine_backend != 'ucontext'
|
|
error('SafeStack is only supported with the ucontext coroutine backend')
|
|
endif
|
|
|
|
@@ -884,6 +886,8 @@ if get_option('tcg').allowed()
|
|
tcg_arch = 'i386'
|
|
elif host_arch == 'ppc64'
|
|
tcg_arch = 'ppc'
|
|
+ elif host_arch == 'wasm32'
|
|
+ tcg_arch = 'wasm32'
|
|
endif
|
|
add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch,
|
|
language: all_languages)
|
|
@@ -1016,7 +1020,7 @@ endif
|
|
# problems on multi-arch where people try to build
|
|
# 32-bit QEMU while pointing at 64-bit glib headers
|
|
|
|
-if not cc.compiles('''
|
|
+if host_arch != 'wasm32' and not cc.compiles('''
|
|
#include <glib.h>
|
|
#include <unistd.h>
|
|
|
|
@@ -1227,9 +1231,11 @@ if not get_option('slirp').auto() or have_system
|
|
slirp_cflags += ['-DLIBSLIRP_STATIC']
|
|
endif
|
|
|
|
- slirp = declare_dependency(dependencies: [slirp_dep],
|
|
- compile_args: slirp_cflags,
|
|
- version: slirp_dep.version())
|
|
+ if slirp_dep.found()
|
|
+ slirp = declare_dependency(dependencies: [slirp_dep],
|
|
+ compile_args: slirp_cflags,
|
|
+ version: slirp_dep.version())
|
|
+ endif
|
|
|
|
# slirp < 4.7 is incompatible with CFI support in QEMU. This is because
|
|
# it passes function pointers within libslirp as callbacks for timers.
|
|
diff --git c/meson_options.txt w/meson_options.txt
|
|
index 5eeaf3eee5..76f7a65b40 100644
|
|
--- c/meson_options.txt
|
|
+++ w/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 c/os-posix.c w/os-posix.c
|
|
index 43f9a43f3f..595fa47866 100644
|
|
--- c/os-posix.c
|
|
+++ w/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 c/qom/object.c w/qom/object.c
|
|
index 9edc06d391..c3d657e6cb 100644
|
|
--- c/qom/object.c
|
|
+++ w/qom/object.c
|
|
@@ -1197,7 +1197,8 @@ GSList *object_class_get_list(const char *implements_type,
|
|
return list;
|
|
}
|
|
|
|
-static gint object_class_cmp(gconstpointer a, gconstpointer b)
|
|
+static gint object_class_cmp(gconstpointer a, gconstpointer b,
|
|
+ gpointer user_data)
|
|
{
|
|
return strcasecmp(object_class_get_name((ObjectClass *)a),
|
|
object_class_get_name((ObjectClass *)b));
|
|
@@ -1207,7 +1208,7 @@ GSList *object_class_get_list_sorted(const char *implements_type,
|
|
bool include_abstract)
|
|
{
|
|
return g_slist_sort(object_class_get_list(implements_type, include_abstract),
|
|
- object_class_cmp);
|
|
+ (GCompareFunc)object_class_cmp);
|
|
}
|
|
|
|
Object *object_ref(void *objptr)
|
|
diff --git c/scripts/meson-buildoptions.sh w/scripts/meson-buildoptions.sh
|
|
index a8066aab03..bd3ae90008 100644
|
|
--- c/scripts/meson-buildoptions.sh
|
|
+++ w/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 c/system/memory.c w/system/memory.c
|
|
index 85f6834cb3..3958314765 100644
|
|
--- c/system/memory.c
|
|
+++ w/system/memory.c
|
|
@@ -1636,7 +1636,7 @@ bool memory_region_init_resizeable_ram(MemoryRegion *mr,
|
|
return true;
|
|
}
|
|
|
|
-#ifdef CONFIG_POSIX
|
|
+#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
|
bool memory_region_init_ram_from_file(MemoryRegion *mr,
|
|
Object *owner,
|
|
const char *name,
|
|
diff --git c/system/physmem.c w/system/physmem.c
|
|
index 75389064a8..addd5d0c4b 100644
|
|
--- c/system/physmem.c
|
|
+++ w/system/physmem.c
|
|
@@ -1235,7 +1235,7 @@ long qemu_maxrampagesize(void)
|
|
return pagesize;
|
|
}
|
|
|
|
-#ifdef CONFIG_POSIX
|
|
+#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
|
static int64_t get_file_size(int fd)
|
|
{
|
|
int64_t size;
|
|
@@ -1941,7 +1941,7 @@ out_free:
|
|
}
|
|
}
|
|
|
|
-#ifdef CONFIG_POSIX
|
|
+#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
|
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
|
|
uint32_t ram_flags, int fd, off_t offset,
|
|
Error **errp)
|
|
@@ -2130,7 +2130,7 @@ static void reclaim_ramblock(RAMBlock *block)
|
|
;
|
|
} else if (xen_enabled()) {
|
|
xen_invalidate_map_cache_entry(block->host);
|
|
-#ifndef _WIN32
|
|
+#if !defined(_WIN32) && !defined(EMSCRIPTEN)
|
|
} else if (block->fd >= 0) {
|
|
qemu_ram_munmap(block->fd, block->host, block->max_length);
|
|
close(block->fd);
|
|
diff --git c/tcg/meson.build w/tcg/meson.build
|
|
index 69ebb4908a..0894a577c2 100644
|
|
--- c/tcg/meson.build
|
|
+++ w/tcg/meson.build
|
|
@@ -15,11 +15,18 @@ tcg_ss.add(files(
|
|
'tcg-op-vec.c',
|
|
))
|
|
|
|
+cpu = host_machine.cpu_family()
|
|
+
|
|
if get_option('tcg_interpreter')
|
|
libffi = dependency('libffi', version: '>=3.0', required: true,
|
|
method: 'pkg-config')
|
|
tcg_ss.add(libffi)
|
|
tcg_ss.add(files('tci.c'))
|
|
+elif cpu == 'wasm32'
|
|
+ libffi = dependency('libffi', version: '>=3.0', required: true,
|
|
+ method: 'pkg-config')
|
|
+ specific_ss.add(libffi)
|
|
+ specific_ss.add(files('wasm32.c'))
|
|
endif
|
|
|
|
tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
|
|
diff --git c/tcg/region.c w/tcg/region.c
|
|
index 478ec051c4..41895c68fa 100644
|
|
--- c/tcg/region.c
|
|
+++ w/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 c/tcg/tcg.c w/tcg/tcg.c
|
|
index 6dc3072f92..0f6c0f5082 100644
|
|
--- c/tcg/tcg.c
|
|
+++ w/tcg/tcg.c
|
|
@@ -131,6 +131,10 @@ static void tcg_out_goto_tb(TCGContext *s, int which);
|
|
static void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|
const TCGArg args[TCG_MAX_OP_ARGS],
|
|
const int const_args[TCG_MAX_OP_ARGS]);
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l);
|
|
+static void tcg_out_init();
|
|
+#endif
|
|
#if TCG_TARGET_MAYBE_vec
|
|
static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
|
|
TCGReg dst, TCGReg src);
|
|
@@ -245,7 +249,7 @@ TCGv_env tcg_env;
|
|
const void *tcg_code_gen_epilogue;
|
|
uintptr_t tcg_splitwx_diff;
|
|
|
|
-#ifndef CONFIG_TCG_INTERPRETER
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
tcg_prologue_fn *tcg_qemu_tb_exec;
|
|
#endif
|
|
|
|
@@ -352,6 +356,9 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l)
|
|
tcg_debug_assert(!l->has_value);
|
|
l->has_value = 1;
|
|
l->u.value_ptr = tcg_splitwx_to_rx(s->code_ptr);
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ tcg_out_label_cb(s, l);
|
|
+#endif
|
|
}
|
|
|
|
TCGLabel *gen_new_label(void)
|
|
@@ -420,6 +427,143 @@ tlb_mask_table_ofs(TCGContext *s, int which)
|
|
sizeof(CPUNegativeOffsetState));
|
|
}
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+
|
|
+#define SUB_BUF_MAX 100000
|
|
+__thread uint8_t sub_buf[SUB_BUF_MAX];
|
|
+__thread uint8_t *sub_buf_ptr;
|
|
+
|
|
+static inline void tcg_sub_out8(TCGContext *s, uint8_t v)
|
|
+{
|
|
+ *sub_buf_ptr++ = v;
|
|
+ if ((sub_buf_ptr - sub_buf) > SUB_BUF_MAX) {
|
|
+ printf("buffer too small"); fflush(stdout);
|
|
+ exit(1);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void tcg_sub_out32(TCGContext *s, uint32_t v)
|
|
+{
|
|
+ memcpy(sub_buf_ptr, &v, sizeof(v));
|
|
+ sub_buf_ptr += 4;
|
|
+}
|
|
+
|
|
+static inline void* cur_sub_buf_ptr()
|
|
+{
|
|
+ return sub_buf_ptr;
|
|
+}
|
|
+
|
|
+static inline int cur_sub_buf_off_rel()
|
|
+{
|
|
+ return (int)sub_buf_ptr - (int)sub_buf;
|
|
+}
|
|
+
|
|
+#define LABEL_MAX 200
|
|
+
|
|
+struct label_placeholder {
|
|
+ int label;
|
|
+ uintptr_t ptr;
|
|
+};
|
|
+
|
|
+struct label_context {
|
|
+ int block_idx;
|
|
+};
|
|
+
|
|
+#define WASM_NUM_HELPER_FUNCS_MAX 200
|
|
+#define WASM_HELPER_ADDED_TYPES_SECTION_MAX 200
|
|
+__thread uint32_t num_helper_funcs;
|
|
+__thread uint32_t target_helper_funcs[WASM_NUM_HELPER_FUNCS_MAX];
|
|
+__thread uint8_t target_helper_types[WASM_HELPER_ADDED_TYPES_SECTION_MAX];
|
|
+__thread int target_helper_types_pos;
|
|
+__thread int wasm_block_idx;
|
|
+__thread struct label_placeholder block_ptr_placeholder[LABEL_MAX];
|
|
+__thread int block_ptr_placeholder_idx_pos;
|
|
+__thread int label_to_block[LABEL_MAX];
|
|
+
|
|
+static int wasm_block_current_idx(TCGContext *s)
|
|
+{
|
|
+ return wasm_block_idx;
|
|
+}
|
|
+
|
|
+static int wasm_alloc_block_idx(TCGContext *s)
|
|
+{
|
|
+ return ++wasm_block_idx;
|
|
+}
|
|
+
|
|
+static void fill_uint32_leb128(uintptr_t bi, uint32_t v) {
|
|
+ uint8_t *b = (uint8_t *)bi;
|
|
+ uint32_t low7 = 0x7f;
|
|
+ // assuems higher bit already written as placeholders
|
|
+ do {
|
|
+ *b |= v & low7;
|
|
+ v >>= 7;
|
|
+ b++;
|
|
+ } while (v != 0);
|
|
+}
|
|
+
|
|
+static int write_uint32_leb128(uintptr_t bi, uint32_t v) {
|
|
+ uint8_t *b = (uint8_t *)bi;
|
|
+ uint32_t low7 = 0x7f;
|
|
+ do {
|
|
+ *b = (uint8_t)(v & low7);
|
|
+ v >>= 7;
|
|
+ if (v != 0)
|
|
+ *b |= 0x80;
|
|
+ b++;
|
|
+ } while (v != 0);
|
|
+
|
|
+ return (int)((uintptr_t)b - (uintptr_t)bi);
|
|
+}
|
|
+
|
|
+static uint8_t * wasm_get_helper_types_begin(TCGContext *s)
|
|
+{
|
|
+ return &(target_helper_types[target_helper_types_pos]);
|
|
+}
|
|
+
|
|
+static void wasm_add_helper_types_pos(TCGContext *s, int i)
|
|
+{
|
|
+ target_helper_types_pos += i;
|
|
+ tcg_debug_assert(target_helper_types_pos <= WASM_HELPER_ADDED_TYPES_SECTION_MAX);
|
|
+}
|
|
+
|
|
+static int wasm_register_helper_alloc_num(TCGContext *s)
|
|
+{
|
|
+ tcg_debug_assert(num_helper_funcs <= WASM_NUM_HELPER_FUNCS_MAX);
|
|
+ return num_helper_funcs++;
|
|
+}
|
|
+
|
|
+static void wasm_register_helper(TCGContext *s, int idx_on_tb, int helper_idx_on_qemu)
|
|
+{
|
|
+ tcg_debug_assert(idx_on_tb <= WASM_NUM_HELPER_FUNCS_MAX);
|
|
+ target_helper_funcs[idx_on_tb] = helper_idx_on_qemu;
|
|
+}
|
|
+
|
|
+static int get_wasm_helper_idx(TCGContext *s, int helper_idx_on_qemu)
|
|
+{
|
|
+ for (int i = 0; i < num_helper_funcs; i++) {
|
|
+ if (target_helper_funcs[i] == helper_idx_on_qemu) {
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void wasm_add_label_context(TCGContext *s, int label, int block)
|
|
+{
|
|
+ tcg_debug_assert(label <= LABEL_MAX);
|
|
+ label_to_block[label] = block;
|
|
+}
|
|
+
|
|
+static void wasm_add_label_block_ptr_placeholder(int label, uintptr_t code_ptr)
|
|
+{
|
|
+ int i = block_ptr_placeholder_idx_pos++;
|
|
+ tcg_debug_assert(i <= LABEL_MAX);
|
|
+ block_ptr_placeholder[i].label = label;
|
|
+ block_ptr_placeholder[i].ptr = code_ptr;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
/* Signal overflow, starting over with fewer guest insns. */
|
|
static G_NORETURN
|
|
void tcg_raise_tb_overflow(TCGContext *s)
|
|
@@ -754,7 +898,7 @@ static const TCGTargetOpDef constraint_sets[] = {
|
|
|
|
#include "tcg-target.c.inc"
|
|
|
|
-#ifndef CONFIG_TCG_INTERPRETER
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
/* Validate CPUTLBDescFast placement. */
|
|
QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) -
|
|
sizeof(CPUNegativeOffsetState))
|
|
@@ -937,7 +1081,7 @@ static TCGHelperInfo info_helper_st128_mmu = {
|
|
| dh_typemask(ptr, 5) /* uintptr_t ra */
|
|
};
|
|
|
|
-#ifdef CONFIG_TCG_INTERPRETER
|
|
+#if defined(EMSCRIPTEN) || defined(CONFIG_TCG_INTERPRETER)
|
|
static ffi_type *typecode_to_ffi(int argmask)
|
|
{
|
|
/*
|
|
@@ -1406,12 +1550,11 @@ void tcg_prologue_init(void)
|
|
{
|
|
TCGContext *s = tcg_ctx;
|
|
size_t prologue_size;
|
|
-
|
|
s->code_ptr = s->code_gen_ptr;
|
|
s->code_buf = s->code_gen_ptr;
|
|
s->data_gen_ptr = NULL;
|
|
|
|
-#ifndef CONFIG_TCG_INTERPRETER
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
tcg_qemu_tb_exec = (tcg_prologue_fn *)tcg_splitwx_to_rx(s->code_ptr);
|
|
#endif
|
|
|
|
@@ -1434,7 +1577,7 @@ void tcg_prologue_init(void)
|
|
prologue_size = tcg_current_code_size(s);
|
|
perf_report_prologue(s->code_gen_ptr, prologue_size);
|
|
|
|
-#ifndef CONFIG_TCG_INTERPRETER
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf),
|
|
(uintptr_t)s->code_buf, prologue_size);
|
|
#endif
|
|
@@ -1471,7 +1614,7 @@ void tcg_prologue_init(void)
|
|
}
|
|
}
|
|
|
|
-#ifndef CONFIG_TCG_INTERPRETER
|
|
+#if !defined(CONFIG_TCG_INTERPRETER) && !defined(EMSCRIPTEN)
|
|
/*
|
|
* Assert that goto_ptr is implemented completely, setting an epilogue.
|
|
* For tci, we use NULL as the signal to return from the interpreter,
|
|
@@ -5904,6 +6047,151 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst,
|
|
tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg);
|
|
}
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+
|
|
+static const uint8_t mod_header_a[] = {
|
|
+ 0x0, 0x61, 0x73, 0x6d, // magic
|
|
+ 0x01, 0x0, 0x0, 0x0, // version
|
|
+ // type section
|
|
+ 0x01, 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x60,
|
|
+ 0x01, 0x7f,
|
|
+ 0x01, 0x7f,
|
|
+
|
|
+};
|
|
+static const uint8_t mod_header_b[] = {
|
|
+ // import section
|
|
+ 0x02, 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x03, 0x65, 0x6e, 0x76,
|
|
+ 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72,
|
|
+ 0x02, 0x03, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+};
|
|
+
|
|
+static const uint8_t mod_header_c[] = {
|
|
+ // function section
|
|
+ 0x03, 2, 1, 0x00,
|
|
+ // global section
|
|
+ 0x06, 0x7e,
|
|
+ 25,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ 0x7e, 0x01, 0x42, 0x00, 0x0b,
|
|
+ // export section
|
|
+ 0x07, 13,
|
|
+ 1,
|
|
+ 0x05, 0x73, 0x74, 0x61, 0x72, 0x74,
|
|
+ 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+};
|
|
+
|
|
+static const uint8_t mod_header_d[] = {
|
|
+ 0x0a, 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+ 0x80, 0x80, 0x80, 0x80, 0x00,
|
|
+
|
|
+ 0x2, 0x2, 0x7f, 0x5, 0x7e,
|
|
+
|
|
+ // initialize the instance
|
|
+ 0x20, 0x0, // local.get $ctx
|
|
+ 0x28, 0, DO_INIT_OFF, // i32.load do_init_ptr
|
|
+ 0x41, 0, // i32.const 0
|
|
+ 0x47, // i32.ne
|
|
+ 0x04, 0x40, // if
|
|
+
|
|
+ 0x23, 14, // global.get $env
|
|
+ 0x50, // i64.eqz
|
|
+ 0x04, 0x40, // if
|
|
+ // fundamental variables
|
|
+ 0x20, 0x0, // local.get $ctx
|
|
+ 0x28, 0, ENV_OFF, // i32.load env
|
|
+ 0xad, // extend
|
|
+ 0x24, 14, // global.set $14
|
|
+ 0x20, 0x0, // local.get $ctx
|
|
+ 0x28, 0, STACK_OFF, // i32.load stack
|
|
+ 0xad, // extend
|
|
+ 0x24, 15, // global.set $15
|
|
+ 0x0b, // end
|
|
+
|
|
+ 0x20, 0x0, // local.get $ctx
|
|
+ 0x41, 0x00, // i32.const 0
|
|
+ 0x36, 0x00, DO_INIT_OFF, // i32.store do_init
|
|
+ 0x42, 0x00, // i64.const 0
|
|
+ 0x24, 16, // global.set $block_ptr
|
|
+ 0x0b, // end
|
|
+
|
|
+ 0x03, 0x40, // loop
|
|
+ 0x23, 16, // global.get $block_ptr
|
|
+ 0x50, // i64.eqz
|
|
+ 0x04, 0x40, // if
|
|
+};
|
|
+
|
|
+static void write_wasm_type_section_size(TCGContext *s, void *header_a_ptr, uint32_t added) {
|
|
+ uint32_t type_section_size = added + 10;
|
|
+ fill_uint32_leb128((uintptr_t)header_a_ptr + 9, type_section_size);
|
|
+ fill_uint32_leb128((uintptr_t)header_a_ptr + 14, num_helper_funcs + 1);
|
|
+}
|
|
+static void write_wasm_memory_size(TCGContext *s, void *header_b_ptr) {
|
|
+ fill_uint32_leb128((uintptr_t)header_b_ptr + 25, (uint32_t)(~0) / 65536);
|
|
+}
|
|
+static void write_wasm_import_section_size(TCGContext *s, void *header_b_ptr, uint32_t added, uint32_t num_imported_funcs) {
|
|
+ uint32_t import_section_size = 35 + added - 11;
|
|
+ fill_uint32_leb128((uintptr_t)header_b_ptr + 1, import_section_size);
|
|
+ fill_uint32_leb128((uintptr_t)header_b_ptr + 6, num_imported_funcs + 1/*buffer+helpers...*/);
|
|
+}
|
|
+static void write_wasm_export_section_size(TCGContext *s, void *header_c_ptr, uint32_t startidx) {
|
|
+ fill_uint32_leb128((uintptr_t)header_c_ptr + 142, startidx);
|
|
+}
|
|
+static void write_wasm_code_size(TCGContext *s, void *header_d_ptr, int code_size, int code_nums) {
|
|
+ code_size = code_size + 66;
|
|
+ fill_uint32_leb128((uintptr_t)header_d_ptr + 1, code_size);
|
|
+ fill_uint32_leb128((uintptr_t)header_d_ptr + 6, code_nums);
|
|
+ fill_uint32_leb128((uintptr_t)header_d_ptr + 11, code_size - 10);
|
|
+}
|
|
+
|
|
+uint8_t *tcg_out_import_entry(TCGContext *s, uint8_t* wasm_blob_ptr, int i, int typeidx)
|
|
+{
|
|
+ *wasm_blob_ptr++ = 6; // helper
|
|
+ *wasm_blob_ptr++ = 0x68;
|
|
+ *wasm_blob_ptr++ = 0x65;
|
|
+ *wasm_blob_ptr++ = 0x6c;
|
|
+ *wasm_blob_ptr++ = 0x70;
|
|
+ *wasm_blob_ptr++ = 0x65;
|
|
+ *wasm_blob_ptr++ = 0x72;
|
|
+ char buf[100];
|
|
+ int n = snprintf(buf, sizeof(buf), "%d", i);
|
|
+ wasm_blob_ptr += write_uint32_leb128((uintptr_t)wasm_blob_ptr, n);
|
|
+ memcpy(wasm_blob_ptr, buf, n);
|
|
+ wasm_blob_ptr += n;
|
|
+ *wasm_blob_ptr++ = 0x00; //type(0)
|
|
+ *wasm_blob_ptr++ = typeidx; //typeidx
|
|
+ return wasm_blob_ptr;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
static void tcg_out_ld_helper_ret(TCGContext *s, const TCGLabelQemuLdst *ldst,
|
|
bool load_sign,
|
|
const TCGLdstHelperParam *parm)
|
|
@@ -6185,6 +6473,31 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start)
|
|
s->code_ptr = s->code_buf;
|
|
s->data_gen_ptr = NULL;
|
|
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ sub_buf_ptr = sub_buf;
|
|
+ tcg_out_init();
|
|
+ num_helper_funcs = 0;
|
|
+ wasm_block_idx = 0;
|
|
+ block_ptr_placeholder_idx_pos = 0;
|
|
+ target_helper_types_pos = 0;
|
|
+ memset(label_to_block, -1, LABEL_MAX);
|
|
+ memset(target_helper_funcs, -1, WASM_NUM_HELPER_FUNCS_MAX);
|
|
+
|
|
+ uint32_t *tci_code_off = (uint32_t*)s->code_ptr;
|
|
+ s->code_ptr += 4;
|
|
+ uint32_t *size_base = (uint32_t*)s->code_ptr;
|
|
+ s->code_ptr += 4;
|
|
+ uint32_t *export_vec_base = (uint32_t*)s->code_ptr;
|
|
+ int export_size = get_core_nums() * 4;
|
|
+ memset(s->code_ptr, 0, export_size);
|
|
+ s->code_ptr += export_size;
|
|
+ *size_base = export_size;
|
|
+
|
|
+ uint8_t *code_begin = s->code_ptr;
|
|
+ s->code_ptr += 4; // placeholder for size
|
|
+ *tci_code_off = s->code_ptr - s->code_buf;
|
|
+#endif
|
|
+
|
|
#ifdef TCG_TARGET_NEED_LDST_LABELS
|
|
QSIMPLEQ_INIT(&s->ldst_labels);
|
|
#endif
|
|
@@ -6286,13 +6599,94 @@ 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)
|
|
+ if (wasm_tci_only_tb) {
|
|
+ for (int i = 0; i < get_core_nums(); i++) {
|
|
+ export_vec_base[i] = WASM_TCI_ONLY_ENTRY;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tcg_sub_out8(s, 0x0b); //end if
|
|
+ tcg_sub_out8(s, 0x0b); //end loop
|
|
+ tcg_sub_out8(s, 0x0); // unreachable
|
|
+ tcg_sub_out8(s, 0x0b); //end func
|
|
+
|
|
+ // fill blocks
|
|
+ for (int i = 0; i < block_ptr_placeholder_idx_pos; i++) {
|
|
+ int label = block_ptr_placeholder[i].label;
|
|
+ int ph = block_ptr_placeholder[i].ptr;
|
|
+ int blk = label_to_block[label];
|
|
+ tcg_debug_assert(blk >= 0);
|
|
+ *(uint8_t*)ph = 0x80;
|
|
+ fill_uint32_leb128(ph, blk);
|
|
+ }
|
|
+
|
|
+ int code_size = (uint32_t)((uintptr_t)s->code_ptr - (uintptr_t)code_begin - 4);
|
|
+ *(uint32_t *)code_begin = code_size;
|
|
+
|
|
+ int sub_buf_len = sub_buf_ptr - sub_buf;
|
|
+ int wasm_body_size = sub_buf_len;
|
|
+
|
|
+ // write header
|
|
+ uint8_t *wasm_blob_ptr = s->code_ptr;
|
|
+ uint8_t *wasm_blob_ptr_base = s->code_ptr;
|
|
+ wasm_blob_ptr += 4; // placeholder for size
|
|
+
|
|
+ uint8_t *header_a_base = wasm_blob_ptr;
|
|
+ memcpy(wasm_blob_ptr, mod_header_a, sizeof(mod_header_a));
|
|
+ wasm_blob_ptr += sizeof(mod_header_a);
|
|
+ memcpy(wasm_blob_ptr, target_helper_types, target_helper_types_pos);
|
|
+ wasm_blob_ptr += target_helper_types_pos;
|
|
+ write_wasm_type_section_size(s, header_a_base, target_helper_types_pos);
|
|
+ uint8_t *header_b_base = wasm_blob_ptr;
|
|
+ memcpy(wasm_blob_ptr, mod_header_b, sizeof(mod_header_b));
|
|
+ wasm_blob_ptr += sizeof(mod_header_b);
|
|
+ uint8_t *header_b_adding_base = wasm_blob_ptr;
|
|
+ for (int i = 0; i < num_helper_funcs; i++) {
|
|
+ wasm_blob_ptr = tcg_out_import_entry(s, wasm_blob_ptr, i, i+1/*type0=start,1=helpers...*/);
|
|
+ }
|
|
+ write_wasm_import_section_size(s, header_b_base, (uint32_t)wasm_blob_ptr - (uint32_t)header_b_adding_base, num_helper_funcs);
|
|
+ write_wasm_memory_size(s, header_b_base);
|
|
+ uint8_t *header_c_base = wasm_blob_ptr;
|
|
+ memcpy(wasm_blob_ptr, mod_header_c, sizeof(mod_header_c));
|
|
+ wasm_blob_ptr += sizeof(mod_header_c);
|
|
+ write_wasm_export_section_size(s, header_c_base, num_helper_funcs);
|
|
+ uint8_t *header_d_ptr = wasm_blob_ptr;
|
|
+ memcpy(wasm_blob_ptr, mod_header_d, sizeof(mod_header_d));
|
|
+ wasm_blob_ptr += sizeof(mod_header_d);
|
|
+ write_wasm_code_size(s, header_d_ptr, wasm_body_size, 1);
|
|
+ s->code_ptr = wasm_blob_ptr;
|
|
+
|
|
+ // write body
|
|
+ memcpy(s->code_ptr, sub_buf, sub_buf_len);
|
|
+ s->code_ptr += sub_buf_len;
|
|
+
|
|
+ // write blob size
|
|
+ if (sub_buf_len > SUB_BUF_MAX) {
|
|
+ printf("sub too large sub_buf_len: %d\n", sub_buf_len); fflush(stdout);
|
|
+ exit(1);
|
|
+ }
|
|
+ *(uint32_t *)wasm_blob_ptr_base = s->code_ptr - wasm_blob_ptr_base - 4;
|
|
+
|
|
+ // record importing helper functions
|
|
+ size_base = (uint32_t*)s->code_ptr;
|
|
+ s->code_ptr += 4;
|
|
+ memcpy(s->code_ptr, target_helper_funcs, num_helper_funcs * 4);
|
|
+ s->code_ptr += num_helper_funcs * 4;
|
|
+ *size_base = num_helper_funcs * 4;
|
|
+
|
|
+ if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) {
|
|
+ return -1;
|
|
+ }
|
|
+#endif
|
|
+
|
|
return tcg_current_code_size(s);
|
|
}
|
|
|
|
diff --git c/tcg/wasm32.c w/tcg/wasm32.c
|
|
new file mode 100644
|
|
index 0000000000..19b31ed4df
|
|
--- /dev/null
|
|
+++ w/tcg/wasm32.c
|
|
@@ -0,0 +1,1275 @@
|
|
+/*
|
|
+ * Tiny Code Generator for QEMU
|
|
+ *
|
|
+ * Wasm integration + ported TCI interpreter from tci.c
|
|
+ *
|
|
+ * Copyright (c) 2009, 2011, 2016 Stefan Weil
|
|
+ *
|
|
+ * This program is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#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"
|
|
+#include "exec/translation-block.h"
|
|
+
|
|
+__thread uintptr_t tci_tb_ptr;
|
|
+__thread bool wasm_tci_only_tb;
|
|
+
|
|
+/* Disassemble TCI bytecode. */
|
|
+int print_insn_tci(bfd_vma addr, disassemble_info *info)
|
|
+{
|
|
+ return 0; //nop
|
|
+}
|
|
+
|
|
+EM_JS(int, instantiate_wasm, (), {
|
|
+ const memory_v = new DataView(HEAP8.buffer);
|
|
+
|
|
+ const tb_ptr = memory_v.getInt32(Module.__wasm32_tb.tb_ptr_ptr, true);
|
|
+ const export_vec_size = memory_v.getInt32(tb_ptr + 4, true);
|
|
+ const export_vec_begin = tb_ptr + 4 + 4;
|
|
+
|
|
+ const tmp_body_size = memory_v.getInt32(export_vec_begin + export_vec_size, true);
|
|
+ const tmp_body_begin = export_vec_begin + export_vec_size + 4;
|
|
+ const wasm_size = memory_v.getInt32(tmp_body_begin + tmp_body_size, true);
|
|
+ const wasm_begin = tmp_body_begin + tmp_body_size + 4;
|
|
+ const import_vec_size = memory_v.getInt32(wasm_begin + wasm_size, true);
|
|
+ const import_vec_begin = wasm_begin + wasm_size + 4;
|
|
+
|
|
+ const wasm = Uint8Array.from(HEAP8.subarray(wasm_begin, wasm_begin + wasm_size));
|
|
+ var helper = {};
|
|
+ for (var i = 0; i < import_vec_size / 4; i++) {
|
|
+ helper[i] = wasmTable.get(memory_v.getInt32(import_vec_begin + i * 4, true));
|
|
+ }
|
|
+ const mod = new WebAssembly.Module(wasm);
|
|
+ const inst = new WebAssembly.Instance(mod, {
|
|
+ "env": {
|
|
+ "buffer": wasmMemory,
|
|
+ },
|
|
+ "helper": helper,
|
|
+ });
|
|
+ var ptr = export_vec_begin + 4 * Module.__wasm32_tb.cur_core_num;
|
|
+ const fidx = addFunction(inst.exports.start, 'ii');
|
|
+ Module.__wasm32_tb.dynamic_functions.add(fidx);
|
|
+ memory_v.setUint32(ptr, fidx, true);
|
|
+
|
|
+ const remove_n = memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, true);
|
|
+ if (remove_n > 500) {
|
|
+ for (var i = 0; i < remove_n * 4; i += 4) {
|
|
+ const fidx = memory_v.getInt32(Module.__wasm32_tb.to_remove_instance_ptr + i, true);
|
|
+ if (Module.__wasm32_tb.dynamic_functions.delete(fidx)) {
|
|
+ removeFunction(fidx);
|
|
+ }
|
|
+ }
|
|
+ memory_v.setInt32(Module.__wasm32_tb.to_remove_instance_idx_ptr, 0, true);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+});
|
|
+
|
|
+EM_JS(void, flush_tb_instances, (), {
|
|
+ const tb = Module.__wasm32_tb;
|
|
+ if (!tb) {
|
|
+ return;
|
|
+ }
|
|
+ const memory_v = new DataView(HEAP8.buffer);
|
|
+ const remove_n = memory_v.getInt32(tb.to_remove_instance_idx_ptr, true);
|
|
+ for (var i = 0; i < remove_n * 4; i += 4) {
|
|
+ const fidx = memory_v.getInt32(tb.to_remove_instance_ptr + i, true);
|
|
+ if (tb.dynamic_functions.delete(fidx)) {
|
|
+ removeFunction(fidx);
|
|
+ }
|
|
+ }
|
|
+ memory_v.setInt32(tb.to_remove_instance_idx_ptr, 0, true);
|
|
+});
|
|
+
|
|
+__thread bool initdone = false;
|
|
+__thread int cur_core_num = -1;
|
|
+__thread int export_vec_off = -1;
|
|
+__thread int all_cores_num = -1;
|
|
+int cur_core_num_max = 0;
|
|
+
|
|
+#define TO_REMOVE_INSTANCE_SIZE 50000
|
|
+__thread static int to_remove_instance[TO_REMOVE_INSTANCE_SIZE];
|
|
+__thread static int to_remove_instance_idx = 0;
|
|
+
|
|
+void remove_tb(void *tb_ptr) {
|
|
+ int32_t *slot = (int32_t*)((uint8_t*)tb_ptr + export_vec_off);
|
|
+ int32_t f = *slot;
|
|
+ if (f <= 0) {
|
|
+ return;
|
|
+ }
|
|
+ *slot = 0;
|
|
+ if (to_remove_instance_idx == TO_REMOVE_INSTANCE_SIZE) {
|
|
+ flush_tb_instances();
|
|
+ }
|
|
+ to_remove_instance[to_remove_instance_idx++] = f;
|
|
+}
|
|
+
|
|
+__thread struct wasmContext ctx = {
|
|
+ .tb_ptr = 0,
|
|
+ .stack = NULL,
|
|
+ /* .func_ptr = 0, */
|
|
+ /* .next_func_ptr = 0, */
|
|
+ .do_init = 1,
|
|
+ .stack128 = NULL,
|
|
+};
|
|
+
|
|
+void set_done_flag()
|
|
+{
|
|
+ ctx.done_flag = 1;
|
|
+}
|
|
+
|
|
+void set_unwinding_flag()
|
|
+{
|
|
+ ctx.unwinding = 1;
|
|
+}
|
|
+
|
|
+typedef uint32_t (*wasm_func_ptr)(struct wasmContext*);
|
|
+
|
|
+int get_core_nums()
|
|
+{
|
|
+ return emscripten_num_logical_cores();
|
|
+}
|
|
+
|
|
+EM_JS(void, init_wasm32_js, (int tb_ptr_ptr, int cur_core_num, int to_remove_instance_ptr, int to_remove_instance_idx_ptr), {
|
|
+ Module.__wasm32_tb = {
|
|
+ tb_ptr_ptr: tb_ptr_ptr,
|
|
+ cur_core_num: cur_core_num,
|
|
+ to_remove_instance_ptr: to_remove_instance_ptr,
|
|
+ to_remove_instance_idx_ptr: to_remove_instance_idx_ptr,
|
|
+ dynamic_functions: new Set(),
|
|
+ };
|
|
+});
|
|
+
|
|
+void init_wasm32()
|
|
+{
|
|
+ if (!initdone) {
|
|
+ cur_core_num = qatomic_fetch_inc(&cur_core_num_max);
|
|
+ export_vec_off = 4 + 4 + cur_core_num * 4;
|
|
+ all_cores_num = get_core_nums();
|
|
+ ctx.stack = (uint64_t*)malloc(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE);
|
|
+ ctx.stack128 = (uint64_t*)malloc(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE);
|
|
+ ctx.tci_tb_ptr = (uint32_t*)&tci_tb_ptr;
|
|
+ init_wasm32_js((int)&ctx.tb_ptr, cur_core_num, (int)to_remove_instance, (int)&to_remove_instance_idx);
|
|
+ initdone = true;
|
|
+ }
|
|
+}
|
|
+
|
|
+__thread uintptr_t tci_tb_ptr;
|
|
+
|
|
+static void tci_write_reg64(tcg_target_ulong *regs, uint32_t high_index,
|
|
+ uint32_t low_index, uint64_t value)
|
|
+{
|
|
+ regs[low_index] = (uint32_t)value;
|
|
+ regs[high_index] = value >> 32;
|
|
+}
|
|
+
|
|
+/* Create a 64 bit value from two 32 bit values. */
|
|
+static uint64_t tci_uint64(uint32_t high, uint32_t low)
|
|
+{
|
|
+ return ((uint64_t)high << 32) + low;
|
|
+}
|
|
+
|
|
+static void tci_args_ldst(uint32_t insn, TCGReg *r0, TCGReg *r1, MemOpIdx *m2, const void *tb_ptr, void **l0)
|
|
+{
|
|
+ int diff = sextract32(insn, 12, 20);
|
|
+ *l0 = diff ? (uint8_t *)tb_ptr + diff : NULL;
|
|
+
|
|
+ uint64_t *data64 = (uint64_t*)*l0;
|
|
+ *r0 = (TCGReg)data64[0];
|
|
+ *r1 = (TCGReg)data64[1];
|
|
+ *m2 = (MemOpIdx)data64[2];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Load sets of arguments all at once. The naming convention is:
|
|
+ * tci_args_<arguments>
|
|
+ * where arguments is a sequence of
|
|
+ *
|
|
+ * b = immediate (bit position)
|
|
+ * c = condition (TCGCond)
|
|
+ * i = immediate (uint32_t)
|
|
+ * I = immediate (tcg_target_ulong)
|
|
+ * l = label or pointer
|
|
+ * m = immediate (MemOpIdx)
|
|
+ * n = immediate (call return length)
|
|
+ * r = register
|
|
+ * s = signed ldst offset
|
|
+ */
|
|
+
|
|
+static void tci_args_l(uint32_t insn, const void *tb_ptr, void **l0)
|
|
+{
|
|
+ int diff = sextract32(insn, 12, 20);
|
|
+ *l0 = diff ? (uint8_t *)tb_ptr + diff : NULL;
|
|
+}
|
|
+
|
|
+static void tci_args_r(uint32_t insn, TCGReg *r0)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_nl(uint32_t insn, const void *tb_ptr,
|
|
+ uint8_t *n0, void **l1)
|
|
+{
|
|
+ *n0 = extract32(insn, 8, 4);
|
|
+ *l1 = sextract32(insn, 12, 20) + (void *)tb_ptr;
|
|
+}
|
|
+
|
|
+static void tci_args_rl(uint32_t insn, const void *tb_ptr,
|
|
+ TCGReg *r0, void **l1)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *l1 = sextract32(insn, 12, 20) + (void *)tb_ptr;
|
|
+}
|
|
+
|
|
+static void tci_args_rr(uint32_t insn, TCGReg *r0, TCGReg *r1)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_ri(uint32_t insn, TCGReg *r0, tcg_target_ulong *i1)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *i1 = sextract32(insn, 12, 20);
|
|
+}
|
|
+
|
|
+static void tci_args_rrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_rrs(uint32_t insn, TCGReg *r0, TCGReg *r1, int32_t *i2)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *i2 = sextract32(insn, 16, 16);
|
|
+}
|
|
+
|
|
+static void tci_args_rrbb(uint32_t insn, TCGReg *r0, TCGReg *r1,
|
|
+ uint8_t *i2, uint8_t *i3)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *i2 = extract32(insn, 16, 6);
|
|
+ *i3 = extract32(insn, 22, 6);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrc(uint32_t insn,
|
|
+ TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGCond *c3)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *c3 = extract32(insn, 20, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1,
|
|
+ TCGReg *r2, uint8_t *i3, uint8_t *i4)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *i3 = extract32(insn, 20, 6);
|
|
+ *i4 = extract32(insn, 26, 6);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1,
|
|
+ TCGReg *r2, TCGReg *r3, TCGReg *r4)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *r3 = extract32(insn, 20, 4);
|
|
+ *r4 = extract32(insn, 24, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrr(uint32_t insn,
|
|
+ TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGReg *r3)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *r3 = extract32(insn, 20, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrrrc(uint32_t insn, TCGReg *r0, TCGReg *r1,
|
|
+ TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGCond *c5)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *r3 = extract32(insn, 20, 4);
|
|
+ *r4 = extract32(insn, 24, 4);
|
|
+ *c5 = extract32(insn, 28, 4);
|
|
+}
|
|
+
|
|
+static void tci_args_rrrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1,
|
|
+ TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGReg *r5)
|
|
+{
|
|
+ *r0 = extract32(insn, 8, 4);
|
|
+ *r1 = extract32(insn, 12, 4);
|
|
+ *r2 = extract32(insn, 16, 4);
|
|
+ *r3 = extract32(insn, 20, 4);
|
|
+ *r4 = extract32(insn, 24, 4);
|
|
+ *r5 = extract32(insn, 28, 4);
|
|
+}
|
|
+
|
|
+static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition)
|
|
+{
|
|
+ bool result = false;
|
|
+ int32_t i0 = u0;
|
|
+ int32_t i1 = u1;
|
|
+ switch (condition) {
|
|
+ case TCG_COND_EQ:
|
|
+ result = (u0 == u1);
|
|
+ break;
|
|
+ case TCG_COND_NE:
|
|
+ result = (u0 != u1);
|
|
+ break;
|
|
+ case TCG_COND_LT:
|
|
+ result = (i0 < i1);
|
|
+ break;
|
|
+ case TCG_COND_GE:
|
|
+ result = (i0 >= i1);
|
|
+ break;
|
|
+ case TCG_COND_LE:
|
|
+ result = (i0 <= i1);
|
|
+ break;
|
|
+ case TCG_COND_GT:
|
|
+ result = (i0 > i1);
|
|
+ break;
|
|
+ case TCG_COND_LTU:
|
|
+ result = (u0 < u1);
|
|
+ break;
|
|
+ case TCG_COND_GEU:
|
|
+ result = (u0 >= u1);
|
|
+ break;
|
|
+ case TCG_COND_LEU:
|
|
+ result = (u0 <= u1);
|
|
+ break;
|
|
+ case TCG_COND_GTU:
|
|
+ result = (u0 > u1);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition)
|
|
+{
|
|
+ bool result = false;
|
|
+ int64_t i0 = u0;
|
|
+ int64_t i1 = u1;
|
|
+ switch (condition) {
|
|
+ case TCG_COND_EQ:
|
|
+ result = (u0 == u1);
|
|
+ break;
|
|
+ case TCG_COND_NE:
|
|
+ result = (u0 != u1);
|
|
+ break;
|
|
+ case TCG_COND_LT:
|
|
+ result = (i0 < i1);
|
|
+ break;
|
|
+ case TCG_COND_GE:
|
|
+ result = (i0 >= i1);
|
|
+ break;
|
|
+ case TCG_COND_LE:
|
|
+ result = (i0 <= i1);
|
|
+ break;
|
|
+ case TCG_COND_GT:
|
|
+ result = (i0 > i1);
|
|
+ break;
|
|
+ case TCG_COND_LTU:
|
|
+ result = (u0 < u1);
|
|
+ break;
|
|
+ case TCG_COND_GEU:
|
|
+ result = (u0 >= u1);
|
|
+ break;
|
|
+ case TCG_COND_LEU:
|
|
+ result = (u0 <= u1);
|
|
+ break;
|
|
+ case TCG_COND_GTU:
|
|
+ result = (u0 > u1);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static uint64_t tlb_load(CPUArchState *env, uint64_t taddr, MemOp mop, uint64_t* ptr, bool is_ld)
|
|
+{
|
|
+ uint64_t *data64 = (uint64_t*)ptr;
|
|
+ unsigned a_mask = (unsigned)data64[3];
|
|
+ int mask_ofs = (int)data64[4];
|
|
+ int8_t page_bits = (int8_t)data64[5];
|
|
+ uint64_t page_mask = (uint64_t)data64[6];
|
|
+ int table_ofs = (uint64_t)data64[7];
|
|
+
|
|
+ unsigned s_bits = mop & MO_SIZE;
|
|
+ unsigned s_mask = (1u << s_bits) - 1;
|
|
+ tcg_target_long compare_mask;
|
|
+ int add_off = offsetof(CPUTLBEntry, addend);
|
|
+
|
|
+ uint64_t tmp0 = taddr >> (page_bits - CPU_TLB_ENTRY_BITS);
|
|
+ uint64_t tmp2 = *(uint64_t*)((uint8_t*)env + mask_ofs);
|
|
+ uint64_t tmp2_b = *(uint64_t*)((uint8_t*)env + table_ofs);
|
|
+ uint64_t tmp3 = (tmp0 & tmp2) + tmp2_b;
|
|
+ int off = off = is_ld ? offsetof(CPUTLBEntry, addr_read)
|
|
+ : offsetof(CPUTLBEntry, addr_write);
|
|
+ uint64_t target = *(uint64_t*)((uint8_t*)tmp3 + off);
|
|
+ uint64_t c_addr = taddr;
|
|
+ if (a_mask < s_mask) {
|
|
+ c_addr += s_mask - a_mask;
|
|
+ }
|
|
+ compare_mask = page_mask | a_mask;
|
|
+ c_addr &= compare_mask;
|
|
+
|
|
+ if (c_addr == target) {
|
|
+ int32_t addend = *(uint32_t*)((uint8_t*)tmp3 + add_off);
|
|
+ uint64_t target_addr = taddr + addend;
|
|
+ return target_addr;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uint64_t tci_qemu_ld(CPUArchState *env, uint64_t taddr,
|
|
+ MemOpIdx oi, const void *tb_ptr, uint64_t* ptr)
|
|
+{
|
|
+ MemOp mop = get_memop(oi);
|
|
+ uintptr_t ra = (uintptr_t)tb_ptr;
|
|
+
|
|
+ uint64_t target_addr = tlb_load(env, taddr, mop, ptr, true);
|
|
+ if (target_addr != 0) {
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UB:
|
|
+ return *(uint8_t*)target_addr;
|
|
+ case MO_SB:
|
|
+ return *(int8_t*)target_addr;
|
|
+ case MO_UW:
|
|
+ return *(uint16_t*)target_addr;
|
|
+ case MO_SW:
|
|
+ return *(int16_t*)target_addr;
|
|
+ case MO_UL:
|
|
+ return *(uint32_t*)target_addr;
|
|
+ case MO_SL:
|
|
+ return *(int32_t*)target_addr;
|
|
+ case MO_UQ:
|
|
+ return *(uint64_t*)target_addr;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UB:
|
|
+ return helper_ldub_mmu(env, taddr, oi, ra);
|
|
+ case MO_SB:
|
|
+ return helper_ldsb_mmu(env, taddr, oi, ra);
|
|
+ case MO_UW:
|
|
+ return helper_lduw_mmu(env, taddr, oi, ra);
|
|
+ case MO_SW:
|
|
+ return helper_ldsw_mmu(env, taddr, oi, ra);
|
|
+ case MO_UL:
|
|
+ return helper_ldul_mmu(env, taddr, oi, ra);
|
|
+ case MO_SL:
|
|
+ return helper_ldsl_mmu(env, taddr, oi, ra);
|
|
+ case MO_UQ:
|
|
+ return helper_ldq_mmu(env, taddr, oi, ra);
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val,
|
|
+ MemOpIdx oi, const void *tb_ptr, uint64_t* ptr)
|
|
+{
|
|
+ MemOp mop = get_memop(oi);
|
|
+ uintptr_t ra = (uintptr_t)tb_ptr;
|
|
+
|
|
+ uint64_t target_addr = tlb_load(env, taddr, mop, ptr, false);
|
|
+ if (target_addr != 0) {
|
|
+ uint64_t mem_bytes = (uint64_t)__builtin_wasm_memory_size(0) << 16;
|
|
+ if (target_addr + (1u << (mop & MO_SIZE)) > mem_bytes) {
|
|
+ TranslationBlock *tb = tcg_tb_lookup(ra);
|
|
+ fprintf(stderr,
|
|
+ "[sd-watch] OOB store pc=%08llx gaddr=%08llx haddr=%08llx size=%u value=%08llx membytes=%08llx\n",
|
|
+ (unsigned long long)(tb ? tb->pc : 0),
|
|
+ (unsigned long long)taddr, (unsigned long long)target_addr,
|
|
+ 1u << (mop & MO_SIZE), (unsigned long long)val,
|
|
+ (unsigned long long)mem_bytes);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if (target_addr != 0) {
|
|
+ switch (mop & MO_SIZE) {
|
|
+ case MO_UB:
|
|
+ *(uint8_t*)target_addr = (uint8_t)val;
|
|
+ break;
|
|
+ case MO_UW:
|
|
+ *(uint16_t*)target_addr = (uint16_t)val;
|
|
+ break;
|
|
+ case MO_UL:
|
|
+ *(uint32_t*)target_addr = (uint32_t)val;
|
|
+ break;
|
|
+ case MO_UQ:
|
|
+ *(uint64_t*)target_addr = (uint64_t)val;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (mop & MO_SIZE) {
|
|
+ case MO_UB:
|
|
+ helper_stb_mmu(env, taddr, val, oi, ra);
|
|
+ break;
|
|
+ case MO_UW:
|
|
+ helper_stw_mmu(env, taddr, val, oi, ra);
|
|
+ break;
|
|
+ case MO_UL:
|
|
+ helper_stl_mmu(env, taddr, val, oi, ra);
|
|
+ break;
|
|
+ case MO_UQ:
|
|
+ helper_stq_mmu(env, taddr, val, oi, ra);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+# define CASE_32_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i64): \
|
|
+ case glue(glue(INDEX_op_, x), _i32):
|
|
+# define CASE_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i64):
|
|
+#else
|
|
+# define CASE_32_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i32):
|
|
+# define CASE_64(x)
|
|
+#endif
|
|
+
|
|
+__thread tcg_target_ulong regs[TCG_TARGET_NB_REGS];
|
|
+
|
|
+static inline uintptr_t tcg_qemu_tb_exec_tci(CPUArchState *env)
|
|
+{
|
|
+ uint32_t *tb_ptr = (uint8_t*)ctx.tb_ptr + *(uint32_t*)ctx.tb_ptr;
|
|
+ uint64_t *stack = ctx.stack;
|
|
+
|
|
+ regs[TCG_AREG0] = (tcg_target_ulong)env;
|
|
+ regs[TCG_REG_CALL_STACK] = (uintptr_t)stack;
|
|
+
|
|
+ for (;;) {
|
|
+ uint32_t insn;
|
|
+ TCGOpcode opc;
|
|
+ TCGReg r0, r1, r2, r3, r4, r5;
|
|
+ tcg_target_ulong t1;
|
|
+ TCGCond condition;
|
|
+ uint8_t pos, len;
|
|
+ uint32_t tmp32;
|
|
+ uint64_t tmp64, taddr;
|
|
+ uint64_t T1, T2;
|
|
+ MemOpIdx oi;
|
|
+ int32_t ofs;
|
|
+ void *ptr;
|
|
+
|
|
+ uint32_t *savep = tb_ptr;
|
|
+ insn = *tb_ptr++;
|
|
+ opc = extract32(insn, 0, 8);
|
|
+
|
|
+ TCGOpDef *def = &tcg_op_defs[opc];
|
|
+
|
|
+ switch (opc) {
|
|
+ case INDEX_op_call:
|
|
+ {
|
|
+ void *call_slots[MAX_CALL_IARGS];
|
|
+ ffi_cif *cif;
|
|
+ void *func;
|
|
+ unsigned i, s, n;
|
|
+
|
|
+ tci_args_nl(insn, tb_ptr, &len, &ptr);
|
|
+ uint64_t *data64 = (uint64_t*)ptr;
|
|
+ func = (void*)data64[0];
|
|
+ cif = (void*)data64[1];
|
|
+
|
|
+ int reg_iarg_base = 8;
|
|
+ if ((uint32_t)func == (uint32_t)helper_lookup_tb_ptr) {
|
|
+ regs[TCG_REG_R0] = (uint32_t)helper_lookup_tb_ptr((CPUArchState *)regs[reg_iarg_base]);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ int reg_idx = 0;
|
|
+ int reg_idx_end = 5; // NUM_OF_IARG_REGS
|
|
+ int stack_idx = 0;
|
|
+ n = cif->nargs;
|
|
+ for (i = s = 0; i < n; ++i) {
|
|
+ ffi_type *t = cif->arg_types[i];
|
|
+ if (reg_idx < reg_idx_end) {
|
|
+ call_slots[i] = ®s[reg_iarg_base + reg_idx];
|
|
+ reg_idx += DIV_ROUND_UP(t->size, 8);
|
|
+ } else {
|
|
+ call_slots[i] = &stack[stack_idx];
|
|
+ stack_idx += DIV_ROUND_UP(t->size, 8);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Helper functions may need to access the "return address" */
|
|
+ tci_tb_ptr = (uintptr_t)tb_ptr;
|
|
+ ffi_call(cif, func, stack, call_slots);
|
|
+ }
|
|
+
|
|
+ switch (len) {
|
|
+ case 0: /* void */
|
|
+ break;
|
|
+ case 1: /* uint32_t */
|
|
+ /*
|
|
+ * The result winds up "left-aligned" in the stack[0] slot.
|
|
+ * Note that libffi has an odd special case in that it will
|
|
+ * always widen an integral result to ffi_arg.
|
|
+ */
|
|
+ if (sizeof(ffi_arg) == 8) {
|
|
+ regs[TCG_REG_R0] = (uint32_t)stack[0];
|
|
+ } else {
|
|
+ regs[TCG_REG_R0] = *(uint32_t *)stack;
|
|
+ }
|
|
+ break;
|
|
+ case 2: /* uint64_t */
|
|
+ /*
|
|
+ * For TCG_TARGET_REG_BITS == 32, the register pair
|
|
+ * must stay in host memory order.
|
|
+ */
|
|
+ memcpy(®s[TCG_REG_R0], stack, 8);
|
|
+ break;
|
|
+ case 3: /* Int128 */
|
|
+ memcpy(®s[TCG_REG_R0], stack, 16);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_br:
|
|
+ tci_args_l(insn, tb_ptr, &ptr);
|
|
+ tb_ptr = ptr;
|
|
+ continue;
|
|
+ case INDEX_op_setcond_i32:
|
|
+ tci_args_rrrc(insn, &r0, &r1, &r2, &condition);
|
|
+ regs[r0] = tci_compare32(regs[r1], regs[r2], condition);
|
|
+ break;
|
|
+ case INDEX_op_movcond_i32:
|
|
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
|
|
+ tmp32 = tci_compare32(regs[r1], regs[r2], condition);
|
|
+ regs[r0] = regs[tmp32 ? r3 : r4];
|
|
+ break;
|
|
+#if TCG_TARGET_REG_BITS == 32
|
|
+ case INDEX_op_setcond2_i32:
|
|
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
|
|
+ T1 = tci_uint64(regs[r2], regs[r1]);
|
|
+ T2 = tci_uint64(regs[r4], regs[r3]);
|
|
+ regs[r0] = tci_compare64(T1, T2, condition);
|
|
+ break;
|
|
+#elif TCG_TARGET_REG_BITS == 64
|
|
+ case INDEX_op_setcond_i64:
|
|
+ tci_args_rrrc(insn, &r0, &r1, &r2, &condition);
|
|
+ regs[r0] = tci_compare64(regs[r1], regs[r2], condition);
|
|
+ break;
|
|
+ case INDEX_op_movcond_i64:
|
|
+ tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition);
|
|
+ tmp32 = tci_compare64(regs[r1], regs[r2], condition);
|
|
+ regs[r0] = regs[tmp32 ? r3 : r4];
|
|
+ break;
|
|
+#endif
|
|
+ CASE_32_64(mov)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = regs[r1];
|
|
+ break;
|
|
+ case INDEX_op_tci_movi:
|
|
+ tci_args_ri(insn, &r0, &t1);
|
|
+ regs[r0] = t1;
|
|
+ break;
|
|
+ case INDEX_op_tci_movl:
|
|
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
|
|
+ regs[r0] = *(tcg_target_ulong *)ptr;
|
|
+ break;
|
|
+
|
|
+ /* Load/store operations (32 bit). */
|
|
+
|
|
+ CASE_32_64(ld8u)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(uint8_t *)ptr;
|
|
+ break;
|
|
+ CASE_32_64(ld8s)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(int8_t *)ptr;
|
|
+ break;
|
|
+ CASE_32_64(ld16u)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(uint16_t *)ptr;
|
|
+ break;
|
|
+ CASE_32_64(ld16s)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(int16_t *)ptr;
|
|
+ break;
|
|
+ case INDEX_op_ld_i32:
|
|
+ CASE_64(ld32u)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(uint32_t *)ptr;
|
|
+ break;
|
|
+ CASE_32_64(st8)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ *(uint8_t *)ptr = regs[r0];
|
|
+ break;
|
|
+ CASE_32_64(st16)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ *(uint16_t *)ptr = regs[r0];
|
|
+ break;
|
|
+ case INDEX_op_st_i32:
|
|
+ CASE_64(st32)
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ *(uint32_t *)ptr = regs[r0];
|
|
+ break;
|
|
+
|
|
+ /* Arithmetic operations (mixed 32/64 bit). */
|
|
+
|
|
+ CASE_32_64(add)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] + regs[r2];
|
|
+ break;
|
|
+ CASE_32_64(sub)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] - regs[r2];
|
|
+ break;
|
|
+ CASE_32_64(mul)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] * regs[r2];
|
|
+ break;
|
|
+ CASE_32_64(and)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] & regs[r2];
|
|
+ break;
|
|
+ CASE_32_64(or)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] | regs[r2];
|
|
+ break;
|
|
+ CASE_32_64(xor)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] ^ regs[r2];
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_andc_i32 || TCG_TARGET_HAS_andc_i64
|
|
+ CASE_32_64(andc)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] & ~regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_orc_i32 || TCG_TARGET_HAS_orc_i64
|
|
+ CASE_32_64(orc)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] | ~regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_eqv_i32 || TCG_TARGET_HAS_eqv_i64
|
|
+ CASE_32_64(eqv)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = ~(regs[r1] ^ regs[r2]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_nand_i32 || TCG_TARGET_HAS_nand_i64
|
|
+ CASE_32_64(nand)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = ~(regs[r1] & regs[r2]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_nor_i32 || TCG_TARGET_HAS_nor_i64
|
|
+ CASE_32_64(nor)
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = ~(regs[r1] | regs[r2]);
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ /* Arithmetic operations (32 bit). */
|
|
+
|
|
+ case INDEX_op_div_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_divu_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_rem_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_remu_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2];
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_clz_i32
|
|
+ case INDEX_op_clz_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ tmp32 = regs[r1];
|
|
+ regs[r0] = tmp32 ? clz32(tmp32) : regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ctz_i32
|
|
+ case INDEX_op_ctz_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ tmp32 = regs[r1];
|
|
+ regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ctpop_i32
|
|
+ case INDEX_op_ctpop_i32:
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = ctpop32(regs[r1]);
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ /* Shift/rotate operations (32 bit). */
|
|
+
|
|
+ case INDEX_op_shl_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint32_t)regs[r1] << (regs[r2] & 31);
|
|
+ break;
|
|
+ case INDEX_op_shr_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint32_t)regs[r1] >> (regs[r2] & 31);
|
|
+ break;
|
|
+ case INDEX_op_sar_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int32_t)regs[r1] >> (regs[r2] & 31);
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_rot_i32
|
|
+ case INDEX_op_rotl_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = rol32(regs[r1], regs[r2] & 31);
|
|
+ break;
|
|
+ case INDEX_op_rotr_i32:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = ror32(regs[r1], regs[r2] & 31);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_deposit_i32
|
|
+ case INDEX_op_deposit_i32:
|
|
+ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len);
|
|
+ regs[r0] = deposit32(regs[r1], pos, len, regs[r2]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_extract_i32
|
|
+ case INDEX_op_extract_i32:
|
|
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
|
|
+ regs[r0] = extract32(regs[r1], pos, len);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_sextract_i32
|
|
+ case INDEX_op_sextract_i32:
|
|
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
|
|
+ regs[r0] = sextract32(regs[r1], pos, len);
|
|
+ break;
|
|
+#endif
|
|
+ case INDEX_op_brcond_i32:
|
|
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
|
|
+ if ((uint32_t)regs[r0]) {
|
|
+ tb_ptr = ptr;
|
|
+ }
|
|
+ break;
|
|
+#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_add2_i32
|
|
+ case INDEX_op_add2_i32:
|
|
+ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5);
|
|
+ T1 = tci_uint64(regs[r3], regs[r2]);
|
|
+ T2 = tci_uint64(regs[r5], regs[r4]);
|
|
+ tci_write_reg64(regs, r1, r0, T1 + T2);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_sub2_i32
|
|
+ case INDEX_op_sub2_i32:
|
|
+ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5);
|
|
+ T1 = tci_uint64(regs[r3], regs[r2]);
|
|
+ T2 = tci_uint64(regs[r5], regs[r4]);
|
|
+ tci_write_reg64(regs, r1, r0, T1 - T2);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_mulu2_i32
|
|
+ case INDEX_op_mulu2_i32:
|
|
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
|
|
+ tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3];
|
|
+ tci_write_reg64(regs, r1, r0, tmp64);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_muls2_i32
|
|
+ case INDEX_op_muls2_i32:
|
|
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
|
|
+ tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3];
|
|
+ tci_write_reg64(regs, r1, r0, tmp64);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64
|
|
+ CASE_32_64(ext8s)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (int8_t)regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 || \
|
|
+ TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64
|
|
+ CASE_32_64(ext16s)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (int16_t)regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ext8u_i32 || TCG_TARGET_HAS_ext8u_i64
|
|
+ CASE_32_64(ext8u)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (uint8_t)regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ext16u_i32 || TCG_TARGET_HAS_ext16u_i64
|
|
+ CASE_32_64(ext16u)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (uint16_t)regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64
|
|
+ CASE_32_64(bswap16)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = bswap16(regs[r1]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_bswap32_i32 || TCG_TARGET_HAS_bswap32_i64
|
|
+ CASE_32_64(bswap32)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = bswap32(regs[r1]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_not_i32 || TCG_TARGET_HAS_not_i64
|
|
+ CASE_32_64(not)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = ~regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_neg_i32 || TCG_TARGET_HAS_neg_i64
|
|
+ CASE_32_64(neg)
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = -regs[r1];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+ /* Load/store operations (64 bit). */
|
|
+
|
|
+ case INDEX_op_ld32s_i64:
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(int32_t *)ptr;
|
|
+ break;
|
|
+ case INDEX_op_ld_i64:
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ regs[r0] = *(uint64_t *)ptr;
|
|
+ break;
|
|
+ case INDEX_op_st_i64:
|
|
+ tci_args_rrs(insn, &r0, &r1, &ofs);
|
|
+ ptr = (void *)(regs[r1] + ofs);
|
|
+ *(uint64_t *)ptr = regs[r0];
|
|
+ break;
|
|
+
|
|
+ /* Arithmetic operations (64 bit). */
|
|
+
|
|
+ case INDEX_op_div_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_divu_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_rem_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2];
|
|
+ break;
|
|
+ case INDEX_op_remu_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2];
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_clz_i64
|
|
+ case INDEX_op_clz_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ctz_i64
|
|
+ case INDEX_op_ctz_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2];
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_ctpop_i64
|
|
+ case INDEX_op_ctpop_i64:
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = ctpop64(regs[r1]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_mulu2_i64
|
|
+ case INDEX_op_mulu2_i64:
|
|
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
|
|
+ mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_muls2_i64
|
|
+ case INDEX_op_muls2_i64:
|
|
+ tci_args_rrrr(insn, &r0, &r1, &r2, &r3);
|
|
+ muls64(®s[r0], ®s[r1], regs[r2], regs[r3]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_add2_i64
|
|
+ case INDEX_op_add2_i64:
|
|
+ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5);
|
|
+ T1 = regs[r2] + regs[r4];
|
|
+ T2 = regs[r3] + regs[r5] + (T1 < regs[r2]);
|
|
+ regs[r0] = T1;
|
|
+ regs[r1] = T2;
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_add2_i64
|
|
+ case INDEX_op_sub2_i64:
|
|
+ tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5);
|
|
+ T1 = regs[r2] - regs[r4];
|
|
+ T2 = regs[r3] - regs[r5] - (regs[r2] < regs[r4]);
|
|
+ regs[r0] = T1;
|
|
+ regs[r1] = T2;
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ /* Shift/rotate operations (64 bit). */
|
|
+
|
|
+ case INDEX_op_shl_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] << (regs[r2] & 63);
|
|
+ break;
|
|
+ case INDEX_op_shr_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = regs[r1] >> (regs[r2] & 63);
|
|
+ break;
|
|
+ case INDEX_op_sar_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = (int64_t)regs[r1] >> (regs[r2] & 63);
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_rot_i64
|
|
+ case INDEX_op_rotl_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = rol64(regs[r1], regs[r2] & 63);
|
|
+ break;
|
|
+ case INDEX_op_rotr_i64:
|
|
+ tci_args_rrr(insn, &r0, &r1, &r2);
|
|
+ regs[r0] = ror64(regs[r1], regs[r2] & 63);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_deposit_i64
|
|
+ case INDEX_op_deposit_i64:
|
|
+ tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len);
|
|
+ regs[r0] = deposit64(regs[r1], pos, len, regs[r2]);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_extract_i64
|
|
+ case INDEX_op_extract_i64:
|
|
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
|
|
+ regs[r0] = extract64(regs[r1], pos, len);
|
|
+ break;
|
|
+#endif
|
|
+#if TCG_TARGET_HAS_sextract_i64
|
|
+ case INDEX_op_sextract_i64:
|
|
+ tci_args_rrbb(insn, &r0, &r1, &pos, &len);
|
|
+ regs[r0] = sextract64(regs[r1], pos, len);
|
|
+ break;
|
|
+#endif
|
|
+ case INDEX_op_brcond_i64:
|
|
+ tci_args_rl(insn, tb_ptr, &r0, &ptr);
|
|
+ if (regs[r0]) {
|
|
+ tb_ptr = ptr;
|
|
+ }
|
|
+ break;
|
|
+ case INDEX_op_ext32s_i64:
|
|
+ case INDEX_op_ext_i32_i64:
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (int32_t)regs[r1];
|
|
+ break;
|
|
+ case INDEX_op_ext32u_i64:
|
|
+ case INDEX_op_extu_i32_i64:
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = (uint32_t)regs[r1];
|
|
+ break;
|
|
+#if TCG_TARGET_HAS_bswap64_i64
|
|
+ case INDEX_op_bswap64_i64:
|
|
+ tci_args_rr(insn, &r0, &r1);
|
|
+ regs[r0] = bswap64(regs[r1]);
|
|
+ break;
|
|
+#endif
|
|
+#endif /* TCG_TARGET_REG_BITS == 64 */
|
|
+
|
|
+ /* QEMU specific operations. */
|
|
+
|
|
+ case INDEX_op_exit_tb:
|
|
+ tci_args_l(insn, tb_ptr, &ptr);
|
|
+ ctx.tb_ptr = 0;
|
|
+ return (uintptr_t)ptr;
|
|
+
|
|
+ case INDEX_op_goto_tb:
|
|
+ tci_args_l(insn, tb_ptr, &ptr);
|
|
+ if (*(uint32_t **)ptr != 0) {
|
|
+ tb_ptr = *(uint32_t **)ptr;
|
|
+ ctx.tb_ptr = tb_ptr;
|
|
+ int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off;
|
|
+ if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) {
|
|
+ /* ponytail: Store TBs use TCI while tracing SD object corruption. */
|
|
+ } else if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) {
|
|
+ *(int32_t*)tb_entry_ptr -= 1;
|
|
+ } else {
|
|
+ // enter to wasm TB
|
|
+ return 0;
|
|
+ }
|
|
+ tb_ptr = (uint8_t*)tb_ptr + *(uint32_t*)tb_ptr;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_goto_ptr:
|
|
+ tci_args_r(insn, &r0);
|
|
+ ptr = (void *)regs[r0];
|
|
+ if (!ptr) {
|
|
+ ctx.tb_ptr = 0;
|
|
+ return 0;
|
|
+ }
|
|
+ tb_ptr = ptr;
|
|
+
|
|
+ ctx.tb_ptr = tb_ptr;
|
|
+ int tb_entry_ptr = (uint32_t)tb_ptr + export_vec_off;
|
|
+ if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) {
|
|
+ /* Continue in TCI. */
|
|
+ } else if ((*(int32_t*)tb_entry_ptr <= 0) && (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM))) {
|
|
+ *(int32_t*)tb_entry_ptr -= 1;
|
|
+ } else {
|
|
+ // enter to wasm TB
|
|
+ return 0;
|
|
+ }
|
|
+ tb_ptr = (uint8_t*)tb_ptr + *(uint32_t*)tb_ptr;
|
|
+
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_qemu_ld_a32_i32:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = (uint32_t)regs[r1];
|
|
+ goto do_ld_i32;
|
|
+ case INDEX_op_qemu_ld_a64_i32:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = regs[r1];
|
|
+ do_ld_i32:
|
|
+ regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr, ptr);
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_qemu_ld_a32_i64:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = (uint32_t)regs[r1];
|
|
+ goto do_ld_i64;
|
|
+ case INDEX_op_qemu_ld_a64_i64:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = regs[r1];
|
|
+ do_ld_i64:
|
|
+ tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr, ptr);
|
|
+ if (TCG_TARGET_REG_BITS == 32) {
|
|
+ tci_write_reg64(regs, r1, r0, tmp64);
|
|
+ } else {
|
|
+ regs[r0] = tmp64;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_qemu_st_a32_i32:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = (uint32_t)regs[r1];
|
|
+ goto do_st_i32;
|
|
+ case INDEX_op_qemu_st_a64_i32:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ taddr = regs[r1];
|
|
+ do_st_i32:
|
|
+ tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr, ptr);
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_qemu_st_a32_i64:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ tmp64 = regs[r0];
|
|
+ taddr = (uint32_t)regs[r1];
|
|
+ goto do_st_i64;
|
|
+ case INDEX_op_qemu_st_a64_i64:
|
|
+ tci_args_ldst(insn, &r0, &r1, &oi, tb_ptr, &ptr);
|
|
+ tmp64 = regs[r0];
|
|
+ taddr = regs[r1];
|
|
+ do_st_i64:
|
|
+ tci_qemu_st(env, taddr, tmp64, oi, tb_ptr, ptr);
|
|
+ break;
|
|
+
|
|
+ case INDEX_op_mb:
|
|
+ /* Ensure ordering for all kinds */
|
|
+ smp_mb();
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env,
|
|
+ const void *v_tb_ptr)
|
|
+{
|
|
+ ctx.env = env;
|
|
+ ctx.tb_ptr = (uint32_t*)v_tb_ptr;
|
|
+ ctx.do_init = 1;
|
|
+ while (true) {
|
|
+ int tb_entry_ptr = (uint32_t)ctx.tb_ptr + export_vec_off;
|
|
+ uint32_t res;
|
|
+ if (*(int32_t*)tb_entry_ptr > 0) {
|
|
+ res = ((wasm_func_ptr)(*(uint32_t*)tb_entry_ptr))(&ctx);
|
|
+ } else if (*(int32_t *)tb_entry_ptr == WASM_TCI_ONLY_ENTRY) {
|
|
+ res = tcg_qemu_tb_exec_tci(env);
|
|
+ } else if (*(int32_t*)tb_entry_ptr > (-1 * INSTANTIATE_NUM)) {
|
|
+ *(int32_t*)tb_entry_ptr -= 1;
|
|
+ res = tcg_qemu_tb_exec_tci(env);
|
|
+ } else {
|
|
+ instantiate_wasm();
|
|
+ res = ((wasm_func_ptr)(*(uint32_t*)tb_entry_ptr))(&ctx);
|
|
+ }
|
|
+ if ((uint32_t)ctx.tb_ptr == 0) {
|
|
+ return res;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
diff --git c/tcg/wasm32.h w/tcg/wasm32.h
|
|
new file mode 100644
|
|
index 0000000000..a8b525abe3
|
|
--- /dev/null
|
|
+++ w/tcg/wasm32.h
|
|
@@ -0,0 +1,49 @@
|
|
+#ifndef TCG_WASM32_H
|
|
+#define TCG_WASM32_H
|
|
+
|
|
+struct wasmContext {
|
|
+ // 0
|
|
+ CPUArchState *env;
|
|
+ // 4
|
|
+ uint64_t *stack;
|
|
+ // 8
|
|
+ uint32_t *tb_ptr;
|
|
+ // 12
|
|
+ uint32_t *tci_tb_ptr;
|
|
+ // 16
|
|
+ uint32_t do_init;
|
|
+ // 20
|
|
+ uint32_t done_flag;
|
|
+ // 24
|
|
+ uint64_t *stack128;
|
|
+ // 28
|
|
+ uint32_t unwinding;
|
|
+};
|
|
+
|
|
+#define ENV_OFF 0
|
|
+#define STACK_OFF 4
|
|
+#define TB_PTR_OFF 8
|
|
+#define HELPER_RET_TB_PTR_OFF 12
|
|
+#define DO_INIT_OFF 16
|
|
+#define DONE_FLAG_OFF 20
|
|
+#define STACK128_OFF 24
|
|
+#define UNWINDING_OFF 28
|
|
+
|
|
+void set_done_flag();
|
|
+
|
|
+void set_unwinding_flag();
|
|
+
|
|
+int get_core_nums();
|
|
+
|
|
+void remove_tb(void *tb_ptr);
|
|
+
|
|
+void flush_tb_instances(void);
|
|
+
|
|
+void init_wasm32();
|
|
+
|
|
+extern __thread bool wasm_tci_only_tb;
|
|
+
|
|
+#define INSTANTIATE_NUM 1500
|
|
+#define WASM_TCI_ONLY_ENTRY (-2147483647 - 1)
|
|
+
|
|
+#endif
|
|
diff --git c/tcg/wasm32/tcg-target-con-set.h w/tcg/wasm32/tcg-target-con-set.h
|
|
new file mode 100644
|
|
index 0000000000..f50a6793b7
|
|
--- /dev/null
|
|
+++ w/tcg/wasm32/tcg-target-con-set.h
|
|
@@ -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)
|
|
diff --git c/tcg/wasm32/tcg-target-con-str.h w/tcg/wasm32/tcg-target-con-str.h
|
|
new file mode 100644
|
|
index 0000000000..56d7aff291
|
|
--- /dev/null
|
|
+++ w/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 c/tcg/wasm32/tcg-target-reg-bits.h w/tcg/wasm32/tcg-target-reg-bits.h
|
|
new file mode 100644
|
|
index 0000000000..196395aef9
|
|
--- /dev/null
|
|
+++ w/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 c/tcg/wasm32/tcg-target.c.inc w/tcg/wasm32/tcg-target.c.inc
|
|
new file mode 100644
|
|
index 0000000000..053c941f99
|
|
--- /dev/null
|
|
+++ w/tcg/wasm32/tcg-target.c.inc
|
|
@@ -0,0 +1,3826 @@
|
|
+/*
|
|
+ * Tiny Code Generator for QEMU
|
|
+ *
|
|
+ * Copyright (c) 2018 SiFive, Inc
|
|
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
|
|
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
|
|
+ * Copyright (c) 2008 Fabrice Bellard
|
|
+ * 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 <emscripten.h>
|
|
+#include <ffi.h>
|
|
+#include "../tcg-pool.c.inc"
|
|
+
|
|
+static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op)
|
|
+{
|
|
+ switch (op) {
|
|
+ case INDEX_op_goto_ptr:
|
|
+ return C_O0_I1(r);
|
|
+
|
|
+ case INDEX_op_ld8u_i32:
|
|
+ case INDEX_op_ld8s_i32:
|
|
+ case INDEX_op_ld16u_i32:
|
|
+ case INDEX_op_ld16s_i32:
|
|
+ case INDEX_op_ld_i32:
|
|
+ case INDEX_op_ld8u_i64:
|
|
+ case INDEX_op_ld8s_i64:
|
|
+ case INDEX_op_ld16u_i64:
|
|
+ case INDEX_op_ld16s_i64:
|
|
+ case INDEX_op_ld32u_i64:
|
|
+ case INDEX_op_ld32s_i64:
|
|
+ case INDEX_op_ld_i64:
|
|
+ case INDEX_op_not_i32:
|
|
+ case INDEX_op_not_i64:
|
|
+ case INDEX_op_neg_i32:
|
|
+ case INDEX_op_neg_i64:
|
|
+ case INDEX_op_ext8s_i32:
|
|
+ case INDEX_op_ext8s_i64:
|
|
+ case INDEX_op_ext16s_i32:
|
|
+ case INDEX_op_ext16s_i64:
|
|
+ case INDEX_op_ext8u_i32:
|
|
+ case INDEX_op_ext8u_i64:
|
|
+ case INDEX_op_ext16u_i32:
|
|
+ case INDEX_op_ext16u_i64:
|
|
+ case INDEX_op_ext32s_i64:
|
|
+ case INDEX_op_ext32u_i64:
|
|
+ case INDEX_op_ext_i32_i64:
|
|
+ case INDEX_op_extu_i32_i64:
|
|
+ case INDEX_op_bswap16_i32:
|
|
+ case INDEX_op_bswap16_i64:
|
|
+ case INDEX_op_bswap32_i32:
|
|
+ case INDEX_op_bswap32_i64:
|
|
+ case INDEX_op_bswap64_i64:
|
|
+ case INDEX_op_extract_i32:
|
|
+ case INDEX_op_extract_i64:
|
|
+ case INDEX_op_sextract_i32:
|
|
+ case INDEX_op_sextract_i64:
|
|
+ case INDEX_op_extrl_i64_i32:
|
|
+ case INDEX_op_extrh_i64_i32:
|
|
+ case INDEX_op_ctpop_i32:
|
|
+ case INDEX_op_ctpop_i64:
|
|
+ return C_O1_I1(r, r);
|
|
+
|
|
+ case INDEX_op_st8_i32:
|
|
+ case INDEX_op_st16_i32:
|
|
+ case INDEX_op_st_i32:
|
|
+ case INDEX_op_st8_i64:
|
|
+ case INDEX_op_st16_i64:
|
|
+ case INDEX_op_st32_i64:
|
|
+ case INDEX_op_st_i64:
|
|
+ return C_O0_I2(r, r);
|
|
+
|
|
+ case INDEX_op_div_i32:
|
|
+ case INDEX_op_div_i64:
|
|
+ case INDEX_op_divu_i32:
|
|
+ case INDEX_op_divu_i64:
|
|
+ case INDEX_op_rem_i32:
|
|
+ case INDEX_op_rem_i64:
|
|
+ case INDEX_op_remu_i32:
|
|
+ case INDEX_op_remu_i64:
|
|
+ case INDEX_op_add_i32:
|
|
+ case INDEX_op_add_i64:
|
|
+ case INDEX_op_sub_i32:
|
|
+ case INDEX_op_sub_i64:
|
|
+ case INDEX_op_mul_i32:
|
|
+ case INDEX_op_mul_i64:
|
|
+ case INDEX_op_and_i32:
|
|
+ case INDEX_op_and_i64:
|
|
+ case INDEX_op_andc_i32:
|
|
+ case INDEX_op_andc_i64:
|
|
+ case INDEX_op_eqv_i32:
|
|
+ case INDEX_op_eqv_i64:
|
|
+ case INDEX_op_nand_i32:
|
|
+ case INDEX_op_nand_i64:
|
|
+ case INDEX_op_nor_i32:
|
|
+ case INDEX_op_nor_i64:
|
|
+ case INDEX_op_or_i32:
|
|
+ case INDEX_op_or_i64:
|
|
+ case INDEX_op_orc_i32:
|
|
+ case INDEX_op_orc_i64:
|
|
+ case INDEX_op_xor_i32:
|
|
+ case INDEX_op_xor_i64:
|
|
+ case INDEX_op_shl_i32:
|
|
+ case INDEX_op_shl_i64:
|
|
+ case INDEX_op_shr_i32:
|
|
+ case INDEX_op_shr_i64:
|
|
+ case INDEX_op_sar_i32:
|
|
+ case INDEX_op_sar_i64:
|
|
+ case INDEX_op_rotl_i32:
|
|
+ case INDEX_op_rotl_i64:
|
|
+ case INDEX_op_rotr_i32:
|
|
+ case INDEX_op_rotr_i64:
|
|
+ case INDEX_op_setcond_i32:
|
|
+ case INDEX_op_setcond_i64:
|
|
+ case INDEX_op_deposit_i32:
|
|
+ case INDEX_op_deposit_i64:
|
|
+ case INDEX_op_clz_i32:
|
|
+ case INDEX_op_clz_i64:
|
|
+ case INDEX_op_ctz_i32:
|
|
+ case INDEX_op_ctz_i64:
|
|
+ return C_O1_I2(r, r, r);
|
|
+
|
|
+ case INDEX_op_brcond_i32:
|
|
+ case INDEX_op_brcond_i64:
|
|
+ return C_O0_I2(r, r);
|
|
+
|
|
+ case INDEX_op_add2_i32:
|
|
+ case INDEX_op_add2_i64:
|
|
+ case INDEX_op_sub2_i32:
|
|
+ case INDEX_op_sub2_i64:
|
|
+ return C_O2_I4(r, r, r, r, r, r);
|
|
+
|
|
+ case INDEX_op_mulu2_i32:
|
|
+ case INDEX_op_mulu2_i64:
|
|
+ case INDEX_op_muls2_i32:
|
|
+ case INDEX_op_muls2_i64:
|
|
+ return C_O2_I2(r, r, r, r);
|
|
+
|
|
+ case INDEX_op_movcond_i32:
|
|
+ case INDEX_op_movcond_i64:
|
|
+ return C_O1_I4(r, r, r, r, r);
|
|
+
|
|
+ case INDEX_op_setcond2_i32:
|
|
+ return C_O1_I4(r, r, r, r, r);
|
|
+ case INDEX_op_brcond2_i32:
|
|
+ return C_O0_I4(r, r, r, r);
|
|
+
|
|
+ case INDEX_op_qemu_ld_a32_i32:
|
|
+ case INDEX_op_qemu_ld_a64_i32:
|
|
+ return C_O1_I1(r, r);
|
|
+
|
|
+ case INDEX_op_qemu_ld_a32_i64:
|
|
+ case INDEX_op_qemu_ld_a64_i64:
|
|
+ return C_O1_I1(r, r);
|
|
+ case INDEX_op_qemu_st_a32_i32:
|
|
+ case INDEX_op_qemu_st_a64_i32:
|
|
+ return C_O0_I2(r, r);
|
|
+ case INDEX_op_qemu_st_a32_i64:
|
|
+ case INDEX_op_qemu_st_a64_i64:
|
|
+ return C_O0_I2(r, r);
|
|
+
|
|
+ case INDEX_op_muluh_i32:
|
|
+ case INDEX_op_mulsh_i32:
|
|
+ return C_O1_I2(r, r, r);
|
|
+ case INDEX_op_extract2_i32:
|
|
+ case INDEX_op_extract2_i64:
|
|
+ return C_O1_I2(r, r, r);
|
|
+
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static const int tcg_target_reg_alloc_order[TCG_TARGET_NB_REGS] = {
|
|
+ TCG_REG_R0,
|
|
+ TCG_REG_R1,
|
|
+ TCG_REG_R2,
|
|
+ TCG_REG_R3,
|
|
+ TCG_REG_R4,
|
|
+ TCG_REG_R5,
|
|
+ TCG_REG_R6,
|
|
+ TCG_REG_R7,
|
|
+ TCG_REG_R8,
|
|
+ TCG_REG_R9,
|
|
+ TCG_REG_R10,
|
|
+ TCG_REG_R11,
|
|
+ TCG_REG_R12,
|
|
+ TCG_REG_R13,
|
|
+ TCG_REG_R14,
|
|
+ TCG_REG_R15,
|
|
+};
|
|
+
|
|
+#define NUM_OF_IARG_REGS 5
|
|
+static const int tcg_target_call_iarg_regs[NUM_OF_IARG_REGS] = {
|
|
+ TCG_REG_R8,
|
|
+ TCG_REG_R9,
|
|
+ TCG_REG_R10,
|
|
+ TCG_REG_R11,
|
|
+ TCG_REG_R12,
|
|
+};
|
|
+
|
|
+static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot)
|
|
+{
|
|
+ tcg_debug_assert(kind == TCG_CALL_RET_NORMAL);
|
|
+ tcg_debug_assert(slot >= 0 && slot < 128 / TCG_TARGET_REG_BITS);
|
|
+ return TCG_REG_R0 + slot;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_DEBUG_TCG
|
|
+static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
|
|
+ "r00",
|
|
+ "r01",
|
|
+ "r02",
|
|
+ "r03",
|
|
+ "r04",
|
|
+ "r05",
|
|
+ "r06",
|
|
+ "r07",
|
|
+ "r08",
|
|
+ "r09",
|
|
+ "r10",
|
|
+ "r11",
|
|
+ "r12",
|
|
+ "r13",
|
|
+ "r14",
|
|
+ "r15",
|
|
+};
|
|
+#endif
|
|
+
|
|
+#define REG_INDEX_IARG_BASE 8
|
|
+static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
|
|
+ 0, // TCG_REG_R0
|
|
+ 1, // TCG_REG_R1
|
|
+ 2, // TCG_REG_R2
|
|
+ 3, // TCG_REG_R3
|
|
+ 4, // TCG_REG_R4
|
|
+ 5, // TCG_REG_R5
|
|
+ 6, // TCG_REG_R6
|
|
+ 7, // TCG_REG_R7
|
|
+ 8, // TCG_REG_R8
|
|
+ 9, // TCG_REG_R9
|
|
+ 10, // TCG_REG_R10
|
|
+ 11, // TCG_REG_R11
|
|
+ 12, // TCG_REG_R12
|
|
+ 13, // TCG_REG_R13
|
|
+ 14, // TCG_REG_R14
|
|
+ 15, // TCG_REG_R15
|
|
+};
|
|
+
|
|
+#define BLOCK_PTR_IDX 16
|
|
+
|
|
+#define CTX_IDX 0
|
|
+#define TMP32_LOCAL_ENV_IDX 1
|
|
+#define TMP32_LOCAL_0_IDX 2
|
|
+#define TMP64_0_IDX 3
|
|
+#define TMP64_1_IDX 4
|
|
+#define TMP64_2_IDX 5
|
|
+#define TMP64_3_IDX 6
|
|
+#define TMP64_4_IDX 7
|
|
+
|
|
+__thread bool env_cached = false;
|
|
+
|
|
+// function index
|
|
+#define RETURN_CALL_IDX 0
|
|
+#define FUNC_HELPER_CALL_IDX 1
|
|
+#define FUNC_CALL_LD_HELPER_IDX 2
|
|
+#define FUNC_CALL_ST_HELPER_IDX 3
|
|
+
|
|
+// table index
|
|
+#define HELPER_TABLE_IDX 0
|
|
+
|
|
+static inline void tcg_wasm_out8(TCGContext *s, uint32_t v)
|
|
+{
|
|
+ tcg_sub_out8(s, v);
|
|
+}
|
|
+
|
|
+static uintptr_t cur_wasm_ptr(TCGContext *s)
|
|
+{
|
|
+ return (uintptr_t)cur_sub_buf_ptr();
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_leb128_sint32_t(TCGContext *s, int32_t v) {
|
|
+ bool more = true;
|
|
+ bool negative = (v < 0);
|
|
+ uint8_t b;
|
|
+ uint32_t low7 = 0x7f;
|
|
+ uint32_t uv = v;
|
|
+ while (more) {
|
|
+ b = uv & low7;
|
|
+ uv >>= 7;
|
|
+ if (negative)
|
|
+ uv |= (~0 << (32 - 7));
|
|
+ if (((uv == 0) && ((b & 0x40) == 0)) || ((uv == -1) && ((b & 0x40) != 0)))
|
|
+ more = false;
|
|
+ else
|
|
+ b |= 0x80;
|
|
+ tcg_wasm_out8(s, b);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_leb128_sint64_t(TCGContext *s, int64_t v) {
|
|
+ bool more = true;
|
|
+ bool negative = (v < 0);
|
|
+ uint8_t b;
|
|
+ uint64_t low7 = 0x7f;
|
|
+ uint64_t uv = v;
|
|
+ while (more) {
|
|
+ b = uv & low7;
|
|
+ uv >>= 7;
|
|
+ if (negative)
|
|
+ uv |= ((int64_t)(~0) << (64 - 7));
|
|
+ if (((uv == 0) && ((b & 0x40) == 0)) || (((int64_t)uv == -1) && ((b & 0x40) != 0)))
|
|
+ more = false;
|
|
+ else
|
|
+ b |= 0x80;
|
|
+ tcg_wasm_out8(s, b);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_leb128_uint32_t(TCGContext *s, uint32_t v) {
|
|
+ uint32_t low7 = 0x7f;
|
|
+ uint8_t b;
|
|
+ do {
|
|
+ b = v & low7;
|
|
+ v >>= 7;
|
|
+ if (v != 0)
|
|
+ b |= 0x80;
|
|
+ tcg_wasm_out8(s, b);
|
|
+ } while (v != 0);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_br(TCGContext *s, int i)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x0c);
|
|
+ tcg_wasm_out8(s, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_if_noret(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x04);
|
|
+ tcg_wasm_out8(s, 0x40);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_if_ret_i64(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x04);
|
|
+ tcg_wasm_out8(s, 0x7e);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_if_ret_i32(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x04);
|
|
+ tcg_wasm_out8(s, 0x7f);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_else(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x05);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_end(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x0b);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i32_eqz(TCGContext *s){ tcg_wasm_out8(s, 0x45); }
|
|
+static void tcg_wasm_out_op_i32_eq(TCGContext *s){ tcg_wasm_out8(s, 0x46); }
|
|
+static void tcg_wasm_out_op_i32_and(TCGContext *s){ tcg_wasm_out8(s, 0x71); }
|
|
+static void tcg_wasm_out_op_i32_or(TCGContext *s){ tcg_wasm_out8(s, 0x72); }
|
|
+//static void tcg_wasm_out_op_i32_xor(TCGContext *s){ tcg_wasm_out8(s, 0x73); }
|
|
+static void tcg_wasm_out_op_i32_shl(TCGContext *s){ tcg_wasm_out8(s, 0x74); }
|
|
+static void tcg_wasm_out_op_i32_shr_s(TCGContext *s){ tcg_wasm_out8(s, 0x75); }
|
|
+static void tcg_wasm_out_op_i32_shr_u(TCGContext *s){ tcg_wasm_out8(s, 0x76); }
|
|
+static void tcg_wasm_out_op_i32_rotl(TCGContext *s){ tcg_wasm_out8(s, 0x77); }
|
|
+static void tcg_wasm_out_op_i32_rotr(TCGContext *s){ tcg_wasm_out8(s, 0x78); }
|
|
+static void tcg_wasm_out_op_i32_clz(TCGContext *s){ tcg_wasm_out8(s, 0x67); }
|
|
+static void tcg_wasm_out_op_i32_ctz(TCGContext *s){ tcg_wasm_out8(s, 0x68); }
|
|
+static void tcg_wasm_out_op_i32_popcnt(TCGContext *s){ tcg_wasm_out8(s, 0x69); }
|
|
+static void tcg_wasm_out_op_i32_add(TCGContext *s){ tcg_wasm_out8(s, 0x6a); }
|
|
+//static void tcg_wasm_out_op_i32_sub(TCGContext *s){ tcg_wasm_out8(s, 0x6b); }
|
|
+//static void tcg_wasm_out_op_i32_mul(TCGContext *s){ tcg_wasm_out8(s, 0x6c); }
|
|
+//static void tcg_wasm_out_op_i32_div_s(TCGContext *s){ tcg_wasm_out8(s, 0x6d); }
|
|
+//static void tcg_wasm_out_op_i32_div_u(TCGContext *s){ tcg_wasm_out8(s, 0x6e); }
|
|
+//static void tcg_wasm_out_op_i32_rem_s(TCGContext *s){ tcg_wasm_out8(s, 0x6f); }
|
|
+//static void tcg_wasm_out_op_i32_rem_u(TCGContext *s){ tcg_wasm_out8(s, 0x70); }
|
|
+static void tcg_wasm_out_op_i32_ne(TCGContext *s){ tcg_wasm_out8(s, 0x47); }
|
|
+//static void tcg_wasm_out_op_i32_le_u(TCGContext *s){ tcg_wasm_out8(s, 0x4d); }
|
|
+
|
|
+static void tcg_wasm_out_op_i64_eqz(TCGContext *s){ tcg_wasm_out8(s, 0x50); }
|
|
+static void tcg_wasm_out_op_i64_eq(TCGContext *s){ tcg_wasm_out8(s, 0x51); }
|
|
+static void tcg_wasm_out_op_i64_and(TCGContext *s){ tcg_wasm_out8(s, 0x83); }
|
|
+static void tcg_wasm_out_op_i64_or(TCGContext *s){ tcg_wasm_out8(s, 0x84); }
|
|
+static void tcg_wasm_out_op_i64_xor(TCGContext *s){ tcg_wasm_out8(s, 0x85); }
|
|
+static void tcg_wasm_out_op_i64_shl(TCGContext *s){ tcg_wasm_out8(s, 0x86); }
|
|
+static void tcg_wasm_out_op_i64_shr_s(TCGContext *s){ tcg_wasm_out8(s, 0x87); }
|
|
+static void tcg_wasm_out_op_i64_shr_u(TCGContext *s){ tcg_wasm_out8(s, 0x88); }
|
|
+static void tcg_wasm_out_op_i64_rotl(TCGContext *s){ tcg_wasm_out8(s, 0x89); }
|
|
+static void tcg_wasm_out_op_i64_rotr(TCGContext *s){ tcg_wasm_out8(s, 0x8a); }
|
|
+static void tcg_wasm_out_op_i64_clz(TCGContext *s){ tcg_wasm_out8(s, 0x79); }
|
|
+static void tcg_wasm_out_op_i64_ctz(TCGContext *s){ tcg_wasm_out8(s, 0x7a); }
|
|
+static void tcg_wasm_out_op_i64_popcnt(TCGContext *s){ tcg_wasm_out8(s, 0x7b); }
|
|
+static void tcg_wasm_out_op_i64_add(TCGContext *s){ tcg_wasm_out8(s, 0x7c); }
|
|
+static void tcg_wasm_out_op_i64_sub(TCGContext *s){ tcg_wasm_out8(s, 0x7d); }
|
|
+static void tcg_wasm_out_op_i64_mul(TCGContext *s){ tcg_wasm_out8(s, 0x7e); }
|
|
+static void tcg_wasm_out_op_i64_div_s(TCGContext *s){ tcg_wasm_out8(s, 0x7f); }
|
|
+static void tcg_wasm_out_op_i64_div_u(TCGContext *s){ tcg_wasm_out8(s, 0x80); }
|
|
+static void tcg_wasm_out_op_i64_rem_s(TCGContext *s){ tcg_wasm_out8(s, 0x81); }
|
|
+static void tcg_wasm_out_op_i64_rem_u(TCGContext *s){ tcg_wasm_out8(s, 0x82); }
|
|
+//static void tcg_wasm_out_op_i64_ne(TCGContext *s){ tcg_wasm_out8(s, 0x52); }
|
|
+static void tcg_wasm_out_op_i64_le_u(TCGContext *s){ tcg_wasm_out8(s, 0x58); }
|
|
+static void tcg_wasm_out_op_i64_lt_u(TCGContext *s){ tcg_wasm_out8(s, 0x54); }
|
|
+static void tcg_wasm_out_op_i64_gt_u(TCGContext *s){ tcg_wasm_out8(s, 0x56); }
|
|
+
|
|
+static void tcg_wasm_out_op_i32_wrap_i64(TCGContext *s){ tcg_wasm_out8(s, 0xa7); }
|
|
+
|
|
+static void tcg_wasm_out_op_var(TCGContext *s, uint8_t instr, uint8_t i)
|
|
+{
|
|
+ tcg_wasm_out8(s, instr);
|
|
+ tcg_wasm_out8(s, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_local_get(TCGContext *s, uint8_t i)
|
|
+{
|
|
+ tcg_wasm_out_op_var(s, 0x20, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_local_set(TCGContext *s, uint8_t i)
|
|
+{
|
|
+ tcg_wasm_out_op_var(s, 0x21, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_local_tee(TCGContext *s, uint8_t i)
|
|
+{
|
|
+ tcg_wasm_out_op_var(s, 0x22, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_global_get(TCGContext *s, uint8_t i)
|
|
+{
|
|
+ tcg_wasm_out_op_var(s, 0x23, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_global_set(TCGContext *s, uint8_t i)
|
|
+{
|
|
+ if (i == tcg_target_reg_index[TCG_REG_R14]) {
|
|
+ env_cached = false;
|
|
+ }
|
|
+ tcg_wasm_out_op_var(s, 0x24, i);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_global_get_r_i32(TCGContext *s, TCGReg r0)
|
|
+{
|
|
+ if (r0 == TCG_REG_R14) {
|
|
+ if (!env_cached) {
|
|
+ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_local_tee(s, TMP32_LOCAL_ENV_IDX);
|
|
+ env_cached = true;
|
|
+ } else {
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_ENV_IDX);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_global_get_r(TCGContext *s, TCGReg r0)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get(s, tcg_target_reg_index[r0]);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_global_set_r(TCGContext *s, TCGReg r0)
|
|
+{
|
|
+ tcg_wasm_out_op_global_set(s, tcg_target_reg_index[r0]);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i32_const(TCGContext *s, int32_t v)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x41);
|
|
+ tcg_wasm_out_leb128_sint32_t(s, v);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_const(TCGContext *s, int64_t v)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x42);
|
|
+ tcg_wasm_out_leb128_sint64_t(s, v);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_loadstore(TCGContext *s, uint8_t instr, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out8(s, instr);
|
|
+ tcg_wasm_out_leb128_uint32_t(s, a);
|
|
+ tcg_wasm_out_leb128_uint32_t(s, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_store(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x37, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i32_store(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x36, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_store8(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x3c, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_store16(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x3d, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_store32(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x3e, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x29, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i32_load(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x28, a, o);
|
|
+}
|
|
+
|
|
+ static void tcg_wasm_out_op_i64_load8_s(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x30, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load8_u(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x31, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load16_s(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x32, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load16_u(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x33, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load32_u(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x35, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_load32_s(TCGContext *s, uint32_t a, uint32_t o)
|
|
+{
|
|
+ tcg_wasm_out_op_loadstore(s, 0x34, a, o);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_return(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x0f);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_call(TCGContext *s, uint32_t func_idx)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0x10);
|
|
+ tcg_wasm_out_leb128_uint32_t(s, func_idx);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_extend_i32_u(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0xad);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_extend_i32_s(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0xac);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_extend8_s(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0xc2);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_i64_extend16_s(TCGContext *s)
|
|
+{
|
|
+ tcg_wasm_out8(s, 0xc3);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_not(TCGContext *s){
|
|
+ tcg_wasm_out_op_i64_const(s, -1);
|
|
+ tcg_wasm_out_op_i64_xor(s);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_set_r_as_i64(TCGContext *s, TCGReg al, TCGReg ah)
|
|
+{
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_4_IDX);
|
|
+
|
|
+ // set lower bits
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_4_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, al);
|
|
+
|
|
+ // set higher bits
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_4_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 32);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ah);
|
|
+}
|
|
+
|
|
+static const struct {
|
|
+ uint8_t i32;
|
|
+ uint8_t i64;
|
|
+} tcg_cond_to_inst[] = {
|
|
+ [TCG_COND_EQ] = { 0x46 /* i32.eq */ , 0x51 /* i64.eq */},
|
|
+ [TCG_COND_NE] = { 0x47 /* i32.ne */ , 0x52 /* i64.ne */},
|
|
+ [TCG_COND_LT] = { 0x48 /* i32.lt_s */ , 0x53 /* i64.lt_s */},
|
|
+ [TCG_COND_GE] = { 0x4e /* i32.ge_s */ , 0x59 /* i64.ge_s */},
|
|
+ [TCG_COND_LE] = { 0x4c /* i32.le_s */ , 0x57 /* i64.le_s */},
|
|
+ [TCG_COND_GT] = { 0x4a /* i32.gt_s */ , 0x55 /* i64.gt_s */},
|
|
+ [TCG_COND_LTU] = { 0x49 /* i32.lt_u */ , 0x54 /* i64.lt_u */},
|
|
+ [TCG_COND_GEU] = { 0x4f /* i32.ge_u */ , 0x5a /* i64.ge_u */},
|
|
+ [TCG_COND_LEU] = { 0x4d /* i32.le_u */ , 0x58 /* i64.le_u */},
|
|
+ [TCG_COND_GTU] = { 0x4b /* i32.gt_u */ , 0x56 /* i64.gt_u */}
|
|
+};
|
|
+
|
|
+static void tcg_wasm_out_op_cond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ uint8_t op = tcg_cond_to_inst[cond].i64;
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out8(s, op);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_cond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ uint8_t op = tcg_cond_to_inst[cond].i32;
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out8(s, op);
|
|
+}
|
|
+
|
|
+#define tcg_wasm_out_i64_calc(op) \
|
|
+ static void tcg_wasm_out_i64_calc_##op(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){ \
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1); \
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2); \
|
|
+ tcg_wasm_out_op_i64_##op(s); \
|
|
+ tcg_wasm_out_op_global_set_r(s, ret); \
|
|
+ }
|
|
+tcg_wasm_out_i64_calc(and);
|
|
+tcg_wasm_out_i64_calc(or);
|
|
+tcg_wasm_out_i64_calc(xor);
|
|
+tcg_wasm_out_i64_calc(shl);
|
|
+tcg_wasm_out_i64_calc(shr_s);
|
|
+tcg_wasm_out_i64_calc(shr_u);
|
|
+tcg_wasm_out_i64_calc(rotl);
|
|
+tcg_wasm_out_i64_calc(rotr);
|
|
+tcg_wasm_out_i64_calc(add);
|
|
+tcg_wasm_out_i64_calc(sub);
|
|
+tcg_wasm_out_i64_calc(mul);
|
|
+tcg_wasm_out_i64_calc(div_s);
|
|
+tcg_wasm_out_i64_calc(div_u);
|
|
+tcg_wasm_out_i64_calc(rem_s);
|
|
+tcg_wasm_out_i64_calc(rem_u);
|
|
+
|
|
+static void tcg_wasm_out_rem_s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) {
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_i64_rem_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_rem_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_rem_u(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) {
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_rem_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_rem_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_div_s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) {
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_i64_div_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_div_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_div_u(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2) {
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_div_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_div_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_shl(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 31);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_shr_u(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 31);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_shr_s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 31);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_shr_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_shr_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+static void tcg_wasm_out_i32_rotl(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_rotl(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_i32_rotr(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_rotr(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_clz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_ret_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_clz(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_clz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_ret_i32(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_clz(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctz64(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_ret_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_ctz(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctz32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_ret_i32(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_ctz(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_not(TCGContext *s, TCGReg ret, TCGReg arg){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_andc(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_orc(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_eqv(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_xor(s);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_nand(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_nor(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_neg(TCGContext *s, TCGReg ret, TCGReg arg){
|
|
+ tcg_wasm_out_op_global_get_r(s, arg);
|
|
+ tcg_wasm_out_op_not(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 1);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load32_u(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld8s(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load8_s(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld8u(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load8_u(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld16s(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load16_s(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld16u(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load16_u(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld32s(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load32_s(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ld32u(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load32_u(s, 0, (uint32_t)offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, val);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get_r(s, val);
|
|
+ tcg_wasm_out_op_i64_store32(s, 0, (uint32_t)offset);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get_r(s, val);
|
|
+ tcg_wasm_out_op_i64_store(s, 0, (uint32_t)offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_st8(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get_r(s, val);
|
|
+ tcg_wasm_out_op_i64_store8(s, 0, (uint32_t)offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_st16(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get_r(s, val);
|
|
+ tcg_wasm_out_op_i64_store16(s, 0, (uint32_t)offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_st32(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, base);
|
|
+ if ((int32_t)offset < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)offset);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_global_get_r(s, val);
|
|
+ tcg_wasm_out_op_i64_store32(s, 0, (uint32_t)offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline bool tcg_wasm_out_sti(TCGContext *s, TCGType type, TCGArg val,
|
|
+ TCGReg base, intptr_t offset)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool tcg_wasm_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, arg);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_movi(TCGContext *s, TCGType type,
|
|
+ TCGReg ret, tcg_target_long arg)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_wasm_out_op_i64_const(s, (int32_t)arg);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_i64_const(s, arg);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_extend8_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_extend16_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_ext32s(s, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_ext32u(s, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extrh_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, rs);
|
|
+ tcg_wasm_out_op_i64_const(s, 32);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, rd);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret,
|
|
+ TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2);
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret,
|
|
+ TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2);
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret,
|
|
+ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i32(s, cond, c1, c2);
|
|
+ tcg_wasm_out_op_if_ret_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, v1);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, v2);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret,
|
|
+ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i64(s, cond, c1, c2);
|
|
+ tcg_wasm_out_op_if_ret_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, v1);
|
|
+ tcg_wasm_out_op_else(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, v2);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_add2(TCGContext *s, TCGReg retl, TCGReg reth,
|
|
+ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh)
|
|
+{
|
|
+ // add higer
|
|
+ tcg_wasm_out_op_global_get_r(s, ah);
|
|
+ tcg_wasm_out_op_global_get_r(s, bh);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+
|
|
+ // add lower
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ tcg_wasm_out_op_global_get_r(s, bl);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+
|
|
+ // get carry
|
|
+ if ((al == retl) && (bl == retl)) {
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ tcg_wasm_out_op_i64_lt_u(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_global_set_r(s, retl);
|
|
+ } else {
|
|
+ tcg_wasm_out_op_global_set_r(s, retl);
|
|
+ tcg_wasm_out_op_global_get_r(s, retl);
|
|
+ if (al == retl) {
|
|
+ tcg_wasm_out_op_global_get_r(s, bl);
|
|
+ } else {
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_lt_u(s);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ }
|
|
+
|
|
+ // add carry to higher
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, reth);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_sub2(TCGContext *s, TCGReg retl, TCGReg reth,
|
|
+ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh)
|
|
+{
|
|
+ // sub higher
|
|
+ tcg_wasm_out_op_global_get_r(s, ah);
|
|
+ tcg_wasm_out_op_global_get_r(s, bh);
|
|
+ tcg_wasm_out_op_i64_sub(s);
|
|
+
|
|
+ // sub lower
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ tcg_wasm_out_op_global_get_r(s, bl);
|
|
+ tcg_wasm_out_op_i64_sub(s);
|
|
+
|
|
+ // get underflow
|
|
+ if (al == retl) {
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ tcg_wasm_out_op_i64_gt_u(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_global_set_r(s, retl);
|
|
+ } else {
|
|
+ tcg_wasm_out_op_global_set_r(s, retl);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, retl);
|
|
+ tcg_wasm_out_op_global_get_r(s, al);
|
|
+ tcg_wasm_out_op_i64_gt_u(s);
|
|
+ }
|
|
+
|
|
+ tcg_wasm_out_op_i64_sub(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, reth);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_mulu2_i32(TCGContext *s, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_mul(s);
|
|
+ tcg_wasm_out_op_set_r_as_i64(s, retl, reth);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_muls2_i32(TCGContext *s, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_mul(s);
|
|
+ tcg_wasm_out_op_set_r_as_i64(s, retl, reth);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_muluh_i32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_mul(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 32);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_mulsh_i32(TCGContext *s, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_mul(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 32);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, ret);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctpop_i32(TCGContext *s, TCGReg dest, TCGReg src)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, src);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_popcnt(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctpop_i64(TCGContext *s, TCGReg dest, TCGReg src)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, src);
|
|
+ tcg_wasm_out_op_i64_popcnt(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_deposit_i32(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len)
|
|
+{
|
|
+ int32_t mask = ((1<<len)-1)<<pos;
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_const(s, ~mask);
|
|
+ tcg_wasm_out_op_i32_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_const(s, pos);
|
|
+ tcg_wasm_out_op_i32_shl(s);
|
|
+ tcg_wasm_out_op_i32_const(s, mask);
|
|
+ tcg_wasm_out_op_i32_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_i32_or(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_deposit_i64(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len)
|
|
+{
|
|
+ int64_t mask = (((int64_t)1<<len)-1)<<pos;
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, ~mask);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, pos);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+ tcg_wasm_out_op_i64_const(s, mask);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extract(TCGContext *s, TCGReg dest, TCGReg arg1, int pos, int len, TCGType type, bool sign)
|
|
+{
|
|
+ int rs;
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ rs = 32 - len;
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ rs = 64 - len;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ int sl = rs - pos;
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ if (sl > 0) {
|
|
+ tcg_wasm_out_op_i64_const(s, sl);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_const(s, rs);
|
|
+ if (sign) {
|
|
+ tcg_wasm_out_op_i64_shr_s(s);
|
|
+ } else {
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+ }
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extract2_i32(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 32-pos);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_const(s, pos);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_const(s, 0xffffffff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_extract2_i64(TCGContext *s, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg2);
|
|
+ tcg_wasm_out_op_i64_const(s, 64-pos);
|
|
+ tcg_wasm_out_op_i64_shl(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, arg1);
|
|
+ tcg_wasm_out_op_i64_const(s, pos);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_bswap64(TCGContext *s, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, src); // ABCDEFGH
|
|
+ tcg_wasm_out_op_i64_const(s, 32);
|
|
+ tcg_wasm_out_op_i64_rotr(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX); // EFGHABCD
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 0xff000000ff000000);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 24);
|
|
+ tcg_wasm_out_op_i64_shr_u(s); // ___E___A
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 0x00ff000000ff0000);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 8);
|
|
+ tcg_wasm_out_op_i64_shr_u(s); // __F___B_
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 0x0000ff000000ff00);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 8);
|
|
+ tcg_wasm_out_op_i64_shl(s); // _G___C__
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, 0x000000ff000000ff);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 24);
|
|
+ tcg_wasm_out_op_i64_shl(s); // H___D___
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_or(s); // HGFEDCBA
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_bswap32(TCGContext *s, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, src);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX); // ABCD
|
|
+ tcg_wasm_out_op_i32_const(s, 16);
|
|
+ tcg_wasm_out_op_i32_rotr(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX); // CDAB
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, 0xff00ff00);
|
|
+ tcg_wasm_out_op_i32_and(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 8);
|
|
+ tcg_wasm_out_op_i32_shr_u(s); // _C_A
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, 0x00ff00ff);
|
|
+ tcg_wasm_out_op_i32_and(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 8);
|
|
+ tcg_wasm_out_op_i32_shl(s); // D_B_
|
|
+
|
|
+ tcg_wasm_out_op_i32_or(s); // DCBA
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_bswap16(TCGContext *s, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, src);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX); // __AB
|
|
+ tcg_wasm_out_op_i32_const(s, 8);
|
|
+ tcg_wasm_out_op_i32_rotr(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX); // B__A
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, 0x000000ff);
|
|
+ tcg_wasm_out_op_i32_and(s); // ___A
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, 0xff000000);
|
|
+ tcg_wasm_out_op_i32_and(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 16);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_wasm_out_op_i32_shr_s(s); // SSB_
|
|
+ } else {
|
|
+ tcg_wasm_out_op_i32_shr_u(s); // 00B_
|
|
+ }
|
|
+
|
|
+ tcg_wasm_out_op_i32_or(s); // **BA
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, dest);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctx_i32_store_const(TCGContext *s, int off, int32_t v)
|
|
+{
|
|
+ tcg_wasm_out_op_local_get(s, CTX_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, v);
|
|
+ tcg_wasm_out_op_i32_store(s, 0, off);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctx_i32_store_r(TCGContext *s, int off, TCGReg r0)
|
|
+{
|
|
+ tcg_wasm_out_op_local_get(s, CTX_IDX);
|
|
+ tcg_wasm_out_op_global_get_r(s, r0);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_store(s, 0, off);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_ctx_i32_load(TCGContext *s, int off)
|
|
+{
|
|
+ tcg_wasm_out_op_local_get(s, CTX_IDX);
|
|
+ tcg_wasm_out_op_i32_load(s, 0, off);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_label_idx(TCGContext *s, int label)
|
|
+{
|
|
+ int block_idx = wasm_alloc_block_idx(s);
|
|
+ wasm_add_label_context(s, label, block_idx);
|
|
+
|
|
+ tcg_wasm_out_op_end(s); // end if of the previous block
|
|
+
|
|
+ // following block
|
|
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, block_idx);
|
|
+ tcg_wasm_out_op_i64_le_u(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ env_cached = false;
|
|
+}
|
|
+
|
|
+__thread int current_label[100];
|
|
+__thread int current_label_pos;
|
|
+
|
|
+static void tcg_out_label_cb(TCGContext *s, TCGLabel *l)
|
|
+{
|
|
+ current_label[current_label_pos++] = l->id;
|
|
+ tcg_debug_assert(current_label_pos < 100);
|
|
+ tcg_wasm_out_label_idx(s, l->id + 1);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_op_br_to_label(TCGContext *s, TCGLabel *l, bool br_if)
|
|
+{
|
|
+ int toploop_depth = 1;
|
|
+ if (br_if) {
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ toploop_depth++;
|
|
+ }
|
|
+ tcg_wasm_out8(s, 0x42); // i64.const
|
|
+ wasm_add_label_block_ptr_placeholder(l->id + 1, cur_wasm_ptr(s));
|
|
+ tcg_wasm_out8(s, 0x80); // filled before instantiation
|
|
+ tcg_wasm_out8(s, 0x80);
|
|
+ tcg_wasm_out8(s, 0x80);
|
|
+ tcg_wasm_out8(s, 0x80);
|
|
+ tcg_wasm_out8(s, 0x00);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+ bool found = false;
|
|
+ for (int i = 0; i < current_label_pos; i++) {
|
|
+ if (current_label[i] == l->id) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (found) {
|
|
+ tcg_wasm_out_op_br(s, toploop_depth); // br to the top of loop
|
|
+ } else {
|
|
+ tcg_wasm_out_op_br(s, toploop_depth - 1); // br to the end of the current block
|
|
+ }
|
|
+ if (br_if) {
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_br(TCGContext *s, TCGLabel *l)
|
|
+{
|
|
+ tcg_wasm_out_op_br_to_label(s, l, false);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1,
|
|
+ TCGReg arg2, TCGLabel *l)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i32(s, cond, arg1, arg2);
|
|
+ tcg_wasm_out_op_br_to_label(s, l, true);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1,
|
|
+ TCGReg arg2, TCGLabel *l)
|
|
+{
|
|
+ tcg_wasm_out_op_cond_i64(s, cond, arg1, arg2);
|
|
+ tcg_wasm_out_op_br_to_label(s, l, true);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_exit_tb(TCGContext *s, uintptr_t arg)
|
|
+{
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, TB_PTR_OFF, 0);
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)arg);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_goto_ptr(TCGContext *s, TCGReg arg)
|
|
+{
|
|
+ tcg_wasm_out_op_global_get_r(s, arg);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_ctx_i32_load(s, TB_PTR_OFF);
|
|
+ tcg_wasm_out_op_i32_eq(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 0);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_br(s, 2); // br to the top of loop
|
|
+ tcg_wasm_out_op_end(s);
|
|
+
|
|
+ tcg_wasm_out_ctx_i32_store_r(s, TB_PTR_OFF, arg);
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, DO_INIT_OFF, 1);
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_goto_tb(TCGContext *s, int which)
|
|
+{
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)get_jmp_target_addr(s, which));
|
|
+ tcg_wasm_out_op_i32_load(s, 0, 0);
|
|
+ tcg_wasm_out_op_local_set(s, TMP32_LOCAL_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_i32_ne(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_ctx_i32_load(s, TB_PTR_OFF);
|
|
+ tcg_wasm_out_op_i32_eq(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_i64_const(s, 0);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_br(s, 3); // br to the top of loop
|
|
+ tcg_wasm_out_op_end(s);
|
|
+
|
|
+ // store jmp target address to buf
|
|
+ tcg_wasm_out_op_local_get(s, CTX_IDX);
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ tcg_wasm_out_op_i32_store(s, 0, TB_PTR_OFF);
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, DO_INIT_OFF, 1);
|
|
+
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+}
|
|
+
|
|
+static void push_arg_i64(TCGContext *s, int *reg_idx, int *stack_offset) {
|
|
+ if (*reg_idx < NUM_OF_IARG_REGS) {
|
|
+ tcg_wasm_out_op_global_get_r(s, REG_INDEX_IARG_BASE + *reg_idx); // arg register
|
|
+ int addend = 1;
|
|
+ *reg_idx = *reg_idx + addend;
|
|
+ } else {
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load(s, 0, *stack_offset);
|
|
+ int addend = 8;
|
|
+ *stack_offset = *stack_offset + addend;
|
|
+ }
|
|
+}
|
|
+
|
|
+void gen_func_wrapper_code(TCGContext *s, const tcg_insn_unit *func, const TCGHelperInfo *info, int func_idx)
|
|
+{
|
|
+ int nargs;
|
|
+ unsigned typemask = info->typemask;
|
|
+ int rettype = typemask & 7;
|
|
+
|
|
+ if (rettype == dh_typecode_i128) {
|
|
+ // receive 128bit return value via the stack buffer
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ }
|
|
+
|
|
+ nargs = 32 - clz32(typemask >> 3);
|
|
+ nargs = DIV_ROUND_UP(nargs, 3);
|
|
+ int stack_offset = 0;
|
|
+ int reg_idx = 0;
|
|
+ int stack128_base = 0;
|
|
+ bool cached_128base = false;
|
|
+ for (int j = 0; j < nargs; ++j) {
|
|
+ int typecode = extract32(typemask, (j + 1) * 3, 3);
|
|
+ if (typecode == dh_typecode_void) {
|
|
+ continue;
|
|
+ }
|
|
+ switch (typecode) {
|
|
+ case dh_typecode_i32:
|
|
+ case dh_typecode_s32:
|
|
+ case dh_typecode_ptr:
|
|
+ push_arg_i64(s, ®_idx, &stack_offset);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ break;
|
|
+ case dh_typecode_i64:
|
|
+ case dh_typecode_s64:
|
|
+ push_arg_i64(s, ®_idx, &stack_offset);
|
|
+ break;
|
|
+ case dh_typecode_i128:
|
|
+ // copy data to 128stack
|
|
+ if (!cached_128base) {
|
|
+ tcg_wasm_out_ctx_i32_load(s, STACK128_OFF);
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX);
|
|
+ cached_128base = true;
|
|
+ }
|
|
+
|
|
+ // push current 128stack pointer
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i32_const(s, stack128_base);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ push_arg_i64(s, ®_idx, &stack_offset);
|
|
+ tcg_wasm_out_op_i64_store(s, 0, stack128_base);
|
|
+ stack128_base += 8;
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_0_IDX);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ push_arg_i64(s, ®_idx, &stack_offset);
|
|
+ tcg_wasm_out_op_i64_store(s, 0, stack128_base);
|
|
+ stack128_base += 8;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tcg_wasm_out_op_call(s, func_idx);
|
|
+
|
|
+ stack_offset = 0;
|
|
+ if (rettype != dh_typecode_void) {
|
|
+ switch (rettype) {
|
|
+ case dh_typecode_i32:
|
|
+ case dh_typecode_s32:
|
|
+ case dh_typecode_ptr:
|
|
+ tcg_wasm_out_op_i64_extend_i32_s(s);
|
|
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
|
|
+ break;
|
|
+ case dh_typecode_i64:
|
|
+ case dh_typecode_s64:
|
|
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
|
|
+ break;
|
|
+ case dh_typecode_i128:
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load(s, 0, stack_offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R0);
|
|
+ stack_offset += 8;
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_REG_CALL_STACK);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load(s, 0, stack_offset);
|
|
+ tcg_wasm_out_op_global_set_r(s, TCG_REG_R1);
|
|
+ stack_offset += 8;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void gen_func_type(TCGContext *s, const TCGHelperInfo *info)
|
|
+{
|
|
+ int nargs;
|
|
+ unsigned typemask = info->typemask;
|
|
+ int rettype = typemask & 7;
|
|
+ uint8_t * buf_start = wasm_get_helper_types_begin(s);
|
|
+ uint8_t * buf_ptr = buf_start;
|
|
+ nargs = 32 - clz32(typemask >> 3);
|
|
+ nargs = DIV_ROUND_UP(nargs, 3);
|
|
+
|
|
+ *buf_ptr++ = 0x60;
|
|
+ uint8_t * buf_ptr_vec_size = buf_ptr;
|
|
+ *buf_ptr += 0x80;
|
|
+ *buf_ptr += 0x80;
|
|
+ *buf_ptr += 0x80;
|
|
+ *buf_ptr += 0x80;
|
|
+ *buf_ptr += 0x00;
|
|
+ *buf_ptr++ = 0; // vector size (placeholder)
|
|
+
|
|
+ int vec_size = 0;
|
|
+
|
|
+ if (rettype == dh_typecode_i128) {
|
|
+ *buf_ptr++ = 0x7f; // i32 (stack buffer pointer)
|
|
+ vec_size++;
|
|
+ }
|
|
+
|
|
+ for (int j = 0; j < nargs; ++j) {
|
|
+ int typecode = extract32(typemask, (j + 1) * 3, 3);
|
|
+ if (typecode == dh_typecode_void) {
|
|
+ continue;
|
|
+ }
|
|
+ switch (typecode) {
|
|
+ case dh_typecode_i32:
|
|
+ case dh_typecode_s32:
|
|
+ case dh_typecode_ptr:
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ vec_size++;
|
|
+ break;
|
|
+ case dh_typecode_i64:
|
|
+ case dh_typecode_s64:
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ vec_size++;
|
|
+ break;
|
|
+ case dh_typecode_i128:
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ vec_size++;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+ fill_uint32_leb128((uintptr_t)buf_ptr_vec_size, vec_size); // fill size
|
|
+
|
|
+ if ((rettype == dh_typecode_void) || (rettype == dh_typecode_i128)) {
|
|
+ *buf_ptr++ = 0x0; // no return value
|
|
+ } else {
|
|
+ *buf_ptr++ = 0x1;
|
|
+ switch (rettype) {
|
|
+ case dh_typecode_i32:
|
|
+ case dh_typecode_s32:
|
|
+ case dh_typecode_ptr:
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ break;
|
|
+ case dh_typecode_i64:
|
|
+ case dh_typecode_s64:
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ }
|
|
+ int sz = (uint32_t)(buf_ptr - buf_start);
|
|
+ wasm_add_helper_types_pos(s, sz);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void gen_func_type_qemu_ld(TCGContext *s, uint32_t oi)
|
|
+{
|
|
+ uint8_t * buf_start = wasm_get_helper_types_begin(s);
|
|
+ uint8_t * buf_ptr = buf_start;
|
|
+ *buf_ptr++ = 0x60;
|
|
+ *buf_ptr++ = 0x4;
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x1;
|
|
+ MemOp mop = get_memop(oi);
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UQ:
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ break;
|
|
+ default:
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ break;
|
|
+ }
|
|
+ int sz = (uint32_t)(buf_ptr - buf_start);
|
|
+ wasm_add_helper_types_pos(s, sz);
|
|
+}
|
|
+
|
|
+static void gen_func_type_qemu_st(TCGContext *s, uint32_t oi)
|
|
+{
|
|
+ uint8_t * buf_start = wasm_get_helper_types_begin(s);
|
|
+ uint8_t * buf_ptr = buf_start;
|
|
+ *buf_ptr++ = 0x60;
|
|
+ *buf_ptr++ = 0x5;
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ MemOp mop = get_memop(oi);
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UQ:
|
|
+ *buf_ptr++ = 0x7e;
|
|
+ break;
|
|
+ default:
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ break;
|
|
+ }
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x7f;
|
|
+ *buf_ptr++ = 0x0;
|
|
+ int sz = (uint32_t)(buf_ptr - buf_start);
|
|
+ wasm_add_helper_types_pos(s, sz);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_call(TCGContext *s, const tcg_insn_unit *func,
|
|
+ const TCGHelperInfo *info)
|
|
+{
|
|
+ // set return position
|
|
+ tcg_wasm_out_ctx_i32_load(s, HELPER_RET_TB_PTR_OFF);
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
|
|
+
|
|
+ tcg_wasm_out_op_i32_store(s, 0, 0);
|
|
+
|
|
+ int func_idx = get_wasm_helper_idx(s, (int)func);
|
|
+ if (func_idx < 0) {
|
|
+ func_idx = wasm_register_helper_alloc_num(s);
|
|
+ tcg_debug_assert(func >= 0);
|
|
+ wasm_register_helper(s, func_idx, (int)func);
|
|
+ gen_func_type(s, info);
|
|
+ }
|
|
+
|
|
+ int target_block_idx = wasm_block_current_idx(s) + 1;
|
|
+ tcg_wasm_out_op_i64_const(s, target_block_idx);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this)
|
|
+ env_cached = false;
|
|
+
|
|
+ int block_idx = wasm_alloc_block_idx(s);
|
|
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, block_idx);
|
|
+ tcg_wasm_out_op_i64_le_u(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, UNWINDING_OFF, 0);
|
|
+ gen_func_wrapper_code(s, func, info, func_idx);
|
|
+
|
|
+ tcg_wasm_out_op_i32_const(s, 1);
|
|
+ tcg_wasm_out_ctx_i32_load(s, UNWINDING_OFF);
|
|
+ tcg_wasm_out_op_i32_eq(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+}
|
|
+
|
|
+void tb_target_set_jmp_target(const TranslationBlock *tb, int n,
|
|
+ uintptr_t jmp_rx, uintptr_t jmp_rw)
|
|
+{
|
|
+ /* Always indirect, nothing to do */
|
|
+}
|
|
+
|
|
+static uint8_t tcg_wasm_out_tlb_load(TCGContext *s, TCGReg addr, MemOpIdx oi, bool is_ld)
|
|
+{
|
|
+ MemOp opc = get_memop(oi);
|
|
+ TCGAtomAlign aa;
|
|
+ unsigned a_mask;
|
|
+
|
|
+ aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false);
|
|
+ a_mask = (1u << aa.align) - 1;
|
|
+
|
|
+ unsigned s_bits = opc & MO_SIZE;
|
|
+ unsigned s_mask = (1u << s_bits) - 1;
|
|
+ tcg_target_long compare_mask;
|
|
+ int mem_index = get_mmuidx(oi);
|
|
+ int fast_ofs = tlb_mask_table_ofs(s, mem_index);
|
|
+ int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask);
|
|
+ int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table);
|
|
+ int add_off = offsetof(CPUTLBEntry, addend);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, addr);
|
|
+ tcg_wasm_out_op_i64_const(s, s->page_bits - CPU_TLB_ENTRY_BITS);
|
|
+ tcg_wasm_out_op_i64_shr_u(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, TCG_AREG0);
|
|
+ int off = mask_ofs;
|
|
+ if ((int64_t)off < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int64_t)off);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ off = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off);
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_2_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, TMP64_2_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r_i32(s, TCG_AREG0);
|
|
+ off = table_ofs;
|
|
+ if ((int64_t)off < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int64_t)off);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ off = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_local_tee(s, TMP32_LOCAL_0_IDX);
|
|
+
|
|
+ off = is_ld ? offsetof(CPUTLBEntry, addr_read)
|
|
+ : offsetof(CPUTLBEntry, addr_write);
|
|
+ if ((int64_t)off < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int64_t)off);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ off = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i64_load(s, 0, (uint64_t)off);
|
|
+
|
|
+ tcg_wasm_out_op_global_get_r(s, addr);
|
|
+ if (a_mask < s_mask) {
|
|
+ tcg_wasm_out_op_i64_const(s, s_mask - a_mask);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+ }
|
|
+ compare_mask = (uint64_t)s->page_mask | a_mask;
|
|
+ tcg_wasm_out_op_i64_const(s, compare_mask);
|
|
+ tcg_wasm_out_op_i64_and(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_eq(s);
|
|
+
|
|
+ tcg_wasm_out_op_i64_const(s, 0);
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_local_get(s, TMP32_LOCAL_0_IDX);
|
|
+ off = add_off;
|
|
+ if ((int64_t)off < 0) {
|
|
+ tcg_wasm_out_op_i32_const(s, (int64_t)off);
|
|
+ tcg_wasm_out_op_i32_add(s);
|
|
+ off = 0;
|
|
+ }
|
|
+ tcg_wasm_out_op_i32_load(s, 0, (uint64_t)off);
|
|
+ tcg_wasm_out_op_i64_extend_i32_u(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, addr);
|
|
+ tcg_wasm_out_op_i64_add(s);
|
|
+ tcg_wasm_out_op_local_set(s, TMP64_0_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_end(s);
|
|
+
|
|
+
|
|
+ return TMP64_0_IDX;
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_qemu_ld_direct(TCGContext *s, TCGReg r, uint8_t base, MemOp opc)
|
|
+{
|
|
+ /* Byte swapping is left to middle-end expansion. */
|
|
+ tcg_debug_assert((opc & MO_BSWAP) == 0);
|
|
+
|
|
+ switch (opc & (MO_SSIZE)) {
|
|
+ case MO_UB:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load8_u(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_SB:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load8_s(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_UW:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load16_u(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_SW:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load16_s(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_UL:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load32_u(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_SL:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load32_s(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ case MO_UQ:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_i64_load(s, 0, 0);
|
|
+ tcg_wasm_out_op_global_set_r(s, r);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void* qemu_ld_helper_ptr(uint32_t oi)
|
|
+{
|
|
+ MemOp mop = get_memop(oi);
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UB:
|
|
+ return helper_ldub_mmu;
|
|
+ case MO_SB:
|
|
+ return helper_ldsb_mmu;
|
|
+ case MO_UW:
|
|
+ return helper_lduw_mmu;
|
|
+ case MO_SW:
|
|
+ return helper_ldsw_mmu;
|
|
+ case MO_UL:
|
|
+ return helper_ldul_mmu;
|
|
+ case MO_SL:
|
|
+ return helper_ldsl_mmu;
|
|
+ case MO_UQ:
|
|
+ return helper_ldq_mmu;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64)
|
|
+{
|
|
+ TCGReg addr_reg;
|
|
+ TCGReg data_reg;
|
|
+ MemOpIdx oi;
|
|
+ MemOp opc;
|
|
+
|
|
+ data_reg = *args++;
|
|
+ addr_reg = *args++;
|
|
+ oi = *args++;
|
|
+ opc = get_memop(oi);
|
|
+
|
|
+ uint8_t base = tcg_wasm_out_tlb_load(s, addr_reg, oi, true);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ // path for miss case
|
|
+ int helper_func_idx = (uint32_t)qemu_ld_helper_ptr(oi);
|
|
+ int func_idx = get_wasm_helper_idx(s, helper_func_idx);
|
|
+ if (func_idx < 0) {
|
|
+ func_idx = wasm_register_helper_alloc_num(s);
|
|
+ tcg_debug_assert(helper_func_idx >= 0);
|
|
+ wasm_register_helper(s, func_idx, helper_func_idx);
|
|
+ gen_func_type_qemu_ld(s, oi);
|
|
+ }
|
|
+
|
|
+ // save function pointer
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0);
|
|
+
|
|
+ tcg_wasm_out_op_else(s);
|
|
+
|
|
+ // fast path
|
|
+ tcg_wasm_out_qemu_ld_direct(s, data_reg, base, opc);
|
|
+
|
|
+ tcg_wasm_out_op_end(s);
|
|
+
|
|
+ int target_block_idx = wasm_block_current_idx(s) + 1;
|
|
+ tcg_wasm_out_op_i64_const(s, target_block_idx);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this)
|
|
+ env_cached = false;
|
|
+
|
|
+ // block for calling helper (+1)
|
|
+ int block_idx = wasm_alloc_block_idx(s);
|
|
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, block_idx);
|
|
+ tcg_wasm_out_op_i64_le_u(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ // call helper
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
|
|
+ tcg_wasm_out_op_i32_const(s, oi);
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
|
|
+
|
|
+ tcg_wasm_out_op_call(s, func_idx);
|
|
+ tcg_wasm_out_op_global_set_r(s, data_reg);
|
|
+ tcg_wasm_out_ctx_i32_load(s, DONE_FLAG_OFF);
|
|
+ tcg_wasm_out_op_i32_eqz(s);
|
|
+
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_qemu_st_direct(TCGContext *s, TCGReg lo, uint8_t base, MemOp opc)
|
|
+{
|
|
+ /* Byte swapping is left to middle-end expansion. */
|
|
+ tcg_debug_assert((opc & MO_BSWAP) == 0);
|
|
+
|
|
+ switch (opc & (MO_SSIZE)) {
|
|
+ case MO_8:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, lo);
|
|
+ tcg_wasm_out_op_i64_store8(s, 0, 0);
|
|
+ break;
|
|
+ case MO_16:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, lo);
|
|
+ tcg_wasm_out_op_i64_store16(s, 0, 0);
|
|
+ break;
|
|
+ case MO_32:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, lo);
|
|
+ tcg_wasm_out_op_i64_store32(s, 0, 0);
|
|
+ break;
|
|
+ case MO_64:
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, lo);
|
|
+ tcg_wasm_out_op_i64_store(s, 0, 0);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void* qemu_st_helper_ptr(uint32_t oi)
|
|
+{
|
|
+ MemOp mop = get_memop(oi);
|
|
+ switch (mop & MO_SIZE) {
|
|
+ case MO_8:
|
|
+ return helper_stb_mmu;
|
|
+ case MO_16:
|
|
+ return helper_stw_mmu;
|
|
+ case MO_32:
|
|
+ return helper_stl_mmu;
|
|
+ case MO_64:
|
|
+ return helper_stq_mmu;
|
|
+ case MO_128:
|
|
+ return helper_st16_mmu;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_wasm_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64)
|
|
+{
|
|
+ TCGReg addr_reg;
|
|
+ TCGReg data_reg;
|
|
+ MemOpIdx oi;
|
|
+ MemOp opc;
|
|
+
|
|
+ data_reg = *args++;
|
|
+ addr_reg = *args++;
|
|
+ oi = *args++;
|
|
+ opc = get_memop(oi);
|
|
+
|
|
+ uint8_t base = tcg_wasm_out_tlb_load(s, addr_reg, oi, false);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ // path for miss case
|
|
+ int helper_func_idx = (uint32_t)qemu_st_helper_ptr(oi);
|
|
+ int func_idx = get_wasm_helper_idx(s, helper_func_idx);
|
|
+ if (func_idx < 0) {
|
|
+ func_idx = wasm_register_helper_alloc_num(s);
|
|
+ tcg_debug_assert(helper_func_idx >= 0);
|
|
+ wasm_register_helper(s, func_idx, helper_func_idx);
|
|
+ gen_func_type_qemu_st(s, oi);
|
|
+ }
|
|
+
|
|
+ // save function pointer
|
|
+ tcg_wasm_out_ctx_i32_store_const(s, DONE_FLAG_OFF, 0);
|
|
+
|
|
+ tcg_wasm_out_op_else(s);
|
|
+
|
|
+ // fast path
|
|
+ tcg_wasm_out_qemu_st_direct(s, data_reg, base, opc);
|
|
+
|
|
+ tcg_wasm_out_op_end(s);
|
|
+
|
|
+ int target_block_idx = wasm_block_current_idx(s) + 1;
|
|
+ tcg_wasm_out_op_i64_const(s, target_block_idx);
|
|
+ tcg_wasm_out_op_global_set(s, BLOCK_PTR_IDX);
|
|
+
|
|
+ tcg_wasm_out_op_end(s); // close if of this block (rewind skips this)
|
|
+ env_cached = false;
|
|
+
|
|
+ // block for calling helper (+1)
|
|
+ int block_idx = wasm_alloc_block_idx(s);
|
|
+ tcg_wasm_out_op_global_get(s, BLOCK_PTR_IDX);
|
|
+ tcg_wasm_out_op_i64_const(s, block_idx);
|
|
+ tcg_wasm_out_op_i64_le_u(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ tcg_wasm_out_op_local_get(s, base);
|
|
+ tcg_wasm_out_op_i64_eqz(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+
|
|
+ // call helper
|
|
+ tcg_wasm_out_op_global_get_r(s, TCG_AREG0);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ tcg_wasm_out_op_global_get_r(s, addr_reg);
|
|
+ MemOp mop = get_memop(oi);
|
|
+ switch (mop & MO_SSIZE) {
|
|
+ case MO_UQ:
|
|
+ tcg_wasm_out_op_global_get_r(s, data_reg);
|
|
+ break;
|
|
+ default:
|
|
+ tcg_wasm_out_op_global_get_r(s, data_reg);
|
|
+ tcg_wasm_out_op_i32_wrap_i64(s);
|
|
+ break;
|
|
+ }
|
|
+ tcg_wasm_out_op_i32_const(s, oi);
|
|
+ tcg_wasm_out_op_i32_const(s, (int32_t)s->code_ptr);
|
|
+
|
|
+ tcg_wasm_out_op_call(s, func_idx);
|
|
+
|
|
+ tcg_wasm_out_ctx_i32_load(s, DONE_FLAG_OFF);
|
|
+ tcg_wasm_out_op_i32_eqz(s);
|
|
+ tcg_wasm_out_op_if_noret(s);
|
|
+ tcg_wasm_out_op_i32_const(s, 0);
|
|
+ tcg_wasm_out_op_return(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+ tcg_wasm_out_op_end(s);
|
|
+}
|
|
+
|
|
+static bool patch_reloc(tcg_insn_unit *code_ptr_i, int type,
|
|
+ intptr_t value, intptr_t addend)
|
|
+{
|
|
+ int32_t *code_ptr = (int32_t*)code_ptr_i;
|
|
+ intptr_t diff = value - (intptr_t)(code_ptr + 1);
|
|
+
|
|
+ tcg_debug_assert(addend == 0);
|
|
+ tcg_debug_assert(type == 20);
|
|
+
|
|
+ if (diff == sextract32(diff, 0, type)) {
|
|
+ tcg_patch32(code_ptr, deposit32(*code_ptr, 32 - type, type, diff));
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void stack_bounds_check(TCGReg base, intptr_t offset)
|
|
+{
|
|
+ if (base == TCG_REG_CALL_STACK) {
|
|
+ tcg_debug_assert(offset >= 0);
|
|
+ tcg_debug_assert(offset < (TCG_STATIC_CALL_ARGS_SIZE +
|
|
+ TCG_STATIC_FRAME_SIZE));
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void tcg_tci_out32(TCGContext *s, uint32_t v)
|
|
+{
|
|
+ tcg_out32(s, v);
|
|
+}
|
|
+
|
|
+static uintptr_t cur_tci_ptr(TCGContext *s)
|
|
+{
|
|
+ return (uintptr_t)s->code_ptr;
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_l(TCGContext *s, TCGOpcode op, TCGLabel *l0)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_out_reloc(s, (void*)cur_tci_ptr(s), 20, l0, 0);
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_p(TCGContext *s, TCGOpcode op, void *p0)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+ intptr_t diff;
|
|
+
|
|
+ /* Special case for exit_tb: map null -> 0. */
|
|
+ if (p0 == NULL) {
|
|
+ diff = 0;
|
|
+ } else {
|
|
+ diff = p0 - (void *)((uint8_t*)cur_tci_ptr(s) + 4);
|
|
+ tcg_debug_assert(diff != 0);
|
|
+ if (diff != sextract32(diff, 0, 20)) {
|
|
+ tcg_raise_tb_overflow(s);
|
|
+ }
|
|
+ }
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 12, 20, diff);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_r(TCGContext *s, TCGOpcode op, TCGReg r0)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_v(TCGContext *s, TCGOpcode op)
|
|
+{
|
|
+ tcg_tci_out32(s, (uint8_t)op);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_ri(TCGContext *s, TCGOpcode op, TCGReg r0, int32_t i1)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_debug_assert(i1 == sextract32(i1, 0, 20));
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 20, i1);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rl(TCGContext *s, TCGOpcode op, TCGReg r0, TCGLabel *l1)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_out_reloc(s, (void*)cur_tci_ptr(s), 20, l1, 0);
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrm(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGArg m2)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_debug_assert(m2 == extract32(m2, 0, 16));
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 16, m2);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrr(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGReg r2)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrs(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, intptr_t i2)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_debug_assert(i2 == sextract32(i2, 0, 16));
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 16, i2);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
|
|
+ TCGReg r1, uint8_t b2, uint8_t b3)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_debug_assert(b2 == extract32(b2, 0, 6));
|
|
+ tcg_debug_assert(b3 == extract32(b3, 0, 6));
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 6, b2);
|
|
+ insn = deposit32(insn, 22, 6, b3);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrc(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGReg r2, TCGCond c3)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 4, c3);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0,
|
|
+ TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ tcg_debug_assert(b3 == extract32(b3, 0, 6));
|
|
+ tcg_debug_assert(b4 == extract32(b4, 0, 6));
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 6, b3);
|
|
+ insn = deposit32(insn, 26, 6, b4);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrrr(TCGContext *s, TCGOpcode op, TCGReg r0,
|
|
+ TCGReg r1, TCGReg r2, TCGReg r3, TCGReg r4)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 4, r3);
|
|
+ insn = deposit32(insn, 24, 4, r4);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrr(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 4, r3);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrrrc(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGReg r2,
|
|
+ TCGReg r3, TCGReg r4, TCGCond c5)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 4, r3);
|
|
+ insn = deposit32(insn, 24, 4, r4);
|
|
+ insn = deposit32(insn, 28, 4, c5);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_op_rrrrrr(TCGContext *s, TCGOpcode op,
|
|
+ TCGReg r0, TCGReg r1, TCGReg r2,
|
|
+ TCGReg r3, TCGReg r4, TCGReg r5)
|
|
+{
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ insn = deposit32(insn, 0, 8, op);
|
|
+ insn = deposit32(insn, 8, 4, r0);
|
|
+ insn = deposit32(insn, 12, 4, r1);
|
|
+ insn = deposit32(insn, 16, 4, r2);
|
|
+ insn = deposit32(insn, 20, 4, r3);
|
|
+ insn = deposit32(insn, 24, 4, r4);
|
|
+ insn = deposit32(insn, 28, 4, r5);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_movi(TCGContext *s, TCGType type,
|
|
+ TCGReg ret, tcg_target_long arg)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+ arg = (int32_t)arg;
|
|
+ /* fall through */
|
|
+ case TCG_TYPE_I64:
|
|
+#endif
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+
|
|
+ if (arg == sextract32(arg, 0, 20)) {
|
|
+ tcg_tci_out_op_ri(s, INDEX_op_tci_movi, ret, arg);
|
|
+ } else {
|
|
+ uint32_t insn = 0;
|
|
+
|
|
+ new_pool_label(s, arg, 20, (void*)cur_tci_ptr(s), 0);
|
|
+ insn = deposit32(insn, 0, 8, INDEX_op_tci_movl);
|
|
+ insn = deposit32(insn, 8, 4, ret);
|
|
+ tcg_tci_out32(s, insn);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val,
|
|
+ TCGReg base, intptr_t offset)
|
|
+{
|
|
+ stack_bounds_check(base, offset);
|
|
+ if (offset != sextract32(offset, 0, 16)) {
|
|
+ tcg_tci_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset);
|
|
+ tcg_tci_out_op_rrr(s, (TCG_TARGET_REG_BITS == 32
|
|
+ ? INDEX_op_add_i32 : INDEX_op_add_i64),
|
|
+ TCG_REG_TMP, TCG_REG_TMP, base);
|
|
+ base = TCG_REG_TMP;
|
|
+ offset = 0;
|
|
+ }
|
|
+ tcg_tci_out_op_rrs(s, op, val, base, offset);
|
|
+}
|
|
+
|
|
+static bool tcg_tci_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_mov_i32, ret, arg);
|
|
+ break;
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_mov_i64, ret, arg);
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext8s_i32, rd, rs);
|
|
+ break;
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext8s_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext8s_i64, rd, rs);
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ if (TCG_TARGET_REG_BITS == 64) {
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext8u_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext8u_i64, rd, rs);
|
|
+ } else {
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext8u_i32);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext8u_i32, rd, rs);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i32, rd, rs);
|
|
+ break;
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext16s_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i64, rd, rs);
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ if (TCG_TARGET_REG_BITS == 64) {
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext16u_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16u_i64, rd, rs);
|
|
+ } else {
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext16u_i32);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16u_i32, rd, rs);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext32s_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext32s_i64, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
|
|
+ tcg_debug_assert(TCG_TARGET_HAS_ext32u_i64);
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext32u_i64, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_tci_out_ext32s(s, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_tci_out_ext32u(s, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_debug_assert(TCG_TARGET_REG_BITS == 64);
|
|
+ tcg_tci_out_mov(s, TCG_TYPE_I32, rd, rs);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_call(TCGContext *s, const tcg_insn_unit *func,
|
|
+ const TCGHelperInfo *info)
|
|
+{
|
|
+ ffi_cif *cif = info->cif;
|
|
+ uint32_t insn = 0;
|
|
+ uint8_t which;
|
|
+
|
|
+ if (cif->rtype == &ffi_type_void) {
|
|
+ which = 0;
|
|
+ } else {
|
|
+ tcg_debug_assert(cif->rtype->size == 4 ||
|
|
+ cif->rtype->size == 8 ||
|
|
+ cif->rtype->size == 16);
|
|
+ which = ctz32(cif->rtype->size) - 1;
|
|
+ }
|
|
+ new_pool_l2(s, 20, (void*)cur_tci_ptr(s), 0, (uintptr_t)func, (uintptr_t)cif);
|
|
+ insn = deposit32(insn, 0, 8, INDEX_op_call);
|
|
+ insn = deposit32(insn, 8, 4, which);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+
|
|
+#if TCG_TARGET_REG_BITS == 64
|
|
+# define CASE_32_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i64): \
|
|
+ case glue(glue(INDEX_op_, x), _i32):
|
|
+# define CASE_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i64):
|
|
+#else
|
|
+# define CASE_32_64(x) \
|
|
+ case glue(glue(INDEX_op_, x), _i32):
|
|
+# define CASE_64(x)
|
|
+#endif
|
|
+
|
|
+static void tcg_tci_out_exit_tb(TCGContext *s, uintptr_t arg)
|
|
+{
|
|
+ tcg_tci_out_op_p(s, INDEX_op_exit_tb, (void *)arg);
|
|
+}
|
|
+
|
|
+static void tcg_tci_out_goto_tb(TCGContext *s, int which)
|
|
+{
|
|
+ /* indirect jump method. */
|
|
+ tcg_tci_out_op_p(s, INDEX_op_goto_tb, (void *)get_jmp_target_addr(s, which));
|
|
+ set_jmp_reset_offset(s, which);
|
|
+}
|
|
+
|
|
+static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
|
|
+{
|
|
+ int32_t *p2 = (int32_t*)p;
|
|
+ memset(p2, 0, sizeof(*p2) * count);
|
|
+}
|
|
+
|
|
+static void tcg_out_goto_ptr(TCGContext *s, TCGOpcode opc, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_op_r(s, opc, arg);
|
|
+ tcg_wasm_out_goto_ptr(s, arg);
|
|
+}
|
|
+static void tcg_out_br(TCGContext *s, TCGOpcode opc, TCGLabel *l)
|
|
+{
|
|
+ tcg_tci_out_op_l(s, opc, l);
|
|
+ tcg_wasm_out_br(s, l);
|
|
+}
|
|
+static void tcg_out_setcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret,
|
|
+ TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrc(s, opc, ret, arg1, arg2, cond);
|
|
+ tcg_wasm_out_setcond_i32(s, cond, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_setcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret,
|
|
+ TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrc(s, opc, ret, arg1, arg2, cond);
|
|
+ tcg_wasm_out_setcond_i64(s, cond, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_movcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret,
|
|
+ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrrrc(s, opc, ret, c1, c2, v1, v2, cond);
|
|
+ tcg_wasm_out_movcond_i32(s, cond, ret, c1, c2, v1, v2);
|
|
+}
|
|
+static void tcg_out_movcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg ret,
|
|
+ TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrrrc(s, opc, ret, c1, c2, v1, v2, cond);
|
|
+ tcg_wasm_out_movcond_i64(s, cond, ret, c1, c2, v1, v2);
|
|
+}
|
|
+static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_tci_out_ldst(s, INDEX_op_ld_i32, val, base, offset);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_tci_out_ldst(s, INDEX_op_ld_i64, val, base, offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ tcg_wasm_out_ld(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld8s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld8s(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld8u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld8u(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld16s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld16s(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld16u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld16u(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld32s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld32s(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_ld32u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_ld32u(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TCG_TYPE_I32:
|
|
+ tcg_tci_out_ldst(s, INDEX_op_st_i32, val, base, offset);
|
|
+ break;
|
|
+ case TCG_TYPE_I64:
|
|
+ tcg_tci_out_ldst(s, INDEX_op_st_i64, val, base, offset);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ }
|
|
+ tcg_wasm_out_st(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_st8(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_st8(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_st16(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_st16(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_st32(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg val, TCGReg base,
|
|
+ intptr_t offset)
|
|
+{
|
|
+ tcg_tci_out_ldst(s, opc, val, base, offset);
|
|
+ tcg_wasm_out_st32(s, type, val, base, offset);
|
|
+}
|
|
+static void tcg_out_i64_calc_add(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_add(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_sub(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_sub(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_mul(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_mul(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_and(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_and(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_or(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_or(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_xor(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_xor(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_shl(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_shl(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_shl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_shl(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_shr_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_shr_u(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_shr_u(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_shr_u(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_shr_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_shr_s(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_shr_s(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_shr_s(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_rotl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_rotl(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i32_rotl(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i32_rotl(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i32_rotr(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i32_rotr(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_rotr(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_i64_calc_rotr(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_div_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_div_s(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_div_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_div_u(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_rem_s(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_rem_s(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_i64_calc_rem_u(TCGContext *s, TCGOpcode opc, TCGType type, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_rem_u(s, type, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_andc(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_andc(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_orc(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_orc(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_eqv(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_eqv(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_nand(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_nand(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_nor(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_nor(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_clz32(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_clz32(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_clz64(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_clz64(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_ctz32(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_ctz32(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_ctz64(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrr(s, opc, ret, arg1, arg2);
|
|
+ tcg_wasm_out_ctz64(s, ret, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_brcond_i32(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg arg1,
|
|
+ TCGReg arg2, TCGLabel *l)
|
|
+{
|
|
+ tcg_tci_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), TCG_REG_TMP, arg1, arg2, cond);
|
|
+ tcg_tci_out_op_rl(s, opc, TCG_REG_TMP, l);
|
|
+ tcg_wasm_out_brcond_i32(s, cond, arg1, arg2, l);
|
|
+
|
|
+}
|
|
+static void tcg_out_brcond_i64(TCGContext *s, TCGOpcode opc, TCGCond cond, TCGReg arg1,
|
|
+ TCGReg arg2, TCGLabel *l)
|
|
+{
|
|
+ tcg_tci_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), TCG_REG_TMP, arg1, arg2, cond);
|
|
+ tcg_tci_out_op_rl(s, opc, TCG_REG_TMP, l);
|
|
+ tcg_wasm_out_brcond_i64(s, cond, arg1, arg2, l);
|
|
+
|
|
+}
|
|
+static void tcg_out_neg(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, ret, arg);
|
|
+ tcg_wasm_out_neg(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_not(TCGContext *s, TCGOpcode opc, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, ret, arg);
|
|
+ tcg_wasm_out_not(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_ctpop_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ tcg_wasm_out_ctpop_i32(s, dest, src);
|
|
+}
|
|
+static void tcg_out_ctpop_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ tcg_wasm_out_ctpop_i64(s, dest, src);
|
|
+}
|
|
+static void tcg_out_add2(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth,
|
|
+ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh)
|
|
+{
|
|
+ tcg_tci_out_op_rrrrrr(s, opc, retl, reth, al, ah, bl, bh);
|
|
+ tcg_wasm_out_add2(s, retl, reth, al, ah, bl, bh);
|
|
+}
|
|
+static void tcg_out_sub2(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth,
|
|
+ TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh)
|
|
+{
|
|
+ tcg_tci_out_op_rrrrrr(s, opc, retl, reth, al, ah, bl, bh);
|
|
+ tcg_wasm_out_sub2(s, retl, reth, al, ah, bl, bh);
|
|
+}
|
|
+static void tcg_out_mulu2_i32(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrr(s, opc, retl, reth, arg1, arg2);
|
|
+ tcg_wasm_out_mulu2_i32(s, retl, reth, arg1, arg2);
|
|
+}
|
|
+static void tcg_out_muls2_i32(TCGContext *s, TCGOpcode opc, TCGReg retl, TCGReg reth, TCGReg arg1, TCGReg arg2)
|
|
+{
|
|
+ tcg_tci_out_op_rrrr(s, opc, retl, reth, arg1, arg2);
|
|
+ tcg_wasm_out_muls2_i32(s, retl, reth, arg1, arg2);
|
|
+}
|
|
+
|
|
+static void tcg_out_bswap16_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i32, dest, dest);
|
|
+ }
|
|
+ tcg_wasm_out_bswap16(s, dest, src, flags);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_wasm_out_ext16s(s, TCG_TYPE_I32, dest, dest);
|
|
+ }
|
|
+}
|
|
+static void tcg_out_bswap16_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext16s_i64, dest, dest);
|
|
+ }
|
|
+ tcg_wasm_out_bswap16(s, dest, src, flags);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_wasm_out_ext16s(s, TCG_TYPE_I64, dest, dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void tcg_out_bswap32_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ tcg_wasm_out_bswap32(s, dest, src, flags);
|
|
+}
|
|
+static void tcg_out_bswap32_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_tci_out_op_rr(s, INDEX_op_ext32s_i64, dest, dest);
|
|
+ }
|
|
+ tcg_wasm_out_bswap32(s, dest, src, flags);
|
|
+ if (flags & TCG_BSWAP_OS) {
|
|
+ tcg_wasm_out_ext32s(s, dest, dest);
|
|
+ }
|
|
+}
|
|
+static void tcg_out_bswap64_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg src, int flags)
|
|
+{
|
|
+ tcg_tci_out_op_rr(s, opc, dest, src);
|
|
+ tcg_wasm_out_bswap64(s, dest, src, flags);
|
|
+}
|
|
+static uint8_t tcg_tci_out_qemu_ldst(TCGContext *s, TCGOpcode opc, const TCGArg *args)
|
|
+{
|
|
+ TCGReg addr_reg = args[1];
|
|
+ MemOpIdx oi = args[2];
|
|
+
|
|
+ MemOp mopc = get_memop(oi);
|
|
+ TCGAtomAlign aa = atom_and_align_for_opc(s, mopc, MO_ATOM_IFALIGN, false);
|
|
+ unsigned a_mask = (1u << aa.align) - 1;
|
|
+
|
|
+ int mem_index = get_mmuidx(oi);
|
|
+ int fast_ofs = tlb_mask_table_ofs(s, mem_index);
|
|
+ int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask);
|
|
+ int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table);
|
|
+
|
|
+ new_pool_l8(s, 20, (void*)cur_tci_ptr(s), 0,
|
|
+ (TCGReg)args[0], (TCGReg)args[1], (TCGArg)args[2], (int32_t)a_mask, (int32_t)mask_ofs, (uint64_t)s->page_bits, s->page_mask, table_ofs);
|
|
+
|
|
+ uint32_t insn = 0;
|
|
+ insn = deposit32(insn, 0, 8, opc);
|
|
+ tcg_tci_out32(s, insn);
|
|
+}
|
|
+static void tcg_out_qemu_ld(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64)
|
|
+{
|
|
+ tcg_tci_out_qemu_ldst(s, opc, args);
|
|
+ tcg_wasm_out_qemu_ld(s, args, is_64);
|
|
+}
|
|
+static void tcg_out_qemu_st(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64)
|
|
+{
|
|
+ wasm_tci_only_tb = true;
|
|
+ tcg_tci_out_qemu_ldst(s, opc, args);
|
|
+ tcg_wasm_out_qemu_st(s, args, is_64);
|
|
+}
|
|
+static void tcg_out_deposit_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len)
|
|
+{
|
|
+ TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64;
|
|
+ tcg_tci_out_op_rrrbb(s, opc, dest, arg1, arg2, pos, len);
|
|
+ tcg_wasm_out_deposit_i32(s, dest, arg1, arg2, pos, len);
|
|
+}
|
|
+static void tcg_out_deposit_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, TCGReg arg2, int pos, int len)
|
|
+{
|
|
+ TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64;
|
|
+ tcg_tci_out_op_rrrbb(s, opc, dest, arg1, arg2, pos, len);
|
|
+ tcg_wasm_out_deposit_i64(s, dest, arg1, arg2, pos, len);
|
|
+}
|
|
+static void tcg_out_extract_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len)
|
|
+{
|
|
+ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32;
|
|
+ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len);
|
|
+ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I32, false);
|
|
+}
|
|
+static void tcg_out_extract_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len)
|
|
+{
|
|
+ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32;
|
|
+ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len);
|
|
+ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I64, false);
|
|
+}
|
|
+static void tcg_out_sextract_i32(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len)
|
|
+{
|
|
+ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32;
|
|
+ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len);
|
|
+ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I32, true);
|
|
+}
|
|
+static void tcg_out_sextract_i64(TCGContext *s, TCGOpcode opc, TCGReg dest, TCGReg arg1, int pos, int len)
|
|
+{
|
|
+ TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32;
|
|
+ tcg_tci_out_op_rrbb(s, opc, dest, arg1, pos, len);
|
|
+ tcg_wasm_out_extract(s, dest, arg1, pos, len, TCG_TYPE_I64, true);
|
|
+}
|
|
+static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext8s(s, type, ret, arg);
|
|
+ tcg_wasm_out_ext8s(s, type, ret, arg);
|
|
+}
|
|
+static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext16s(s, type, ret, arg);
|
|
+ tcg_wasm_out_ext16s(s, type, ret, arg);
|
|
+}
|
|
+static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext8u(s, ret, arg);
|
|
+ tcg_wasm_out_ext8u(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext16u(s, ret, arg);
|
|
+ tcg_wasm_out_ext16u(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext32s(s, ret, arg);
|
|
+ tcg_wasm_out_ext32s(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_ext32u(s, ret, arg);
|
|
+ tcg_wasm_out_ext32u(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_exts_i32_i64(s, ret, arg);
|
|
+ tcg_wasm_out_exts_i32_i64(s, ret, arg);
|
|
+}
|
|
+static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_extu_i32_i64(s, ret, arg);
|
|
+ tcg_wasm_out_extu_i32_i64(s, ret, arg);
|
|
+}
|
|
+
|
|
+static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs)
|
|
+{
|
|
+ tcg_tci_out_extrl_i64_i32(s, rd, rs);
|
|
+ tcg_wasm_out_extrl_i64_i32(s, rd, rs);
|
|
+}
|
|
+
|
|
+static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
|
|
+{
|
|
+ tcg_tci_out_mov(s, type, ret, arg);
|
|
+ tcg_wasm_out_mov(s, type, ret, arg);
|
|
+ return true;
|
|
+}
|
|
+static void tcg_out_movi(TCGContext *s, TCGType type,
|
|
+ TCGReg ret, tcg_target_long arg)
|
|
+{
|
|
+ tcg_tci_out_movi(s, type, ret, arg);
|
|
+ tcg_wasm_out_movi(s, type, ret, arg);
|
|
+}
|
|
+static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs,
|
|
+ tcg_target_long imm)
|
|
+{
|
|
+ g_assert_not_reached();
|
|
+}
|
|
+static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg)
|
|
+{
|
|
+ tcg_tci_out_exit_tb(s, arg);
|
|
+ tcg_wasm_out_exit_tb(s, arg);
|
|
+}
|
|
+static void tcg_out_goto_tb(TCGContext *s, int which)
|
|
+{
|
|
+ tcg_tci_out_goto_tb(s, which);
|
|
+ tcg_wasm_out_goto_tb(s, which);
|
|
+}
|
|
+static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val,
|
|
+ TCGReg base, intptr_t ofs)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target,
|
|
+ const TCGHelperInfo *info)
|
|
+{
|
|
+ tcg_tci_out_call(s, target, info);
|
|
+ tcg_wasm_out_call(s, target, info);
|
|
+}
|
|
+
|
|
+static void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|
+ const TCGArg args[TCG_MAX_OP_ARGS],
|
|
+ const int const_args[TCG_MAX_OP_ARGS])
|
|
+{
|
|
+ switch (opc) {
|
|
+ case INDEX_op_goto_ptr:
|
|
+ tcg_out_goto_ptr(s, opc, args[0]);
|
|
+ break;
|
|
+ case INDEX_op_br:
|
|
+ tcg_out_br(s, opc, arg_label(args[0]));
|
|
+ break;
|
|
+ case INDEX_op_setcond_i32:
|
|
+ tcg_out_setcond_i32(s, opc, args[3], args[0], args[1], args[2]);//
|
|
+ break;
|
|
+ case INDEX_op_setcond_i64:
|
|
+ tcg_out_setcond_i64(s, opc, args[3], args[0], args[1], args[2]);//
|
|
+ break;
|
|
+ case INDEX_op_movcond_i32:
|
|
+ tcg_out_movcond_i32(s, opc, args[5], args[0], args[1], args[2], args[3], args[4]);//
|
|
+ break;
|
|
+ case INDEX_op_movcond_i64:
|
|
+ tcg_out_movcond_i64(s, opc, args[5], args[0], args[1], args[2], args[3], args[4]);//
|
|
+ break;
|
|
+ case INDEX_op_ld_i64:
|
|
+ tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld8s_i32:
|
|
+ case INDEX_op_ld8s_i64:
|
|
+ tcg_out_ld8s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld8u_i32:
|
|
+ case INDEX_op_ld8u_i64:
|
|
+ tcg_out_ld8u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld16s_i32:
|
|
+ case INDEX_op_ld16s_i64:
|
|
+ tcg_out_ld16s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld16u_i32:
|
|
+ case INDEX_op_ld16u_i64:
|
|
+ tcg_out_ld16u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld32u_i64:
|
|
+ tcg_out_ld32u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ld_i32:
|
|
+ case INDEX_op_ld32s_i64:
|
|
+ tcg_out_ld32s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_st_i64:
|
|
+ tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_st8_i32:
|
|
+ case INDEX_op_st8_i64:
|
|
+ tcg_out_st8(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_st16_i32:
|
|
+ case INDEX_op_st16_i64:
|
|
+ tcg_out_st16(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_st_i32:
|
|
+ case INDEX_op_st32_i64:
|
|
+ tcg_out_st32(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_add_i32:
|
|
+ case INDEX_op_add_i64:
|
|
+ tcg_out_i64_calc_add(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_sub_i32:
|
|
+ case INDEX_op_sub_i64:
|
|
+ tcg_out_i64_calc_sub(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_mul_i32:
|
|
+ case INDEX_op_mul_i64:
|
|
+ tcg_out_i64_calc_mul(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_and_i32:
|
|
+ case INDEX_op_and_i64:
|
|
+ tcg_out_i64_calc_and(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_or_i32:
|
|
+ case INDEX_op_or_i64:
|
|
+ tcg_out_i64_calc_or(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_xor_i32:
|
|
+ case INDEX_op_xor_i64:
|
|
+ tcg_out_i64_calc_xor(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_shl_i32:
|
|
+ tcg_out_shl(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_shl_i64:
|
|
+ tcg_out_shl(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_shr_i32:
|
|
+ tcg_out_shr_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_shr_i64:
|
|
+ tcg_out_shr_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_sar_i32:
|
|
+ tcg_out_shr_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_sar_i64:
|
|
+ tcg_out_shr_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rotl_i32:
|
|
+ tcg_out_i32_rotl(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rotl_i64:
|
|
+ tcg_out_i64_calc_rotl(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rotr_i32:
|
|
+ tcg_out_i32_rotr(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rotr_i64:
|
|
+ tcg_out_i64_calc_rotr(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_div_i32:
|
|
+ tcg_out_i64_calc_div_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_div_i64:
|
|
+ tcg_out_i64_calc_div_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_divu_i32:
|
|
+ tcg_out_i64_calc_div_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_divu_i64:
|
|
+ tcg_out_i64_calc_div_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rem_i32:
|
|
+ tcg_out_i64_calc_rem_s(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_rem_i64:
|
|
+ tcg_out_i64_calc_rem_s(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_remu_i32:
|
|
+ tcg_out_i64_calc_rem_u(s, opc, TCG_TYPE_I32, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_remu_i64:
|
|
+ tcg_out_i64_calc_rem_u(s, opc, TCG_TYPE_I64, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_andc_i32:
|
|
+ case INDEX_op_andc_i64:
|
|
+ tcg_out_andc(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_orc_i32:
|
|
+ case INDEX_op_orc_i64:
|
|
+ tcg_out_orc(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_eqv_i32:
|
|
+ case INDEX_op_eqv_i64:
|
|
+ tcg_out_eqv(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_nand_i32:
|
|
+ case INDEX_op_nand_i64:
|
|
+ tcg_out_nand(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_nor_i32:
|
|
+ case INDEX_op_nor_i64:
|
|
+ tcg_out_nor(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_clz_i32:
|
|
+ tcg_out_clz32(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_clz_i64:
|
|
+ tcg_out_clz64(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ctz_i32:
|
|
+ tcg_out_ctz32(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ctz_i64:
|
|
+ tcg_out_ctz64(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_brcond_i32:
|
|
+ tcg_out_brcond_i32(s, opc, args[2], args[0], args[1], arg_label(args[3]));
|
|
+ break;
|
|
+ case INDEX_op_brcond_i64:
|
|
+ tcg_out_brcond_i64(s, opc, args[2], args[0], args[1], arg_label(args[3]));
|
|
+ break;
|
|
+ case INDEX_op_neg_i32:
|
|
+ case INDEX_op_neg_i64:
|
|
+ tcg_out_neg(s, opc, args[0], args[1]);
|
|
+ break;
|
|
+ case INDEX_op_not_i32:
|
|
+ case INDEX_op_not_i64:
|
|
+ tcg_out_not(s, opc, args[0], args[1]);
|
|
+ break;
|
|
+ case INDEX_op_ctpop_i32:
|
|
+ tcg_out_ctpop_i32(s, opc, args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_ctpop_i64:
|
|
+ tcg_out_ctpop_i64(s, opc, args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_add2_i32:
|
|
+ case INDEX_op_add2_i64:
|
|
+ tcg_out_add2(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
+ break;
|
|
+ case INDEX_op_sub2_i32:
|
|
+ case INDEX_op_sub2_i64:
|
|
+ tcg_out_sub2(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
+ break;
|
|
+ case INDEX_op_qemu_ld_a32_i32:
|
|
+ case INDEX_op_qemu_ld_a64_i32:
|
|
+ tcg_out_qemu_ld(s, opc, args, false);
|
|
+ break;
|
|
+ case INDEX_op_qemu_st_a32_i32:
|
|
+ case INDEX_op_qemu_st_a64_i32:
|
|
+ tcg_out_qemu_st(s, opc, args, false);
|
|
+ break;
|
|
+ case INDEX_op_qemu_ld_a32_i64:
|
|
+ case INDEX_op_qemu_ld_a64_i64:
|
|
+ tcg_out_qemu_ld(s, opc, args, true);
|
|
+ break;
|
|
+ case INDEX_op_qemu_st_a32_i64:
|
|
+ case INDEX_op_qemu_st_a64_i64:
|
|
+ tcg_out_qemu_st(s, opc, args, true);
|
|
+ break;
|
|
+ case INDEX_op_extrl_i64_i32:
|
|
+ tcg_out_extrl_i64_i32(s, args[0], args[1]);
|
|
+ break;
|
|
+ case INDEX_op_mb:
|
|
+ tcg_tci_out_op_v(s, opc);
|
|
+ tcg_wasm_out8(s, 0x01); // nop
|
|
+ break;
|
|
+ case INDEX_op_extract_i32:
|
|
+ tcg_out_extract_i32(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ case INDEX_op_extract_i64:
|
|
+ tcg_out_extract_i64(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ case INDEX_op_sextract_i32:
|
|
+ tcg_out_sextract_i32(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ case INDEX_op_sextract_i64:
|
|
+ tcg_out_sextract_i64(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ case INDEX_op_deposit_i32:
|
|
+ tcg_out_deposit_i32(s, opc, args[0], args[1], args[2], args[3], args[4]);
|
|
+ break;
|
|
+ case INDEX_op_deposit_i64:
|
|
+ tcg_out_deposit_i64(s, opc, args[0], args[1], args[2], args[3], args[4]);
|
|
+ break;
|
|
+ case INDEX_op_bswap16_i32:
|
|
+ tcg_out_bswap16_i32(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_bswap16_i64:
|
|
+ tcg_out_bswap16_i64(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_bswap32_i32:
|
|
+ tcg_out_bswap32_i32(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_bswap32_i64:
|
|
+ tcg_out_bswap32_i64(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_bswap64_i64:
|
|
+ tcg_out_bswap64_i64(s, opc, args[0], args[1], args[2]);
|
|
+ break;
|
|
+ case INDEX_op_muls2_i32:
|
|
+ tcg_out_muls2_i32(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ case INDEX_op_mulu2_i32:
|
|
+ tcg_out_mulu2_i32(s, opc, args[0], args[1], args[2], args[3]);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached();
|
|
+ break;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+void tcg_out_init() {
|
|
+ current_label_pos = 0;
|
|
+ env_cached = false;
|
|
+ wasm_tci_only_tb = false;
|
|
+}
|
|
+
|
|
+/* Test if a constant matches the constraint. */
|
|
+static bool tcg_target_const_match(int64_t val, int ct, TCGType type,
|
|
+ TCGCond cond, int vece)
|
|
+{
|
|
+ return ct & TCG_CT_CONST;
|
|
+}
|
|
+
|
|
+static void tcg_out_tb_start(TCGContext *s)
|
|
+{
|
|
+ /* nothing to do */
|
|
+}
|
|
+
|
|
+bool tcg_target_has_memory_bswap(MemOp memop)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void tcg_target_init(TCGContext *s)
|
|
+{
|
|
+ /* The current code uses uint8_t for tcg operations. */
|
|
+ tcg_debug_assert(tcg_op_defs_max <= UINT8_MAX);
|
|
+
|
|
+ /* Registers available for 32 bit operations. */
|
|
+ tcg_target_available_regs[TCG_TYPE_I64] = BIT(TCG_TARGET_NB_REGS) - 1;
|
|
+ tcg_target_available_regs[TCG_TYPE_I32] = BIT(TCG_TARGET_NB_REGS) - 1;
|
|
+ /*
|
|
+ * The interpreter "registers" are in the local stack frame and
|
|
+ * cannot be clobbered by the called helper functions. However,
|
|
+ * the interpreter assumes a 64-bit return value and assigns to
|
|
+ * the return value registers.
|
|
+ */
|
|
+ tcg_target_call_clobber_regs =
|
|
+ MAKE_64BIT_MASK(TCG_REG_R0, 128 / TCG_TARGET_REG_BITS);
|
|
+
|
|
+ s->reserved_regs = 0;
|
|
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP);
|
|
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
|
|
+
|
|
+ /* The call arguments come first, followed by the temp storage. */
|
|
+ tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE,
|
|
+ TCG_STATIC_FRAME_SIZE);
|
|
+}
|
|
+
|
|
+/* Generate global QEMU prologue and epilogue code. */
|
|
+static inline void tcg_target_qemu_prologue(TCGContext *s)
|
|
+{
|
|
+}
|
|
diff --git c/tcg/wasm32/tcg-target.h w/tcg/wasm32/tcg-target.h
|
|
new file mode 100644
|
|
index 0000000000..7346c3a76e
|
|
--- /dev/null
|
|
+++ w/tcg/wasm32/tcg-target.h
|
|
@@ -0,0 +1,153 @@
|
|
+/*
|
|
+ * Tiny Code Generator for QEMU
|
|
+ *
|
|
+ * Copyright (c) 2009, 2011 Stefan Weil
|
|
+ *
|
|
+ * Based on tci/tcg-target.h
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
+ * of this software and associated documentation files (the "Software"), to deal
|
|
+ * in the Software without restriction, including without limitation the rights
|
|
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
+ * copies of the Software, and to permit persons to whom the Software is
|
|
+ * furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
+ * THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef TCG_TARGET_H
|
|
+#define TCG_TARGET_H
|
|
+
|
|
+#define TCG_TARGET_INSN_UNIT_SIZE 1
|
|
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32
|
|
+#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
|
|
+
|
|
+/* Optional instructions. */
|
|
+
|
|
+#define TCG_TARGET_HAS_bswap16_i32 1
|
|
+#define TCG_TARGET_HAS_bswap32_i32 1
|
|
+#define TCG_TARGET_HAS_div_i32 1
|
|
+#define TCG_TARGET_HAS_rem_i32 1
|
|
+#define TCG_TARGET_HAS_ext8s_i32 1
|
|
+#define TCG_TARGET_HAS_ext16s_i32 1
|
|
+#define TCG_TARGET_HAS_ext8u_i32 1
|
|
+#define TCG_TARGET_HAS_ext16u_i32 1
|
|
+#define TCG_TARGET_HAS_andc_i32 1
|
|
+#define TCG_TARGET_HAS_deposit_i32 1
|
|
+#define TCG_TARGET_HAS_extract_i32 1
|
|
+#define TCG_TARGET_HAS_sextract_i32 1
|
|
+#define TCG_TARGET_HAS_extract2_i32 0
|
|
+#define TCG_TARGET_HAS_eqv_i32 1
|
|
+#define TCG_TARGET_HAS_nand_i32 1
|
|
+#define TCG_TARGET_HAS_nor_i32 1
|
|
+#define TCG_TARGET_HAS_clz_i32 1
|
|
+#define TCG_TARGET_HAS_ctz_i32 1
|
|
+#define TCG_TARGET_HAS_ctpop_i32 1
|
|
+#define TCG_TARGET_HAS_neg_i32 1
|
|
+#define TCG_TARGET_HAS_not_i32 1
|
|
+#define TCG_TARGET_HAS_orc_i32 1
|
|
+#define TCG_TARGET_HAS_rot_i32 1
|
|
+#define TCG_TARGET_HAS_movcond_i32 1
|
|
+#define TCG_TARGET_HAS_negsetcond_i32 0
|
|
+#define TCG_TARGET_HAS_muls2_i32 1
|
|
+#define TCG_TARGET_HAS_muluh_i32 0
|
|
+#define TCG_TARGET_HAS_mulsh_i32 0
|
|
+#define TCG_TARGET_HAS_qemu_st8_i32 0
|
|
+
|
|
+#define TCG_TARGET_HAS_extr_i64_i32 0
|
|
+#define TCG_TARGET_HAS_extrl_i64_i32 1
|
|
+#define TCG_TARGET_HAS_extrh_i64_i32 0
|
|
+#define TCG_TARGET_HAS_bswap16_i64 1
|
|
+#define TCG_TARGET_HAS_bswap32_i64 1
|
|
+#define TCG_TARGET_HAS_bswap64_i64 1
|
|
+#define TCG_TARGET_HAS_deposit_i64 1
|
|
+#define TCG_TARGET_HAS_extract_i64 1
|
|
+#define TCG_TARGET_HAS_sextract_i64 1
|
|
+#define TCG_TARGET_HAS_extract2_i64 0
|
|
+#define TCG_TARGET_HAS_div_i64 1
|
|
+#define TCG_TARGET_HAS_rem_i64 1
|
|
+#define TCG_TARGET_HAS_ext8s_i64 1
|
|
+#define TCG_TARGET_HAS_ext16s_i64 1
|
|
+#define TCG_TARGET_HAS_ext32s_i64 1
|
|
+#define TCG_TARGET_HAS_ext8u_i64 1
|
|
+#define TCG_TARGET_HAS_ext16u_i64 1
|
|
+#define TCG_TARGET_HAS_ext32u_i64 1
|
|
+#define TCG_TARGET_HAS_andc_i64 1
|
|
+#define TCG_TARGET_HAS_eqv_i64 1
|
|
+#define TCG_TARGET_HAS_nand_i64 1
|
|
+#define TCG_TARGET_HAS_nor_i64 1
|
|
+#define TCG_TARGET_HAS_clz_i64 1
|
|
+#define TCG_TARGET_HAS_ctz_i64 1
|
|
+#define TCG_TARGET_HAS_ctpop_i64 1
|
|
+#define TCG_TARGET_HAS_neg_i64 1
|
|
+#define TCG_TARGET_HAS_not_i64 1
|
|
+#define TCG_TARGET_HAS_orc_i64 1
|
|
+#define TCG_TARGET_HAS_rot_i64 1
|
|
+#define TCG_TARGET_HAS_negsetcond_i64 0
|
|
+#define TCG_TARGET_HAS_tst 0
|
|
+#define TCG_TARGET_HAS_tst_vec 0
|
|
+#define TCG_TARGET_HAS_movcond_i64 1
|
|
+#define TCG_TARGET_HAS_muls2_i64 0
|
|
+#define TCG_TARGET_HAS_add2_i32 1
|
|
+#define TCG_TARGET_HAS_sub2_i32 1
|
|
+#define TCG_TARGET_HAS_mulu2_i32 1
|
|
+#define TCG_TARGET_HAS_add2_i64 1
|
|
+#define TCG_TARGET_HAS_sub2_i64 1
|
|
+#define TCG_TARGET_HAS_mulu2_i64 0
|
|
+#define TCG_TARGET_HAS_muluh_i64 0
|
|
+#define TCG_TARGET_HAS_mulsh_i64 0
|
|
+
|
|
+#define TCG_TARGET_HAS_qemu_ldst_i128 0
|
|
+
|
|
+/* Number of registers available. */
|
|
+#define TCG_TARGET_NB_REGS 16
|
|
+
|
|
+/* List of registers which are used by TCG. */
|
|
+typedef enum {
|
|
+ TCG_REG_R0 = 0,
|
|
+ TCG_REG_R1,
|
|
+ TCG_REG_R2,
|
|
+ TCG_REG_R3,
|
|
+ TCG_REG_R4,
|
|
+ TCG_REG_R5,
|
|
+ TCG_REG_R6,
|
|
+ TCG_REG_R7,
|
|
+ TCG_REG_R8,
|
|
+ TCG_REG_R9,
|
|
+ TCG_REG_R10,
|
|
+ TCG_REG_R11,
|
|
+ TCG_REG_R12,
|
|
+ TCG_REG_R13,
|
|
+ TCG_REG_R14,
|
|
+ TCG_REG_R15,
|
|
+
|
|
+ TCG_REG_TMP = TCG_REG_R13,
|
|
+ TCG_AREG0 = TCG_REG_R14,
|
|
+ TCG_REG_CALL_STACK = TCG_REG_R15,
|
|
+} TCGReg;
|
|
+
|
|
+/* Used for function call generation. */
|
|
+#define TCG_TARGET_CALL_STACK_OFFSET 0
|
|
+#define TCG_TARGET_STACK_ALIGN 8
|
|
+#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL
|
|
+#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL
|
|
+#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL
|
|
+#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL
|
|
+
|
|
+#define HAVE_TCG_QEMU_TB_EXEC
|
|
+#define TCG_TARGET_NEED_POOL_LABELS
|
|
+
|
|
+#define TCG_TARGET_DEFAULT_MO (0)
|
|
+
|
|
+#define TCG_TARGET_HAS_MEMORY_BSWAP 0
|
|
+
|
|
+#endif /* TCG_TARGET_H */
|
|
diff --git c/util/cacheflush.c w/util/cacheflush.c
|
|
index a08906155a..babbb644e5 100644
|
|
--- c/util/cacheflush.c
|
|
+++ w/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 c/util/coroutine-fiber.c w/util/coroutine-fiber.c
|
|
new file mode 100644
|
|
index 0000000000..2ce1575362
|
|
--- /dev/null
|
|
+++ w/util/coroutine-fiber.c
|
|
@@ -0,0 +1,123 @@
|
|
+/*
|
|
+ * 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_);
|
|
+
|
|
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
|
+ set_unwinding_flag();
|
|
+#endif
|
|
+
|
|
+ set_current(to_);
|
|
+ to->action = action;
|
|
+ emscripten_fiber_swap(&from->fiber, &to->fiber);
|
|
+ return from->action;
|
|
+}
|
|
+
|
|
+Coroutine *qemu_coroutine_self(void)
|
|
+{
|
|
+ Coroutine *self = get_current();
|
|
+
|
|
+ if (!self) {
|
|
+ CoroutineEmscripten *leaderp = get_leader();
|
|
+ if (!leaderp) {
|
|
+ leaderp = g_malloc0(sizeof(*leaderp));
|
|
+ leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size);
|
|
+ leaderp->asyncify_stack_size = leader_asyncify_stack_size;
|
|
+ emscripten_fiber_init_from_current_context(&leaderp->fiber, leaderp->asyncify_stack, leaderp->asyncify_stack_size);
|
|
+ set_leader(leaderp);
|
|
+ }
|
|
+ self = &leaderp->base;
|
|
+ set_current(self);
|
|
+ }
|
|
+ return self;
|
|
+}
|
|
+
|
|
+bool qemu_in_coroutine(void)
|
|
+{
|
|
+ Coroutine *self = get_current();
|
|
+
|
|
+ return self && self->caller;
|
|
+}
|
|
diff --git c/util/int128.c w/util/int128.c
|
|
index df6c6331bd..c442ff4da8 100644
|
|
--- c/util/int128.c
|
|
+++ w/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 c/util/meson.build w/util/meson.build
|
|
index 5d8bef9891..e107fbfb69 100644
|
|
--- c/util/meson.build
|
|
+++ w/util/meson.build
|
|
@@ -11,7 +11,9 @@ if host_os != 'windows'
|
|
endif
|
|
util_ss.add(files('compatfd.c'))
|
|
util_ss.add(files('event_notifier-posix.c'))
|
|
- util_ss.add(files('mmap-alloc.c'))
|
|
+ if host_os != 'emscripten'
|
|
+ util_ss.add(files('mmap-alloc.c'))
|
|
+ endif
|
|
freebsd_dep = []
|
|
if host_os == 'freebsd'
|
|
freebsd_dep = util
|
|
diff --git c/util/mmap-alloc.c w/util/mmap-alloc.c
|
|
index ed14f9c64d..8c8708ca4d 100644
|
|
--- c/util/mmap-alloc.c
|
|
+++ w/util/mmap-alloc.c
|
|
@@ -250,6 +250,11 @@ void *qemu_ram_mmap(int fd,
|
|
uint32_t qemu_map_flags,
|
|
off_t map_offset)
|
|
{
|
|
+#if defined(EMSCRIPTEN)
|
|
+ void *ptr;
|
|
+ ptr = mmap_activate(0, size + align, fd, qemu_map_flags, map_offset);
|
|
+ return (void *)QEMU_ALIGN_UP((uintptr_t)ptr, align);
|
|
+#else
|
|
const size_t guard_pagesize = mmap_guard_pagesize(fd);
|
|
size_t offset, total;
|
|
void *ptr, *guardptr;
|
|
@@ -292,6 +297,7 @@ void *qemu_ram_mmap(int fd,
|
|
}
|
|
|
|
return ptr;
|
|
+#endif
|
|
}
|
|
|
|
void qemu_ram_munmap(int fd, void *ptr, size_t size)
|
|
diff --git c/util/oslib-posix.c w/util/oslib-posix.c
|
|
index 11b35e48fb..760c3d4f4c 100644
|
|
--- c/util/oslib-posix.c
|
|
+++ w/util/oslib-posix.c
|
|
@@ -58,6 +58,7 @@
|
|
#include <lwp.h>
|
|
#endif
|
|
|
|
+#include "qemu/memalign.h"
|
|
#include "qemu/mmap-alloc.h"
|
|
|
|
#define MAX_MEM_PREALLOC_THREAD_COUNT 16
|
|
@@ -192,14 +193,18 @@ fail_close:
|
|
void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared,
|
|
bool noreserve)
|
|
{
|
|
+ size_t align = QEMU_VMALLOC_ALIGN;
|
|
+#ifndef EMSCRIPTEN
|
|
const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) |
|
|
(noreserve ? QEMU_MAP_NORESERVE : 0);
|
|
- size_t align = QEMU_VMALLOC_ALIGN;
|
|
void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0);
|
|
|
|
if (ptr == MAP_FAILED) {
|
|
return NULL;
|
|
}
|
|
+#else
|
|
+ void *ptr = qemu_memalign(align, size);
|
|
+#endif
|
|
|
|
if (alignment) {
|
|
*alignment = align;
|
|
@@ -212,7 +217,11 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared,
|
|
void qemu_anon_ram_free(void *ptr, size_t size)
|
|
{
|
|
trace_qemu_anon_ram_free(ptr, size);
|
|
+#ifndef EMSCRIPTEN
|
|
qemu_ram_munmap(-1, ptr, size);
|
|
+#else
|
|
+ qemu_vfree(ptr);
|
|
+#endif
|
|
}
|
|
|
|
void qemu_socket_set_block(int fd)
|
|
@@ -573,7 +582,11 @@ bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads,
|
|
{
|
|
static gsize initialized;
|
|
int ret;
|
|
+#ifndef EMSCRIPTEN
|
|
size_t hpagesize = qemu_fd_getpagesize(fd);
|
|
+#else
|
|
+ size_t hpagesize = qemu_real_host_page_size();
|
|
+#endif
|
|
size_t numpages = DIV_ROUND_UP(sz, hpagesize);
|
|
bool use_madv_populate_write;
|
|
struct sigaction act;
|