use wg_ prefix for wasmgen, add tests to makefile

This commit is contained in:
Awal Garg 2018-06-22 18:30:25 +05:30 committed by Fabian
parent 6251730fee
commit 03f0da9525
18 changed files with 579 additions and 1413 deletions

View file

@ -1,3 +1,3 @@
#!/usr/bin/env bash
set -e
make codegen-test
make wasmgen-test

View file

@ -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

View file

@ -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];
}
}
}

View file

@ -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) &reg16[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) &reg16[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) &reg32s[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) &reg32s[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) &reg16[r_dest]);
load_aligned_u16(&instruction_body, (int32_t) &reg16[r_src]);
store_aligned_u16(&instruction_body);
wg_push_i32(instruction_body, (int32_t) &reg16[r_dest]);
wg_load_aligned_u16(instruction_body, (int32_t) &reg16[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) &reg32s[r_dest]);
load_aligned_i32(&instruction_body, (int32_t) &reg32s[r_src]);
store_aligned_i32(&instruction_body);
wg_push_i32(instruction_body, (int32_t) &reg32s[r_dest]);
wg_load_aligned_i32(instruction_body, (int32_t) &reg32s[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) &reg16[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) &reg16[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) &reg32s[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) &reg32s[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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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++);
}
}

View file

@ -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

View file

@ -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);
}

View 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

View file

@ -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();

View file

@ -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) &reg32s[r]);
gen_load_aligned_i32_from_stack(0);
gen_set_local(value_local);
wg_push_i32(instruction_body, (uint32_t) &reg32s[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) &reg32s[r]);
wg_push_i32(instruction_body, (int32_t) &reg32s[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) &reg16[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) &reg32s[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;
}

View file

@ -1,5 +1,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include "../codegen/codegen.h"
#include "../shared.h"

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}