From 03f0da9525dd6ca3241c7eb9d2650c9128c4d54a Mon Sep 17 00:00:00 2001 From: Awal Garg Date: Fri, 22 Jun 2018 18:30:25 +0530 Subject: [PATCH] use wg_ prefix for wasmgen, add tests to makefile --- .travis-run-codegen.sh | 2 +- Makefile | 14 + src/browser/starter.js | 110 ++--- src/native/codegen/codegen.c | 639 ++++++++++-------------------- src/native/codegen/codegen.h | 51 +-- src/native/codegen/cstring.h | 16 - src/native/codegen/module_init.h | 218 ---------- src/native/codegen/util.h | 100 ----- src/native/codegen/wasm_opcodes.h | 213 ---------- src/native/codegen/wasm_util.h | 135 ------- src/native/codegen/wasmgen.h | 72 ++++ src/native/cpu.c | 73 ++-- src/native/instructions.c | 22 +- src/native/profiler/opstats.c | 1 + src/wasmgen/src/c_api.rs | 10 +- src/wasmgen/src/codegen.rs | 152 ++----- src/wasmgen/src/wasm_opcodes.rs | 1 - src/wasmgen/src/wasm_util.rs | 163 ++++++-- 18 files changed, 579 insertions(+), 1413 deletions(-) delete mode 100644 src/native/codegen/cstring.h delete mode 100644 src/native/codegen/module_init.h delete mode 100644 src/native/codegen/util.h delete mode 100644 src/native/codegen/wasm_opcodes.h delete mode 100644 src/native/codegen/wasm_util.h create mode 100644 src/native/codegen/wasmgen.h diff --git a/.travis-run-codegen.sh b/.travis-run-codegen.sh index ffdffef2..6a17bdbe 100755 --- a/.travis-run-codegen.sh +++ b/.travis-run-codegen.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash set -e -make codegen-test +make wasmgen-test diff --git a/Makefile b/Makefile index b7b6d7a5..120247e4 100644 --- a/Makefile +++ b/Makefile @@ -223,6 +223,20 @@ build/v86-debug.wasm: src/native/*.c src/native/*.h src/native/codegen/*.c src/n -o build/v86-debug.wasm ls -lh build/v86-debug.wasm +build/wasmgen.wasm: src/wasmgen/src/*.rs src/wasmgen/Cargo.toml + mkdir -p build/ + -ls -lh build/wasmgen.wasm + (cd src/wasmgen && cargo +nightly rustc --release --target wasm32-unknown-unknown -- -Clink-args="--import-memory") + mv src/wasmgen/target/wasm32-unknown-unknown/release/wasmgen.wasm build/wasmgen.wasm + ls -lh build/wasmgen.wasm + +build/wasmgen-debug.wasm: src/wasmgen/src/*.rs src/wasmgen/Cargo.toml + mkdir -p build/ + -ls -lh build/wasmgen-debug.wasm + (cd src/wasmgen && cargo +nightly rustc --target wasm32-unknown-unknown -- -Clink-args="--import-memory") + mv src/wasmgen/target/wasm32-unknown-unknown/debug/wasmgen.wasm build/wasmgen-debug.wasm + ls -lh build/wasmgen-debug.wasm + build/codegen-test.wasm: src/native/*.c src/native/*.h src/native/codegen/*.c src/native/codegen/*.h mkdir -p build -ls -lh build/codegen-test.wasm diff --git a/src/browser/starter.js b/src/browser/starter.js index ecaf9731..229322fe 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -287,73 +287,77 @@ function V86Starter(options) } const wasmgen_exports = [ - // these are used by C as is - "gen_eqz_i32", - "gen_if_void", - "gen_else", - "gen_loop_void", - "gen_block_void", - "gen_block_end", - "gen_return", - "gen_brtable_and_cases", - "gen_br", - "gen_get_local", - "gen_set_local", - "gen_const_i32", - "gen_unreachable", - "gen_drop", - "gen_increment_mem32", - "gen_increment_variable", - - // these are wrapped around without the rs_ prefix by C - "rs_gen_fn0_const", - "rs_gen_fn0_const_ret", - "rs_gen_fn1_const", - "rs_gen_fn1_const_ret", - "rs_gen_fn2_const", - "rs_gen_fn3_const", - "rs_gen_call_fn1_ret", - "rs_gen_call_fn2", - "rs_get_fn_idx", - - // these are exported to C with the gen_ prefix attached via JS "new_buf", - "reset", "finish", - "get_op_ptr", - "get_op_len", + "reset", + "get_fn_idx", "include_buffer", - "push_i32", - "push_u32", - "load_aligned_u16", - "load_aligned_i32", - "store_aligned_u16", - "store_aligned_i32", - "add_i32", - "and_i32", - "or_i32", - "shl_i32", - "call_fn", - "call_fn_with_arg", + + "wg_push_i32", + "wg_push_u32", + "wg_load_aligned_u16", + "wg_load_aligned_i32", + "wg_store_aligned_u16", + "wg_store_aligned_i32", + "wg_add_i32", + "wg_and_i32", + "wg_or_i32", + "wg_shl_i32", + "wg_call_fn", + "wg_call_fn_with_arg", + "wg_eq_i32", + "wg_ne_i32", + "wg_le_i32", + "wg_lt_i32", + "wg_ge_i32", + "wg_gt_i32", + "wg_if_i32", + "wg_block_i32", + "wg_tee_local", + "wg_xor_i32", + "wg_load_unaligned_i32_from_stack", + "wg_load_aligned_i32_from_stack", + "wg_store_unaligned_i32", + "wg_shr_u32", + "wg_shr_i32", + "wg_eqz_i32", + "wg_if_void", + "wg_else", + "wg_loop_void", + "wg_block_void", + "wg_block_end", + "wg_return", + "wg_drop", + "wg_brtable_and_cases", + "wg_br", + "wg_get_local", + "wg_set_local", + "wg_unreachable", + "wg_increment_mem32", + "wg_increment_variable", + "wg_load_aligned_u16_from_stack", + "wg_fn0_const", + "wg_fn0_const_ret", + "wg_fn1_const", + "wg_fn1_const_ret", + "wg_fn2_const", + "wg_fn3_const", + "wg_call_fn1_ret", + "wg_call_fn2", ]; function reexport_wasmgen_functions(wasmgen) { for(const fn_name of wasmgen_exports) { - if(fn_name.startsWith("gen_")) + if(fn_name.startsWith("wg_")) { // used as is via C wasm_shared_funcs[`_${fn_name}`] = wasmgen.exports[fn_name]; } - else if(fn_name.startsWith("rs_")) - { - // wrapped around by C - wasm_shared_funcs[`_${fn_name}`] = wasmgen.exports[fn_name.replace("rs_", "")]; - } else { - // prefix "gen_" attached by JS - wasm_shared_funcs[`_gen_${fn_name}`] = wasmgen.exports[fn_name]; + // prefix "wg_" attached by JS + wasm_shared_funcs[`_wg_${fn_name}`] = wasmgen.exports[fn_name]; } } } diff --git a/src/native/codegen/codegen.c b/src/native/codegen/codegen.c index 7c17d436..e6a98086 100644 --- a/src/native/codegen/codegen.c +++ b/src/native/codegen/codegen.c @@ -5,145 +5,81 @@ #include "../const.h" #include "../cpu.h" #include "../global_pointers.h" +#include "wasmgen.h" #include "codegen.h" -#include "cstring.h" -#include "module_init.h" -#include "util.h" -#include "wasm_opcodes.h" -#include "wasm_util.h" - -static Buffer op = { .start = codegen_buffer_op, .ptr = codegen_buffer_op, .len = 0x100000 }; -static Buffer cs = { .start = codegen_buffer_cs, .ptr = codegen_buffer_cs, .len = 0x100000 }; -Buffer instruction_body = { - .start = codegen_buffer_instruction_body, - .ptr = codegen_buffer_instruction_body, - .len = 0x100000, -}; - -static uint8_t* op_ptr_reset_location; -static int32_t import_table_size_reset_value; -static int32_t import_table_count_reset_value; static void jit_add_seg_offset(int32_t default_segment); static void jit_resolve_modrm32_(int32_t modrm_byte); static void jit_resolve_modrm16_(int32_t modrm_byte); - -void gen_init(void) -{ - // wasm magic header - write_raw_u8(&op, 0); write_raw_u8(&op, 'a'); write_raw_u8(&op, 's'); write_raw_u8(&op, 'm'); - - // wasm version in leb128, 4 bytes - write_raw_u8(&op, WASM_VERSION); write_raw_u8(&op, 0); write_raw_u8(&op, 0); write_raw_u8(&op, 0); - - write_type_section(); - write_import_section_preamble(); - - // add initial imports - uint8_t _fn_get_seg_idx = write_import_entry("get_seg", 7, FN1_RET_TYPE_INDEX); - assert(_fn_get_seg_idx == fn_get_seg_idx); - UNUSED(_fn_get_seg_idx); - - // store state of current pointers etc. so we can reset them later - op_ptr_reset_location = op.ptr; - import_table_size_reset_value = import_table_size; - import_table_count_reset_value = import_table_count; -} +PackedStr pack_str(char const* fn_name, uint8_t fn_len); void gen_reset(void) { - op.ptr = op_ptr_reset_location; - cs.ptr = cs.start; - import_table_size = import_table_size_reset_value; - import_table_count = import_table_count_reset_value; + wg_reset(); + cs = wg_new_buf(); + instruction_body = wg_new_buf(); + add_get_seg_import(); } -uintptr_t gen_finish(int32_t no_of_locals_i32) +void add_get_seg_import(void) { - write_memory_import(); - write_function_section(1); - write_export_section(); - - uint8_t* ptr_code_section_size = (uint8_t*) 0; // initialized below - uint8_t* ptr_fn_body_size = (uint8_t*) 0; // this as well - - // write code section preamble - write_raw_u8(&op, SC_CODE); - - ptr_code_section_size = op.ptr; // we will write to this location later - write_raw_u8(&op, 0); write_raw_u8(&op, 0); // write temp val for now using 4 bytes - write_raw_u8(&op, 0); write_raw_u8(&op, 0); - - write_raw_u8(&op, 1); // number of function bodies: just 1 - - // same as above but for body size of the function - ptr_fn_body_size = op.ptr; - write_raw_u8(&op, 0); write_raw_u8(&op, 0); - write_raw_u8(&op, 0); write_raw_u8(&op, 0); - - write_raw_u8(&op, 1); // count of local blocks - write_raw_u8(&op, no_of_locals_i32); write_raw_u8(&op, TYPE_I32); // locals of type i32 - - copy_code_section(); - - // write code section epilogue - write_raw_u8(&op, OP_END); - - // write the actual sizes to the pointer locations stored above. We subtract 4 from the actual - // value because the ptr itself points to four bytes - write_fixed_leb32_to_ptr(ptr_fn_body_size, op.ptr - ptr_fn_body_size - 4); - write_fixed_leb32_to_ptr(ptr_code_section_size, op.ptr - ptr_code_section_size - 4); - - return (uintptr_t) op.ptr; + uint16_t _fn_get_seg_idx = get_fn_idx("get_seg", 7, FN1_RET_TYPE_INDEX); + assert(_fn_get_seg_idx == fn_get_seg_idx); + UNUSED(_fn_get_seg_idx); } -uintptr_t gen_get_final_offset(void) +PackedStr pack_str(char const* fn_name, uint8_t fn_len) { - return (uintptr_t) op.ptr; + assert(fn_len <= 16); + + union { + PackedStr pstr; + uint8_t u8s[16]; + } ret = { { 0 } }; + + for(int i = 0; i < fn_len; i++) + { + ret.u8s[i] = fn_name[i]; + } + return ret.pstr; +} + +uint16_t get_fn_idx(char const* fn, uint8_t fn_len, uint8_t fn_type) +{ + PackedStr pstr = pack_str(fn, fn_len); + return wg_get_fn_idx(pstr.a, pstr.b, fn_type); } void gen_increment_mem32(int32_t addr) { - push_i32(&cs, addr); - - load_aligned_i32(&cs, addr); - push_i32(&cs, 1); - add_i32(&cs); - - store_aligned_i32(&cs); + wg_increment_mem32(cs, addr); } void gen_increment_variable(int32_t variable_address, int32_t n) { - push_i32(&cs, variable_address); - - load_aligned_i32(&cs, variable_address); - push_i32(&cs, n); - add_i32(&cs); - - store_aligned_i32(&cs); + wg_increment_variable(cs, variable_address, n); } void gen_increment_instruction_pointer(int32_t n) { - push_i32(&cs, (int32_t)instruction_pointer); // store address of ip + wg_push_i32(cs, (int32_t)instruction_pointer); // store address of ip - load_aligned_i32(&cs, (int32_t)instruction_pointer); // load ip + wg_load_aligned_i32(cs, (int32_t)instruction_pointer); // load ip - push_i32(&cs, n); + wg_push_i32(cs, n); - add_i32(&cs); - store_aligned_i32(&cs); // store it back in + wg_add_i32(cs); + wg_store_aligned_i32(cs); // store it back in } void gen_relative_jump(int32_t n) { // add n to instruction_pointer (without setting the offset as above) - push_i32(&instruction_body, (int32_t)instruction_pointer); - load_aligned_i32(&instruction_body, (int32_t)instruction_pointer); - push_i32(&instruction_body, n); - add_i32(&instruction_body); - store_aligned_i32(&instruction_body); + wg_push_i32(instruction_body, (int32_t)instruction_pointer); + wg_load_aligned_i32(instruction_body, (int32_t)instruction_pointer); + wg_push_i32(instruction_body, n); + wg_add_i32(instruction_body); + wg_store_aligned_i32(instruction_body); } void gen_increment_timestamp_counter(uint32_t n) @@ -153,156 +89,156 @@ void gen_increment_timestamp_counter(uint32_t n) void gen_set_previous_eip_offset_from_eip(int32_t n) { - push_i32(&cs, (int32_t)previous_ip); // store address of previous ip - load_aligned_i32(&cs, (int32_t)instruction_pointer); // load ip + wg_push_i32(cs, (int32_t)previous_ip); // store address of previous ip + wg_load_aligned_i32(cs, (int32_t)instruction_pointer); // load ip if(n != 0) { - push_i32(&cs, n); - add_i32(&cs); // add constant to ip value + wg_push_i32(cs, n); + wg_add_i32(cs); // add constant to ip value } - store_aligned_i32(&cs); // store it as previous ip + wg_store_aligned_i32(cs); // store it as previous ip } void gen_set_previous_eip(void) { - push_i32(&cs, (int32_t)previous_ip); // store address of previous ip - load_aligned_i32(&cs, (int32_t)instruction_pointer); // load ip - store_aligned_i32(&cs); // store it as previous ip + wg_push_i32(cs, (int32_t)previous_ip); // store address of previous ip + wg_load_aligned_i32(cs, (int32_t)instruction_pointer); // load ip + wg_store_aligned_i32(cs); // store it as previous ip } void gen_clear_prefixes(void) { - push_i32(&instruction_body, (int32_t)prefixes); // load address of prefixes - push_i32(&instruction_body, 0); - store_aligned_i32(&instruction_body); + wg_push_i32(instruction_body, (int32_t)prefixes); // load address of prefixes + wg_push_i32(instruction_body, 0); + wg_store_aligned_i32(instruction_body); } void gen_add_prefix_bits(int32_t mask) { assert(mask >= 0 && mask < 0x100); - push_i32(&instruction_body, (int32_t)prefixes); // load address of prefixes + wg_push_i32(instruction_body, (int32_t)prefixes); // load address of prefixes - load_aligned_i32(&instruction_body, (int32_t)prefixes); // load old value - push_i32(&instruction_body, mask); - or_i32(&instruction_body); + wg_load_aligned_i32(instruction_body, (int32_t)prefixes); // load old value + wg_push_i32(instruction_body, mask); + wg_or_i32(instruction_body); - store_aligned_i32(&instruction_body); + wg_store_aligned_i32(instruction_body); } void gen_fn0_const_ret(char const* fn, uint8_t fn_len) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN0_RET_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN0_RET_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_fn0_const(char const* fn, uint8_t fn_len) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN0_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN0_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_set_reg16_fn0(char const* fn, uint8_t fn_len, int32_t reg) { // generates: reg16[reg] = fn() - int32_t fn_idx = get_fn_index(fn, fn_len, FN0_RET_TYPE_INDEX); - push_i32(&instruction_body, (int32_t) ®16[reg]); - call_fn(&instruction_body, fn_idx); - store_aligned_u16(&instruction_body); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN0_RET_TYPE_INDEX); + wg_push_i32(instruction_body, (int32_t) ®16[reg]); + wg_call_fn(instruction_body, fn_idx); + wg_store_aligned_u16(instruction_body); } void gen_set_reg32s_fn0(char const* fn, uint8_t fn_len, int32_t reg) { // generates: reg32s[reg] = fn() - int32_t fn_idx = get_fn_index(fn, fn_len, FN0_RET_TYPE_INDEX); - push_i32(&instruction_body, (int32_t) ®32s[reg]); - call_fn(&instruction_body, fn_idx); - store_aligned_i32(&instruction_body); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN0_RET_TYPE_INDEX); + wg_push_i32(instruction_body, (int32_t) ®32s[reg]); + wg_call_fn(instruction_body, fn_idx); + wg_store_aligned_i32(instruction_body); } void gen_fn1_const_ret(char const* fn, uint8_t fn_len, int32_t arg0) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_RET_TYPE_INDEX); - push_i32(&instruction_body, arg0); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_RET_TYPE_INDEX); + wg_push_i32(instruction_body, arg0); + wg_call_fn(instruction_body, fn_idx); } void gen_call_fn1_ret(char const* fn, uint8_t fn_len) { // generates: fn( _ ) where _ must be left on the stack before calling this, and fn returns a value - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_RET_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_RET_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_call_fn1(char const* fn, uint8_t fn_len) { // generates: fn( _ ) where _ must be left on the stack before calling this - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_fn1_const(char const* fn, uint8_t fn_len, int32_t arg0) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX); - push_i32(&instruction_body, arg0); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_TYPE_INDEX); + wg_push_i32(instruction_body, arg0); + wg_call_fn(instruction_body, fn_idx); } void gen_set_reg16_r(int32_t r_dest, int32_t r_src) { // generates: reg16[r_dest] = reg16[r_src] - push_i32(&instruction_body, (int32_t) ®16[r_dest]); - load_aligned_u16(&instruction_body, (int32_t) ®16[r_src]); - store_aligned_u16(&instruction_body); + wg_push_i32(instruction_body, (int32_t) ®16[r_dest]); + wg_load_aligned_u16(instruction_body, (int32_t) ®16[r_src]); + wg_store_aligned_u16(instruction_body); } void gen_set_reg32_r(int32_t r_dest, int32_t r_src) { // generates: reg32s[r_dest] = reg32s[r_src] - push_i32(&instruction_body, (int32_t) ®32s[r_dest]); - load_aligned_i32(&instruction_body, (int32_t) ®32s[r_src]); - store_aligned_i32(&instruction_body); + wg_push_i32(instruction_body, (int32_t) ®32s[r_dest]); + wg_load_aligned_i32(instruction_body, (int32_t) ®32s[r_src]); + wg_store_aligned_i32(instruction_body); } void gen_fn1_reg16(char const* fn, uint8_t fn_len, int32_t reg) { // generates: fn(reg16[reg]) - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX); - load_aligned_u16(&instruction_body, (int32_t) ®16[reg]); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_TYPE_INDEX); + wg_load_aligned_u16(instruction_body, (int32_t) ®16[reg]); + wg_call_fn(instruction_body, fn_idx); } void gen_fn1_reg32s(char const* fn, uint8_t fn_len, int32_t reg) { // generates: fn(reg32s[reg]) - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX); - load_aligned_i32(&instruction_body, (int32_t) ®32s[reg]); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_TYPE_INDEX); + wg_load_aligned_i32(instruction_body, (int32_t) ®32s[reg]); + wg_call_fn(instruction_body, fn_idx); } void gen_call_fn2(char const* fn, uint8_t fn_len) { // generates: fn( _, _ ) where _ must be left on the stack before calling this - int32_t fn_idx = get_fn_index(fn, fn_len, FN2_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN2_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_fn2_const(char const* fn, uint8_t fn_len, int32_t arg0, int32_t arg1) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN2_TYPE_INDEX); - push_i32(&instruction_body, arg0); - push_i32(&instruction_body, arg1); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN2_TYPE_INDEX); + wg_push_i32(instruction_body, arg0); + wg_push_i32(instruction_body, arg1); + wg_call_fn(instruction_body, fn_idx); } void gen_fn3_const(char const* fn, uint8_t fn_len, int32_t arg0, int32_t arg1, int32_t arg2) { - int32_t fn_idx = get_fn_index(fn, fn_len, FN3_TYPE_INDEX); - push_i32(&instruction_body, arg0); - push_i32(&instruction_body, arg1); - push_i32(&instruction_body, arg2); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN3_TYPE_INDEX); + wg_push_i32(instruction_body, arg0); + wg_push_i32(instruction_body, arg1); + wg_push_i32(instruction_body, arg2); + wg_call_fn(instruction_body, fn_idx); } void gen_safe_read32(void) @@ -311,51 +247,51 @@ void gen_safe_read32(void) // inline, bailing to safe_read32s_slow if necessary const int32_t address_local = GEN_LOCAL_SCRATCH0; - gen_tee_local(address_local); + wg_tee_local(instruction_body, address_local); // Pseudo: base_on_stack = (uint32_t)address >> 12; - gen_const_i32(12); - shr_u32(&instruction_body); + wg_push_i32(instruction_body, 12); + wg_shr_u32(instruction_body); SCALE_INDEX_FOR_ARRAY32(tlb_data); // Pseudo: entry = tlb_data[base_on_stack]; const int32_t entry_local = GEN_LOCAL_SCRATCH1; - load_aligned_i32_from_stack(&instruction_body, (uint32_t) tlb_data); - gen_tee_local(entry_local); + wg_load_aligned_i32_from_stack(instruction_body, (uint32_t) tlb_data); + wg_tee_local(instruction_body, entry_local); // Pseudo: bool can_use_fast_path = (entry & 0xFFF & ~TLB_READONLY & ~TLB_GLOBAL & ~(cpl == 3 ? 0 : TLB_NO_USER) == TLB_VALID && // (address & 0xFFF) <= (0x1000 - 4)); - gen_const_i32(0xFFF & ~TLB_READONLY & ~TLB_GLOBAL & ~(*cpl == 3 ? 0 : TLB_NO_USER)); - and_i32(&instruction_body); + wg_push_i32(instruction_body, 0xFFF & ~TLB_READONLY & ~TLB_GLOBAL & ~(*cpl == 3 ? 0 : TLB_NO_USER)); + wg_and_i32(instruction_body); - gen_const_i32(TLB_VALID); - gen_eq_i32(); + wg_push_i32(instruction_body, TLB_VALID); + wg_eq_i32(instruction_body); - gen_get_local(address_local); - gen_const_i32(0xFFF); - and_i32(&instruction_body); - gen_const_i32(0x1000 - 4); - gen_le_i32(); + wg_get_local(instruction_body, address_local); + wg_push_i32(instruction_body, 0xFFF); + wg_and_i32(instruction_body); + wg_push_i32(instruction_body, 0x1000 - 4); + wg_le_i32(instruction_body); - and_i32(&instruction_body); + wg_and_i32(instruction_body); // Pseudo: // if(can_use_fast_path) leave_on_stack(mem8[entry & ~0xFFF ^ address]); - gen_if_i32(); - gen_get_local(entry_local); - gen_const_i32(~0xFFF); - and_i32(&instruction_body); - gen_get_local(address_local); - xor_i32(&instruction_body); + wg_if_i32(instruction_body); + wg_get_local(instruction_body, entry_local); + wg_push_i32(instruction_body, ~0xFFF); + wg_and_i32(instruction_body); + wg_get_local(instruction_body, address_local); + wg_xor_i32(instruction_body); - load_unaligned_i32_from_stack(&instruction_body, (uint32_t) mem8); + wg_load_unaligned_i32_from_stack(instruction_body, (uint32_t) mem8); // Pseudo: // else { leave_on_stack(safe_read32s_slow(address)); } - gen_else(); - gen_get_local(address_local); + wg_else(instruction_body); + wg_get_local(instruction_body, address_local); gen_call_fn1_ret("safe_read32s_slow", 17); - gen_block_end(); + wg_block_end(instruction_body); } void gen_safe_write32(int32_t local_for_address, int32_t local_for_value) @@ -374,47 +310,47 @@ void gen_safe_write32(int32_t local_for_address, int32_t local_for_value) assert(local_for_address == GEN_LOCAL_SCRATCH0); assert(local_for_value == GEN_LOCAL_SCRATCH1); - gen_get_local(local_for_address); + wg_get_local(instruction_body, local_for_address); // Pseudo: base_on_stack = (uint32_t)address >> 12; - gen_const_i32(12); - shr_u32(&instruction_body); + wg_push_i32(instruction_body, 12); + wg_shr_u32(instruction_body); SCALE_INDEX_FOR_ARRAY32(tlb_data); // entry_local is only used in the following block, so the scratch variable can be reused later { // Pseudo: entry = tlb_data[base_on_stack]; const int32_t entry_local = GEN_LOCAL_SCRATCH2; - load_aligned_i32_from_stack(&instruction_body, (uint32_t) tlb_data); - gen_tee_local(entry_local); + wg_load_aligned_i32_from_stack(instruction_body, (uint32_t) tlb_data); + wg_tee_local(instruction_body, entry_local); // Pseudo: bool can_use_fast_path = (entry & 0xFFF & ~TLB_GLOBAL & ~(cpl == 3 ? 0 : TLB_NO_USER) == TLB_VALID && // (address & 0xFFF) <= (0x1000 - 4)); - gen_const_i32(0xFFF & ~TLB_GLOBAL & ~(*cpl == 3 ? 0 : TLB_NO_USER)); - and_i32(&instruction_body); + wg_push_i32(instruction_body, 0xFFF & ~TLB_GLOBAL & ~(*cpl == 3 ? 0 : TLB_NO_USER)); + wg_and_i32(instruction_body); - gen_const_i32(TLB_VALID); - gen_eq_i32(); + wg_push_i32(instruction_body, TLB_VALID); + wg_eq_i32(instruction_body); - gen_get_local(local_for_address); - gen_const_i32(0xFFF); - and_i32(&instruction_body); - gen_const_i32(0x1000 - 4); - gen_le_i32(); + wg_get_local(instruction_body, local_for_address); + wg_push_i32(instruction_body, 0xFFF); + wg_and_i32(instruction_body); + wg_push_i32(instruction_body, 0x1000 - 4); + wg_le_i32(instruction_body); - and_i32(&instruction_body); + wg_and_i32(instruction_body); // Pseudo: // if(can_use_fast_path) // { // phys_addr = entry & ~0xFFF ^ address; - gen_if_void(); + wg_if_void(instruction_body); - gen_get_local(entry_local); - gen_const_i32(~0xFFF); - and_i32(&instruction_body); - gen_get_local(local_for_address); - xor_i32(&instruction_body); + wg_get_local(instruction_body, entry_local); + wg_push_i32(instruction_body, ~0xFFF); + wg_and_i32(instruction_body); + wg_get_local(instruction_body, local_for_address); + wg_xor_i32(instruction_body); } // entry_local isn't needed anymore, so we overwrite it @@ -423,9 +359,9 @@ void gen_safe_write32(int32_t local_for_address, int32_t local_for_value) // /* continued within can_use_fast_path branch */ // mem8[phys_addr] = value; - gen_tee_local(phys_addr_local); - gen_get_local(local_for_value); - store_unaligned_i32_with_offset(&instruction_body, (uint32_t) mem8); + wg_tee_local(instruction_body, phys_addr_local); + wg_get_local(instruction_body, local_for_value); + wg_store_unaligned_i32(instruction_body, (uint32_t) mem8); // Only call jit_dirty_cache_single if absolutely necessary // Pseudo: @@ -437,184 +373,40 @@ void gen_safe_write32(int32_t local_for_address, int32_t local_for_value) // } // } - gen_get_local(phys_addr_local); - gen_const_i32(12); - shr_u32(&instruction_body); + wg_get_local(instruction_body, phys_addr_local); + wg_push_i32(instruction_body, 12); + wg_shr_u32(instruction_body); SCALE_INDEX_FOR_ARRAY32(page_first_jit_cache_entry); - load_aligned_i32_from_stack(&instruction_body, (uint32_t) page_first_jit_cache_entry); + wg_load_aligned_i32_from_stack(instruction_body, (uint32_t) page_first_jit_cache_entry); - gen_const_i32(JIT_CACHE_ARRAY_NO_NEXT_ENTRY); - gen_ne_i32(); + wg_push_i32(instruction_body, JIT_CACHE_ARRAY_NO_NEXT_ENTRY); + wg_ne_i32(instruction_body); - gen_get_local(phys_addr_local); - gen_const_i32(12); - shr_u32(&instruction_body); - gen_const_i32(1); - shl_i32(&instruction_body); - load_aligned_u16_from_stack(&instruction_body, (uint32_t) page_entry_points); + wg_get_local(instruction_body, phys_addr_local); + wg_push_i32(instruction_body, 12); + wg_shr_u32(instruction_body); + wg_push_i32(instruction_body, 1); + wg_shl_i32(instruction_body); + wg_load_aligned_u16_from_stack(instruction_body, (uint32_t) page_entry_points); - gen_const_i32(ENTRY_POINT_END); - gen_ne_i32(); + wg_push_i32(instruction_body, ENTRY_POINT_END); + wg_ne_i32(instruction_body); - or_i32(&instruction_body); + wg_or_i32(instruction_body); - gen_if_void(); - gen_get_local(phys_addr_local); + wg_if_void(instruction_body); + wg_get_local(instruction_body, phys_addr_local); gen_call_fn1("jit_dirty_cache_single", 22); - gen_block_end(); + wg_block_end(instruction_body); // Pseudo: // else { safe_read32_slow(address, value); } - gen_else(); - gen_get_local(local_for_address); - gen_get_local(local_for_value); + wg_else(instruction_body); + wg_get_local(instruction_body, local_for_address); + wg_get_local(instruction_body, local_for_value); gen_call_fn2("safe_write32_slow", 17); - gen_block_end(); -} - -void gen_add_i32(void) -{ - add_i32(&instruction_body); -} - -void gen_eqz_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32EQZ); -} - -void gen_eq_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32EQ); -} - -void gen_ne_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32NE); -} - -void gen_le_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32LES); -} - -void gen_lt_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32LTS); -} - -void gen_ge_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32GES); -} - -void gen_gt_i32(void) -{ - write_raw_u8(&instruction_body, OP_I32GTS); -} - -void gen_if_void(void) -{ - write_raw_u8(&instruction_body, OP_IF); - write_raw_u8(&instruction_body, TYPE_VOID_BLOCK); -} - -void gen_if_i32(void) -{ - write_raw_u8(&instruction_body, OP_IF); - write_raw_u8(&instruction_body, TYPE_I32); -} - -void gen_else(void) -{ - write_raw_u8(&instruction_body, OP_ELSE); -} - -void gen_loop_void(void) -{ - write_raw_u8(&instruction_body, OP_LOOP); - write_raw_u8(&instruction_body, TYPE_VOID_BLOCK); -} - -void gen_block_void(void) -{ - write_raw_u8(&instruction_body, OP_BLOCK); - write_raw_u8(&instruction_body, TYPE_VOID_BLOCK); -} - -void gen_block_i32(void) -{ - write_raw_u8(&instruction_body, OP_BLOCK); - write_raw_u8(&instruction_body, TYPE_I32); -} - -void gen_block_end(void) -{ - write_raw_u8(&instruction_body, OP_END); -} - -void gen_return(void) -{ - write_raw_u8(&instruction_body, OP_RETURN); -} - -// Generate a br_table where an input of [i] will branch [i]th outer block, -// where [i] is passed on the wasm stack -void gen_brtable_and_cases(int32_t cases_count) -{ - assert(cases_count >= 0); - - write_raw_u8(&instruction_body, OP_BRTABLE); - write_leb_u32(&instruction_body, cases_count); - - for(int32_t i = 0; i < cases_count + 1; i++) - { - write_leb_u32(&instruction_body, i); - } -} - -void gen_br(int32_t depth) -{ - write_raw_u8(&instruction_body, OP_BR); - write_leb_i32(&instruction_body, depth); -} - -void gen_get_local(int32_t idx) -{ - write_raw_u8(&instruction_body, OP_GETLOCAL); - write_leb_i32(&instruction_body, idx); -} - -void gen_set_local(int32_t idx) -{ - write_raw_u8(&instruction_body, OP_SETLOCAL); - write_leb_i32(&instruction_body, idx); -} - -void gen_tee_local(int32_t idx) -{ - write_raw_u8(&instruction_body, OP_TEELOCAL); - write_leb_i32(&instruction_body, idx); -} - -void gen_const_i32(int32_t v) -{ - push_i32(&instruction_body, v); -} - -void gen_unreachable(void) -{ - write_raw_u8(&instruction_body, OP_UNREACHABLE); -} - -void gen_load_aligned_i32_from_stack(uint32_t byte_offset) -{ - load_aligned_i32_from_stack(&instruction_body, byte_offset); -} - -void gen_store_aligned_i32(void) -{ - store_aligned_i32(&instruction_body); + wg_block_end(instruction_body); } #define MODRM_ENTRY(n, work)\ @@ -641,18 +433,18 @@ void gen_store_aligned_i32(void) static void inline gen_modrm_entry_0(int32_t segment, int32_t reg16_idx_1, int32_t reg16_idx_2, int32_t imm) { // generates: fn( ( reg1 + reg2 + imm ) & 0xFFFF ) - load_aligned_u16(&instruction_body, reg16_idx_1); - load_aligned_u16(&instruction_body, reg16_idx_2); - add_i32(&instruction_body); + wg_load_aligned_u16(instruction_body, reg16_idx_1); + wg_load_aligned_u16(instruction_body, reg16_idx_2); + wg_add_i32(instruction_body); if(imm) { - push_i32(&instruction_body, imm); - add_i32(&instruction_body); + wg_push_i32(instruction_body, imm); + wg_add_i32(instruction_body); } - push_i32(&instruction_body, 0xFFFF); - and_i32(&instruction_body); + wg_push_i32(instruction_body, 0xFFFF); + wg_and_i32(instruction_body); jit_add_seg_offset(segment); } @@ -660,16 +452,16 @@ static void inline gen_modrm_entry_0(int32_t segment, int32_t reg16_idx_1, int32 static void gen_modrm_entry_1(int32_t segment, int32_t reg16_idx, int32_t imm) { // generates: fn ( ( reg + imm ) & 0xFFFF ) - load_aligned_u16(&instruction_body, reg16_idx); + wg_load_aligned_u16(instruction_body, reg16_idx); if(imm) { - push_i32(&instruction_body, imm); - add_i32(&instruction_body); + wg_push_i32(instruction_body, imm); + wg_add_i32(instruction_body); } - push_i32(&instruction_body, 0xFFFF); - and_i32(&instruction_body); + wg_push_i32(instruction_body, 0xFFFF); + wg_and_i32(instruction_body); jit_add_seg_offset(segment); } @@ -693,14 +485,14 @@ static void jit_add_seg_offset(int32_t default_segment) return; } - push_i32(&instruction_body, seg); - call_fn(&instruction_body, fn_get_seg_idx); - add_i32(&instruction_body); + wg_push_i32(instruction_body, seg); + wg_call_fn(instruction_body, fn_get_seg_idx); + wg_add_i32(instruction_body); } static void gen_modrm_entry_2() { - push_i32(&instruction_body, read_imm16()); + wg_push_i32(instruction_body, read_imm16()); jit_add_seg_offset(DS); } @@ -737,12 +529,12 @@ static void jit_resolve_modrm16_(int32_t modrm_byte) static void gen_modrm32_entry(int32_t segment, int32_t reg32s_idx, int32_t imm) { // generates: fn ( reg + imm ) - load_aligned_i32(&instruction_body, reg32s_idx); + wg_load_aligned_i32(instruction_body, reg32s_idx); if(imm) { - push_i32(&instruction_body, imm); - add_i32(&instruction_body); + wg_push_i32(instruction_body, imm); + wg_add_i32(instruction_body); } jit_add_seg_offset(segment); @@ -788,11 +580,11 @@ static void jit_resolve_sib(bool mod) // Where base is accessed from memory if base_is_mem_access or written as a constant otherwise if(base_is_mem_access) { - load_aligned_i32(&instruction_body, base_addr); + wg_load_aligned_i32(instruction_body, base_addr); } else { - push_i32(&instruction_body, base); + wg_push_i32(instruction_body, base); } jit_add_seg_offset(seg); @@ -809,13 +601,11 @@ static void jit_resolve_sib(bool mod) uint8_t s = sib_byte >> 6 & 3; - load_aligned_i32(&instruction_body, (int32_t)(reg32s + m)); - // We don't use push_u32 here either since s will fit in 1 byte - write_raw_u8(&instruction_body, OP_I32CONST); - write_raw_u8(&instruction_body, s); - shl_i32(&instruction_body); + wg_load_aligned_i32(instruction_body, (int32_t)(reg32s + m)); + wg_push_i32(instruction_body, s); + wg_shl_i32(instruction_body); - add_i32(&instruction_body); + wg_add_i32(instruction_body); } static void modrm32_special_case_1(void) @@ -826,8 +616,8 @@ static void modrm32_special_case_1(void) if(imm) { - push_i32(&instruction_body, imm); - add_i32(&instruction_body); + wg_push_i32(instruction_body, imm); + wg_add_i32(instruction_body); } } @@ -839,8 +629,8 @@ static void modrm32_special_case_2(void) if(imm) { - push_i32(&instruction_body, imm); - add_i32(&instruction_body); + wg_push_i32(instruction_body, imm); + wg_add_i32(instruction_body); } } @@ -848,7 +638,7 @@ static void gen_modrm32_entry_1() { int32_t imm = read_imm32s(); - push_i32(&instruction_body, imm); + wg_push_i32(instruction_body, imm); jit_add_seg_offset(DS); } @@ -897,33 +687,34 @@ void gen_modrm_fn2(char const* fn, uint8_t fn_len, int32_t arg0, int32_t arg1) { // generates: fn( _, arg0, arg1 ) - push_i32(&instruction_body, arg0); - push_i32(&instruction_body, arg1); + wg_push_i32(instruction_body, arg0); + wg_push_i32(instruction_body, arg1); - int32_t fn_idx = get_fn_index(fn, fn_len, FN3_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN3_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_modrm_fn1(char const* fn, uint8_t fn_len, int32_t arg0) { // generates: fn( _, arg0 ) - push_i32(&instruction_body, arg0); + wg_push_i32(instruction_body, arg0); - int32_t fn_idx = get_fn_index(fn, fn_len, FN2_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN2_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_modrm_fn0(char const* fn, uint8_t fn_len) { // generates: fn( _ ) - int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX); - call_fn(&instruction_body, fn_idx); + int32_t fn_idx = get_fn_idx(fn, fn_len, FN1_TYPE_INDEX); + wg_call_fn(instruction_body, fn_idx); } void gen_commit_instruction_body_to_cs(void) { - append_buffer(&cs, &instruction_body); - instruction_body.ptr = instruction_body.start; + wg_include_buffer(cs); + wg_include_buffer(instruction_body); } + diff --git a/src/native/codegen/codegen.h b/src/native/codegen/codegen.h index 1bb0a948..19ef9256 100644 --- a/src/native/codegen/codegen.h +++ b/src/native/codegen/codegen.h @@ -2,7 +2,7 @@ #include -#include "util.h" +#include "wasmgen.h" #define FN0_TYPE_INDEX 0 #define FN1_TYPE_INDEX 1 @@ -24,19 +24,19 @@ "codegen: Elements assumed to be 4 bytes." \ ); \ /* Shift the index to make it byte-indexed, not array-indexed */ \ - gen_const_i32(2); \ - shl_i32(&instruction_body); + wg_push_i32(instruction_body, 2); \ + wg_shl_i32(instruction_body); -extern Buffer instruction_body; +uint8_t* cs; +uint8_t* instruction_body; -static uint8_t const fn_get_seg_idx = 0; +static uint16_t const fn_get_seg_idx = 0; -void gen_init(void); void gen_reset(void); -uintptr_t gen_finish(int32_t no_of_locals_i32); -uintptr_t gen_get_final_offset(void); +// uintptr_t gen_finish(int32_t no_of_locals_i32); +void add_get_seg_import(void); -int32_t get_fn_index(char const* fn, uint8_t fn_len, uint8_t type_index); +uint16_t get_fn_idx(char const* fn, uint8_t fn_len, uint8_t fn_type); // Generate function call with constant arguments void gen_fn0_const(char const* fn, uint8_t fn_len); @@ -68,39 +68,6 @@ void gen_call_fn2(char const* fn, uint8_t fn_len); void gen_safe_read32(void); void gen_safe_write32(int32_t local_for_address, int32_t local_for_value); -void gen_add_i32(void); -void gen_eqz_i32(void); -void gen_eq_i32(void); -void gen_ne_i32(void); -void gen_le_i32(void); -void gen_lt_i32(void); -void gen_ge_i32(void); -void gen_gt_i32(void); - -void gen_if_void(void); -void gen_if_i32(void); -void gen_else(void); -void gen_loop_void(void); -void gen_block_void(void); -void gen_block_i32(void); -void gen_block_end(void); -void gen_return(void); - -void gen_brtable_and_cases(int32_t); - -void gen_br(int32_t depth); - -void gen_const_i32(int32_t); - -void gen_get_local(int32_t); -void gen_set_local(int32_t); -void gen_tee_local(int32_t); - -void gen_unreachable(void); - -void gen_load_aligned_i32_from_stack(uint32_t byte_offset); -void gen_store_aligned_i32(void); - void gen_modrm_resolve(int32_t modrm_byte); void gen_modrm_fn0(char const* fn, uint8_t fn_len); void gen_modrm_fn1(char const* fn, uint8_t fn_len, int32_t arg0); diff --git a/src/native/codegen/cstring.h b/src/native/codegen/cstring.h deleted file mode 100644 index f0cdf0e0..00000000 --- a/src/native/codegen/cstring.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -// everything here is copied from musl - -#include -#include - -// from strncmp.c - -static int strncmp(const char *_l, const char *_r, size_t n) -{ - const unsigned char *l=(void *)_l, *r=(void *)_r; - if (!n--) return 0; - for (; *l && *r && n && *l == *r ; l++, r++, n--); - return *l - *r; -} diff --git a/src/native/codegen/module_init.h b/src/native/codegen/module_init.h deleted file mode 100644 index f4b5ab0c..00000000 --- a/src/native/codegen/module_init.h +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once - -#include - -#include "codegen.h" -#include "cstring.h" -#include "util.h" -#include "wasm_opcodes.h" - -static Buffer op; -static Buffer cs; - -static void write_type_section() -{ - write_raw_u8(&op, SC_TYPE); - - uint8_t* ptr_section_size = op.ptr; - write_raw_u8(&op, 0); - - write_raw_u8(&op, NR_FN_TYPE_INDEXES); // number of type descriptors - - // FN0 - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 0); // no args - write_raw_u8(&op, 0); // no return val - - // FN1 - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 1); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, 0); - - // FN2 - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 2); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, 0); - - // FN3 - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 3); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, 0); - - // FN0_RET - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 0); - write_raw_u8(&op, 1); - write_raw_u8(&op, TYPE_I32); - - // FN1_RET - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 1); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, 1); - write_raw_u8(&op, TYPE_I32); - - // FN2_RET - write_raw_u8(&op, TYPE_FUNC); - write_raw_u8(&op, 2); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, TYPE_I32); - write_raw_u8(&op, 1); - write_raw_u8(&op, TYPE_I32); - - *ptr_section_size = (op.ptr - 1) - ptr_section_size; -} - -// Import table - -static uint8_t* ptr_import_count = (uint8_t*) 0; -static uint8_t* ptr_import_entries = (uint8_t*) 0; -static uint8_t* ptr_import_table_size = (uint8_t*) 0; - -// The import table size is written in leb encoding, which we can't read by simple dereferencing so -// we store the actual value separately. This is needed since we reserve two bytes for the import -// table size as it can exceed 127 -// Default value is two as the section starts with containing two bytes for the import count -static int32_t import_table_size = 2; - -static int32_t import_table_count = 0; - -// Goes over the import block to find index of an import entry by function name -// Returns -1 if not found -static int32_t get_import_index(char const* fn, uint8_t fn_len) -{ - uint8_t* offset = ptr_import_entries; - for(int32_t i = 0; i < import_table_count; i++) - { - offset += 1; // skip length of module name - offset += 1; // skip module name itself - uint8_t len = *offset++; - char* name = (char*) offset; - if (len == fn_len && strncmp(name, fn, fn_len) == 0) - { - return i; - } - offset += len; // skip the string - offset += 1; // skip import kind - offset += 1; // skip type index - } - return -1; -} - -static void set_import_table_size(int32_t size) -{ - assert(size < 0x4000); - import_table_size = size; - write_fixed_leb16_to_ptr(ptr_import_table_size, size); -} - -static void set_import_table_count(int32_t count) -{ - assert(count < 0x4000); - import_table_count = count; - write_fixed_leb16_to_ptr(ptr_import_count, import_table_count); -} - -static void write_import_section_preamble() -{ - write_raw_u8(&op, SC_IMPORT); - - ptr_import_table_size = op.ptr; // store current pointer location to write into later on - write_raw_u8(&op, 1 | 0b10000000); write_raw_u8(&op, 0); // 1 in 2 byte leb - - // same as above but for count of entries - ptr_import_count = op.ptr; - write_raw_u8(&op, 0); - write_raw_u8(&op, 0); - - // here after starts the actual list of imports - ptr_import_entries = op.ptr; -} - -static void write_memory_import() -{ - write_raw_u8(&op, 1); - write_raw_u8(&op, 'e'); - write_raw_u8(&op, 1); - write_raw_u8(&op, 'm'); - - write_raw_u8(&op, EXT_MEMORY); - - write_raw_u8(&op, 0); // memory flag, 0 for no maximum memory limit present - write_leb_u32(&op, 256); // initial memory length of 256 pages, takes 2 bytes in leb128 - - set_import_table_count(import_table_count + 1); - set_import_table_size(import_table_size + 1 + 1 + 1 + 1 + 1 + 1 + 2); -} - -static int32_t write_import_entry(char const* fn_name, uint8_t fn_name_len, uint8_t type_index) -{ - write_raw_u8(&op, 1); // length of module name - write_raw_u8(&op, 'e'); // module name - write_raw_u8(&op, fn_name_len); - assert(fn_name_len < 0x80); - for (uint8_t i = 0; i < fn_name_len; i++) - { - assert(fn_name[i] >= 0); - write_raw_u8(&op, fn_name[i]); - } - write_raw_u8(&op, EXT_FUNCTION); - assert(type_index < 0x80); - write_raw_u8(&op, type_index); - set_import_table_count(import_table_count + 1); - - set_import_table_size(import_table_size + 1 + 1 + 1 + fn_name_len + 1 + 1); - - return import_table_count - 1; -} - -static void write_function_section(int32_t count) -{ - write_raw_u8(&op, SC_FUNCTION); - assert(1 + count < 0x80 && count >= 0); - write_raw_u8(&op, 1 + count); // length of this section - write_raw_u8(&op, count); // count of signature indices - - for(int32_t i = 0; i < count; i++) - { - write_raw_u8(&op, FN1_TYPE_INDEX); - } -} - -static void write_export_section() -{ - write_raw_u8(&op, SC_EXPORT); - write_raw_u8(&op, 1 + 1 + 1 + 1 + 2); // size of this section - write_raw_u8(&op, 1); // count of table: just one function exported - - write_raw_u8(&op, 1); // length of exported function name - write_raw_u8(&op, 'f'); // function name - write_raw_u8(&op, EXT_FUNCTION); - - // index of the exported function - // function space starts with imports. index of last import is import count - 1 - // the last import however is a memory, so we subtract one from that - assert(import_table_count - 1 < 0x4000 && import_table_count >= 1); - write_fixed_leb16(&op, import_table_count - 1); -} - -int32_t get_fn_index(char const* fn, uint8_t fn_len, uint8_t type_index) -{ - int32_t fn_idx = get_import_index(fn, fn_len); - if (fn_idx == -1) - { - return write_import_entry(fn, fn_len, type_index); - } - return fn_idx; -} - -static void copy_code_section() -{ - append_buffer(&op, &cs); -} diff --git a/src/native/codegen/util.h b/src/native/codegen/util.h deleted file mode 100644 index f92a2250..00000000 --- a/src/native/codegen/util.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "../const.h" -#include "../log.h" - -typedef struct Buffer { - uint8_t* const start; - uint8_t* ptr; - int32_t const len; -} Buffer; - - -static void inline write_raw_u8(Buffer* buf, int32_t v) -{ - assert(v >= 0 && v < 0x100); - assert(buf->ptr < buf->start + buf->len); - *buf->ptr++ = v; -} - -static void write_leb_i32(Buffer* buf, int32_t v) -{ - // Super complex stuff. See the following: - // https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer - // http://llvm.org/doxygen/LEB128_8h_source.html#l00048 - - bool more = true; - bool negative = v < 0; - const uint32_t SIZE = 32; - while (more) - { - uint8_t byte = v & 0b1111111; // get last 7 bits - v >>= 7; // shift them away from the value - if (negative) - { - v |= ((uint32_t)~0 << (SIZE - 7)); // extend sign - } - uint8_t sign_bit = byte & (1 << 6); - if ((v == 0 && sign_bit == 0) || (v == -1 && sign_bit != 0)) - { - more = false; - } - else - { - byte |= 0b10000000; // turn on MSB - } - write_raw_u8(buf, byte); - } -} - -static void write_leb_u32(Buffer* buf, uint32_t v) -{ - do { - uint8_t byte = v & 0b1111111; // get last 7 bits - v >>= 7; // shift them away from the value - if (v != 0) - { - byte |= 0b10000000; // turn on MSB - } - write_raw_u8(buf, byte); - } while (v != 0); -} - -static void write_fixed_leb16(Buffer* buf, int32_t x) -{ - dbg_assert(x >= 0 && x < (1 << 14)); // we have 14 bits of available space in 2 bytes for leb - write_raw_u8(buf, (x & 0b1111111) | 0b10000000); - write_raw_u8(buf, x >> 7); -} - -static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, int32_t x) -{ - dbg_assert(x >= 0 && x < (1 << 14)); // we have 14 bits of available space in 2 bytes for leb - *(ptr ) = (x & 0b1111111) | 0b10000000; - *(ptr + 1) = x >> 7; -} - -static void inline write_fixed_leb32_to_ptr(uint8_t* ptr, uint32_t x) -{ - dbg_assert(x < (1 << 28)); // we have 28 bits of available space in 4 bytes for leb - *(ptr ) = (x & 0b1111111) | 0b10000000; - *(ptr + 1) = (x >> 7 & 0b1111111) | 0b10000000; - *(ptr + 2) = (x >> 14 & 0b1111111) | 0b10000000; - *(ptr + 3) = (x >> 21 & 0b1111111); -} - -static void append_buffer(Buffer *dest, Buffer *src) -{ - assert(dest->len - (dest->ptr - dest->start) >= (src->ptr - src->start)); - - uint8_t* offset = src->start; - while (offset < src->ptr) - { - write_raw_u8(dest, *offset++); - } -} diff --git a/src/native/codegen/wasm_opcodes.h b/src/native/codegen/wasm_opcodes.h deleted file mode 100644 index be015a51..00000000 --- a/src/native/codegen/wasm_opcodes.h +++ /dev/null @@ -1,213 +0,0 @@ -// https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#high-level-structure -#define WASM_VERSION 0x1 - -// Section codes -#define SC_TYPE 1 -#define SC_IMPORT 2 -#define SC_FUNCTION 3 -#define SC_TABLE 4 -#define SC_MEMORY 5 -#define SC_GLOBAL 6 -#define SC_EXPORT 7 -#define SC_START 8 -#define SC_ELEMENT 9 -#define SC_CODE 10 -#define SC_DATA 11 - -// https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#language-types -#define TYPE_I32 0x7f -#define TYPE_I64 0x7e -#define TYPE_F32 0x7d -#define TYPE_F64 0x7c -#define TYPE_ANYFUNC 0x70 -#define TYPE_FUNC 0x60 -#define TYPE_VOID_BLOCK 0x40 - -// https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#external_kind -#define EXT_FUNCTION 0 -#define EXT_TABLE 1 -#define EXT_MEMORY 2 -#define EXT_GLOBAL 3 - -// Taken from wasm2ast's source code and modified with vim magic -#define OP_UNREACHABLE 0x00 -#define OP_NOP 0x01 -#define OP_BLOCK 0x02 -#define OP_LOOP 0x03 -#define OP_IF 0x04 -#define OP_ELSE 0x05 -#define OP_TRY 0x06 -#define OP_CATCH 0x07 -#define OP_THROW 0x08 -#define OP_RETHROW 0x09 -#define OP_CATCHALL 0x0a -#define OP_END 0x0b -#define OP_BR 0x0c -#define OP_BRIF 0x0d -#define OP_BRTABLE 0x0e -#define OP_RETURN 0x0f -#define OP_CALL 0x10 -#define OP_CALLINDIRECT 0x11 -#define OP_DROP 0x1a -#define OP_SELECT 0x1b -#define OP_GETLOCAL 0x20 -#define OP_SETLOCAL 0x21 -#define OP_TEELOCAL 0x22 -#define OP_GETGLOBAL 0x23 -#define OP_SETGLOBAL 0x24 -#define OP_I32LOAD 0x28 -#define OP_I64LOAD 0x29 -#define OP_F32LOAD 0x2a -#define OP_F64LOAD 0x2b -#define OP_I32LOAD8S 0x2c -#define OP_I32LOAD8U 0x2d -#define OP_I32LOAD16S 0x2e -#define OP_I32LOAD16U 0x2f -#define OP_I64LOAD8S 0x30 -#define OP_I64LOAD8U 0x31 -#define OP_I64LOAD16S 0x32 -#define OP_I64LOAD16U 0x33 -#define OP_I64LOAD32S 0x34 -#define OP_I64LOAD32U 0x35 -#define OP_I32STORE 0x36 -#define OP_I64STORE 0x37 -#define OP_F32STORE 0x38 -#define OP_F64STORE 0x39 -#define OP_I32STORE8 0x3a -#define OP_I32STORE16 0x3b -#define OP_I64STORE8 0x3c -#define OP_I64STORE16 0x3d -#define OP_I64STORE32 0x3e -#define OP_CURRENTMEMORY 0x3f -#define OP_GROWMEMORY 0x40 -#define OP_I32CONST 0x41 -#define OP_I64CONST 0x42 -#define OP_F32CONST 0x43 -#define OP_F64CONST 0x44 -#define OP_I32EQZ 0x45 -#define OP_I32EQ 0x46 -#define OP_I32NE 0x47 -#define OP_I32LTS 0x48 -#define OP_I32LTU 0x49 -#define OP_I32GTS 0x4a -#define OP_I32GTU 0x4b -#define OP_I32LES 0x4c -#define OP_I32LEU 0x4d -#define OP_I32GES 0x4e -#define OP_I32GEU 0x4f -#define OP_I64EQZ 0x50 -#define OP_I64EQ 0x51 -#define OP_I64NE 0x52 -#define OP_I64LTS 0x53 -#define OP_I64LTU 0x54 -#define OP_I64GTS 0x55 -#define OP_I64GTU 0x56 -#define OP_I64LES 0x57 -#define OP_I64LEU 0x58 -#define OP_I64GES 0x59 -#define OP_I64GEU 0x5a -#define OP_F32EQ 0x5b -#define OP_F32NE 0x5c -#define OP_F32LT 0x5d -#define OP_F32GT 0x5e -#define OP_F32LE 0x5f -#define OP_F32GE 0x60 -#define OP_F64EQ 0x61 -#define OP_F64NE 0x62 -#define OP_F64LT 0x63 -#define OP_F64GT 0x64 -#define OP_F64LE 0x65 -#define OP_F64GE 0x66 -#define OP_I32CLZ 0x67 -#define OP_I32CTZ 0x68 -#define OP_I32POPCNT 0x69 -#define OP_I32ADD 0x6a -#define OP_I32SUB 0x6b -#define OP_I32MUL 0x6c -#define OP_I32DIVS 0x6d -#define OP_I32DIVU 0x6e -#define OP_I32REMS 0x6f -#define OP_I32REMU 0x70 -#define OP_I32AND 0x71 -#define OP_I32OR 0x72 -#define OP_I32XOR 0x73 -#define OP_I32SHL 0x74 -#define OP_I32SHRS 0x75 -#define OP_I32SHRU 0x76 -#define OP_I32ROTL 0x77 -#define OP_I32ROTR 0x78 -#define OP_I64CLZ 0x79 -#define OP_I64CTZ 0x7a -#define OP_I64POPCNT 0x7b -#define OP_I64ADD 0x7c -#define OP_I64SUB 0x7d -#define OP_I64MUL 0x7e -#define OP_I64DIVS 0x7f -#define OP_I64DIVU 0x80 -#define OP_I64REMS 0x81 -#define OP_I64REMU 0x82 -#define OP_I64AND 0x83 -#define OP_I64OR 0x84 -#define OP_I64XOR 0x85 -#define OP_I64SHL 0x86 -#define OP_I64SHRS 0x87 -#define OP_I64SHRU 0x88 -#define OP_I64ROTL 0x89 -#define OP_I64ROTR 0x8a -#define OP_F32ABS 0x8b -#define OP_F32NEG 0x8c -#define OP_F32CEIL 0x8d -#define OP_F32FLOOR 0x8e -#define OP_F32TRUNC 0x8f -#define OP_F32NEAREST 0x90 -#define OP_F32SQRT 0x91 -#define OP_F32ADD 0x92 -#define OP_F32SUB 0x93 -#define OP_F32MUL 0x94 -#define OP_F32DIV 0x95 -#define OP_F32MIN 0x96 -#define OP_F32MAX 0x97 -#define OP_F32COPYSIGN 0x98 -#define OP_F64ABS 0x99 -#define OP_F64NEG 0x9a -#define OP_F64CEIL 0x9b -#define OP_F64FLOOR 0x9c -#define OP_F64TRUNC 0x9d -#define OP_F64NEAREST 0x9e -#define OP_F64SQRT 0x9f -#define OP_F64ADD 0xa0 -#define OP_F64SUB 0xa1 -#define OP_F64MUL 0xa2 -#define OP_F64DIV 0xa3 -#define OP_F64MIN 0xa4 -#define OP_F64MAX 0xa5 -#define OP_F64COPYSIGN 0xa6 -#define OP_I32WRAPI64 0xa7 -#define OP_I32TRUNCSF32 0xa8 -#define OP_I32TRUNCUF32 0xa9 -#define OP_I32TRUNCSF64 0xaa -#define OP_I32TRUNCUF64 0xab -#define OP_I64EXTENDSI32 0xac -#define OP_I64EXTENDUI32 0xad -#define OP_I64TRUNCSF32 0xae -#define OP_I64TRUNCUF32 0xaf -#define OP_I64TRUNCSF64 0xb0 -#define OP_I64TRUNCUF64 0xb1 -#define OP_F32CONVERTSI32 0xb2 -#define OP_F32CONVERTUI32 0xb3 -#define OP_F32CONVERTSI64 0xb4 -#define OP_F32CONVERTUI64 0xb5 -#define OP_F32DEMOTEF64 0xb6 -#define OP_F64CONVERTSI32 0xb7 -#define OP_F64CONVERTUI32 0xb8 -#define OP_F64CONVERTSI64 0xb9 -#define OP_F64CONVERTUI64 0xba -#define OP_F64PROMOTEF32 0xbb -#define OP_I32REINTERPRETF32 0xbc -#define OP_I64REINTERPRETF64 0xbd -#define OP_F32REINTERPRETI32 0xbe -#define OP_F64REINTERPRETI64 0xbf - -#define MEM_NO_ALIGN 0 -#define MEM_ALIGN16 1 -#define MEM_ALIGN32 2 diff --git a/src/native/codegen/wasm_util.h b/src/native/codegen/wasm_util.h deleted file mode 100644 index 5c4b73c8..00000000 --- a/src/native/codegen/wasm_util.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include - -#include "wasm_opcodes.h" -#include "util.h" - -static void inline push_i32(Buffer* buf, int32_t v) -{ - write_raw_u8(buf, OP_I32CONST); - write_leb_i32(buf, v); -} - -static void inline push_u32(Buffer* buf, uint32_t v) -{ - write_raw_u8(buf, OP_I32CONST); - write_leb_u32(buf, v); -} - -static void inline load_aligned_u16(Buffer* buf, uint32_t addr) -{ - // doesn't cause a failure in the generated code, but it will be much slower - assert((addr & 1) == 0); - - write_raw_u8(buf, OP_I32CONST); - write_leb_i32(buf, addr); - write_raw_u8(buf, OP_I32LOAD16U); - write_raw_u8(buf, MEM_ALIGN16); - // Immediate offset - write_raw_u8(buf, 0); -} - -static void inline load_aligned_u16_from_stack(Buffer* buf, uint32_t byte_offset) -{ - write_raw_u8(buf, OP_I32LOAD16U); - write_raw_u8(buf, MEM_ALIGN16); - write_leb_u32(buf, byte_offset); -} - -static void inline load_unaligned_i32_from_stack(Buffer* buf, uint32_t byte_offset) -{ - write_raw_u8(buf, OP_I32LOAD); - write_raw_u8(buf, MEM_NO_ALIGN); - write_leb_u32(buf, byte_offset); -} - -static void inline load_aligned_i32_from_stack(Buffer* buf, uint32_t byte_offset) -{ - write_raw_u8(buf, OP_I32LOAD); - write_raw_u8(buf, MEM_ALIGN32); - write_leb_u32(buf, byte_offset); -} - -static void inline load_aligned_i32(Buffer* buf, uint32_t addr) -{ - // doesn't cause a failure in the generated code, but it will be much slower - assert((addr & 3) == 0); - - push_i32(buf, addr); - load_aligned_i32_from_stack(buf, 0); -} - -static void inline store_aligned_u16(Buffer* buf) -{ - write_raw_u8(buf, OP_I32STORE16); - write_raw_u8(buf, MEM_ALIGN16); - // Immediate offset - write_raw_u8(buf, 0); -} - -static void inline store_aligned_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32STORE); - write_raw_u8(buf, MEM_ALIGN32); - // Immediate offset - write_raw_u8(buf, 0); -} - -// XXX: Function naming should be consistent regarding both alignment and accepting an -// offset. Leaving as-is for the Rust port to cleanup -static void inline store_unaligned_i32_with_offset(Buffer* buf, uint32_t byte_offset) -{ - write_raw_u8(buf, OP_I32STORE); - write_raw_u8(buf, MEM_NO_ALIGN); - write_leb_u32(buf, byte_offset); -} - -static void inline add_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32ADD); -} - -static void inline and_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32AND); -} - -static void inline or_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32OR); -} - -static void inline xor_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32XOR); -} - -static void inline shl_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32SHL); -} - -static void inline shr_u32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32SHRU); -} - -static void inline shr_i32(Buffer* buf) -{ - write_raw_u8(buf, OP_I32SHRS); -} - -static void inline call_fn(Buffer* buf, int32_t fn_idx) -{ - write_raw_u8(buf, OP_CALL); - assert(fn_idx >= 0); - write_leb_u32(buf, fn_idx); -} - -static void inline call_fn_with_arg(Buffer* buf, int32_t fn_idx, int32_t arg0) -{ - push_i32(buf, arg0); - call_fn(buf, fn_idx); -} - diff --git a/src/native/codegen/wasmgen.h b/src/native/codegen/wasmgen.h new file mode 100644 index 00000000..f9668c2d --- /dev/null +++ b/src/native/codegen/wasmgen.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +typedef struct PackedStr { + uint64_t a; + uint64_t b; +} PackedStr; + +#define PSTR_TY uint64_t, uint64_t + +extern uint8_t* wg_new_buf(void); +extern void wg_finish(uint8_t no_of_locals_i32); +extern void wg_reset(void); +extern uint16_t wg_get_fn_idx(PSTR_TY, uint8_t fn_type); +extern void wg_include_buffer(uint8_t* buf); + +extern void wg_push_i32(uint8_t* buf, int32_t v); +extern void wg_push_u32(uint8_t* buf, uint32_t v); +extern void wg_load_aligned_u16(uint8_t* buf, uint32_t addr); +extern void wg_load_aligned_i32(uint8_t* buf, uint32_t addr); +extern void wg_store_aligned_u16(uint8_t* buf); +extern void wg_store_aligned_i32(uint8_t* buf); +extern void wg_add_i32(uint8_t* buf); +extern void wg_and_i32(uint8_t* buf); +extern void wg_or_i32(uint8_t* buf); +extern void wg_shl_i32(uint8_t* buf); +extern void wg_call_fn(uint8_t* buf, uint16_t fn_idx); +extern void wg_call_fn_with_arg(uint8_t* buf, uint16_t fn_idx, int32_t arg0); +extern void wg_eq_i32(uint8_t* buf); +extern void wg_ne_i32(uint8_t* buf); +extern void wg_le_i32(uint8_t* buf); +extern void wg_lt_i32(uint8_t* buf); +extern void wg_ge_i32(uint8_t* buf); +extern void wg_gt_i32(uint8_t* buf); +extern void wg_if_i32(uint8_t* buf); +extern void wg_block_i32(uint8_t* buf); +extern void wg_tee_local(uint8_t* buf, int32_t idx); +extern void wg_xor_i32(uint8_t* buf); +extern void wg_load_unaligned_i32_from_stack(uint8_t* buf, uint32_t byte_offset); +extern void wg_load_aligned_i32_from_stack(uint8_t* buf, uint32_t byte_offset); +extern void wg_store_unaligned_i32(uint8_t* buf, uint32_t byte_offset); +extern void wg_shr_u32(uint8_t* buf); +extern void wg_shr_i32(uint8_t* buf); +extern void wg_eqz_i32(uint8_t* buf); +extern void wg_if_void(uint8_t* buf); +extern void wg_else(uint8_t* buf); +extern void wg_loop_void(uint8_t* buf); +extern void wg_block_void(uint8_t* buf); +extern void wg_block_end(uint8_t* buf); +extern void wg_return(uint8_t* buf); +extern void wg_drop(uint8_t* buf); +extern void wg_brtable_and_cases(uint8_t* buf, int32_t cases_count); +extern void wg_br(uint8_t* buf, int32_t depth); +extern void wg_get_local(uint8_t* buf, int32_t idx); +extern void wg_set_local(uint8_t* buf, int32_t idx); +extern void wg_unreachable(uint8_t* buf); +extern void wg_increment_mem32(uint8_t* buf, int32_t addr); +extern void wg_increment_variable(uint8_t* buf, int32_t addr, int32_t n); +extern void wg_load_aligned_u16_from_stack(uint8_t* buf, uint32_t byte_offset); + +extern void wg_fn0_const(uint8_t* buf, PSTR_TY); +extern void wg_fn0_const_ret(uint8_t* buf, PSTR_TY); +extern void wg_fn1_const(uint8_t* buf, PSTR_TY, int32_t arg0); +extern void wg_fn1_const_ret(uint8_t* buf, PSTR_TY, int32_t arg0); +extern void wg_fn2_const(uint8_t* buf, PSTR_TY, int32_t arg0, int32_t arg1); +extern void wg_fn3_const(uint8_t* buf, PSTR_TY, int32_t arg0, int32_t arg1, int32_t arg2); +extern void wg_call_fn1_ret(uint8_t* buf, PSTR_TY); +extern void wg_call_fn2(uint8_t* buf, PSTR_TY); + +#undef PSTR_TY + diff --git a/src/native/cpu.c b/src/native/cpu.c index 50301b69..8b988c8e 100644 --- a/src/native/cpu.c +++ b/src/native/cpu.c @@ -5,6 +5,7 @@ #include #include "codegen/codegen.h" +#include "codegen/wasmgen.h" #include "const.h" #include "cpu.h" #include "global_pointers.h" @@ -1514,52 +1515,52 @@ static void jit_generate(uint32_t phys_addr) gen_reset(); // set state local variable to the initial state passed as the first argument - gen_get_local(GEN_LOCAL_ARG_INITIAL_STATE); - gen_set_local(GEN_LOCAL_STATE); + wg_get_local(instruction_body, GEN_LOCAL_ARG_INITIAL_STATE); + wg_set_local(instruction_body, GEN_LOCAL_STATE); // initialise max_iterations - gen_const_i32(JIT_MAX_ITERATIONS_PER_FUNCTION); - gen_set_local(GEN_LOCAL_ITERATION_COUNTER); + wg_push_i32(instruction_body, JIT_MAX_ITERATIONS_PER_FUNCTION); + wg_set_local(instruction_body, GEN_LOCAL_ITERATION_COUNTER); // main state machine loop - gen_loop_void(); + wg_loop_void(instruction_body); if(JIT_ALWAYS_USE_LOOP_SAFETY || requires_loop_limit) { profiler_stat_increment(S_COMPILE_WITH_LOOP_SAFETY); // decrement max_iterations - gen_get_local(GEN_LOCAL_ITERATION_COUNTER); - gen_const_i32(-1); - gen_add_i32(); - gen_set_local(GEN_LOCAL_ITERATION_COUNTER); + wg_get_local(instruction_body, GEN_LOCAL_ITERATION_COUNTER); + wg_push_i32(instruction_body, -1); + wg_add_i32(instruction_body); + wg_set_local(instruction_body, GEN_LOCAL_ITERATION_COUNTER); // if max_iterations == 0: return - gen_get_local(GEN_LOCAL_ITERATION_COUNTER); - gen_eqz_i32(); - gen_if_void(); - gen_return(); - gen_block_end(); + wg_get_local(instruction_body, GEN_LOCAL_ITERATION_COUNTER); + wg_eqz_i32(instruction_body); + wg_if_void(instruction_body); + wg_return(instruction_body); + wg_block_end(instruction_body); } - gen_block_void(); // for the default case + wg_block_void(instruction_body); // for the default case // generate the opening blocks for the cases for(int32_t i = 0; i < basic_blocks.length; i++) { - gen_block_void(); + wg_block_void(instruction_body); } - gen_get_local(GEN_LOCAL_STATE); - gen_brtable_and_cases(basic_blocks.length); + wg_get_local(instruction_body, GEN_LOCAL_STATE); + wg_brtable_and_cases(instruction_body, basic_blocks.length); for(int32_t i = 0; i < basic_blocks.length; i++) { // Case [i] will jump after the [i]th block, so we first generate the // block end opcode and then the code for that block - gen_block_end(); + wg_block_end(instruction_body); struct basic_block block = basic_blocks.blocks[i]; @@ -1594,10 +1595,10 @@ static void jit_generate(uint32_t phys_addr) assert(next_bb_index != -1); // set state variable to next basic block - gen_const_i32(next_bb_index); - gen_set_local(GEN_LOCAL_STATE); + wg_push_i32(instruction_body, next_bb_index); + wg_set_local(instruction_body, GEN_LOCAL_STATE); - gen_br(basic_blocks.length - i); // to the loop + wg_br(instruction_body, basic_blocks.length - i); // to the loop } else { @@ -1608,7 +1609,7 @@ static void jit_generate(uint32_t phys_addr) const char* condition = condition_functions[block.condition_index]; gen_fn0_const_ret(condition, strlen(condition)); - gen_if_void(); + wg_if_void(instruction_body); // Branch taken @@ -1627,16 +1628,16 @@ static void jit_generate(uint32_t phys_addr) &basic_blocks, block.next_block_branch_taken_addr); assert(next_basic_block_branch_taken_index != -1); - gen_const_i32(next_basic_block_branch_taken_index); - gen_set_local(GEN_LOCAL_STATE); + wg_push_i32(instruction_body, next_basic_block_branch_taken_index); + wg_set_local(instruction_body, GEN_LOCAL_STATE); } else { // Jump to different page - gen_return(); + wg_return(instruction_body); } - gen_else(); + wg_else(instruction_body); { // Branch not taken @@ -1645,13 +1646,13 @@ static void jit_generate(uint32_t phys_addr) &basic_blocks, block.next_block_addr); assert(next_basic_block_index != -1); - gen_const_i32(next_basic_block_index); - gen_set_local(GEN_LOCAL_STATE); + wg_push_i32(instruction_body, next_basic_block_index); + wg_set_local(instruction_body, GEN_LOCAL_STATE); } - gen_block_end(); + wg_block_end(instruction_body); - gen_br(basic_blocks.length - i); // to the loop + wg_br(instruction_body, basic_blocks.length - i); // to the loop } } else @@ -1660,17 +1661,17 @@ static void jit_generate(uint32_t phys_addr) assert(block.condition_index == -1); // Exit this function - gen_return(); + wg_return(instruction_body); } } - gen_block_end(); // default case - gen_unreachable(); + wg_block_end(instruction_body); // default case + wg_unreachable(instruction_body); - gen_block_end(); // loop + wg_block_end(instruction_body); // loop gen_commit_instruction_body_to_cs(); - gen_finish(GEN_NO_OF_LOCALS); + wg_finish(GEN_NO_OF_LOCALS); cached_state_flags state_flags = pack_current_state_flags(); diff --git a/src/native/instructions.c b/src/native/instructions.c index ff8967f8..484a6374 100644 --- a/src/native/instructions.c +++ b/src/native/instructions.c @@ -5,7 +5,7 @@ #include "arith.h" #include "codegen/codegen.h" -#include "codegen/wasm_util.h" +#include "codegen/wasmgen.h" #include "const.h" #include "cpu.h" #include "fpu.h" @@ -535,11 +535,11 @@ void instr32_89_mem_jit(int32_t modrm_byte, int32_t r) const int32_t value_local = GEN_LOCAL_SCRATCH1; gen_modrm_resolve(modrm_byte); - gen_set_local(address_local); + wg_set_local(instruction_body, address_local); - gen_const_i32((uint32_t) ®32s[r]); - gen_load_aligned_i32_from_stack(0); - gen_set_local(value_local); + wg_push_i32(instruction_body, (uint32_t) ®32s[r]); + wg_load_aligned_i32_from_stack(instruction_body, 0); + wg_set_local(instruction_body, value_local); gen_safe_write32(address_local, value_local); } @@ -565,12 +565,12 @@ void instr16_8B_mem_jit(int32_t modrm_byte, int32_t r) void instr32_8B_mem_jit(int32_t modrm_byte, int32_t r) { // Pseudo: reg32s[r] = safe_read32s(modrm_resolve(modrm_byte)); - gen_const_i32((int32_t) ®32s[r]); + wg_push_i32(instruction_body, (int32_t) ®32s[r]); gen_modrm_resolve(modrm_byte); gen_safe_read32(); - gen_store_aligned_i32(); + wg_store_aligned_i32(instruction_body); } void instr_8C_check_sreg(int32_t sreg) { @@ -620,22 +620,22 @@ void instr32_8D_mem(int32_t addr, int32_t r) { void instr16_8D_mem_jit(int32_t modrm_byte) { int32_t loc = (int32_t) ®16[get_reg16_index(modrm_byte >> 3 & 7)]; - push_u32(&instruction_body, loc); + wg_push_u32(instruction_body, loc); // override prefix, so modrm_resolve does not return the segment part *prefixes |= SEG_PREFIX_ZERO; gen_modrm_resolve(modrm_byte); - store_aligned_u16(&instruction_body); + wg_store_aligned_u16(instruction_body); *prefixes = 0; } void instr32_8D_mem_jit(int32_t modrm_byte) { int32_t loc = (int32_t) ®32s[modrm_byte >> 3 & 7]; - push_u32(&instruction_body, loc); + wg_push_u32(instruction_body, loc); // override prefix, so modrm_resolve does not return the segment part *prefixes |= SEG_PREFIX_ZERO; gen_modrm_resolve(modrm_byte); - store_aligned_i32(&instruction_body); + wg_store_aligned_i32(instruction_body); *prefixes = 0; } diff --git a/src/native/profiler/opstats.c b/src/native/profiler/opstats.c index 8b35d86a..074d3c5c 100644 --- a/src/native/profiler/opstats.c +++ b/src/native/profiler/opstats.c @@ -1,5 +1,6 @@ #include #include +#include #include "../codegen/codegen.h" #include "../shared.h" diff --git a/src/wasmgen/src/c_api.rs b/src/wasmgen/src/c_api.rs index 0f2d87a5..01dc2a64 100644 --- a/src/wasmgen/src/c_api.rs +++ b/src/wasmgen/src/c_api.rs @@ -59,17 +59,17 @@ mod tests { let buf1 = unsafe { new_buf().as_mut().expect("get buf1") }; let buf2 = unsafe { new_buf().as_mut().expect("get buf2") }; - gen_fn0_const_ret(buf1, pack_str("foo")); - gen_fn0_const_ret(buf1, pack_str("bar")); + wg_fn0_const_ret(buf1, pack_str("foo")); + wg_fn0_const_ret(buf1, pack_str("bar")); include_buffer(buf1); finish(2); reset(); - push_i32(buf1, 2); - gen_call_fn1_ret(buf2, pack_str("baz")); - gen_drop(buf2); + wg_push_i32(buf1, 2); + wg_call_fn1_ret(buf2, pack_str("baz")); + wg_drop(buf2); include_buffer(buf1); include_buffer(buf2); diff --git a/src/wasmgen/src/codegen.rs b/src/wasmgen/src/codegen.rs index fc97c0fd..efacd442 100644 --- a/src/wasmgen/src/codegen.rs +++ b/src/wasmgen/src/codegen.rs @@ -1,172 +1,70 @@ -use ::wasm_opcodes::*; use ::util::*; use ::wasm_util::*; use ::module_init::*; +// for functions using the global module singleton + #[no_mangle] -pub fn gen_fn0_const(buf: &mut Vec, fn_name: PackedStr) { +pub fn wg_fn0_const(buf: &mut Vec, fn_name: PackedStr) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN0_TYPE_INDEX); - call_fn(buf, fn_idx); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_fn0_const_ret(buf: &mut Vec, fn_name: PackedStr) { +pub fn wg_fn0_const_ret(buf: &mut Vec, fn_name: PackedStr) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN0_RET_TYPE_INDEX); - call_fn(buf, fn_idx); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_fn1_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32) { +pub fn wg_fn1_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN1_TYPE_INDEX); - push_i32(buf, arg0); - call_fn(buf, fn_idx); + wg_push_i32(buf, arg0); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_fn1_const_ret(buf: &mut Vec, fn_name: PackedStr, arg0: i32) { +pub fn wg_fn1_const_ret(buf: &mut Vec, fn_name: PackedStr, arg0: i32) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN1_RET_TYPE_INDEX); - push_i32(buf, arg0); - call_fn(buf, fn_idx); + wg_push_i32(buf, arg0); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_fn2_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32, arg1: i32) { +pub fn wg_fn2_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32, arg1: i32) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN2_TYPE_INDEX); - push_i32(buf, arg0); - push_i32(buf, arg1); - call_fn(buf, fn_idx); + wg_push_i32(buf, arg0); + wg_push_i32(buf, arg1); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_fn3_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32, arg1: i32, arg2: i32) { +pub fn wg_fn3_const(buf: &mut Vec, fn_name: PackedStr, arg0: i32, arg1: i32, arg2: i32) { let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN3_TYPE_INDEX); - push_i32(buf, arg0); - push_i32(buf, arg1); - push_i32(buf, arg2); - call_fn(buf, fn_idx); + wg_push_i32(buf, arg0); + wg_push_i32(buf, arg1); + wg_push_i32(buf, arg2); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_call_fn1_ret(buf: &mut Vec, fn_name: PackedStr) { +pub fn wg_call_fn1_ret(buf: &mut Vec, fn_name: PackedStr) { // generates: fn( _ ) where _ must be left on the stack before calling this, and fn returns a value let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN1_RET_TYPE_INDEX); - call_fn(buf, fn_idx); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn gen_call_fn2(buf: &mut Vec, fn_name: PackedStr) { +pub fn wg_call_fn2(buf: &mut Vec, fn_name: PackedStr) { // generates: fn( _, _ ) where _, _ must be left on the stack before calling this let m = get_module(); let fn_idx = m.get_fn_index(fn_name, FN2_TYPE_INDEX); - call_fn(buf, fn_idx); -} - -#[no_mangle] -pub fn gen_eqz_i32(buf: &mut Vec) { - buf.push(OP_I32EQZ); -} - -#[no_mangle] -pub fn gen_if_void(buf: &mut Vec) { - buf.push(OP_IF); - buf.push(TYPE_VOID_BLOCK); -} - -#[no_mangle] -pub fn gen_else(buf: &mut Vec) { - buf.push(OP_ELSE); -} - -#[no_mangle] -pub fn gen_loop_void(buf: &mut Vec) { - buf.push(OP_LOOP); - buf.push(TYPE_VOID_BLOCK); -} - -#[no_mangle] -pub fn gen_block_void(buf: &mut Vec) { - buf.push(OP_BLOCK); - buf.push(TYPE_VOID_BLOCK); -} - -#[no_mangle] -pub fn gen_block_end(buf: &mut Vec) { - buf.push(OP_END); -} - -#[no_mangle] -pub fn gen_return(buf: &mut Vec) { - buf.push(OP_RETURN); -} - -#[no_mangle] -pub fn gen_drop(buf: &mut Vec) { - buf.push(OP_DROP); -} - -// Generate a br_table where an input of [i] will branch [i]th outer block, -// where [i] is passed on the wasm stack -#[no_mangle] -pub fn gen_brtable_and_cases(buf: &mut Vec, cases_count: i32) { - assert!(cases_count >= 0); - - buf.push(OP_BRTABLE); - write_leb_u32(buf, cases_count as u32); - - for i in 0..(cases_count + 1) { - write_leb_u32(buf, i as u32); - } -} - -#[no_mangle] -pub fn gen_br(buf: &mut Vec, depth: i32) { - buf.push(OP_BR); - write_leb_i32(buf, depth); -} - -#[no_mangle] -pub fn gen_get_local(buf: &mut Vec, idx: i32) { - buf.push(OP_GETLOCAL); - write_leb_i32(buf, idx); -} - -#[no_mangle] -pub fn gen_set_local(buf: &mut Vec, idx: i32) { - buf.push(OP_SETLOCAL); - write_leb_i32(buf, idx); -} - -#[no_mangle] -pub fn gen_const_i32(buf: &mut Vec, v: i32) { - push_i32(buf, v); -} - -#[no_mangle] -pub fn gen_unreachable(buf: &mut Vec) { - buf.push(OP_UNREACHABLE); -} - -#[no_mangle] -pub fn gen_increment_mem32(buf: &mut Vec, addr: u32) { - push_i32(buf, addr as i32); - load_aligned_i32(buf, addr); - push_i32(buf, 1); - add_i32(buf); - store_aligned_i32(buf); -} - -#[no_mangle] -pub fn gen_increment_variable(buf: &mut Vec, variable_address: u32, n: i32) { - push_i32(buf, variable_address as i32); - load_aligned_i32(buf, variable_address as u32); - push_i32(buf, n); - add_i32(buf); - store_aligned_i32(buf); + wg_call_fn(buf, fn_idx); } diff --git a/src/wasmgen/src/wasm_opcodes.rs b/src/wasmgen/src/wasm_opcodes.rs index 7a10526b..c5348084 100644 --- a/src/wasmgen/src/wasm_opcodes.rs +++ b/src/wasmgen/src/wasm_opcodes.rs @@ -218,4 +218,3 @@ c!(OP_F64REINTERPRETI64, 0xbf); c!(MEM_NO_ALIGN, 0); c!(MEM_ALIGN16, 1); c!(MEM_ALIGN32, 2); -c!(MEM_IMM_OFFSET, 0); \ No newline at end of file diff --git a/src/wasmgen/src/wasm_util.rs b/src/wasmgen/src/wasm_util.rs index 297ba6a6..ee120662 100644 --- a/src/wasmgen/src/wasm_util.rs +++ b/src/wasmgen/src/wasm_util.rs @@ -2,19 +2,19 @@ use ::wasm_opcodes::*; use ::util::*; #[no_mangle] -pub fn push_i32(buf: &mut Vec, v: i32) { +pub fn wg_push_i32(buf: &mut Vec, v: i32) { buf.push(OP_I32CONST); write_leb_i32(buf, v); } #[no_mangle] -pub fn push_u32(buf: &mut Vec, v: u32) { +pub fn wg_push_u32(buf: &mut Vec, v: u32) { buf.push(OP_I32CONST); write_leb_u32(buf, v); } #[no_mangle] -pub fn load_aligned_u16(buf: &mut Vec, addr: u32) { +pub fn wg_load_aligned_u16(buf: &mut Vec, addr: u32) { // doesn't cause a failure in the generated code, but it will be much slower dbg_assert!((addr & 1) == 0); @@ -26,50 +26,50 @@ pub fn load_aligned_u16(buf: &mut Vec, addr: u32) { } #[no_mangle] -pub fn load_aligned_i32(buf: &mut Vec, addr: u32) { +pub fn wg_load_aligned_i32(buf: &mut Vec, addr: u32) { // doesn't cause a failure in the generated code, but it will be much slower dbg_assert!((addr & 3) == 0); - push_i32(buf, addr as i32); - load_aligned_i32_from_stack(buf, 0); + wg_push_i32(buf, addr as i32); + wg_load_aligned_i32_from_stack(buf, 0); } #[no_mangle] -pub fn store_aligned_u16(buf: &mut Vec) { +pub fn wg_store_aligned_u16(buf: &mut Vec) { buf.push(OP_I32STORE16); buf.push(MEM_ALIGN16); buf.push(0); // immediate offset } #[no_mangle] -pub fn store_aligned_i32(buf: &mut Vec) { +pub fn wg_store_aligned_i32(buf: &mut Vec) { buf.push(OP_I32STORE); buf.push(MEM_ALIGN32); buf.push(0); // immediate offset } #[no_mangle] -pub fn add_i32(buf: &mut Vec) { +pub fn wg_add_i32(buf: &mut Vec) { buf.push(OP_I32ADD); } #[no_mangle] -pub fn and_i32(buf: &mut Vec) { +pub fn wg_and_i32(buf: &mut Vec) { buf.push(OP_I32AND); } #[no_mangle] -pub fn or_i32(buf: &mut Vec) { +pub fn wg_or_i32(buf: &mut Vec) { buf.push(OP_I32OR); } #[no_mangle] -pub fn shl_i32(buf: &mut Vec) { +pub fn wg_shl_i32(buf: &mut Vec) { buf.push(OP_I32SHL); } #[no_mangle] -pub fn call_fn(buf: &mut Vec, fn_idx: u16) { +pub fn wg_call_fn(buf: &mut Vec, fn_idx: u16) { buf.push(OP_CALL); let buf_len = buf.len(); buf.push(0); buf.push(0); @@ -77,73 +77,73 @@ pub fn call_fn(buf: &mut Vec, fn_idx: u16) { } #[no_mangle] -pub fn call_fn_with_arg(buf: &mut Vec, fn_idx: u16, arg0: i32) { - push_i32(buf, arg0); - call_fn(buf, fn_idx); +pub fn wg_call_fn_with_arg(buf: &mut Vec, fn_idx: u16, arg0: i32) { + wg_push_i32(buf, arg0); + wg_call_fn(buf, fn_idx); } #[no_mangle] -pub fn eq_i32(buf: &mut Vec) { +pub fn wg_eq_i32(buf: &mut Vec) { buf.push(OP_I32EQ); } #[no_mangle] -pub fn ne_i32(buf: &mut Vec) { +pub fn wg_ne_i32(buf: &mut Vec) { buf.push(OP_I32NE); } #[no_mangle] -pub fn le_i32(buf: &mut Vec) { +pub fn wg_le_i32(buf: &mut Vec) { buf.push(OP_I32LES); } #[no_mangle] -pub fn lt_i32(buf: &mut Vec) { +pub fn wg_lt_i32(buf: &mut Vec) { buf.push(OP_I32LTS); } #[no_mangle] -pub fn ge_i32(buf: &mut Vec) { +pub fn wg_ge_i32(buf: &mut Vec) { buf.push(OP_I32GES); } #[no_mangle] -pub fn gt_i32(buf: &mut Vec) { +pub fn wg_gt_i32(buf: &mut Vec) { buf.push(OP_I32GTS); } #[no_mangle] -pub fn if_i32(buf: &mut Vec) { +pub fn wg_if_i32(buf: &mut Vec) { buf.push(OP_IF); buf.push(TYPE_I32); } #[no_mangle] -pub fn block_i32(buf: &mut Vec) { +pub fn wg_block_i32(buf: &mut Vec) { buf.push(OP_BLOCK); buf.push(TYPE_I32); } #[no_mangle] -pub fn tee_local(buf: &mut Vec, idx: i32) { +pub fn wg_tee_local(buf: &mut Vec, idx: i32) { buf.push(OP_TEELOCAL); write_leb_i32(buf, idx); } #[no_mangle] -pub fn xor_i32(buf: &mut Vec) { +pub fn wg_xor_i32(buf: &mut Vec) { buf.push(OP_I32XOR); } #[no_mangle] -pub fn load_unaligned_i32_from_stack(buf: &mut Vec, byte_offset: u32) { +pub fn wg_load_unaligned_i32_from_stack(buf: &mut Vec, byte_offset: u32) { buf.push(OP_I32LOAD); buf.push(MEM_NO_ALIGN); write_leb_u32(buf, byte_offset); } #[no_mangle] -pub fn load_aligned_i32_from_stack(buf: &mut Vec, byte_offset: u32) { +pub fn wg_load_aligned_i32_from_stack(buf: &mut Vec, byte_offset: u32) { buf.push(OP_I32LOAD); buf.push(MEM_ALIGN32); write_leb_u32(buf, byte_offset); @@ -152,18 +152,119 @@ pub fn load_aligned_i32_from_stack(buf: &mut Vec, byte_offset: u32) { // XXX: Function naming should be consistent regarding both alignment and accepting an // offset. Leaving as-is for the Rust port to cleanup #[no_mangle] -pub fn store_unaligned_i32(buf: &mut Vec, byte_offset: u32) { +pub fn wg_store_unaligned_i32(buf: &mut Vec, byte_offset: u32) { buf.push(OP_I32STORE); buf.push(MEM_NO_ALIGN); write_leb_u32(buf, byte_offset); } #[no_mangle] -pub fn shr_u32(buf: &mut Vec) { +pub fn wg_shr_u32(buf: &mut Vec) { buf.push(OP_I32SHRU); } #[no_mangle] -pub fn shr_i32(buf: &mut Vec) { +pub fn wg_shr_i32(buf: &mut Vec) { buf.push(OP_I32SHRS); } + +#[no_mangle] +pub fn wg_eqz_i32(buf: &mut Vec) { + buf.push(OP_I32EQZ); +} + +#[no_mangle] +pub fn wg_if_void(buf: &mut Vec) { + buf.push(OP_IF); + buf.push(TYPE_VOID_BLOCK); +} + +#[no_mangle] +pub fn wg_else(buf: &mut Vec) { + buf.push(OP_ELSE); +} + +#[no_mangle] +pub fn wg_loop_void(buf: &mut Vec) { + buf.push(OP_LOOP); + buf.push(TYPE_VOID_BLOCK); +} + +#[no_mangle] +pub fn wg_block_void(buf: &mut Vec) { + buf.push(OP_BLOCK); + buf.push(TYPE_VOID_BLOCK); +} + +#[no_mangle] +pub fn wg_block_end(buf: &mut Vec) { + buf.push(OP_END); +} + +#[no_mangle] +pub fn wg_return(buf: &mut Vec) { + buf.push(OP_RETURN); +} + +#[no_mangle] +pub fn wg_drop(buf: &mut Vec) { + buf.push(OP_DROP); +} + +// Generate a br_table where an input of [i] will branch [i]th outer block, +// where [i] is passed on the wasm stack +#[no_mangle] +pub fn wg_brtable_and_cases(buf: &mut Vec, cases_count: i32) { + assert!(cases_count >= 0); + + buf.push(OP_BRTABLE); + write_leb_u32(buf, cases_count as u32); + + for i in 0..(cases_count + 1) { + write_leb_u32(buf, i as u32); + } +} + +#[no_mangle] +pub fn wg_br(buf: &mut Vec, depth: i32) { + buf.push(OP_BR); + write_leb_i32(buf, depth); +} + +#[no_mangle] +pub fn wg_get_local(buf: &mut Vec, idx: i32) { + buf.push(OP_GETLOCAL); + write_leb_i32(buf, idx); +} + +#[no_mangle] +pub fn wg_set_local(buf: &mut Vec, idx: i32) { + buf.push(OP_SETLOCAL); + write_leb_i32(buf, idx); +} + +#[no_mangle] +pub fn wg_unreachable(buf: &mut Vec) { + buf.push(OP_UNREACHABLE); +} + +#[no_mangle] +pub fn wg_increment_mem32(buf: &mut Vec, addr: i32) { + wg_increment_variable(buf, addr, 1) +} + +#[no_mangle] +pub fn wg_increment_variable(buf: &mut Vec, addr: i32, n: i32) { + wg_push_i32(buf, addr); + wg_load_aligned_i32(buf, addr as u32); + wg_push_i32(buf, n); + wg_add_i32(buf); + wg_store_aligned_i32(buf); +} + +#[no_mangle] +pub fn wg_load_aligned_u16_from_stack(buf: &mut Vec, byte_offset: u32) { + buf.push(OP_I32LOAD16U); + buf.push(MEM_ALIGN16); + write_leb_u32(buf, byte_offset); +}