use wg_ prefix for wasmgen, add tests to makefile
This commit is contained in:
parent
6251730fee
commit
03f0da9525
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
make codegen-test
|
||||
make wasmgen-test
|
||||
|
|
14
Makefile
14
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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// everything here is copied from musl
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// 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;
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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++);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -1,135 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
72
src/native/codegen/wasmgen.h
Normal file
72
src/native/codegen/wasmgen.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../codegen/codegen.h"
|
||||
#include "../shared.h"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<u8>, fn_name: PackedStr) {
|
||||
pub fn wg_fn0_const(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr) {
|
||||
pub fn wg_fn0_const_ret(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr, arg0: i32) {
|
||||
pub fn wg_fn1_const(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr, arg0: i32) {
|
||||
pub fn wg_fn1_const_ret(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr, arg0: i32, arg1: i32) {
|
||||
pub fn wg_fn2_const(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr, arg0: i32, arg1: i32, arg2: i32) {
|
||||
pub fn wg_fn3_const(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr) {
|
||||
pub fn wg_call_fn1_ret(buf: &mut Vec<u8>, 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<u8>, fn_name: PackedStr) {
|
||||
pub fn wg_call_fn2(buf: &mut Vec<u8>, 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<u8>) {
|
||||
buf.push(OP_I32EQZ);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_if_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_IF);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_else(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_ELSE);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_loop_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_LOOP);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_block_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_BLOCK);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_block_end(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_END);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_return(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_RETURN);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_drop(buf: &mut Vec<u8>) {
|
||||
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<u8>, 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<u8>, depth: i32) {
|
||||
buf.push(OP_BR);
|
||||
write_leb_i32(buf, depth);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_get_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
buf.push(OP_GETLOCAL);
|
||||
write_leb_i32(buf, idx);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_set_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
buf.push(OP_SETLOCAL);
|
||||
write_leb_i32(buf, idx);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_const_i32(buf: &mut Vec<u8>, v: i32) {
|
||||
push_i32(buf, v);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_unreachable(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_UNREACHABLE);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gen_increment_mem32(buf: &mut Vec<u8>, 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<u8>, 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -2,19 +2,19 @@ use ::wasm_opcodes::*;
|
|||
use ::util::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn push_i32(buf: &mut Vec<u8>, v: i32) {
|
||||
pub fn wg_push_i32(buf: &mut Vec<u8>, v: i32) {
|
||||
buf.push(OP_I32CONST);
|
||||
write_leb_i32(buf, v);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn push_u32(buf: &mut Vec<u8>, v: u32) {
|
||||
pub fn wg_push_u32(buf: &mut Vec<u8>, v: u32) {
|
||||
buf.push(OP_I32CONST);
|
||||
write_leb_u32(buf, v);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn load_aligned_u16(buf: &mut Vec<u8>, addr: u32) {
|
||||
pub fn wg_load_aligned_u16(buf: &mut Vec<u8>, 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<u8>, addr: u32) {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn load_aligned_i32(buf: &mut Vec<u8>, addr: u32) {
|
||||
pub fn wg_load_aligned_i32(buf: &mut Vec<u8>, 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<u8>) {
|
||||
pub fn wg_store_aligned_u16(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32STORE16);
|
||||
buf.push(MEM_ALIGN16);
|
||||
buf.push(0); // immediate offset
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn store_aligned_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_store_aligned_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32STORE);
|
||||
buf.push(MEM_ALIGN32);
|
||||
buf.push(0); // immediate offset
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn add_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_add_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32ADD);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn and_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_and_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32AND);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn or_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_or_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32OR);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn shl_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_shl_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32SHL);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn call_fn(buf: &mut Vec<u8>, fn_idx: u16) {
|
||||
pub fn wg_call_fn(buf: &mut Vec<u8>, 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<u8>, fn_idx: u16) {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn call_fn_with_arg(buf: &mut Vec<u8>, fn_idx: u16, arg0: i32) {
|
||||
push_i32(buf, arg0);
|
||||
call_fn(buf, fn_idx);
|
||||
pub fn wg_call_fn_with_arg(buf: &mut Vec<u8>, 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<u8>) {
|
||||
pub fn wg_eq_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32EQ);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn ne_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_ne_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32NE);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn le_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_le_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32LES);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn lt_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_lt_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32LTS);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn ge_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_ge_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32GES);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn gt_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_gt_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32GTS);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn if_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_if_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_IF);
|
||||
buf.push(TYPE_I32);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn block_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_block_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_BLOCK);
|
||||
buf.push(TYPE_I32);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn tee_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
pub fn wg_tee_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
buf.push(OP_TEELOCAL);
|
||||
write_leb_i32(buf, idx);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn xor_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_xor_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32XOR);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn load_unaligned_i32_from_stack(buf: &mut Vec<u8>, byte_offset: u32) {
|
||||
pub fn wg_load_unaligned_i32_from_stack(buf: &mut Vec<u8>, 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<u8>, byte_offset: u32) {
|
||||
pub fn wg_load_aligned_i32_from_stack(buf: &mut Vec<u8>, 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<u8>, 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<u8>, byte_offset: u32) {
|
||||
pub fn wg_store_unaligned_i32(buf: &mut Vec<u8>, 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<u8>) {
|
||||
pub fn wg_shr_u32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32SHRU);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn shr_i32(buf: &mut Vec<u8>) {
|
||||
pub fn wg_shr_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32SHRS);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_eqz_i32(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_I32EQZ);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_if_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_IF);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_else(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_ELSE);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_loop_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_LOOP);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_block_void(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_BLOCK);
|
||||
buf.push(TYPE_VOID_BLOCK);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_block_end(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_END);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_return(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_RETURN);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_drop(buf: &mut Vec<u8>) {
|
||||
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<u8>, 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<u8>, depth: i32) {
|
||||
buf.push(OP_BR);
|
||||
write_leb_i32(buf, depth);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_get_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
buf.push(OP_GETLOCAL);
|
||||
write_leb_i32(buf, idx);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_set_local(buf: &mut Vec<u8>, idx: i32) {
|
||||
buf.push(OP_SETLOCAL);
|
||||
write_leb_i32(buf, idx);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_unreachable(buf: &mut Vec<u8>) {
|
||||
buf.push(OP_UNREACHABLE);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_increment_mem32(buf: &mut Vec<u8>, addr: i32) {
|
||||
wg_increment_variable(buf, addr, 1)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wg_increment_variable(buf: &mut Vec<u8>, 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<u8>, byte_offset: u32) {
|
||||
buf.push(OP_I32LOAD16U);
|
||||
buf.push(MEM_ALIGN16);
|
||||
write_leb_u32(buf, byte_offset);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue